source: josm/trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportQueue.java@ 12770

Last change on this file since 12770 was 12770, checked in by bastiK, 7 years ago

see #15229 - remove GUI references from BugReport and BugReportQueue

signature of BugReport#getReportText changed without deprecation
(probably not used by any external plugin)

  • Property svn:eol-style set to native
File size: 5.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools.bugreport;
3
4import java.util.ArrayList;
5import java.util.LinkedList;
6import java.util.concurrent.CopyOnWriteArrayList;
7import java.util.function.Predicate;
8
9import org.openstreetmap.josm.tools.Logging;
10
11/**
12 * This class handles the display of the bug report dialog.
13 * @author Michael Zangl
14 * @since 10819
15 */
16public class BugReportQueue {
17
18 private static final BugReportQueue INSTANCE = new BugReportQueue();
19
20 public static final BugReportHandler FALLBACK_BUGREPORT_HANDLER = (e, index) -> {
21 e.printStackTrace();
22 return BugReportQueue.SuppressionMode.NONE;
23 };
24
25 private final LinkedList<ReportedException> reportsToDisplay = new LinkedList<>();
26 private boolean suppressAllMessages;
27 private final ArrayList<ReportedException> suppressFor = new ArrayList<>();
28 private Thread displayThread;
29 private BugReportHandler bugReportHandler = FALLBACK_BUGREPORT_HANDLER;
30 private final CopyOnWriteArrayList<Predicate<ReportedException>> handlers = new CopyOnWriteArrayList<>();
31 private int displayedErrors;
32
33 private boolean inReportDialog;
34
35 /**
36 * Class that handles reporting a bug to the user.
37 */
38 public interface BugReportHandler {
39 /**
40 * Handle the bug report for a given exception
41 * @param e The exception to display
42 * @param exceptionCounter A counter of how many exceptions have already been worked on
43 * @return The new suppression status
44 */
45 SuppressionMode handle(ReportedException e, int exceptionCounter);
46 }
47
48 /**
49 * The suppression mode that should be used after the dialog was closed.
50 */
51 public enum SuppressionMode {
52 /**
53 * Suppress no dialogs.
54 */
55 NONE,
56 /**
57 * Suppress only the ones that are for the same error
58 */
59 SAME,
60 /**
61 * Suppress all report dialogs
62 */
63 ALL
64 }
65
66 /**
67 * Submit a new error to be displayed
68 * @param report The error to display
69 */
70 public synchronized void submit(ReportedException report) {
71 Logging.logWithStackTrace(Logging.LEVEL_ERROR, "Handled by bug report queue", report.getCause());
72 if (suppressAllMessages || suppressFor.stream().anyMatch(report::isSame)) {
73 Logging.info("User requested to skip error " + report);
74 } else if (reportsToDisplay.size() > 100 || reportsToDisplay.stream().filter(report::isSame).count() >= 10) {
75 Logging.warn("Too many errors. Dropping " + report);
76 } else {
77 reportsToDisplay.add(report);
78 if (displayThread == null) {
79 displayThread = new Thread(new BugReportDisplayRunnable(), "bug-report-display");
80 displayThread.start();
81 }
82 notifyAll();
83 }
84 }
85
86 private class BugReportDisplayRunnable implements Runnable {
87
88 private volatile boolean running = true;
89
90 @Override
91 public void run() {
92 try {
93 while (running) {
94 ReportedException e = getNext();
95 handleDialogResult(e, displayFor(e));
96 }
97 } catch (InterruptedException e) {
98 displayFor(BugReport.intercept(e));
99 Thread.currentThread().interrupt();
100 }
101 }
102 }
103
104 private synchronized void handleDialogResult(ReportedException e, SuppressionMode suppress) {
105 if (suppress == SuppressionMode.ALL) {
106 suppressAllMessages = true;
107 reportsToDisplay.clear();
108 } else if (suppress == SuppressionMode.SAME) {
109 suppressFor.add(e);
110 reportsToDisplay.removeIf(e::isSame);
111 }
112 displayedErrors++;
113 inReportDialog = false;
114 }
115
116 private synchronized ReportedException getNext() throws InterruptedException {
117 while (reportsToDisplay.isEmpty()) {
118 wait();
119 }
120 inReportDialog = true;
121 return reportsToDisplay.removeFirst();
122 }
123
124 private SuppressionMode displayFor(ReportedException e) {
125 if (handlers.stream().anyMatch(p -> p.test(e))) {
126 Logging.trace("Intercepted by handler.");
127 return SuppressionMode.NONE;
128 }
129 return bugReportHandler.handle(e, getDisplayedErrors());
130 }
131
132 private synchronized int getDisplayedErrors() {
133 return displayedErrors;
134 }
135
136 /**
137 * Check if the dialog is shown. Should only be used for e.g. debugging.
138 * @return <code>true</code> if the exception handler is still showing the exception to the user.
139 */
140 public synchronized boolean exceptionHandlingInProgress() {
141 return !reportsToDisplay.isEmpty() || inReportDialog;
142 }
143
144 public void setBugReportHandler(BugReportHandler bugReportHandler) {
145 this.bugReportHandler = bugReportHandler;
146 }
147
148 /**
149 * Allows you to peek or even intercept the bug reports.
150 * @param handler The handler. It can return false to stop all further handling of the exception.
151 * @since 10886
152 */
153 public void addBugReportHandler(Predicate<ReportedException> handler) {
154 handlers.add(handler);
155 }
156
157 /**
158 * Gets the global bug report queue
159 * @return The queue
160 * @since 10886
161 */
162 public static BugReportQueue getInstance() {
163 return INSTANCE;
164 }
165}
Note: See TracBrowser for help on using the repository browser.