- Timestamp:
- 2016-07-22T22:21:19+02:00 (8 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/tools
- Files:
-
- 2 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/tools/bugreport/BugReport.java
r10412 r10585 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.tools.bugreport; 3 4 import java.io.PrintWriter; 5 import java.io.StringWriter; 6 import java.util.concurrent.CopyOnWriteArrayList; 7 8 import org.openstreetmap.josm.actions.ShowStatusReportAction; 3 9 4 10 /** … … 20 26 * try { 21 27 * ... your code ... 22 * } catch ( Throwablet) {28 * } catch (RuntimeException t) { 23 29 * throw BugReport.intercept(t).put("id", id).put("tag", tag); 24 30 * } … … 32 38 */ 33 39 public final class BugReport { 40 private boolean includeStatusReport = true; 41 private boolean includeData = true; 42 private boolean includeAllStackTraces; 43 private ReportedException exception; 44 private final CopyOnWriteArrayList<BugReportListener> listeners = new CopyOnWriteArrayList<>(); 45 34 46 /** 35 47 * Create a new bug report 36 48 * @param e The {@link ReportedException} to use. No more data should be added after creating the report. 37 49 */ 38 private BugReport(ReportedException e) { 39 // TODO: Use this class to create the bug report. 50 BugReport(ReportedException e) { 51 this.exception = e; 52 includeAllStackTraces = e.mayHaveConcurrentSource(); 53 } 54 55 /** 56 * Get if this report should include a system status report 57 * @return <code>true</code> to include it. 58 * @since 10585 59 */ 60 public boolean getIncludeStatusReport() { 61 return includeStatusReport; 62 } 63 64 /** 65 * Set if this report should include a system status report 66 * @param includeStatusReport if the status report should be included 67 * @since 10585 68 */ 69 public void setIncludeStatusReport(boolean includeStatusReport) { 70 this.includeStatusReport = includeStatusReport; 71 fireChange(); 72 } 73 74 /** 75 * Get if this report should include the data that was traced. 76 * @return <code>true</code> to include it. 77 * @since 10585 78 */ 79 public boolean getIncludeData() { 80 return includeData; 81 } 82 83 /** 84 * Set if this report should include the data that was traced. 85 * @param includeData if data should be included 86 * @since 10585 87 */ 88 public void setIncludeData(boolean includeData) { 89 this.includeData = includeData; 90 fireChange(); 91 } 92 93 /** 94 * Get if this report should include the stack traces for all other threads. 95 * @return <code>true</code> to include it. 96 * @since 10585 97 */ 98 public boolean getIncludeAllStackTraces() { 99 return includeAllStackTraces; 100 } 101 102 /** 103 * Sets if this report should include the stack traces for all other threads. 104 * @param includeAllStackTraces if all stack traces should be included 105 * @since 10585 106 */ 107 public void setIncludeAllStackTraces(boolean includeAllStackTraces) { 108 this.includeAllStackTraces = includeAllStackTraces; 109 fireChange(); 110 } 111 112 /** 113 * Gets the full string that should be send as error report. 114 * @return The string. 115 * @since 10585 116 */ 117 public String getReportText() { 118 StringWriter stringWriter = new StringWriter(); 119 PrintWriter out = new PrintWriter(stringWriter); 120 if (getIncludeStatusReport()) { 121 out.println(ShowStatusReportAction.getReportHeader()); 122 } 123 if (getIncludeData()) { 124 exception.printReportDataTo(out); 125 } 126 exception.printReportStackTo(out); 127 if (getIncludeAllStackTraces()) { 128 exception.printReportThreadsTo(out); 129 } 130 return stringWriter.toString().replaceAll("\r", ""); 131 } 132 133 /** 134 * Add a new change listener. 135 * @param listener The listener 136 * @since 10585 137 */ 138 public void addChangeListener(BugReportListener listener) { 139 listeners.add(listener); 140 } 141 142 /** 143 * Remove a change listener. 144 * @param listener The listener 145 * @since 10585 146 */ 147 public void removeChangeListener(BugReportListener listener) { 148 listeners.remove(listener); 149 } 150 151 private void fireChange() { 152 listeners.stream().forEach(l -> l.bugReportChanged(this)); 40 153 } 41 154 … … 75 188 return "?"; 76 189 } 190 191 /** 192 * A listener that listens to changes to this report. 193 * @author Michael Zangl 194 * @since 10585 195 */ 196 @FunctionalInterface 197 public interface BugReportListener { 198 /** 199 * Called whenever this bug report was changed, e.g. the data to be included in it. 200 * @param report The report that was changed. 201 */ 202 void bugReportChanged(BugReport report); 203 } 77 204 } -
trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportExceptionHandler.java
r10420 r10585 197 197 198 198 static JPanel buildPanel(final Throwable e) { 199 StringWriter stack = new StringWriter(); 200 PrintWriter writer = new PrintWriter(stack); 199 DebugTextDisplay textarea; 201 200 if (e instanceof ReportedException) { 202 201 // Temporary! 203 ((ReportedException) e).printReportDataTo(writer); 204 ((ReportedException) e).printReportStackTo(writer); 202 textarea = new DebugTextDisplay(new BugReport((ReportedException) e)); 205 203 } else { 206 e.printStackTrace(writer); 207 } 208 209 String text = ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString(); 210 text = text.replaceAll("\r", ""); 204 StringWriter stack = new StringWriter(); 205 e.printStackTrace(new PrintWriter(stack)); 206 textarea = new DebugTextDisplay(ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString()); 207 } 211 208 212 209 JPanel p = new JPanel(new GridBagLayout()); … … 220 217 "file a bug report in our bugtracker using this link:")), 221 218 GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 222 p.add(new JButton(new ReportBugAction(text )), GBC.eop().insets(8, 0, 0, 0));219 p.add(new JButton(new ReportBugAction(textarea.getCodeText())), GBC.eop().insets(8, 0, 0, 0)); 223 220 p.add(new JMultilineLabel( 224 221 tr("There the error information provided below should already be " + … … 230 227 "below at this URL:")), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 231 228 p.add(new UrlLabel(Main.getJOSMWebsite()+"/newticket", 2), GBC.eop().insets(8, 0, 0, 0)); 232 233 // Wiki formatting for manual copy-paste234 DebugTextDisplay textarea = new DebugTextDisplay(text);235 229 236 230 if (textarea.copyToClippboard()) { -
trunk/src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java
r10306 r10585 15 15 */ 16 16 public class DebugTextDisplay extends JScrollPane { 17 private final String text; 17 private static final String CODE_PATTERN = "{{{%n%s%n}}}"; 18 private String text; 19 private JosmTextArea textArea; 18 20 19 21 /** 20 * Creates a new text are with the fixed text21 * @ param textToDisplay The text to display.22 * Creates a new text area. 23 * @since 10585 22 24 */ 23 public DebugTextDisplay(String textToDisplay) { 24 text = "{{{\n" + Utils.strip(textToDisplay) + "\n}}}"; 25 JosmTextArea textArea = new JosmTextArea(text); 25 private DebugTextDisplay() { 26 textArea = new JosmTextArea(); 26 27 textArea.setCaretPosition(0); 27 28 textArea.setEditable(false); … … 31 32 32 33 /** 33 * Copies the debug text to the clippboard. 34 * Creates a new text area with an inital text to display 35 * @param textToDisplay The text to display. 36 */ 37 public DebugTextDisplay(String textToDisplay) { 38 this(); 39 setCodeText(textToDisplay); 40 } 41 42 /** 43 * Creates a new text area that displays the bug report data 44 * @param report The bug report data to display. 45 * @since 10585 46 */ 47 public DebugTextDisplay(BugReport report) { 48 this(); 49 setCodeText(report.getReportText()); 50 report.addChangeListener(e -> setCodeText(report.getReportText())); 51 } 52 53 /** 54 * Sets the text that should be displayed in this view. 55 * @param textToDisplay The text 56 */ 57 private void setCodeText(String textToDisplay) { 58 text = Utils.strip(textToDisplay).replaceAll("\r", ""); 59 textArea.setText(String.format(CODE_PATTERN, text)); 60 } 61 62 /** 63 * Copies the debug text to the clippboard. This includes the code tags for trac. 34 64 * @return <code>true</code> if copy was successful 35 65 */ 36 66 public boolean copyToClippboard() { 37 return Utils.copyToClipboard(text); 67 return Utils.copyToClipboard(String.format(CODE_PATTERN, text)); 68 } 69 70 /** 71 * Gets the text this are displays, without the code tag. 72 * @return The stripped text set by {@link #setCodeText(String)} 73 * @since 10585 74 */ 75 public String getCodeText() { 76 return text; 38 77 } 39 78 } -
trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java
r10306 r10585 3 3 4 4 import java.io.PrintWriter; 5 import java.io.Serializable; 6 import java.lang.reflect.InvocationTargetException; 5 7 import java.util.ArrayList; 6 8 import java.util.Arrays; 7 9 import java.util.Collection; 8 10 import java.util.Collections; 11 import java.util.ConcurrentModificationException; 9 12 import java.util.IdentityHashMap; 13 import java.util.Iterator; 10 14 import java.util.LinkedList; 11 15 import java.util.Map; 12 16 import java.util.Map.Entry; 17 import java.util.NoSuchElementException; 13 18 import java.util.Set; 14 19 15 20 import org.openstreetmap.josm.Main; 21 import org.openstreetmap.josm.tools.StreamUtils; 16 22 17 23 /** … … 25 31 */ 26 32 public class ReportedException extends RuntimeException { 33 /** 34 * How many entries of a collection to include in the bug report. 35 */ 27 36 private static final int MAX_COLLECTION_ENTRIES = 30; 28 /** 29 * 30 */ 37 31 38 private static final long serialVersionUID = 737333873766201033L; 39 32 40 /** 33 41 * We capture all stack traces on exception creation. This allows us to trace synchonization problems better. We cannot be really sure what … … 35 43 */ 36 44 private final transient Map<Thread, StackTraceElement[]> allStackTraces; 37 private final transientLinkedList<Section> sections = new LinkedList<>();45 private final LinkedList<Section> sections = new LinkedList<>(); 38 46 private final transient Thread caughtOnThread; 39 47 private final Throwable exception; … … 144 152 } 145 153 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. 154 return hasSameStackTrace(new CauseTraceIterator(), e.exception); 155 } 156 157 private static boolean hasSameStackTrace(CauseTraceIterator causeTraceIterator, Throwable e2) { 158 if (!causeTraceIterator.hasNext()) { 159 // all done. 153 160 return true; 154 161 } 155 dejaVu.add(e1); 156 162 Throwable e1 = causeTraceIterator.next(); 157 163 StackTraceElement[] t1 = e1.getStackTrace(); 158 164 StackTraceElement[] t2 = e2.getStackTrace(); … … 167 173 return false; 168 174 } else if (c1 != null) { 169 return hasSameStackTrace( dejaVu, c1, c2);175 return hasSameStackTrace(causeTraceIterator, c2); 170 176 } else { 171 177 return true; … … 228 234 } 229 235 230 private static class SectionEntry { 236 237 /** 238 * Check if this exception may be caused by a threading issue. 239 * @return <code>true</code> if it is. 240 * @since 10585 241 */ 242 public boolean mayHaveConcurrentSource() { 243 return StreamUtils.toStream(new CauseTraceIterator()) 244 .anyMatch(t -> t instanceof ConcurrentModificationException || t instanceof InvocationTargetException); 245 } 246 247 /** 248 * Iterates over the causes for this exception. Ignores cycles and aborts iteration then. 249 * @author Michal Zangl 250 * @since 10585 251 */ 252 private final class CauseTraceIterator implements Iterator<Throwable> { 253 private Throwable current = exception; 254 private final Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>()); 255 256 @Override 257 public boolean hasNext() { 258 return current != null; 259 } 260 261 @Override 262 public Throwable next() { 263 if (!hasNext()) { 264 throw new NoSuchElementException(); 265 } 266 Throwable toReturn = current; 267 advance(); 268 return toReturn; 269 } 270 271 private void advance() { 272 dejaVu.add(current); 273 current = current.getCause(); 274 if (current != null && dejaVu.contains(current)) { 275 current = null; 276 } 277 } 278 } 279 280 private static class SectionEntry implements Serializable { 281 282 private static final long serialVersionUID = 1L; 283 231 284 private final String key; 232 285 private final String value; … … 249 302 } 250 303 251 private static class Section { 304 private static class Section implements Serializable { 305 306 private static final long serialVersionUID = 1L; 252 307 253 308 private final String sectionName;
Note:
See TracChangeset
for help on using the changeset viewer.