Wednesday, December 30, 2009

Event Notification with Apache Camel

I ever tried using Apache Camel to solve the event notification problem.

Given a Sensor (event source) and a Display (listener), here's how I would implement it:

public class Sensor {

    @EndpointInject(uri="seda:sensor.events")
    private ProducerTemplate sensorProducer;
    
    private void fireSensor(SensorEvent sensorEvent) {
        sensorProducer.sendBody(sensorEvent);
    }

}

public class Display {

    @Consume(uri="seda:sensor.events")
    public void sensorUpdated(SensorEvent sensorEvent) {
        // ... code to update ...  
    }

}

Advantages


As you can see, the code is pretty concise and elegant. Even Listener interfaces are not needed here. Simply agree on an event object, and the endpoint (channel) URI, and the event is wired up.

POJO is somewhat preserved, if not for ProducerTemplate.

It's possible to receive response from listeners (asynchronously), but I think it should be thought as a feature of the routing engine, not the event notification framework.

Issues


The above isn't a true publish-subscribe though, since Camel's seda component won't support multiple consumers (multicasting) until Camel 2.2.0 is released.

It also uses hard coded endpoint URIs, which is quick and convenient for singleton event sources but falls apart when there are several event sources of the same type.

A routing engine introduces one more concept besides event sources, listeners, and event objects: endpoints (channels a.k.a. topics). I hope it's possible to remove this, but all publish-subscribe event notification frameworks use it, including Dojo AJAX Toolkit's dojo.publish.

The event source object can be put inside the event object so the listeners can filter it, which addresses one side of the problem.

Making endpoint URIs dynamic is another alternative, i.e. referring to its own Spring bean name via BeanNameAware.

Yet another alternative is having a router-consumer that routes the messages (events) to actual listeners. This way, listeners attach to the router similar to old JavaBean event notification way. But I think this is getting too complex.

Dynamic Endpoints

Here's some code to illustrate how to implement event notification with Apache Camel dynamic endpoints.

public class Sensor { 

    // dynamically created or injected
    private Endpoint sensorEndpoint;
    @EndpointInject
    private ProducerTemplate producer;
    
    private void fireSensor(SensorEvent sensorEvent) {
        producer.sendBody(sensorEndpoint, sensorEvent);
    }

}

public class Display {

    @Autowired
    private Endpoint sensorEndpoint;
    private Consumer sensorConsumer;
    
    @PostConstruct
    public void initialize() {
        // BeanProcessor should automatically
        // match the method based on argument type
        sensorConsumer = sensorEndpoint.createConsumer(
            new BeanProcessor(this, sensorEndpoint.getCamelContext()));
        sensorConsumer.start();
    }
    
    @PreDestroy
    public void destroy() {
        sensorConsumer.stop();
    }

    public void sensorUpdated(SensorEvent sensorEvent) {
        // ... code to update ...  
    }

}

It's far from elegant. Especially the part where the Event Driven Consumer has to be created, configured, started, and stopped manually.

It's also somewhat decoupled, since the consumer needs to only know the endpoint object or the endpoint URI. And the producer doesn't have to iterate all consumers and call their callbacks one by one.

Update: I've written a more advanced proof-of-concept event notification with Apache Camel here.

3 comments:

  1. You can use ref in @EndpointInject to refer to an Endpoint which you can register in the Registry. This is more dynamic than the static url option offers.

    ReplyDelete
  2. And you can use @Consumer on your POJO to easily let it consume from some endpoint. See more at: http://camel.apache.org/pojo-consuming.html

    Thats more elegant.

    ReplyDelete
  3. Thank you Claus.

    I'll explore the @Consume on specific injected Endpoint object (not endpoint URI) as you suggested. There's no such example on http://camel.apache.org/pojo-consuming.html though. Adequate documentation on this technique would be very helpful.

    Anyway, I've posted another article on using Camel as event notification here:

    http://spring-java-ee.blogspot.com/2010/01/advanced-event-notification-framework.html

    Different technique, using Camel proxy beans instead of annotating POJOs.

    ReplyDelete