Handling Exceptions

What are best practices for handling exceptions?

Do not ignore Exceptions

Never use an empty catch block to handle exceptions. At least add a comment why it is save to swallow the exception at this point. Better in almost all scenarios is to make a log entry so that the occurrence of the exception can be seen at the selected log level.

Release Resources

Make sure to clean up your resources prior to forward to the exception handling. The standard pattern for acquiring and releasing a resource (a InputStream in the given example) is:

InputStream in = null;
try {
  in = openStream();
  doSomething(in);
}
catch(final IOException e) {
  // handle the exception here: log or throw, 
  // probably throw a business exception
  // ...
}
finally {
  IOUtils.closeQuietly(in)
}

Please note that sometimes it would be of interest to log that closing a stream did not work. In this case logging an warn message would be helpful. Using closeQuietly from commons IO does not log anything. The reason why this is most often ok is that failure on closing the stream is tragic for the system resources, but not for the program. There is nothing it could do and no reason why it should not go on. The information is already read.

Do not log and throw

'Log and Throw' is defined as an antipattern by Tim McCune in Exception-Handling Antipatterns. The point is that the log file should mention each exception (with it's stack trace) only once. Otherwise this poses a burden on the service team wading through MBs of log messages always iterating the same exception at different levels.

On the other side it is always a problem to know who is the last in the row to be responsible to do the logging. It is a burden to have the same exception logged multiple times, but it is a disaster if an exception is not logged at all.

We still prefer to use the antipattern to log and throw an exception at the tier points (where e.g. the business tier hands the exception to the presentation tier). This is especially the case if the tiers are deployed on different nodes. The logger can then be configured to log or not log on a package basis on the production machine to prevent logging the same exception multiple times.

Do not catch java.lang.Exception

Catch Exception as in

try {
  ...
}
catch(final Exception e) {
}

also catches any RuntimeException which may obscure a problem in the code. More often than not the programmer does not have the caught runtime exception in mind if the catch for the java.lang.Exception is written. Design Exception Class Hierarchy prevents the need to catch java.lang.Exceptions. Otherwise there is an individual catch clause required for every checked or unchecked exception that has to be handled.

Catch late

Exceptions need not to be caught at the first time their occur. The latter in the call hierarchy the exception is handled, the more information is gathered and the more expressive the message can be made. So let an exception bubble up the call hierarchy and catch it as late as possible. But catch as early as all information to describe the exception situation is collected and do not throw an exception that is of a foreign API or semantic layer.

Extract try-catch Blocks

The try-catch-finally structure is complicated enough. To write clean code Robert C. Martin advices to only call methods in those blocks. This will make code easier to understand and modify.

The example below is taken from his book Clean Code, p. 47, slightly modified:

public void delete(final Page page) {
  try {
    deletePageAndAllReferences(page);
  }
  catch(final DeletionException e) {
    logError(e);
  }

This is used instead of

public void delete(final Page page) {
  try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
  }
  catch(final DeletionException e) {
    logger.log(e.getMessage());
  }