package com.onaro.sanscreen.client.error; import java.beans.ExceptionListener; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import javax.swing.event.EventListenerList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.onaro.commons.util.concurrent.OnaroExecutors; /** * A central error handler provides a singleton service for handling errors in the client. It allows for the * registration of different errorHandlers for different errors and situations.
The errorHandlers are kept in a * list. When an error occur, this list of errorHandlers is scanned and each handler is requested to handle the error * until some handler chooses to handle it (this is similar to the chain of responsibility pattern). The * errorHandlers are responsible to solve threading problems such as using the swing in the wrong thread. To avoid * synchronization problems, the errors are put on a queue and a separate thread dispatches them for handling. */ public final class CentralErrorHandler implements ExceptionListener { static final Logger logger = LogManager.getLogger(CentralErrorHandler.class); /** * The single instance of this central error handler. */ private static final CentralErrorHandler singleton = new CentralErrorHandler(); /** * Gets the single instance of this central error handler. * * @return the single instance */ public static CentralErrorHandler getInstance() { return singleton; } /** * The list of errorHandlers. The first handler in the list is the last one that is scanned. */ final EventListenerList errorHandlers = new EventListenerList(); /** * If no handler agrees to handle an error the error is delivered to this handler which as a last resort. */ ErrorHandler defaultHandler; /** * Holds the errors that are waiting to be handled by the dispatching thread. */ final Queuenull
* @param context
* provides additional information that may be used by the errorHandlers to evaluate an error, a context
* should provide readable toString()
so it could be used for logging, yet it may be
* null
* @param throwable
* the error itself
*/
public static void handle(String shortDescription, Object context, Throwable throwable) {
logger.warn(shortDescription + " - " + throwable.getMessage(), throwable); //$NON-NLS-1$
ErrorEvent event = new ErrorEvent(shortDescription, throwable);
singleton.pendingErrorEvents.add(event);
}
/**
* Deliver an error to handle using a null
context. The error is put on a queue and the handling
* thread will dispatch it and the list of errorHandlers is searched backward for a handler that agrees to handle
* this error. If no handler takes the responsibility than the default handler will handle it.
*
* @param shortDescription
* a short description of the error, may be null
* @param exception
* the error itself
*/
public static void handle(String shortDescription, Throwable exception) {
handle(shortDescription, null, exception);
}
/**
* Sets the default handler which is used if no handler agreed to handle an error.
*
* @param defaultHandler
* the default handler, may be null to set an handler that only reports the error to the log
*/
public void setDefaultHandler(ErrorHandler defaultHandler) {
this.defaultHandler = defaultHandler;
}
/**
* Adds an error handler to the end of the errorHandlers list, so it'll be the first handler that is requested to
* handle an error.
*
* @param handler
* the handler to add
*/
public void addHandler(ErrorHandler handler) {
errorHandlers.add(ErrorHandler.class, handler);
}
/**
* Removes an error handler.
*
* @param handler
* the handler to remove
*/
public void removeHandler(ErrorHandler handler) {
errorHandlers.remove(ErrorHandler.class, handler);
}
/**
* The dispatcher thread is responsible to fetch errors from the queue and search for a proper handler.
*/
private class Dispatcher implements Runnable {
public void run() {
ErrorEvent event = pendingErrorEvents.poll();
while (event != null) {
handle(event);
event = pendingErrorEvents.poll();
}
}
private void handle(ErrorEvent event) {
for (ErrorHandler handler : errorHandlers.getListeners(ErrorHandler.class)) {
if (handler.handle(event.desc, event.error)) {
return;
}
}
ErrorHandler defaultHandler = CentralErrorHandler.this.defaultHandler;
if (defaultHandler != null) {
defaultHandler.handle(event.desc, event.error);
}
}
}
public void exceptionThrown(Exception e) {
if (e == null) return;
handle(null, e);
}
static final class ErrorEvent {
String desc;
Throwable error;
public ErrorEvent(String desc, Throwable error) {
this.desc = desc;
this.error = error;
}
}
}