The Agile Architect

Home » 2012 » March

Monthly Archives: March 2012

Exceptional Programming

How you manage exceptions in a Java program can significantly affect the maintainability and stability of your code. To a programmer transitioning from a different language, managing exception may seem like unnecessary overhead but to the experienced programmer they are a necessary part of a professional API.

Interestingly, I am observing that the favorite way for novice programmers to deal with exceptions is to catch them and report an error (frequently to the console) which happens to be in general the worse way to deal with them. I think this behavior stems from a tendency to associate exceptions with code problems. Indeed, in C where exceptions are uncaught and uninformative they almost always indicate a catastrophic code failure. In Java on the other hand, exceptions are a standard way of communicating failures from one class to another in a controlled way. Throwing the right exceptions safeguards your code against misuse.

Types of Java Exceptions

The Java exception hierarchy starts at Throwable which has only two direct subclasses: Error and Exception. Application code should not throw Error nor try to catch it. The type of Throwable that most programmers have to deal with is Exception. There are two flavors of Exception: Checked and Unchecked. Every exception that subclasses RuntimeException is unchecked whereas every exception that subclasses Exception is checked.

Here are the compiler rules regarding checked and unchecked exceptions:

Unchecked Exceptions: A method does not need to declare any unchecked exceptions that it throws. Unchecked exceptions do not need to be caught or explicitly declared as thrown by the code that uses them. If an unchecked exception is not caught explicitly it will propagate through the call stack and be thrown by the entry point. If this happens on a standalone JVM, the JVM will exit.

Checked Exceptions: A method that throws a checked exception must declare it in a method signature using the throws keyword. Any code that calls that method must either catch the exception or throw it by declaring it with the throws keyword.

Exceptional Programming Practices

When to throw unchecked exceptions

Application code should only throw unchecked exceptions only when it encounters a condition under which the application is not going to be able to continue operation. For example, if your application depends on a database and you fail to establish a database connection, this is a good candidate for an unchecked exception. There is no way to handle this failure so the best thing to do is fail fast.

There are however some programmers, and some frameworks, that relax the conditions for throwing unchecked exceptions. For example, the Spring framework will throw unchecked exceptions for any database failure even if it is a single query. The rationale for doing it is that there is that there is nothing the calling code can do to recover from the error, therefore even if it is not a problem that affects the whole application, it should still be propagated. It is true that 90% of the time, there is nothing to do with these exceptions but propagate them. Nevertheless, the framework clearly documents the unchecked exceptions that it throws so that programmers can still catch and handle them if they so wish.

When to catch unchecked exceptions

Unchecked exceptions should only be caught under the following two scenarios:

1. Catch unchecked exceptions in the application tier that interacts with the User. For example, if you have a Swing or Web client, displaying a stack-trace to the end-user who can’t do anything about it is unadvisable. The code should catch the exception, ideally send a notification to support (via email for example) , and produce a friendly (or at least friendlier) message for the user informing them that there was a problem.

2. Catch a unchecked exception if your code knows how to handle it gracefully. For example, suppose your code is using an Object argument whose real type belongs to one of three classes (not necessarily a good design but a good example). You can try casting it to each of the possible classes, catch the ClassCastException and proceed (see code fragment below).

 public void convertAndUse(Object object) {
        try {
            BigInteger bi = (BigInteger) object;
            //TODO use it
            return;
        } catch (ClassCastException cce) {
            //continue
        }
        try {
            String s = (String) object;
            //TODO use it
            return;
        } catch (ClassCastException cce) {
            //continue
        }

When to catch checked exceptions

You should catch unchecked exceptions in two circumstances:

1. To re-throw them as part of a new exception. We will discuss when and how to do this in the next section.

2. If your code knows how to recover from the exception. This is identical to the second scenarion in the case of unchecked exceptions.

The Art of Throwing

When you call a method that throws a checked exception and you don’t know how to handle the exception, you have two choices:

1. Throw the exception as it is.

2. Create a new exception to throw.

If you decide to throw a new exception, you should preserve the original exception by passing it into the constructor of the new exception. This is called chaining exceptions and it makes it much easier to determine the original cause when looking at a stack trace (see code sample below).

 public void retrievePassword() throws PasswordAccessException {
        try {
            InputStream fis = new FileInputStream(passwordFile);
            //TODO get password from stream
        } catch (FileNotFoundException e) {
           throw new PasswordAccessException("The password could not be retrieved.",e);
        }
    }

Under what circumstances should a programmer create new exceptions rather than propagate existing ones? The answer should be sought in the Object Oriented Programming principles of Encapsulation and Abstraction. These two concepts are similar and what they amount to in practice is sparing the user of class (or interface) the technicalities of the underlying implementation.

Consider for example the following interface for sending an email message:

public interface EmailNotificationService {

    void sendEmail(String from, String subject, String[] to, String content);

}

The interface affords any number of implementations. For example, Java Mail, Spring’s JavaMailSender, or a lower level implementation that explicitly manages the communication with the mail server. The point is that the interface hides the implementer’s choices from the user.

Now how about exceptions? Depending on the implementer’s choice,each different technical decisions will throw a different exception. For example, Java Mail may throw the (checked) MessagingException whereas Spring would throw the (unchecked) MailSendException. Throwing either of these will reveal the implementation, therefore proper encapsulation dictates that a new exception is created to abstract the specific error message, as follows:

public interface EmailNotificationService {

  void sendEmail(String from, String subject, String[] to, String content)
            throws EmailNotificationException ;

}

Summary

In conclusion, Java Exceptions are part of the API of a class or interface and are used to communicate the fact that a method failed to complete its intended function in a controlled manner. Exceptions (both checked and unchecked) should only be caught if the code that catches them can recover from the original problem. In all other cases, exceptions should be propagated. Exceptions can be chained and should be chained as part of a higher-level exception whenever code encapsulation must be preserved.