Refactoring Exception Handling in Java

It seems that database and storage code are especially prone to be mere delegators, such as:

class DatabaseConnection {
    void write(String table, String key, Object value) {
        database.write(table, key, value);
    }
}

That wouldn’t be so painful if not for the number of exceptions in this type of code, and that the exceptions are often converted from one type (such as “DatabaseConnection” or “NetworkException”) to a different one (such as the more generic “StorageException”).

That leads to code much like in the following scenario, where we have a more generic Storage class that delegates to a database (via a DatabaseConnection). The idea of the Storage class is that it doesn’t need to be to a database, and could instead be to a filesystem or to memory. As described above, the Storage class throws the more generic StorageException, as well the PermissionException, which the DatabaseConnection also throws.

The database connection has the following (partial, obviously) definition:

class DatabaseConnection {
    public void connect(User user) throws DatabaseException, NetworkException, PermissionException {
    }

    public void write(String table, String key, Object value) throws DatabaseException, NetworkException, PermissionException {
    }

    public Object read(String table, String key) throws DatabaseException, NetworkException, PermissionException {
        return null;
    }

    public void disconnect() throws DatabaseException, NetworkException, PermissionException {
    }
}    

And our exceptions and the minimal User class are:

class DatabaseException extends Exception {
}

class NetworkException extends Exception {
}

class PermissionException extends Exception {
}

class User {
}

class StorageException extends Exception {
    public StorageException(Exception cause) {
        super(cause);
    }
}

The first implementation of the Storage class is the version so common that it seems that there must be a name for this type of write-a-delegate-by-copying-and-pasting (WADBCAP?).

class Storage {
    private final DatabaseConnection dbc;

    public Storage(User user) throws StorageException, PermissionException {
        try {
            dbc = new DatabaseConnection();
            dbc.connect(user);
        }
        catch (DatabaseException de) {
            throw new StorageException(de);
        }
        catch (NetworkException ne) {
            throw new StorageException(ne);
        }
    }

    public void write(String table, String key, Object value) throws StorageException, PermissionException {
        try {
            dbc.write(table, key, value);
        }
        catch (DatabaseException de) {
            throw new StorageException(de);
        }
        catch (NetworkException ne) {
            throw new StorageException(ne);
        }
    }

    public Object read(String table, String key) throws StorageException, PermissionException {
        try {
            return dbc.read(table, key);
        }
        catch (DatabaseException de) {
            throw new StorageException(de);
        }
        catch (NetworkException ne) {
            throw new StorageException(ne);
        }
    }

    public void disconnect() throws StorageException, PermissionException {
        try {
            dbc.disconnect();
        }
        catch (DatabaseException de) {
            throw new StorageException(de);
        }
        catch (NetworkException ne) {
            throw new StorageException(ne);
        }
    }
}    

The redundancy there is overwhelming, so let’s try to reduce it. From a Ruby perspective, it is obvious that using closures would fix this, but we’re in a version of Java (1.6) that doesn’t have closures, and a quick reading of this suggests that combining closures and checked exceptions will not be for the weak or fainthearted.

A closure is really just an anonymous (unnamed) method, which is impossible in Java, but there is a close approximation: an anonymous inner class. For the above code, we want some code that will delegate to our quasi-closure, but handle the exceptions for us. Ergo something like, where we’ll implement the execute method:

abstract class DatabaseStorageAction {
    public abstract Object execute() throws DatabaseException, NetworkException, PermissionException;

    public Object run() throws StorageException, PermissionException {
        try {
            return execute();
        }
        catch (DatabaseException de) {
            throw new StorageException(de);
        }
        catch (NetworkException ne) {
            throw new StorageException(ne);
        }
    }
}

An improved version of the above class would handle generic types, so that types other than Object could be expected from the execute method.

But it’s good enough for now, so let’s write a new version of the Storage class:

class StoragePartTwo {
    private DatabaseConnection dbc;

    public StoragePartTwo(final User user) throws StorageException, PermissionException {
        DatabaseStorageAction dsa = new DatabaseStorageAction() {
                public Object execute() throws DatabaseException, NetworkException, PermissionException {
                    dbc = new DatabaseConnection();
                    dbc.connect(user);
                    return null;
                }
            };
        dsa.run();
    }

    public void write(final String table, final String key, final Object value) throws StorageException, PermissionException {
        DatabaseStorageAction dsa = new DatabaseStorageAction() {
                public Object execute() throws DatabaseException, NetworkException, PermissionException {
                    dbc.write(table, key, value);
                    return null;
                }
            };
        dsa.run();
    }

    public Object read(final String table, final String key) throws StorageException, PermissionException {
        DatabaseStorageAction dsa = new DatabaseStorageAction() {
                public Object execute() throws DatabaseException, NetworkException, PermissionException {
                    return dbc.read(table, key);
                }
            };
        return dsa.run();
    }

    public void disconnect() throws StorageException, PermissionException {
        DatabaseStorageAction dsa = new DatabaseStorageAction() {
                public Object execute() throws DatabaseException, NetworkException, PermissionException {
                    dbc.disconnect();
                    return null;
                }
            };
        dsa.run();
    }
}    

It is arguable as to which is better in the above example, but as the number of exception types grows, and the pre- and post-closure code is more complex, the latter version is more justified.

Advertisements

One thought on “Refactoring Exception Handling in Java

  1. Pingback: Tests Result in Better Coders | Jeff Pace's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s