Pluggable Exception Handlers
Jakob Jenkov |
Pluggable exception handlers is a technique that allows the users of a component to customize the exception handling of that component. Instead of handling, enriching, wrapping and/or logging the exception, the exception handling is delegated to an exception handler. Here is how an exception handler interface could look in code:
public interface ExceptionHandler { public void handle(Exception e, String errorMessage); }
And here is a class that uses it:
public class Component{ protected ExceptionHandler exceptionHandler = null; public void setExceptionHandler(ExceptionHandler handler){ this.exceptionHandler = handler; } public void processFile(String fileName){ FileInputStream input = null; try{ input = new FileInputStream(fileName); processStream(input); } catch (IOException e){ this.exceptionHandler.handle(e, "error processing file: " + fileName); } } protected void processStream(InputStream input) throws IOException{ //do something with the stream. } }
Notice how the catch clause in the processFile method does not wrap, rethrow or log caught IOException's. The exceptions are passed to the ExceptionHandler instance's handle() method. The exception handler can then decide what to do with it. Should the exception be ignored? Logged? Wrapped in a different exception? Or just rethrown as it is? The exception handler can decide for itself. Note however, that the exception handler cannot throw checked exceptions that were not declared in the method where the exception was caught. For instance, the processFile method does not declare any checked exceptions. Thus, the ExceptionHandler's handle() method cannot throw any checked exceptions. However, the ExceptionHandler can throw all the unchecked exceptions it wants (RuntimeException and subclasses of it).
Where to use Pluggable Exception Handlers?
Pluggable exception handlers are most effective in situations where the exceptions occurring can be handled sensibly in different ways. For instance, when validating an XML document, or an HTML form, you may not always want to stop the validation at the first validation error. In some situations you might want to continue validation to catch all validation exceptions thrown, and show them all to the user at the same time. This saves the user from having to correct an error, validate, correct error, validate over and over again. All errors can be caught and corrected in one iteration.
Implementation
There is no standard ExceptionHandler interface in Java. You will have to define one yourself, matching the needs of your component or application. Exactly how many parameters the ExceptionHandler's handle method should take depends on what the component is trying to do. As a minimum though, I would recommend taking the caught exception and a text explaining what tried to do at the time of the error.
It can be a good idea to provide a default implementation of your ExceptionHandler interface, and have your components initialized with an instance of it. This saves the user of the component from having to plugin an exception handler in the situations where the desired handling of the exception is the same as the default implementation provides. For instance, a default implementation could just rethrow the exception, or wrap it in a component specific exception.
Code Examples
Here are a few examples of exception handlers.
public class IgnoringHandler implements ExceptionHandler{ public void handle(Exception e, String message) { //do nothing, just ignore the exception } }
public class WrappingHandler implements ExceptionHandler{ public void handle(Exception e, String message){ throw new RuntimeException(message, e); } }
public class CollectingHandler implements ExceptionHandler{ List exceptions = new ArrayList(); public List getExceptions(){ return this.exceptions; } public void handle(Exception e, String message){ this.exceptions.add(e); //message is ignored here, but could have been //collected too. } }
Tweet | |
Jakob Jenkov |