source: josm/trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java@ 10306

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

sonar - pmd:ImmutableField + remove unused code

  • Property svn:eol-style set to native
File size: 8.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools.bugreport;
3
4import java.io.PrintWriter;
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.IdentityHashMap;
10import java.util.LinkedList;
11import java.util.Map;
12import java.util.Map.Entry;
13import java.util.Set;
14
15import org.openstreetmap.josm.Main;
16
17/**
18 * This is a special exception that cannot be directly thrown.
19 * <p>
20 * It is used to capture more information about an exception that was already thrown.
21 *
22 * @author Michael Zangl
23 * @see BugReport
24 * @since 10285
25 */
26public class ReportedException extends RuntimeException {
27 private static final int MAX_COLLECTION_ENTRIES = 30;
28 /**
29 *
30 */
31 private static final long serialVersionUID = 737333873766201033L;
32 /**
33 * We capture all stack traces on exception creation. This allows us to trace synchonization problems better. We cannot be really sure what
34 * happened but we at least see which threads
35 */
36 private final transient Map<Thread, StackTraceElement[]> allStackTraces;
37 private final transient LinkedList<Section> sections = new LinkedList<>();
38 private final transient Thread caughtOnThread;
39 private final Throwable exception;
40 private String methodWarningFrom;
41
42 ReportedException(Throwable exception) {
43 this(exception, Thread.currentThread());
44 }
45
46 ReportedException(Throwable exception, Thread caughtOnThread) {
47 super(exception);
48 this.exception = exception;
49
50 allStackTraces = Thread.getAllStackTraces();
51 this.caughtOnThread = caughtOnThread;
52 }
53
54 /**
55 * Displays a warning for this exception. The program can then continue normally. Does not block.
56 */
57 public void warn() {
58 methodWarningFrom = BugReport.getCallingMethod(2);
59 // TODO: Open the dialog.
60 }
61
62 /**
63 * Starts a new debug data section. This normally does not need to be called manually.
64 *
65 * @param sectionName
66 * The section name.
67 */
68 public void startSection(String sectionName) {
69 sections.add(new Section(sectionName));
70 }
71
72 /**
73 * Prints the captured data of this report to a {@link PrintWriter}.
74 *
75 * @param out
76 * The writer to print to.
77 */
78 public void printReportDataTo(PrintWriter out) {
79 out.println("=== REPORTED CRASH DATA ===");
80 for (Section s : sections) {
81 s.printSection(out);
82 out.println();
83 }
84
85 if (methodWarningFrom != null) {
86 out.println("Warning issued by: " + methodWarningFrom);
87 out.println();
88 }
89 }
90
91 /**
92 * Prints the stack trace of this report to a {@link PrintWriter}.
93 *
94 * @param out
95 * The writer to print to.
96 */
97 public void printReportStackTo(PrintWriter out) {
98 out.println("=== STACK TRACE ===");
99 out.println(niceThreadName(caughtOnThread));
100 getCause().printStackTrace(out);
101 out.println();
102 }
103
104 /**
105 * Prints the stack traces for other threads of this report to a {@link PrintWriter}.
106 *
107 * @param out
108 * The writer to print to.
109 */
110 public void printReportThreadsTo(PrintWriter out) {
111 out.println("=== RUNNING THREADS ===");
112 for (Entry<Thread, StackTraceElement[]> thread : allStackTraces.entrySet()) {
113 out.println(niceThreadName(thread.getKey()));
114 if (caughtOnThread.equals(thread.getKey())) {
115 out.println("Stacktrace see above.");
116 } else {
117 for (StackTraceElement e : thread.getValue()) {
118 out.println(e);
119 }
120 }
121 out.println();
122 }
123 }
124
125 private static String niceThreadName(Thread thread) {
126 String name = "Thread: " + thread.getName() + " (" + thread.getId() + ')';
127 ThreadGroup threadGroup = thread.getThreadGroup();
128 if (threadGroup != null) {
129 name += " of " + threadGroup.getName();
130 }
131 return name;
132 }
133
134 /**
135 * Checks if this exception is considered the same as an other exception. This is the case if both have the same cause and message.
136 *
137 * @param e
138 * The exception to check against.
139 * @return <code>true</code> if they are considered the same.
140 */
141 public boolean isSame(ReportedException e) {
142 if (!getMessage().equals(e.getMessage())) {
143 return false;
144 }
145
146 Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
147 return hasSameStackTrace(dejaVu, this.exception, e.exception);
148 }
149
150 private static boolean hasSameStackTrace(Set<Throwable> dejaVu, Throwable e1, Throwable e2) {
151 if (dejaVu.contains(e1)) {
152 // cycle. If it was the same until here, we assume both have that cycle.
153 return true;
154 }
155 dejaVu.add(e1);
156
157 StackTraceElement[] t1 = e1.getStackTrace();
158 StackTraceElement[] t2 = e2.getStackTrace();
159
160 if (!Arrays.equals(t1, t2)) {
161 return false;
162 }
163
164 Throwable c1 = e1.getCause();
165 Throwable c2 = e2.getCause();
166 if ((c1 == null) != (c2 == null)) {
167 return false;
168 } else if (c1 != null) {
169 return hasSameStackTrace(dejaVu, c1, c2);
170 } else {
171 return true;
172 }
173 }
174
175 /**
176 * Adds some debug values to this exception.
177 *
178 * @param key
179 * The key to add this for. Does not need to be unique but it would be nice.
180 * @param value
181 * The value.
182 * @return This exception for easy chaining.
183 */
184 public ReportedException put(String key, Object value) {
185 String string;
186 try {
187 if (value == null) {
188 string = "null";
189 } else if (value instanceof Collection) {
190 string = makeCollectionNice((Collection<?>) value);
191 } else if (value.getClass().isArray()) {
192 string = makeCollectionNice(Arrays.asList(value));
193 } else {
194 string = value.toString();
195 }
196 } catch (RuntimeException t) {
197 Main.warn(t);
198 string = "<Error calling toString()>";
199 }
200 sections.getLast().put(key, string);
201 return this;
202 }
203
204 private static String makeCollectionNice(Collection<?> value) {
205 int lines = 0;
206 StringBuilder str = new StringBuilder();
207 for (Object e : value) {
208 str.append("\n - ");
209 if (lines <= MAX_COLLECTION_ENTRIES) {
210 str.append(e);
211 } else {
212 str.append("\n ... (")
213 .append(value.size())
214 .append(" entries)");
215 break;
216 }
217 }
218 return str.toString();
219 }
220
221 @Override
222 public String toString() {
223 return new StringBuilder(48)
224 .append("CrashReportedException [on thread ")
225 .append(caughtOnThread)
226 .append(']')
227 .toString();
228 }
229
230 private static class SectionEntry {
231 private final String key;
232 private final String value;
233
234 SectionEntry(String key, String value) {
235 this.key = key;
236 this.value = value;
237 }
238
239 /**
240 * Prints this entry to the output stream in a line.
241 * @param out The stream to print to.
242 */
243 public void print(PrintWriter out) {
244 out.print(" - ");
245 out.print(key);
246 out.print(": ");
247 out.println(value);
248 }
249 }
250
251 private static class Section {
252
253 private final String sectionName;
254 private final ArrayList<SectionEntry> entries = new ArrayList<>();
255
256 Section(String sectionName) {
257 this.sectionName = sectionName;
258 }
259
260 /**
261 * Add a key/value entry to this section.
262 * @param key The key. Need not be unique.
263 * @param value The value.
264 */
265 public void put(String key, String value) {
266 entries.add(new SectionEntry(key, value));
267 }
268
269 /**
270 * Prints this section to the output stream.
271 * @param out The stream to print to.
272 */
273 public void printSection(PrintWriter out) {
274 out.println(sectionName + ':');
275 if (entries.isEmpty()) {
276 out.println("No data collected.");
277 } else {
278 for (SectionEntry e : entries) {
279 e.print(out);
280 }
281 }
282 }
283 }
284}
Note: See TracBrowser for help on using the repository browser.