Wednesday, December 15, 2004

Messaging with annotations

I'm working on a talk for SD West 2005 with a former coworker, Gregor Hohpe of ThoughtWorks. We coded a simple Java messaging system for single VM apps. Most messaging systems are based on messages segregated by subject, usually a String field. Our system though is type-based. Messages implement (or extend) Message, and receivers provide methods receive(Message) to receive published messages.

The dispatch loop makes extensive use of reflection to find a "best matching" method. If you have:

public class FooReceiver extends Receiver {
    public void receiver(final Message message) { }

    public void receiver(final FooMessage message) { }

Then if a FooMessage (or a subtype) comes along, the better matching method receives the message, otherwise the more general method does.

But I have found a better way: annotations :-) Consider:

public @interface MessageHandler {
    Class[] value();

This bit of annotation magic lets me say this in my receiver class:

public class FooReceiver {
    void receiveAllTypes(final Message message) { }

    void receiveFooTypes(final FooMessage message) { }

Now if a FooMessage comes along, both methods see the message: logically, there is no method overloading to consider since they are differently-named methods. But this has several other advantages:

  • No need to extend or implement anything; just annotate the methods
  • Methods can have better names than just receive; it's the annotation, not the name, which counts
  • Interestingly, you can write methods to handle disjoint types:
public class DisjointReceiver {
    @MessageHandler({FooMessage.class, BarMessage.class})
    void handleEitherFooOrBarType(final Message message) { }

Nifty — there is no need for FooMessage or BarMessage to be related types. In fact, to carry the disjunction one step further, I could dispense entirely with the Message class! Hmmm...

UPDATE: I fixed the bad links. That's what I get for blogging while woozy with flu.

Post a Comment