source: josm/trunk/src/org/openstreetmap/josm/tools/bugreport/BugReport.java@ 10597

Last change on this file since 10597 was 10597, checked in by Don-vip, 8 years ago

see #12905 - sonar - pmd:BooleanGetMethodName - A getX() method which returns a boolean should be named isX()

  • Property svn:eol-style set to native
File size: 6.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools.bugreport;
3
4import java.io.PrintWriter;
5import java.io.StringWriter;
6import java.util.concurrent.CopyOnWriteArrayList;
7
8import org.openstreetmap.josm.actions.ShowStatusReportAction;
9
10/**
11 * This class contains utility methods to create and handle a bug report.
12 * <p>
13 * It allows you to configure the format and request to send the bug report.
14 * <p>
15 * It also contains the main entry point for all components to use the bug report system: Call {@link #intercept(Throwable)} to start handling an
16 * exception.
17 * <h1> Handling Exceptions </h1>
18 * In your code, you should add try...catch blocks for any runtime exceptions that might happen. It is fine to catch throwable there.
19 * <p>
20 * You should then add some debug information there. This can be the OSM ids that caused the error, information on the data you were working on
21 * or other local variables. Make sure that no excpetions may occur while computing the values. It is best to send plain local variables to
22 * put(...). If you need to do computations, put them into a lambda expression. Then simply throw the throwable you got from the bug report.
23 * The global exception handler will do the rest.
24 * <pre>
25 * int id = ...;
26 * String tag = "...";
27 * try {
28 * ... your code ...
29 * } catch (RuntimeException t) {
30 * throw BugReport.intercept(t).put("id", id).put("tag", () -> x.getTag());
31 * }
32 * </pre>
33 *
34 * Instead of re-throwing, you can call {@link ReportedException#warn()}. This will display a warning to the user and allow it to either report
35 * the exception or ignore it.
36 *
37 * @author Michael Zangl
38 * @since 10285
39 */
40public final class BugReport {
41 private boolean includeStatusReport = true;
42 private boolean includeData = true;
43 private boolean includeAllStackTraces;
44 private ReportedException exception;
45 private final CopyOnWriteArrayList<BugReportListener> listeners = new CopyOnWriteArrayList<>();
46
47 /**
48 * Create a new bug report
49 * @param e The {@link ReportedException} to use. No more data should be added after creating the report.
50 */
51 BugReport(ReportedException e) {
52 this.exception = e;
53 includeAllStackTraces = e.mayHaveConcurrentSource();
54 }
55
56 /**
57 * Determines if this report should include a system status report
58 * @return <code>true</code> to include it.
59 * @since 10597
60 */
61 public boolean isIncludeStatusReport() {
62 return includeStatusReport;
63 }
64
65 /**
66 * Set if this report should include a system status report
67 * @param includeStatusReport if the status report should be included
68 * @since 10585
69 */
70 public void setIncludeStatusReport(boolean includeStatusReport) {
71 this.includeStatusReport = includeStatusReport;
72 fireChange();
73 }
74
75 /**
76 * Determines if this report should include the data that was traced.
77 * @return <code>true</code> to include it.
78 * @since 10597
79 */
80 public boolean isIncludeData() {
81 return includeData;
82 }
83
84 /**
85 * Set if this report should include the data that was traced.
86 * @param includeData if data should be included
87 * @since 10585
88 */
89 public void setIncludeData(boolean includeData) {
90 this.includeData = includeData;
91 fireChange();
92 }
93
94 /**
95 * Determines if this report should include the stack traces for all other threads.
96 * @return <code>true</code> to include it.
97 * @since 10597
98 */
99 public boolean isIncludeAllStackTraces() {
100 return includeAllStackTraces;
101 }
102
103 /**
104 * Sets if this report should include the stack traces for all other threads.
105 * @param includeAllStackTraces if all stack traces should be included
106 * @since 10585
107 */
108 public void setIncludeAllStackTraces(boolean includeAllStackTraces) {
109 this.includeAllStackTraces = includeAllStackTraces;
110 fireChange();
111 }
112
113 /**
114 * Gets the full string that should be send as error report.
115 * @return The string.
116 * @since 10585
117 */
118 public String getReportText() {
119 StringWriter stringWriter = new StringWriter();
120 PrintWriter out = new PrintWriter(stringWriter);
121 if (isIncludeStatusReport()) {
122 out.println(ShowStatusReportAction.getReportHeader());
123 }
124 if (isIncludeData()) {
125 exception.printReportDataTo(out);
126 }
127 exception.printReportStackTo(out);
128 if (isIncludeAllStackTraces()) {
129 exception.printReportThreadsTo(out);
130 }
131 return stringWriter.toString().replaceAll("\r", "");
132 }
133
134 /**
135 * Add a new change listener.
136 * @param listener The listener
137 * @since 10585
138 */
139 public void addChangeListener(BugReportListener listener) {
140 listeners.add(listener);
141 }
142
143 /**
144 * Remove a change listener.
145 * @param listener The listener
146 * @since 10585
147 */
148 public void removeChangeListener(BugReportListener listener) {
149 listeners.remove(listener);
150 }
151
152 private void fireChange() {
153 listeners.stream().forEach(l -> l.bugReportChanged(this));
154 }
155
156 /**
157 * This should be called whenever you want to add more information to a given exception.
158 * @param t The throwable that was thrown.
159 * @return A {@link ReportedException} to which you can add additional information.
160 */
161 public static ReportedException intercept(Throwable t) {
162 ReportedException e;
163 if (t instanceof ReportedException) {
164 e = (ReportedException) t;
165 } else {
166 e = new ReportedException(t);
167 }
168 e.startSection(getCallingMethod(2));
169 return e;
170 }
171
172 /**
173 * Find the method that called us.
174 *
175 * @param offset
176 * How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod().
177 * @return The method name.
178 */
179 public static String getCallingMethod(int offset) {
180 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
181 String className = BugReport.class.getName();
182 for (int i = 0; i < stackTrace.length - offset; i++) {
183 StackTraceElement element = stackTrace[i];
184 if (className.equals(element.getClassName()) && "getCallingMethod".equals(element.getMethodName())) {
185 StackTraceElement toReturn = stackTrace[i + offset];
186 return toReturn.getClassName().replaceFirst(".*\\.", "") + '#' + toReturn.getMethodName();
187 }
188 }
189 return "?";
190 }
191
192 /**
193 * A listener that listens to changes to this report.
194 * @author Michael Zangl
195 * @since 10585
196 */
197 @FunctionalInterface
198 public interface BugReportListener {
199 /**
200 * Called whenever this bug report was changed, e.g. the data to be included in it.
201 * @param report The report that was changed.
202 */
203 void bugReportChanged(BugReport report);
204 }
205}
Note: See TracBrowser for help on using the repository browser.