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

Last change on this file since 16913 was 16913, checked in by simon04, 4 years ago

fix #19698 - Refactoring: make private fields final

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