Exceptional Java - Design the failure case - Part 2
A number of simple principles can create order and reduce complexity. And the result can be real software quality.
Read the first part of this post…
Design with exceptions
When designing exception handling there are two kinds of situations. Utility API designers have to think about how their code will be used. Application designers have to understand the API behavior with respect to exceptions and to handle those exceptions in a consistent and meaningful way paying attention to all the above enumerated concerns. Framework designers are usually in the middle since they use other APIs and frameworks and provide their own API for other clients.
API exception creation design principles
As an API designer you should strive to achieve three main goals:
- Preserve encapsulation - this means you should not throw exceptions that reveal API implementation details. Sometimes it is fine to throw standard Java exceptions from your API. If I am implementing a file management library that builds on Java IO classes it is fine to propagate IOException instances. On the other hand if I implement a data management API that happens to use various data sources like files, databases and sockets then probably I want to hide all the implementation details and provide my own exception classes.
- Design for usability - do not throw a huge number of unrelated exceptions that will force the clients to have multiple catch blocks doing the same thing. In the ideal case you should have for each API or framework a top level exception class defined (possibly abstract). All the exceptions thrown from the public interfaces of the API should be derived directly or indirectly from the top level exception class. This way encapsulation is preserved, extensibility insured and the client code can decide at what level of detail to treat the API’s exceptions.
- Offer detailed context information - even if you throw domain specific exceptions you should embed the low level cause for logging purposes at higher level. It is really important for support/maintenance/debugging/repair to record failures and as much context information as possible.
Application exception handling design principles
- The application is responsible for configuring the frameworks it uses and then for properly reacting to their failure cases. The application has to have a consistent way of dealing with exceptions. This can be done by smoothing the differences between various frameworks and APIs in exception generation as early as possible.
- The application provides standard policies for catching, re-throwing and logging exceptions.
- The application is responsible for keeping all the frameworks in a consistent state after failure.
Throw principles
- Use unchecked exceptions to signal bugs
- Use checked exceptions to signal situations from which one can recover, domain specific failures. Keep in mind that what domain specific means changes with the code layer. For the JDBC driver SQLException is a domain specific exception but for a financial application this is a low level exception while maybe NotEnoughMoneyException is a domain specific exception.
Catch principles
- Don’t cast your net too wide: don’t catch Exception or Throwable to avoid swallowing RuntimeException
- Use finally and think about the need to know when you are in a success or failure situations
- Don’t use exceptions for flow control, keep them for exceptional situations
- Don’t suppress or ignore exceptions with empty catch blocks. If you really have to do this then comment the empty block with reasons for your decision
- Log exceptions once at highest possible level or when context details are needed and they can be lost
- Think about implications of exceptions thrown in catch and finally blocks by the cleanup code
When an exception is thrown in a catch or finally block and it is not handled there then the original exception is replaced and the new exception is raised. This might be fine or not for the client code handling the exception. One has to think about this problem because it can change the execution path in the handling code.
Dangers and traps - 2
Related to this last point here is a piece of code you can play with for understanding:
package com.littletutorials.ex;
import java.util.*;
public class ExceptionLost
{
private static final int NO_EXCEPTION = 0;
private static final int RT_EXCEPTION = 1;
private static final int EXCEPTION = 2;
private static final int ERROR = 3;
private static final int THROWABLE = 4;
private static final String EXCEPTION_IN_JOB = "EXCEPTION_IN_JOB";
private static final String EXCEPTION_IN_CLEAN_FINALLY = "EXCEPTION_IN_CLEAN_FINALLY";
private static final String EXCEPTION_IN_CLEAN_EX = "EXCEPTION_IN_CLEAN_EX";
private static final String EXCEPTION_IN_CLEAN_RTEX = "EXCEPTION_IN_CLEAN_RTEX";
private Map<String, Integer> scenario = new HashMap<String, Integer>();
private void setScenario(Integer jobEx, Integer clRtEx, Integer clEx, Integer clFin)
{
scenario.put(EXCEPTION_IN_JOB, jobEx);
scenario.put(EXCEPTION_IN_CLEAN_RTEX, clRtEx);
scenario.put(EXCEPTION_IN_CLEAN_EX, clEx);
scenario.put(EXCEPTION_IN_CLEAN_FINALLY, clFin);
}
public void execute() throws Throwable
{
Throwable t = null;
try
{
job(scenario.get(EXCEPTION_IN_JOB));
}
catch (RuntimeException e)
{
t = e;
System.out.println("RuntimeException " +
e.getClass().getName() + " caught: " + e.getMessage());
e.printStackTrace(System.out);
cleanupRtEx(scenario.get(EXCEPTION_IN_CLEAN_RTEX));
System.out.println("RuntimeException cleanup done!");
}
catch (Exception e)
{
t = e;
System.out.println("Exception " +
e.getClass().getName() + " caught: " + e.getMessage());
e.printStackTrace(System.out);
cleanupEx(scenario.get(EXCEPTION_IN_CLEAN_EX));
System.out.println("Exception cleanup done!");
}
finally
{
System.out.println("Executing finally block");
if (t != null)
{
System.out.println("Finally after " +
t.getClass().getName() + " caught: " + t.getMessage());
}
cleanupFinally(scenario.get(EXCEPTION_IN_CLEAN_FINALLY));
System.out.println("Finally cleanup done!");
}
}
private void job(int type) throws Throwable
{
final String methodName = "job()";
throwException(type, methodName);
}
private void cleanupRtEx(int type) throws Throwable
{
final String methodName = "cleanupRtEx()";
throwException(type, methodName);
}
private void cleanupEx(int type) throws Throwable
{
final String methodName = "cleanupEx()";
throwException(type, methodName);
}
private void cleanupFinally(int type) throws Throwable
{
final String methodName = "cleanupFinally()";
throwException(type, methodName);
}
private void throwException(int type, final String methodName) throws Throwable
{
switch (type)
{
case NO_EXCEPTION:
break;
case RT_EXCEPTION:
throw new RuntimeException("Unchecked exception from " + methodName);
case EXCEPTION:
throw new Exception("Checked exception from " + methodName);
case ERROR:
throw new Error("Error from " + methodName);
case THROWABLE:
throw new Throwable("Throwable from " + methodName);
}
}
public static void main(String[] args) throws Throwable
{
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
System.out.println("Uncaught exception");
System.out.println(
"Thread: " + t.getName() +
" Throwable " + e.getClass().getName() +
" caught: " + e.getMessage());
e.printStackTrace(System.out);
System.out.println("Uncaught exception");
}
});
ExceptionLost t = new ExceptionLost();
t.setScenario(NO_EXCEPTION, NO_EXCEPTION, NO_EXCEPTION, NO_EXCEPTION);
t.execute();
}
}
This class allows you to create various scenarios where exceptions are thrown from cleanup code and watch the effects. Changing the parameters for the call on line 126 allows you to specify if and what exceptions are thrown from the various methods of the class. Understanding this behavior should provide more insight into how delicate exception handling design is, beyond the checked vs. unchecked debate.
The education factor
Creating quality software is more a result of how good and motivated the programmers are then a result of how good the process is. Good people can make any process work. A good process can only ensure that people will do consistently as good a job as they are capable. If we hold these affirmations true, then the only way to add value is to increase the programmers’ ability to do a good job. The best way of doing this is through education. Senior team members should educate and monitor more junior people. Most programmers focus 99% of their attention on the success path and ignore the failure paths. This has various reasons:
- We prefer to think positively
- We are optimistic (that should never happen)
- It is more fun to solve problems then to write guarded code
- Schools and books always focus on how to do things not on how to not allow things to go wrong
- People get rewarded for solving problems fast and then rewarded again when they solve failures. Usually people don’t get rewarded for taking longer to solve the problems right and write bug free code. Bug free code also means programmers miss the chance to be heroes later when everything crashes.
It takes dedication to educate people and make them take pride in a job well done. But this is the one true solution to what looks like a global software quality crisis. Make education part of the software development process.
This post is part of a series on exceptions:
- Thoughts on Java exceptions
- Some exceptions are more equal than others
- Less than perfect exceptions hierarchy
- Checked exceptions are priceless… For everything else there is the RuntimeException
- Design the failure case - Part 1
- Design the failure case - Part 2
- Exception design relativity
- Bad advice on exceptions from Joel
Java exception related links:
- Sun Exceptions Tutorial
- Failure and Exceptions, A Conversation with James Gosling, Part II by Bill Venners
- The Trouble with Checked Exceptions, A Conversation with Anders Hejlsberg, Part II by Bill Venners with Bruce Eckel
- A Conversation with Bruce Lindsay: Engineering for Failure
- Bruce Eckel: Does Java need Checked Exceptions?
- Java theory and practice: The exceptions debate
- Old News: Java Checked vs. Unchecked Exceptions
- Exceptions in the Java Language and Virtual Machine by Bill Venners
- Effective Java Exceptions
- Java Exception Handling on jenkov.com
- In favour of Checked Exceptions





nice articles.
Hey Daniel,
great post, especially the “The education factor”, it’s essentially what I thought about in one of my previous blog posts: http://dlinsin.blogspot.com/2008/05/do-you-care-about-your-code.html.
As for the design for failure, I would really like to read your opinion on the usability aspect of it. I wrote about a horrible experience here http://dlinsin.blogspot.com/2008/02/design-for-failure.html and I recommend a recent blog post of Coding Horror (http://www.codinghorror.com/blog/archives/001118.html).
Keep up the good work!