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 Queue pendingErrorEvents = new ConcurrentLinkedQueue(); /** * A private constructor for preventing instantiation of this class. */ private CentralErrorHandler() { // The dispatcher thread is responsible to fetch errors from the queue and search // for a proper handler OnaroExecutors.scheduleWithFixedDelay(new Dispatcher(), 100); } /** * TODO: refactor and remove the context parameter. * * Deliver an error to handle. 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 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; } } }