aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2015-04-03 00:07:10 -0700
committerBen Gruver <bgruv@google.com>2015-04-03 00:07:10 -0700
commit5deac6ede291dbdc58f53a802aaab6ccacae0f7b (patch)
treedb83f5811866b00db9cd401276fcfbf211c45dfc
parent93ae7badcd0bf77580b9ccd6f7febe662279b4d0 (diff)
downloadsmali-5deac6ede291dbdc58f53a802aaab6ccacae0f7b.tar.gz
Add an error reporter that can create issues on github
-rw-r--r--smalidea/META-INF/plugin.xml1
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java127
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java172
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java90
4 files changed, 390 insertions, 0 deletions
diff --git a/smalidea/META-INF/plugin.xml b/smalidea/META-INF/plugin.xml
index 866056a4..cedfd4ab 100644
--- a/smalidea/META-INF/plugin.xml
+++ b/smalidea/META-INF/plugin.xml
@@ -32,6 +32,7 @@
<referencesSearch implementation="org.jf.smalidea.findUsages.SmaliClassReferenceSearcher"/>
<usageTargetProvider implementation="org.jf.smalidea.findUsages.SmaliUsageTargetProvider" />
<usageTypeProvider implementation="org.jf.smalidea.findUsages.SmaliUsageTypeProvider"/>
+ <errorHandler implementation="org.jf.smalidea.errorReporting.ErrorReporter"/>
</extensions>
<application-components>
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java
new file mode 100644
index 00000000..92aef72b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jf.smalidea.errorReporting;
+
+import com.intellij.diagnostic.IdeErrorsDialog;
+import com.intellij.diagnostic.LogMessageEx;
+import com.intellij.diagnostic.ReportMessages;
+import com.intellij.errorreport.bean.ErrorBean;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.idea.IdeaLogger;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
+import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
+import com.intellij.openapi.diagnostic.SubmittedReportInfo;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Consumer;
+
+import java.awt.*;
+import java.util.Map;
+
+/**
+ * Sends crash reports to Github.
+ *
+ * Based on the go-lang plugin's error reporter
+ * (https://github.com/dlsniper/google-go-lang-idea-plugin/commit/c451006cc9fc926ca347189951baa94f4032c5c4)
+ */
+public class ErrorReporter extends ErrorReportSubmitter {
+
+ @Override
+ public String getReportActionText() {
+ return "Report as issue on smali's github repo";
+ }
+
+ @Override
+ public boolean submit(IdeaLoggingEvent[] events, String additionalInfo, Component parentComponent,
+ final Consumer<SubmittedReportInfo> consumer) {
+ IdeaLoggingEvent event = events[0];
+ ErrorBean bean = new ErrorBean(event.getThrowable(), IdeaLogger.ourLastActionId);
+
+ final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent);
+
+ bean.setDescription(additionalInfo);
+ bean.setMessage(event.getMessage());
+
+ Throwable throwable = event.getThrowable();
+ if (throwable != null) {
+ final PluginId pluginId = IdeErrorsDialog.findPluginId(throwable);
+ if (pluginId != null) {
+ final IdeaPluginDescriptor ideaPluginDescriptor = PluginManager.getPlugin(pluginId);
+ if (ideaPluginDescriptor != null && !ideaPluginDescriptor.isBundled()) {
+ bean.setPluginName(ideaPluginDescriptor.getName());
+ bean.setPluginVersion(ideaPluginDescriptor.getVersion());
+ }
+ }
+ }
+
+ Object data = event.getData();
+
+ if (data instanceof LogMessageEx) {
+ bean.setAttachments(((LogMessageEx)data).getAttachments());
+ }
+
+ Map<String, String> reportValues = ITNProxy.createParameters(bean);
+
+ final Project project = CommonDataKeys.PROJECT.getData(dataContext);
+
+ Consumer<String> successCallback = new Consumer<String>() {
+ @Override
+ public void consume(String token) {
+ final SubmittedReportInfo reportInfo = new SubmittedReportInfo(
+ null, "Issue " + token, SubmittedReportInfo.SubmissionStatus.NEW_ISSUE);
+ consumer.consume(reportInfo);
+
+ ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
+ "Submitted",
+ NotificationType.INFORMATION,
+ null).setImportant(false).notify(project);
+ }
+ };
+
+ Consumer<Exception> errorCallback = new Consumer<Exception>() {
+ @Override
+ public void consume(Exception e) {
+ String message = String.format("<html>There was an error while creating a GitHub issue: %s<br>" +
+ "Please consider manually creating an issue on the " +
+ "<a href=\"https://github.com/JesusFreke/smali/issues\">Smali Issue Tracker</a></html>",
+ e.getMessage());
+ ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
+ message,
+ NotificationType.ERROR,
+ NotificationListener.URL_OPENING_LISTENER).setImportant(false).notify(project);
+ }
+ };
+
+ GithubFeedbackTask task = new GithubFeedbackTask(project, "Submitting error report", true, reportValues,
+ successCallback, errorCallback);
+
+ if (project == null) {
+ task.run(new EmptyProgressIndicator());
+ } else {
+ ProgressManager.getInstance().run(task);
+ }
+ return true;
+ }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java
new file mode 100644
index 00000000..28149494
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jf.smalidea.errorReporting;
+
+import com.google.common.io.CharStreams;
+import com.google.gson.Gson;
+import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Consumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class GithubFeedbackTask extends Task.Backgroundable {
+ private final Consumer<String> myCallback;
+ private final Consumer<Exception> myErrorCallback;
+ private final Map<String, String> myParams;
+
+ public GithubFeedbackTask(@Nullable Project project,
+ @NotNull String title,
+ boolean canBeCancelled,
+ Map<String, String> params,
+ final Consumer<String> callback,
+ final Consumer<Exception> errorCallback) {
+ super(project, title, canBeCancelled);
+
+ myParams = params;
+ myCallback = callback;
+ myErrorCallback = errorCallback;
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ indicator.setIndeterminate(true);
+ try {
+ String token = sendFeedback(myParams);
+ myCallback.consume(token);
+ }
+ catch (Exception e) {
+ myErrorCallback.consume(e);
+ }
+ }
+
+ private static String getToken() {
+ InputStream stream = GithubFeedbackTask.class.getClassLoader().getResourceAsStream("token");
+ if (stream == null) {
+ return null;
+ }
+ try {
+ return CharStreams.toString(new InputStreamReader(stream, "UTF-8"));
+ } catch (IOException ex) {
+ return null;
+ }
+ }
+
+ public static String sendFeedback(Map<String, String> environmentDetails) throws IOException {
+ String url = "https://api.github.com/repos/JesusFreke/smali/issues";
+ String userAgent = "smalidea plugin";
+
+ IdeaPluginDescriptorImpl pluginDescriptor =
+ (IdeaPluginDescriptorImpl) PluginManager.getPlugin(PluginId.getId("org.jf.smalidea"));
+
+ if (pluginDescriptor != null) {
+ String name = pluginDescriptor.getName();
+ String version = pluginDescriptor.getVersion();
+ userAgent = name + " (" + version + ")";
+ }
+
+ HttpURLConnection httpURLConnection = connect(url);
+ httpURLConnection.setDoOutput(true);
+ httpURLConnection.setRequestMethod("POST");
+ httpURLConnection.setRequestProperty("User-Agent", userAgent);
+ httpURLConnection.setRequestProperty("Content-Type", "application/json");
+
+ String token = getToken();
+ if (token != null) {
+ httpURLConnection.setRequestProperty("Authorization", "token " + token);
+ }
+ OutputStream outputStream = httpURLConnection.getOutputStream();
+
+ try {
+ outputStream.write(convertToGithubIssueFormat(environmentDetails));
+ } finally {
+ outputStream.close();
+ }
+
+ int responseCode = httpURLConnection.getResponseCode();
+ if (responseCode != 201) {
+ throw new RuntimeException("Expected HTTP_CREATED (201), obtained " + responseCode);
+ }
+
+ return Long.toString(System.currentTimeMillis());
+ }
+
+ private static byte[] convertToGithubIssueFormat(Map<String, String> environmentDetails) {
+ LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(5);
+ result.put("title", "[auto-generated] Crash in plugin");
+ result.put("body", generateGithubIssueBody(environmentDetails));
+
+ return ((new Gson()).toJson(result)).getBytes(Charset.forName("UTF-8"));
+ }
+
+ private static String generateGithubIssueBody(Map<String, String> body) {
+ String errorDescription = body.get("error.description");
+ if (errorDescription == null) {
+ errorDescription = "";
+ }
+ body.remove("error.description");
+
+ String errorMessage = body.get("error.message");
+ if (errorMessage == null || errorMessage.isEmpty()) {
+ errorMessage = "invalid error";
+ }
+ body.remove("error.message");
+
+ String stackTrace = body.get("error.stacktrace");
+ if (stackTrace == null || stackTrace.isEmpty()) {
+ stackTrace = "invalid stacktrace";
+ }
+ body.remove("error.stacktrace");
+
+ String result = "";
+
+ if (!errorDescription.isEmpty()) {
+ result += errorDescription + "\n\n";
+ }
+
+ for (Map.Entry<String, String> entry : body.entrySet()) {
+ result += entry.getKey() + ": " + entry.getValue() + "\n";
+ }
+
+ result += "\n```\n" + stackTrace + "\n```\n";
+
+ result += "\n```\n" + errorMessage + "\n```";
+
+ return result;
+ }
+
+ private static HttpURLConnection connect(String url) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) ((new URL(url)).openConnection());
+ connection.setConnectTimeout(5000);
+ connection.setReadTimeout(5000);
+ return connection;
+ }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java
new file mode 100644
index 00000000..5bf93e32
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jf.smalidea.errorReporting;
+
+import com.intellij.errorreport.bean.ErrorBean;
+import com.intellij.idea.IdeaLogger;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ex.ApplicationInfoEx;
+import com.intellij.openapi.diagnostic.Attachment;
+import com.intellij.openapi.updateSettings.impl.UpdateSettings;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.SystemProperties;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ * @author stathik
+ * @since Aug 4, 2003
+ */
+public class ITNProxy {
+
+ public static Map<String, String> createParameters(ErrorBean error) {
+ Map<String, String> params = ContainerUtil.newLinkedHashMap(40);
+
+ params.put("protocol.version", "1");
+
+ params.put("os.name", SystemProperties.getOsName());
+ params.put("java.version", SystemProperties.getJavaVersion());
+ params.put("java.vm.vendor", SystemProperties.getJavaVmVendor());
+
+ ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
+ ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance();
+ Application application = ApplicationManager.getApplication();
+ params.put("app.name", namesInfo.getProductName());
+ params.put("app.name.full", namesInfo.getFullProductName());
+ params.put("app.name.version", appInfo.getVersionName());
+ params.put("app.eap", Boolean.toString(appInfo.isEAP()));
+ params.put("app.internal", Boolean.toString(application.isInternal()));
+ params.put("app.build", appInfo.getBuild().asString());
+ params.put("app.version.major", appInfo.getMajorVersion());
+ params.put("app.version.minor", appInfo.getMinorVersion());
+ params.put("app.build.date", format(appInfo.getBuildDate()));
+ params.put("app.build.date.release", format(appInfo.getMajorReleaseBuildDate()));
+ params.put("app.compilation.timestamp", IdeaLogger.getOurCompilationTimestamp());
+
+ UpdateSettings updateSettings = UpdateSettings.getInstance();
+ params.put("update.channel.status", updateSettings.getSelectedChannelStatus().getCode());
+ params.put("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ","));
+
+ params.put("plugin.name", error.getPluginName());
+ params.put("plugin.version", error.getPluginVersion());
+
+ params.put("last.action", error.getLastAction());
+ params.put("previous.exception", error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException()));
+
+ params.put("error.message", error.getMessage());
+ params.put("error.stacktrace", error.getStackTrace());
+ params.put("error.description", error.getDescription());
+
+ params.put("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId()));
+
+ for (Attachment attachment : error.getAttachments()) {
+ params.put("attachment.name", attachment.getName());
+ params.put("attachment.value", attachment.getEncodedBytes());
+ }
+
+ return params;
+ }
+
+ private static String format(Calendar calendar) {
+ return calendar == null ? null : Long.toString(calendar.getTime().getTime());
+ }
+}