I am Paul Wilson; Mere Complexities Limited, sells my consulting, coaching, and coding services. I am passionate about Agile, particularly Test Driven Development.


The Java UnrecoverableFailureException pattern

As an exercise, I have written up the UnrecoverableFailureException - mentioned here - as a pattern. Intent Enable client code to ignore exceptions from which it is unable to recover. Hides implementation information implicit in implementation specific checked exceptions. Motivation In many applications, certainly web applications, the only sensible response to most exceptions is to display a "there was some internal message" to a user: it is not sensible to attempt recovery from most exceptions; there is nothing the application can do, except log and inform, if the database has died. This solution is to wrap those exceptions that the client cannot be reasonably expected to handle with a single application wide UnrecoverableFailureException, distinguishing these general failures with those that the client may handle in a specific way. An UnrecoverableFailureException is used to indicate that a method is unable to fulfill its postcondition due to some external problem from which the client is unable to recover. Applicability The UnrecoverableFailureException is particularly applicable to layered applications in which the lowest layer may always potentially fail; each request to a typical web application involves interaction with the database, resulting in a potential SQLException. It is innapropriate for general purpose library APIs, where the capabilities of the client to recover from an exception are unknown. As far as I am aware, this pattern is only applicable to Java with its peculiar concept of checked exceptions. Collaborations Root cause Exception UnrecoverableFailureExceptions typically wrap a root cause exception that originates from outside the application. Thrower An UnrecoverableFailureException is thrown by code that encounters such an exception. Thrower's logger A common way of using logger APIs, such as log4j or java.util.logging, is to provide a single static logger per class. Passing the logger to the exception on construction enables the error to be logged at the same time as throwing the exception. Client The client of the Thrower propogates the UnrecoverableFailureException without catching. Last minute catch block The UnrecoverableFailureException is usually caught and handled in a common message loop, or by some other common mechanism. This might include a standard error page, in web.xml or struts.config. A typical strategy is to display a common "There has been an internal failure; please try again" to the user. Consequences Cleaner code Adopting this simple pattern takes a great deal of bother from Java exceptions. The decision to make an exception unrecoverable takes place in one place and then no longer bothers any client. I have found that this pattern greatly reduces the number of try catch blocks, as wrapped exceptions are no longer caught and wrapped again. Encapsulation An UnrecoverableFailureException does not give away implementation details; as SQLException tells the client that the supplier is using JDBC to access a database. Some information leakage from client to supplier The supplier needs to know which exceptions are of interest to the client. In practice this is seldom much of a problem: suppliers are written to service the needs of a client; if mistaken the code can be corrected. Implementation Consider
  • Make the UnrecoverableFailureException a runtime (unchecked) exception. If the client cannot handle the exception there is no point declaring it. In a typical web application nearly every method could potentially throw the UnrecoverableFailureException. If you are uncomfortable with this, no matter: you can always make the exception unchecked later.
  • Log in the exception constructor. This ensures that the error is logged once and once only.
  • UnrecoverableFailureException is too long a name for some tastes. Use a shorter title (eg FatalException) if you like.
Sample Code
public class UnrecoverableFailureException extends RuntimeException
{
public UnrecoverableFailureException(String message, Logger clientLogger)
{
   super(message);
   clientLogger.severe(message);
}

public UnrecoverableFailureException(Logger clientLogger, String message, Exception cause)
{
   super(message, cause);
   clientLogger.log(Level.SEVERE, message, cause);
}

public UnrecoverableFailureException(Logger clientLogger, Throwable cause)
{
   super(cause);
   clientLogger.log(Level.SEVERE, "unrecoverable exception", cause);
}

public UnrecoverableFailureException(Logger logger, String message)
{
   super(message);
   logger.severe(message);
}
}
Related patterns throws java.lang.Exception Rather than wrap the cause, an alternative is to simply declare a method to throw java.lang.Exception. This requires even less code and trouble. I dislike this aproach:-
  • in a typical web application all, or nearly all, requests will result in database interaction. As a result most applicaiton methods will need to declare that they throw java.lang.Exception. Being universal, the declaration conveys no useful information. Other application domains, however, may apply this pattern with more point.
  • other checked exceptions are hidden. I believe that there is still a place for checked exceptions, indicating uncheckable precondition failures. If the method also throws java.lang.Exception then this masks the checked exception, making it essentially a runtime exception.
  • it is not obvious that the decision to throw java.lang.Exception was taken with purpose: it gives the impression of sloppiness, and sloppy code attracts sloppy code.
throws application specific exception Another approach is to mandate that all exceptions thrown in the application extend a single checked application specific exception. Where I would prefer to declare an UnrecoverableFailureException, this base exception is declared to be thrown. This is effectively the same as declaring that java.lang.Exception is thrown, with the added burden of having to wrap external exceptions.

0 Comments:

Post a Comment

<< Home

subscribe here subscribe

About me

picture

Conference

RailsConf Europe 2008
Scotland on Rails Organiser

Previous blog posts

Blog archive

Other links: