Friday, February 07, 2014

ServiceBinder

I've released ServiceBinder 0.2 to Maven Central (details below; soon to be 0.3 with more documentation). I wrote this to fix a common problem for my projects.

I like using the JDK ServiceLoader for discovery: it is simple to use and understand and always available. And Kawaguchi's META-INF/services generator makes use as simple as an annotation.

However, ServiceLoader is missing one key feature for me. It requires a default constructor for implementations, and I am a fan of constructor injection.

ServiceBinder fills this gap. It discovers service classes as ServiceLoader does, and injects them with Guice or Spring. See the GitHub site for examples and source. A taste:

Guice

public final class SampleModule
        extends AbstractModule {
    @Override
    protected void configure() {
        ServiceBinder.with(binder()).bind(Bob.class);
    }
}

Spring Framework

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
ServiceBinder.with(context).bind(Bob.class);
context.refresh();

Saturday, February 01, 2014

The Java fake return trick

Sometimes we have a method that does nothing but throw an exception (after munging arguments a bit). For example:

static void fail(Bob bob, Fred fred, Exception cause) {
    throw new SadError(format(
            "Sorry, but {} and {} did not meet today",
            bob, fred), cause);
}

Great when used as:

void friends(Bob bob, Fred fred) {
    try {
        meetAgain(bob, fred);
    } catch (MissedTrainException e) {
        fail(bob, fred, e);
    }
}

But what about this?

PhoneNumber exchange(Bob bob, Fred fred {
    try {
        return beamBusinessCards(bob, fred);
    } catch (LostPhoneException e) {
        fail(bob, fred, e);
        return null; // Never reached
    }
}

There's a neat generics trick to help:

static <R> R fail(Bob bob, Fred fred, Exception cause) {
    // Code unchanged - return ignored
}

Now we can write:

PhoneNumber exchange(Bob bob, Fred fred {
    try {
        return beamBusinessCards(bob, fred);
    } catch (LostPhone e) {
        return fail(bob, fred, e);
    }
}

The main downside is readability for the casual reviewer. Unless expecting this technique, he may think the new catch body returns a value. It may help to limit use to Exception, forcing handling in the caller method, but not with RuntimeException.