Ten practices for perfect Java Exception handling

8.  Rethrow with a cause

In most projects, the most common “exception handling” code is actually just to rethrow the exception. While the guidelines above should help you drastically reduce the number of catch-blocks you code, it’s crucial to code the remaining ones correctly.

First and most important, always include the cause. Constructing and throwing a wrapper exception must always provide full details of the underlying failure.

Logging should generally be left to the outermost handler. This may seem counter-intuitive — shouldn’t all exceptions be logged? — but if your outermost handlers are comprehensive & correct, all exceptions will be logged there. Avoid duplication.

In some methods, we may want to “catch and rethrow” exceptions to provide a more-informative message/ or additional diagnostic information.

9.  Add informative messages

While throwing runtime exceptions outward requires the minimal amount of code, we may sometimes want better information as to what action & data failures the program failed on.

The ideal, is for exception messages (and the program itself) to be self-diagnosing. Any developer should be able to read the exception message & immediately understand what failed.

Great informative reporting requires only a few steps:

  1.  Throw a type, appropriate to the underlying cause;
  2.  Message stating what failed;  in business English.
  3. Include the primary keys for data being accessed.

Here’s an example.

public Customer loadCustomer (int id) {
    try {
        //... load the Customer

    } catch (SQLException x) {
        throw new AppSQLException("error loading customer: id="+id, x);
    }
}

10. Use logging

Last but not least, logging is your friend. Exceptions should be logged at the outermost handler, to capture a comprehensive record of failures.

Methods throwing exceptions can also log error/ or warning lines to enrich the log output & increase the available information. Since major facts (eg primary keys) should be in the exception message, this is normally used to provide more minor & contextual detail.

As well as recording exceptions, logging should also record your program’s activity & decisions to understand why an exception occurred. Good practice is to log major business requests, decisions, outcomes and actions — these provide the context to understand what your program is doing & why.

Several logging frameworks are available in Java:

  • Log4J — long-standing & most popular
  • Logback — successor to Log4J
  • Slf4J — universal API; plugs into an underlying logging framework
  • Apache Commons logging — older common API; less favored these days
  • java.util.logging (JULI) — unpopular Java built-in logging

Any of the first three are great. Java util logging (JULI) is not recommended, due to it’s awkward API and configuration. Log4J, Logback and Slf4J all provide excellent APIs to log your program’s activity & exceptions.

Conclusion

The foundations of clean exception-handling, are to avoid redundant catch blocks & make throwing easy.

As we all know, catch blocks are prone to “swallowing exceptions”, silently continuing despite failure, rethrowing without the cause, and many other common or garden coding errors. Minimizing the need for these increases developer productivity & avoids whole classes of errors.

Best practice is to concentrate on throwing & informative reporting, while concentrating actual handling in just a few well-defined catch blocks at the outermost level. (In many cases the container provides these.)

In between, there may sometimes be a need to wrap & rethrow — especially with checked exceptions.

Declaring a simple exception hierarchy, makes it easy for developers to find & throw the right exception. Categorizing by cause & using systematic naming will help developers to quickly find these.

Exceptions in general have two crucial & overarching goals:

  1. prevent incorrect/ erroneous business actions being taken,  and
  2. diagnose errors for subsequent resolution.

Exception handling code should first and foremost ensure that  code fails fast, and does not “run on” to produce uninitialized or erroneous results after a failure.

Recovery code, such as default values, should be used thoughtfully; reliability code, such as retrying, brings its own potential failure modes. Such added complexity should only be introduced where a well-defined need exists.

Following these practices will help minimize the amount of code you need to write, eliminate boilerplate, and deliver more reliable, self-diagnosing software.

The goal is for exceptions & logging to identify where the application was, what it was attempting to do, and exactly what error occurred.

Achieving this will reduce the number of errors you need to see & help you diagnose them in seconds — bringing you to true exception-handling nirvana.

Do you need cleaner, better exception-handling? Share your story here.

References:
Oracle: Barry Ruzek,  Effective Java Exceptions
Google Testing blog:  Checked exceptions, you have to go
Jacob Jenkov: Checked or Unchecked Exceptions
Logging — our most effective debugging tool
Checked exceptions: Java’s biggest mistake

