Java Exception Rule Book

This post consists of a briefly outlined set of Java Exception rules, or best practices, with an accompanying look at rule compliance using specific transient network and database layer Exceptions.

The rules are based on the relevant rules from Joshua Bloch’s lauded and highly recommended Effective Java, 2nd Edition. Bloch is arguably the authority on the subject, and his legacy includes being listed as author of the Sun JDK Throwable implementation (see @author in the source code).


The author has violated these rules hundreds if not thousands of times over the last decade. As a mitigating factor it can be argued that the rules in themselves represent exception utopia, and so, it is highly unlikely that they will all be consistently followed in any given code base, or put differently, that even with the best of intentions any software engineer will follow all of them due to say project timeline pressures.

Such pressure may however be a symptom of not asserting one’s own standards when a unit of work or project commences, to alleviate pressure one can always clearly communicate one’s own non negotiable engineering standards at the start of a project.

Rule 1: Never use exceptions for ordinary control flow

import java.util.ArrayList;
import java.util.Iterator;

* Do not, use Exceptions for flow control, this is an example of what not to do.
* @author nico
public class DontUseExceptionsForFlowControl {

 public static void main(String[] args) {

   ArrayList list = new ArrayList();

   list.add("hello world");

   // Let's to the wrong thing, and jump out of this loop with an iterator related
   // Exception...

   Iterator iter = list.iterator();

   while(true) {

     try {


     } catch (java.util.NoSuchElementException e) {

        // Using Exception for flow control, so can safely ignore this one.
        // TODO Stop using Exceptions for flow control.




Using Exceptions for ordinary control flow violates the Principle of Least Astonishment which states “the result of performing some operation should be obvious, consistent, and predictable, based upon the name of the operation and other clues”.

Rule 2: Never write APIs that force others to use Exceptions for ordinary control flow

* Database layer API, implementation detail agnostic.
public interface EmailDatabaseHelper {

 // TODO refactor, this forces the user to catch checked exception EmailDoesNotExistException, with an email
 // address not existing being in no way exceptional, and hence in ordinary control flow
 public void doesEmailExist(String email) throws EmailDoesNotExistException;

Rule 3: Use runtime exceptions for programmer errors and checked exceptions where recovery is possible

Note that this rule does not state, as you may expect, that you must use unchecked exceptions for programmer errors, since that casts too wide a net. Unchecked throwables include runtime exceptions and errors, with the latter conventionally reserved for JVM error reporting under conditions where continued execution is impossible.

Rule 4: When uncertain as to whether a condition is recoverable or not, use an unchecked exception

The reasoning behind this is simple, if you do not know how an API user will recover from the checked exception, do not place the burden on the API user in terms of trying to figure out how, and potentially wasting time concluding that its not possible.

Rule 5: Only use checked exceptions if the API user can take action to recover from the said exception

If the only course of action, that you could take, when confronted with your own checked exception, is a variation of the below example then chances are good that you should not be using a checked exception. The checked exception adds no value, so, either attempt to refactor as per Rule 6, or use an unchecked exception.

} catch (CheckedExceptionWithNoUsefulActionPossible e) {
  logger.error("an error occurred");
  System.exit(1); // or stopping the current thread

Rule 6: Refactor, where possible, checked exceptions that violate rule 5 into a state-checking method and unchecked exception


try {
} catch (BallCannotBeKickedException e) {
  // recover from exceptional conditions

After. State-checking method and unchecked exception has been introduced.

// ball will not be accessed concurrently, and so the calling sequence is safe
if (ball.isKickeable()) {
} else {
  // recover from exceptional condition

Rule 7: Clearly document if the unrecoverability of an unchecked exception is likely to be transient in nature

Certain conditions are unrecoverable at a particular instant but not permanently, and a prime example is an exception related to a lock being held on a database table on which say an update is being attempted. Methods that throw unchecked exceptions where the exception relates to a condition that may be transient in nature should document this fact, that is that the condition may be transient, and suggest a retry in the Javadoc comment associated with the unchecked exception.

Rule 8: Use the standard Java platform library unchecked exceptions where appropriate, do not re-invent the wheel

Reuse the standard Java platform library unchecked exceptions whereever possible, whilst honouring their documented semantics. A prime example is IllegalArgumentException. See the subclasses of RuntimeException for further candidates.

Rule 9: When dealing with lower-level exceptions inappropriate to the higher-level abstraction, perform either exception translation or exception chaining

Exception translation.

try {
     // lower level method invocation e.g. JPA call
} catch(LowerLevelMethodInvocationException e) {
     // now translate the lower level exception into an exception that matches
     // the higher level abstraction in our current context
     throw new HigherLevelException(...);

Exception chaining with chaining-aware contructor.

class HigherLevelAbstractionException extends Exception {
    HigherLevelAbstractionException(Throwable cause) {
try {
     // lower level method invocation e.g. JPA call
} catch(LowerLevelMethodInvocationException e) {
     // now translate the lower level exception into an exception that matches
     // the higher level abstraction in our current context
     throw new HigherLevelAbstractionException(e); // use chaining-aware constructor

Rule 10: You MUST document all exceptions, checked and unchecked, thrown by your methods

Excuse the all-caps and repetition will all exceptions being defined, but proper documentation is in no way negotiable and is every software engineers professional responsibility. The Javadoc @throws tag must be used to document both checked and unchecked exceptions in terms of the conditions under which they will be thrown. The only difference between checked and unchecked exceptions is that you must only define checked exceptions with the throws keyword, do not include unchecked exceptions here.

Rule 11: Force including the values of all parameters and fields that comprise the cause of an exception in its detail message

Support personnel or fellow programmers, when faced with a stack trace in say a log file, require pertinent data in order to ascertain exactly what caused an exception. Without the values of all parameters and fields that comprised the exception, it may be impossible to reproduce an exception.

In terms of forcing including pertinent parameter and field data in the detail message of exception, simply do not provide a constructor that has a string parameter as detail message, rather force the user to specify the said data in the constructor.

Consider the following checked exception, which has accessor methods since as per Rule 5 such exceptions should be used for recoverable exceptions.

* @author Nico
public class ServiceOperationException extends Exception {

private String suppliedApiKey;

* Construct an ServiceOperationException.
* @param suppliedApiKey    the API key supplied to the service user upon successful registration
public ServiceOperationException(String suppliedApiKey) {

// detail message
super("Supplied API Key: " + suppliedApiKey);

// capture for recovery purposes
this.suppliedApiKey = suppliedApiKey;

public String getSuppliedApiKey() {
return suppliedApiKey;

public void setSuppliedApiKey(String suppliedApiKey) {
this.suppliedApiKey = suppliedApiKey;

Rule 12: Ensure that your methods are failure atomic and if not document this fact in your API

Methods that are failure atomic leave an object in the state it was prior to the method invocation in the event of failure. Either ensure parameters have their validity checked before proceeding to to the actual work (and modification) in a method invoked on a mutable object, or ensure the relevant class is modified to be immutable.

Rule 13: Never ignore exceptions

From time to time you may see empty catch blocks, and in the worst case, catch blocks that catch java.lang.Exception (as listed in Tim McCune’s Exception-Handling Antipatterns) or java.lang.Throwable. This is a cardinal sin, and should never be done. At the very least, the exception should be logged (that is the entire stack trace should be logged) with an appropriate log level. In some cases, it may be justifiable to take no action, but in such cases, comments must justify why no action is taken.


Some exceptions are associated with conditions that are possibly but not necessarily transient in nature, and so, when faced with them, the appropriate course of action is to automatically retry the operation. The Spring Batch Retry mechanism is geared for exactly such exceptions.

In terms of the rules, Rule 3, 4, 5, 6 and 7 are applicable, given our definition of the conditions being transient, but not with absolute certainty. In other words, given the lack of certainty as to whether the condition is recoverable, an unchecked exception should be used, as per Rule 3, 4 and Rule 5. If we choose not adhere to this rule, and feel the condition is indeed recoverable, and use a checked exception, as per Rule 5, then as per Rule 6 one should refactor into an unchecked exception with an accompanying state-checking method.

So, in short, we should use:

  • unchecked exception (if uncertain if recoverable) or if unrecoverability is a certainty
  • checked exception if recoverable
  • even better, unchecked exception with a state-checking method if recoverable

Database Layer

Consider the following exceptions, that one may see with a Spring / JPA / Hibernate stack:

  1. UnexpectedRollbackException (Spring)
  2. OptimisticLockException (JPA)
  3. LockAcquisitionException (Hibernate)

The decision to make all of these unchecked exceptions complies with the rules, given that within the context of a single transaction, the underlying condition is not recoverable. Within the context of retries, and so multiple transactions, the condition may indeed be recoverable due to the transient nature of the underlying conditions (a lock on a table will most likely not be permanently held). So, methods that throw these exceptions should firstly document that they throw these exceptions with @throws Javadoc (as per Rule 10) and then also document the fact that the user may wish to attempt retries (as per Rule 7).

Network Layer

Another example of an exception that may point to a transient condition is a SocketException, especially when considering its subclasses, BindException, ConnectException, NoRouteToHostException and PortUnreachableException. These exceptions are all checked exceptions, and this is incorrect since at the instant an associated method was called, recovery would not necessarily be possible, that is, recovery is not a certainty, so Rule 3 is violated. Rather, as per Rule 4, the exceptions should be unchecked and the transient nature of the exceptions should be documented as per Rule 7.

Key References

  1. Chapter 9 of Joshua Bloch’s (the author of java.lang.Throwable) Effective Java, Second Edition. If you don’t own a copy, buy one, you won’t regret it.
  2. Tim McCune’s Exception-Handling Antipatterns

Roll Your Own Java Daemon OutOfMemory Handler

In this post I share one way of getting your Java daemon up and running again after it crashed to a grinding halt with a dreaded java.lang.OutOfMemoryError using Java 5. Our concern here is not looking into potential memory leaks, rather we take a sys admin view and our concern is simply to get a system daemon that had been knocked down, back up again.

First some context, I had the privilege of being able to dedicate a few weeks to sys admin work, and concentrated on writing an extensive shell script to install and configure JBoss 4.2.2.GA on Ubuntu 10.4 LTS. I won’t go into the details, but the one relevant issue I had to grapple with was suitable JVM memory options for the in effect “Hello World” root web site I configured as Tomcat root. I ended up configuring the JVM with what I took to be bare minimum amounts of memory and then started experimenting with Apache JMeter. To my horror one of my first iterations of a very basic HTTP Test Plan resulted in a java.lang.OutOfMemoryError meaning that my simple “Hello World” web site had keeled over thanks to the underlying JVM running out of memory. In my mind the next step was to take a sys admin view and simply to try to get the service back up once it had died.

The first consideration is how would one detect that an OutOfMemoryError occurred. One option is to write a script that looks at your jboss log file, waiting to pounce once it sees an OutOfMemoryError, such work is entirely unnecessary however thanks to the -XX:+HeapDumpOnOutOfMemoryError command-line option which was introduced in Java SE release 5.0 update 7 (search for HeapDumpOnOutOfMemoryError on this page for more details). As detailed in the Sun Java 5 Troubleshooting and Diagnostic Guide, this options tells the VM to generate a heap dump when the first thread throws a java.lang.OutOfMemoryError because the Java heap or the permanent generation is full. Regretfully there is no option in any release of the Sun Java 5 JVM to run a script upon the occurrence of an OutOfMemoryError (this has been rectified in the Sun Java 6 JVM with the -XX:OnOutOfMemoryError=”<cmd args>;
<cmd args>” option) however the -XX:HeapDumpPath options gives one the option to write the HeapDump to a file, and so one can react to that event.

I could not upgrade to the Sun Java 6 JVM, and so chose to use the option of  writing the HeapDump to a file, resulting in the relevant section of my JBoss 4.2.2GA run.conf file looking like this:

# Specify options to pass to the Java VM.
if [ "x$JAVA_OPTS" = "x" ]; then
JAVA_OPTS="-Xms64m -Xmx64m -XX:PermSize=64m -XX:MaxPermSize=64m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/jboss4/heapdump -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djboss.server.log.dir=/var/log/jboss4"

The next step was to write the bash script that would handle the above event and to update crontab to run this script every minute. In effect we are polling for the existence of the HeapDump file, there is probably a much better way of doing this, and please suggest a better way if you know of one. Here is, which works but is still a work in progress as you’ll see in the comments.


# This script will check for the existance of Java heap dump files which would
# be produced as a result of the Java 5 JVM being run with the following options:
# -XX:+HeapDumpOnOutOfMemoryError
# -XX:HeapDumpPath=/var/log/jboss4/heapdump
# If a heap dump file is found, the script will kill -9 the JVM process running
# JBOSS identified by /var/run/ and the start JBOSS with
# /etc/init.d/jboss start, but it will also delete the file, which
# we expect to be recreated once JBOSS starts again.
# This script will be run by cron every minute. There may be a better, say
# event-driven way to do this.

# To run every minute from cron, put this in /etc/crontab:
# */1 * * * * root /usr/local/bin/gateway-fan
# place script in /root/scripts

if [ -e /var/log/jboss4/heapdump ]; then

 logger -p local1.crit -t HEAPDUMP "heap dump detected as a result of HeapDumpOnOutOfMemoryError JVM flag"

 # check if /var/log/jboss4/heapdumps directory exists, if not create it, then
 # move /var/log/jboss4/heapdump file into the mentioned directory for backup
 # purposes

 if [ ! -e /var/log/jboss4/heapdumps ]; then
 mkdir /var/log/jboss4/heapdumps
 chown -R jboss4:jboss4 /var/log/jboss4/heapdumps

 mv /var/log/jboss4/heapdump /var/log/jboss4/heapdumps

 logger -p local1.crit -t HEAPDUMP "heap dump file backed up to var/log/jboss4/heapdumps"

 JBOSS4_PID=`cat /var/run/`

 # now kill the process with the JVM that had run out of memory
 # TODO double check that the said JVM is in fact in the state we think it is in? i.e. what if
 # this script is run, the heapdump file exists BUT the issue had somehow been resolved before
 # this script has been run, then we'll potentially kill a operational production JVM which
 # would be unacceptable
 kill -9 $JBOSS4_PID
 rm /var/run/

 logger -p local1.crit -t HEAPDUMP "killed process with pid identified by /var/run/"

 # now start the JBoss
 /etc/init.d/jboss4 start

 logger -p local1.crit -t HEAPDUMP "command issued to start jboss4"

As a final note, the “Hello World” Tomcat hosted website I was referring to, serving just an image and some text, is shown below. In my mind memory leaks are not an issue to look into, or should not be, when it comes to serving a basic html page.