Mark Needham

Thoughts on Software Development

Keep Java checked exceptions in a bounded context

with 4 comments

One of the features that I dislike in Java compared to C# is checked exceptions.

For me an exception is about a situation which is exceptional, and if we know that there is a possibility of it happening and even have that possibility defined in our code then it doesn’t seem all that exceptional to me.

Having said that they do at least provide information which you can’t help but notice about what can go wrong when you make a call to a particular method.

The problem is that often these checked exceptions just get passed on – i.e. not handled – until we end up with an exception on the page the user sees which is completely irrelevant to the action they are trying to undertake.

To give an example, we have been using the OGNL library to hydrate some objects for testing using the builder pattern.

We have something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FooBuilder {
    private String bar;
 
    public FooBuilder setBar(String bar) {
        this.bar = bar;
        return this;
    }
 
    public Foo toFoo() {
        Foo foo = new Foo();
        setValue(foo, "bar", bar);
        return foo;
    }
 
    protected void setValue(Object object, String propertyName, Object propertyValue) {
        try {
            OgnlWrapper.setValue(object, propertyName, propertyValue);
        } catch (OgnlException e) {
            throw new RuntimeException(e);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import ognl.DefaultMemberAccess;
import ognl.MemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
 
public class OgnlWrapper {
 
    public static void setValue(Object object, String propertyName, Object propertyValue) throws OgnlException {
        Ognl.setValue(propertyName, createOgnlContext(), object, propertyValue);
    }
 
    private static OgnlContext createOgnlContext() {
        MemberAccess memberAccess = new DefaultMemberAccess(true);
        OgnlContext ognlContext = new OgnlContext();
        ognlContext.setMemberAccess(memberAccess);
        return ognlContext;
    }
}

We can then build an instance of ‘Foo’ like so:

Foo foo = new FooBuilder().setBar("barValue").toFoo();

What is interesting here is not the OGNL library in itself but the checked ‘OgnlException’ which the ‘Ognl.setValue(…)’ method defines.

If I am using the FooBuilder I don’t care how the Foo object is created, all I care is that I get it. Therefore we don’t want to bubble the implementation details of how we are creating the object upwards.

I only care about the OgnlException if I am calling the OgnlWrapper and therefore that is where the exception should be caught and then rethrown as a Runtime exception.

I like to refer to this area of OgnlWrapper callees as being a bounded context – that exception should only be applicable in that particular area and beyond that it should not exist.

Doing this allows us more flexibility around the way we implement things. If I decide in the future to use a different library instead of OGNL to do the same job I don’t need to worry that the callees of FooBuilder will all need to be updated. I can just make the change inside FooBuilder and that’s it!

Be Sociable, Share!

Written by Mark Needham

October 23rd, 2008 at 9:22 pm

Posted in Java

Tagged with ,

  • Darren Hobbs

    Wrapping a checked exception in a RuntimeException does not make it cease to exist! Or perhaps I misunderstood. I have recently learned how to stop worrying and love checked exceptions. There is a simple rule for happy exception handling. If something can be reasonably expected to fail and needs to be dealt with, throw a checked exception Good examples are IO and network issues. (We have a class called EnvironmentException that we wrap all exceptions of this category in). All other exceptions are bugs. I recommend creating a class Defect that extends RuntimeException and using it to wrap all exceptions that can ONLY occur due to programmer error. Then it’s easy to have a top-level catch block to report the problem.

  • http://www.markhneedham.com Mark Needham

    This is true and I think your idea of wrapping these exceptions with Environment Exception is better than the example I’ve provided.

    I was more saying it’s better not to bubble up Checked Exceptions like the OgnlException up our code since classes further away don’t actually care about that specific error. In this example they would just care that there was a problem building Foo.

  • Pingback: Testing Hibernate mappings: Testing Equality at Mark Needham

  • jonnie savell

    I have no idea whether we are in agreement, but here is what I think.

    The exception thrown from methods within a particular library should not affect any code other than the immediate client (as you appear to say). If the methods within this library throw a checked exception, then you can throw a RuntimeException (if you feel that nothing can be done because of the seriousness of the error). All the same, there are times when the client knows better than the library what constitutes an error from which recovery cannot occur.

    There is a preferable alternative. Client code can throw its own checked exception (supplying the checked exception you just caught as the cause). This latter technique forces a cascade of similar decisions from client to client. I believe that this is the correct technique. The cause information is preserved in the event that one who debugs may find it useful, but the bounded context is kept free from these details in outer scopes.

    I like to create checked exceptions in the code I write because I want to do more than inform client code of the nature of the problem when it happens; I want to force client code to statically acknowledge the possibility of it happening exists (by forcing it to catch). Client code may undermine this obligation by rethrowing unchecked exceptions and this is OK. Therefore, the fact that my library throws a checked exception doesn’t deeply upset your client code or its clients.

    Well, you don’t appear to argue against my thesis, but you like C# which does insofar as it steals my ability to throw checked exceptions (or maybe it is .NET and not C# which does this, but I don’t care).

    This is a theft most worthy of a rant!