1 package org.simantics.aeri.ui.redmine;
4 import java.io.IOException;
6 import java.util.Objects;
8 import javax.annotation.PostConstruct;
9 import javax.inject.Inject;
11 import org.eclipse.core.runtime.IConfigurationElement;
12 import org.eclipse.core.runtime.IProgressMonitor;
13 import org.eclipse.core.runtime.IStatus;
14 import org.eclipse.e4.core.contexts.IEclipseContext;
15 import org.eclipse.epp.internal.logging.aeri.ide.IServerDescriptor;
16 import org.eclipse.epp.logging.aeri.core.ILink;
17 import org.eclipse.epp.logging.aeri.core.IModelFactory;
18 import org.eclipse.epp.logging.aeri.core.IProblemState;
19 import org.eclipse.epp.logging.aeri.core.IReport;
20 import org.eclipse.epp.logging.aeri.core.IReportProcessor;
21 import org.eclipse.epp.logging.aeri.core.ISendOptions;
22 import org.eclipse.epp.logging.aeri.core.IServerConnection;
23 import org.eclipse.epp.logging.aeri.core.IStackTraceElement;
24 import org.eclipse.epp.logging.aeri.core.ISystemSettings;
25 import org.eclipse.epp.logging.aeri.core.IThrowable;
26 import org.eclipse.epp.logging.aeri.core.ProblemStatus;
27 import org.eclipse.epp.logging.aeri.core.Severity;
28 import org.eclipse.epp.logging.aeri.core.util.Links;
29 import org.eclipse.epp.logging.aeri.core.util.Reports;
30 import org.eclipse.epp.logging.aeri.core.util.Statuses;
31 import org.simantics.aeri.redmine.core.settings.RedmineAERISettings;
33 import com.google.common.util.concurrent.AbstractIdleService;
34 import com.taskadapter.redmineapi.Params;
35 import com.taskadapter.redmineapi.RedmineException;
36 import com.taskadapter.redmineapi.RedmineManager;
37 import com.taskadapter.redmineapi.RedmineManagerFactory;
38 import com.taskadapter.redmineapi.bean.Issue;
39 import com.taskadapter.redmineapi.bean.IssueFactory;
40 import com.taskadapter.redmineapi.internal.ResultsWrapper;
46 public class RedmineServerConnection extends AbstractIdleService implements IServerConnection {
48 private static final String LINE_SEPARATOR = System.lineSeparator();
50 private final IServerDescriptor server;
51 private final ISystemSettings systemSettings;
52 private final RedmineAERISettings redmineSettings;
53 private final File configurationArea;
54 private final ILink redmineUrl;
55 private final String projectId;
57 private RedmineManager redmine;
60 public RedmineServerConnection(IServerDescriptor descriptor, RedmineAERISettings redmineSettings, ISystemSettings system, File configurationArea) {
61 this.systemSettings = Objects.requireNonNull(system);
62 this.redmineSettings = Objects.requireNonNull(redmineSettings);
63 this.configurationArea = Objects.requireNonNull(configurationArea);
64 this.server = Objects.requireNonNull(descriptor);
65 this.projectId = Objects.requireNonNull(getProjectIdParameter(descriptor));
67 this.redmineUrl = Links.Link(descriptor, "org.simantics.aeri.ui.redmine.redmine.link");
70 private static String getProjectIdParameter(IServerDescriptor server) {
71 IConfigurationElement element = server.getConfigurationElement();
72 IConfigurationElement[] children = element.getChildren("parameter"); //$NON-NLS-1$
73 for (IConfigurationElement child : children) {
74 if ("org.simantics.aeri.ui.redmine.redmine.project_id".equals(child.getAttribute("key"))) {
75 return child.getAttribute("value");
82 private void e4Start() {
87 public IProblemState interested(IStatus status, IEclipseContext eventScopeContext, IProgressMonitor monitor) {
88 IProblemState res = IModelFactory.eINSTANCE.createProblemState();
90 String fingerprint = Statuses.traceIdentityHash(status);
92 Params params = new Params().add("project_id", projectId).add("f[]", "subject").add("op[subject]", "~").add("v[subject][]", fingerprint);
94 ResultsWrapper<Issue> results = redmine.getIssueManager().getIssues(params);
95 List<Issue> issues = results.getResults();
96 if (issues.isEmpty()) {
97 res.setStatus(ProblemStatus.NEW);
98 res.setMessage("New issue found! Please send a report");
100 res.setStatus(ProblemStatus.CONFIRMED);
102 StringBuilder sb = new StringBuilder();
103 sb.append("This issue has already been reported ").append(issues.size()).append(" times:").append(LINE_SEPARATOR).append(LINE_SEPARATOR);
105 for (Issue issue : issues) {
106 String bugurl = redmineUrl.getHref() + "/issues/" + issue.getId();
107 sb.append("<a href=\"" + bugurl + "\">#" + issue.getId() + "</a>").append(LINE_SEPARATOR);
109 sb.append(LINE_SEPARATOR);
110 sb.append("Either edit an existing issue listed above or send a new report.");
111 res.setMessage(sb.toString());
113 } catch (RedmineException e) {
115 res.setStatus(ProblemStatus.FAILURE);
116 res.setMessage(e.getMessage());
123 * See <a href="http://www.redmine.org/projects/redmine/wiki/Rest_IssueStatuses">Redmine REST API</a>
128 private static ProblemStatus resolveStatus(Issue issue) {
129 switch (issue.getStatusId()) {
132 return ProblemStatus.CONFIRMED;
135 return ProblemStatus.FIXED;
137 return ProblemStatus.NEEDINFO;
139 return ProblemStatus.WONTFIX;
141 return ProblemStatus.NEEDINFO;
143 return ProblemStatus.NEW;
148 public IReport transform(IStatus status, IEclipseContext context) {
149 ISendOptions options = context.get(ISendOptions.class);
150 IReport report = Reports.newReport(status);
151 report.setComment(options.getComment());
152 for (IReportProcessor processor : options.getEnabledProcessors()) {
153 processor.process(report, status, context);
155 report.setAnonymousId(options.getReporterId());
156 report.setName(options.getReporterName());
157 report.setEmail(options.getReporterEmail());
158 report.setSeverity(options.getSeverity());
163 public IProblemState submit(IStatus status, IEclipseContext context, IProgressMonitor monitor) throws IOException {
164 IReport report = transform(status, context);
165 String fingerprint = Statuses.traceIdentityHash(status);
166 IProblemState response = upload(report, monitor, fingerprint);
168 String message = response.getMessage();
169 response.setMessage(message);
173 private static void appendStackTrace(IThrowable throwable, StringBuilder builder) {
174 builder.append(String.format("%s: %s", throwable.getClassName(), throwable.getMessage())).append(LINE_SEPARATOR);
175 for (IStackTraceElement element : throwable.getStackTrace()) {
176 builder.append(String.format("\t at %s.%s(%s:%s)", element.getClassName(), element.getMethodName(),
177 element.getFileName(), element.getLineNumber())).append(LINE_SEPARATOR);
179 IThrowable cause = throwable.getCause();
181 builder.append("Caused by: ");
182 appendStackTrace(cause, builder);
186 private IProblemState upload(IReport report, IProgressMonitor monitor, String fingerprint) {
187 IProblemState result = IModelFactory.eINSTANCE.createProblemState();
188 org.eclipse.epp.logging.aeri.core.IStatus status = report.getStatus();
189 String issueName = status.getMessage();
190 String description = generateReportMessage(report);
193 Issue newIssue = IssueFactory.create(27, issueName + " [AERI=" + fingerprint+"]");
194 newIssue.setDescription(description);
195 newIssue.setPriorityId(resolvePriority(report));
197 Issue createdIssue = redmine.getIssueManager().createIssue(newIssue);
198 Integer id = createdIssue.getId();
199 Links.addLink(result, Links.REL_SUBMISSION, redmineUrl.getHref() + "/issues/" + id, "Submission URL: ");
201 String message = "View reported issue: <a href=\"" + result.getLinks().iterator().next().getValue().getHref() + "\">#" + id + "</a>";
202 result.setMessage(message);
203 } catch (RedmineException e) {
205 result.setStatus(ProblemStatus.FAILURE);
206 result.setMessage(e.getMessage());
211 private String generateReportMessage(IReport report) {
212 String comment = report.getComment();
213 org.eclipse.epp.logging.aeri.core.IStatus status = report.getStatus();
215 StringBuilder sb = new StringBuilder();
217 sb.append(comment).append("\n");
219 sb.append(LINE_SEPARATOR);
220 sb.append("h2. Product Information").append(LINE_SEPARATOR);
222 sb.append("\n<pre>\n");
223 appendProductInformation(report, sb);
224 sb.append("</pre>\n");
226 sb.append("h2. Exception stack trace").append(LINE_SEPARATOR);
227 sb.append("\n<pre>\n");
228 appendStackTrace(status.getException(), sb);
229 sb.append("</pre>\n");
231 return sb.toString();
236 * eclipseBuildId some-id
237 eclipseProduct org.simantics.desktop.product.desktopProduct
238 javaRuntimeVersion 1.8.0_111-b14
244 private void appendProductInformation(IReport report, StringBuilder sb) {
245 sb.append(LINE_SEPARATOR);
246 sb.append("eclipseProduct ").append(report.getEclipseProduct()).append(LINE_SEPARATOR);
247 sb.append("eclipseBuildId ").append(report.getEclipseBuildId()).append(LINE_SEPARATOR);
248 sb.append("javaRuntimeVersion ").append(report.getJavaRuntimeVersion()).append(LINE_SEPARATOR);
249 sb.append("osgiWs ").append(report.getOsgiWs()).append(LINE_SEPARATOR);
250 sb.append("osgiOs ").append(report.getOsgiOs()).append(LINE_SEPARATOR);
251 sb.append("osgiOsVersion ").append(report.getOsgiOsVersion()).append(LINE_SEPARATOR);
252 sb.append("osgiArch ").append(report.getOsgiArch()).append(LINE_SEPARATOR);
253 sb.append(LINE_SEPARATOR);
257 * See <a href="http://www.redmine.org/projects/redmine/wiki/Rest_Enumerations">Redmine REST API</a>
262 private Integer resolvePriority(IReport report) {
263 Severity severity = report.getSeverity();
264 switch (severity.getValue()) {
265 case Severity.CRITICAL_VALUE:
267 case Severity.MAJOR_VALUE:
269 case Severity.MINOR_VALUE:
277 public void discarded(IStatus status, IEclipseContext eventScopeContext) {
282 protected void startUp() throws Exception {
283 this.redmine = RedmineManagerFactory.createWithApiKey(redmineUrl.getHref(), redmineSettings.getApiKey());
287 protected void shutDown() throws Exception {