10 thoughts on “Ten practices for perfect Java Exception handling”

  1. “However, [check exceptions] were never conceived to include unpredictable low-level & infrastructure failures — which can occur anywhere in code, at any time, and for which no effective business alternates exist.”

    That’s a darn interesting statement.
    Out of curiosity, what is your source for that? Who conceived them?
    Reason I wonder is, the JDK itself is horribly cluttered with all these ridiculous checked-exceptions that a client virtually never want to catch — and more keep coming with every new release…
    So if your stmt is correct, it means the people who decide on the JDK have completely misunderstood what checked-exceptions were intended for?

    1. Yes, this was the original intent of checked exceptions — a language mechanism to identify possible contingencies and ensure they were handled.

      This worked great for reporting predictable non-success outcomes back to callers.

      The trouble is that it does not make sense at all for unpredictable low-level failures.. which can effectively happen anywhere, anytime, and are impossible for application code to recover from. Forcing these to be declared is essentially meaningless, since the declarations are needed everywhere.

      This understanding is a synthesis of a range of sources. Barry Ruzek probably expresses this best, but here are a few:

      Barry Ruzek, Oracle: Effective Java Exceptions pg 2
      Smartics: Faults and Contingency Exceptions
      Expert One-on-One J2EE Design and Development: Interview with Rod Johnson

  2. Great effort and a really good site Tom. I’ve been enjoying reading these articles and have learned a lot from them already.

  3. Good explanation tom, i got more detail about catch in java. i will share my friend also those details.but i want more example to given. am waiting your given example and explanation. thanks

  4. Why have AppSqlException, AppFileException and …, Considering there already is SqlException, FileException, …
    Isn’t it better to have a generic AppException?

    About No3 (Keep your exception heirarchy simple) isn’t it best for AppSQLException, AppFileException, …. (and all other internal Exceptions) to be extended from our own AppException?
    This way we can treat AppExceptions and RuntimeExceptions differently. So if an AppException is thrown we can be sure that the Application is still in the right state and keep going but when a RuntimeException is thrown we can consider it fatal and let the Application go down.

    1. AppSqlException and AppConfigException might be better examples — most apps use both SQL and Configuration, for example, but somewhat fewer combine SQL with file management.

      Anyway, the point is that 1) these are runtime exceptions, and 2) they can inherit from a common type.

      Here I’m proposing an application-level FailureException. In the past (as you suggest), I’ve also seen an AppException used — however that design didn’t include the distinction between failures & recoverable exceptions.

      Regarding recovery: your thinking is wrong. In general, we can’t recover. Sometimes we can retry (with added cost & complexity). But our primary goal is to fail cleanly.

      In the very large part, our code should just aim to fail cleanly. We should never assume that a given exception type lets us globally ignore failed business logic, as that means our application will be running in an incorrect state & worse, persist that wrongness to the DB.

      Thanks for your comments, glad you appreciated the article!

  5. Hi Tom,

    I read your post “Checked exceptions: Java’s biggest mistake”. I had a similar post: http://tri-katch.blogspot.com/2016/02/java-checked-exception-is-bad.html.

    I have been thinking about redesigning exception mechanism (not only limited to Java) for 9+ years (http://www.theserverside.com/news/thread.tss?thread_id=43820#225361). And here is the result: http://tri-katch.blogspot.com/2015/05/catch-code-proposal-to-expand-catch-in.html

    Could you take a look and comment?

    Thanks.
    Bo

    1. Hi Bo, thanks for your interest & comments! I’ve looked at your proposal, but it seems to suggest using strings/ fault codes as a kind of replacement for exception subtypes.

      My emphasis is against checked exceptions in general; but specifically, I believe that exception-handling & genuine recovery are relatively rare in code.

      These are the circumstances where a ‘catch by type’ is appropriate and I find the combination of code locality & Java exception types to be sufficient and appropriate for that purpose. So, I don’t support special language support to catch by string/ or fault-code.

      Thanks for your interesting suggestion however!

      Regards,
      Tom

  6. Thanks for your great article that summarizes most of my personal practices from 20+ years of Java development.

    There’s only one statement I’m missing: “An exception is the signal from a method to its caller that it didn’t fulfill its contract.” So, as a caller, if I don’t get an exception, I can safely assume the method did its job, completely. (Of course, the question still has to be answered case-by-case, which special outcomes lie within the contract, thus not being worth an exception.)

    Even though that might seem self-explanatory, there’s lots of mis-understanding among developers on that topic.

Leave a Reply

Your email address will not be published. Required fields are marked *