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.
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.

0 Comments:
Post a Comment
<< Home