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

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

fix javadoc warnings

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