Friday 23 September 2011

Bringing Exceptional Order to Exception Chaos

Synopsis
In this blog post I attempt to:
  • start describing a reasonable exception handling strategy,
  • give examples of what needs to be documented in an exception handling policy,
  • and present a design that matches the described policy to get you started.
The exception handling policy revolves around a web application, but I aim to make it general enough that it can be used with any application.

Introduction
Why Do We Need An Exception Handling Strategy? We need an exception handling strategy to ensure that all errors are caught and handled in an appropriate manner which is essential to providing a good user experience; to give a counter example, how cheesed are you when a program crashes and looses your data?
Another useful benefit, is that categorising exceptions reduces the code required to handle them.
A few figures to concentrate the mind:
  • Of the five major projects I have worked on to date,
    • only one had an explicit exception policies,
    • and another two had an emergent policy based around the rules in Effective Java and the Spring framework respectively.
  • No project I have worked on has had time planned at the start to design and build an exception policy.
  • With a few exceptions, all of the programmers I have worked with do not follow the micro principles laid out in Effective Java. This policy was inspired by Brett Norris, who has subsequently left the company.
Information SourcesThere are already plenty of sources of advice, some of which are primary sources of information for this post:
There are plenty of contradictions in what different authors think makes good exception handling. I have based my primary sources around Effective Java as it is detailed and well reasoned, and Spring because when I have used it, I have found it to be elegant and functional.

Designing an Exception Policy
The problem that an exception policy is trying to solve is that if exceptions are not properly categorized then they have a tendency to be handled by the container or the programmer. In line with the principle of least pain, the framework should handle as many exceptions as possible, this means that developers do not have to handle the exception, and it is handled gracefully to give a good user experience.
Things to think about when you are designing exceptions:
  • Should the exception be a checked or unchecked exception?
    • Effective Java - Item 40: Use checked exceptions for recoverable conditions and run-time exceptions for programming errors.
  • Categorization to help the framework handle the exceptions. Who are the clients? What are the recovery strategies?
    • Effective Java - Item 41: Avoid unnecessary use of checked exceptions
    • Effective Java - Item 42: Favour the use of standard exceptions
    • Effective Java - Item 43: Throw exceptions that are appropriate to the abstraction
  • What data should be stored in exceptions for use by the user and the framework?
    • Effective Java - Item 45: Include failure-capture information in detail messages
The attached exception policy, creates a new exception for each category of exception that could be used, and when implemented chooses the correct nature of whether it is checked or not. The policy document also includes advice for when to use each type of exception. This policy may not be perfect, but it:
  • Is practical as it follows the basic rules laid out in Effective Java,
  • It follows the categorisation of exceptions as Spring does. The exceptions depart from Spring in that they assume that they will generate behaviour in the application by terminating a request etc, rather than categorising on what caused the exception. These exceptions are designed to be used in an application, not a framework, so I think this is a reasonable choice to make as the programmer should have the information to make the choice and it should speed up development by limiting the number of exception cases that have to be handled. This does not preclude the use of exception handlers and exception translation to get different behaviour.
  • Another important factor is that it is simple – there are only a few rules for developers to remember.

Implementing the Exception Policy
Following the advice above, I derived the following exceptions:

Categorisation of exceptions

Those of you familiar with Effective Java will point out that I am breaking the advice that is issued in Item 40 that you should not subclass Error. My reasoning for doing this is that if we want to terminate the application then we do not want a programmer to catch the exception, and even if they have code like the code below, it the desired behaviour will still be exhibited.

try {
    methodWhichTerminatesApplication();
}
catch(Exception e) {
    // Try to stop the application from terminating by wrapping the error.
    throw new RuntimeException(e);
}

Clearly these errors need to be used with caution.

You can then add failure capture information to these classes, by implementing an ExceptionInformation interface and making all the implemented methods delegate to an helper class – ExceptionInformationHelper. This is required because we have to inherit from one of the subclasses of Throwable to get the exceptional behaviour from the JVM.

Failure Capture Information

This results in code like the following when throwing exceptions:

String givenName = request.getGivenName();
String lastName = request.getLastName();
PostCode postcode = request.getPostCode();
throw new SoftwareException().set("givenName", givenName).set("lastName", lastName).set("postCode", postCode);

Alternatively you could use either the variable argument version of the set method or use a factory method to create the exception.

throw new SecurityException.create(request);

When modifying the exception policy to your situation, you may be tempted to introduce new classes. These may be handled by the framework, but in a sufficiently different way as to justify a separate class. When tweaking the policy you should ask yourself whether an existing exception is suitable, for example “Could a RecoverableException be used for a ValidationException, and should recoverable exception be a superclass of ValidationException?”

Implementing the Framework
The framework is going to be specific to your application, however in a typical web application you would need to do the following:
  • Log the exception. As your exceptions have a uniform interface to get information out of them, the getState() method, then the logging portion framework can handle all instances of ExceptionInformation in the same way. A useful way of logging large object graphs that these exceptions can carry in their state, is to use an Apache Commons ReflectiveToStringBuilder class. You have to be careful that lazy instantiation from objects, such as Hibernate proxies, does not generate another error in the error handling code. A custom ToStringStyle class can be used to avoid this problem.
  • Display error screens. Typically you want to have a set of error screens, one for security exceptions which gives very little information and at least a general catch all some screen for everything else. The general catch all screen gives some information that allows you to tie the response on screen to the message in the log; this should be a unique request ID.
  • Another helpful features include a screen ID on every screen so that when the user phones up to report an error, the help desk can identify exactly which screen the error is coming from without having to trawl log files. This can help solve difficulties quickly.
What do you have to do to use this policy?
You will have to:
  • Implement the design, Example
  • Write and publish your policy document. Some tweaking to the policy might be necessary, such as adding security exceptions and validation exceptions. Example
  • Ensure that developers understand and use the policy. This is best done if team leaders have buy in to the policy and can police it as it is no easy task.
What are the consequences of using this policy?
Some of the consequences are:
  • Subclassing of exceptions should become much less common as developers have exceptions that provide most of what they need. Between the standard java exceptions, and general categories already defined they should be able to cope with more than ninety percent of exceptions in a typical application. The remaining exceptions should probably subclass RecoverableException. 
  • The security of the application should be easier to reason about because of the simplified design. This should lead to better security.
  • The framework provides a common place to handle exceptions and log them. This, coupled with a uniform interface to access the exception data, allows logging to be performed by a single piece of code. This means that log messages will all be in the same format and contain useful information. This increases the chance that the team maintaining the code, will be able to identify the problem without calling the user back to get a detailed use case, reproducing the defect in a development environment, and attaching a debugger. This makes the process much faster.
I hope this has given you some ideas for handling errors in a more stream lined manner in your own application. Have fun!

No comments:

Post a Comment