Wednesday, December 30, 2009

How Java Developers Solve the Event Notification Problem

Event Notification ... Event Handling ... Publish-Subscribe Messaging ... Message Passing ... or whatever you call it.

I'm not the first person to care about this problem. (but it's a problem that I'd rather not care about, especially after I met Qt's Signals and Slots... Java beaten by C++? Come on!)

Note: I'm not talking about enterprise or distributed messaging, just typical event handling mechanism in a single application.

Here are some examples of people contributing solutions to Event Notification in one way or another:
I'm not so impressed. This is such a fundamental infrastructure problem that seems virtually untouched by the Java developers... for almost 15 years!!

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.

EventBus Event Notification Framework

EventBus is an Event Notification Framework.

Following example is from EventBus Getting Started guide.

Statusbar that updates based on published events, and no need to register statusbar control/widget as listener of publisher(s). Without EventBus, statusbar will need to be added as listener to many classes. Statusbar can also be created and destroyed at any time.

public StatusBar extends JLabel {
    public StatusBar() {
        AnnotationProcessor.process(this);
    }
    @EventSubscriber(eventClass=StatusEvent.class)
    public void updateStatus(StatusEvent statusEvent) {
        this.setText(statusEvent.getStatusText();
    }
}

A similar project is ELF (Event Listener Framework) but it seems to be less mature.

Taken from my StackOverflow answer about Generic annotation-driven event notification framework.

Generics for Event Listener Pattern

To workaround the somethingListener -> somethingEvent mapping.

I'm not the first person to care about Event Notification. (but it's a problem that I'd rather not care about, especially after I met Qt's Signals and Slots... Java beaten by C++? Come on!)
    Maybe we can use Java 5 generics to save some code...
    // Common interface for all listeners. 
    public interface Listener<E> {
      public void notify(E event);
    }
    
    public class PedalEvent {
      // any custom properties wherever necessary
    }
    
    public class PedalListener implements Listener<PedalEvent> {
      public void notify(PedalEvent event) {
         // ... implementation ... 
      } 
    } 
    
    public class Car {
      public void addPedalListener(Listener<PedalEvent> pedalListener); 
      public void removePedalListener(Listener<PedalEvent> pedalListener); 
    } 
    
    
    What do you think?

    Event Driven Publish-Subscribe Implementation Ideas

    Some ideas for impementing publish-subscribe event notification:

    Publish-Subscribe Event Driven Programming

    Publish-Subscribe messaging consists of loosely coupled system of topic/channel, publisher (provider), and subscriber (consumer).

    Event Driven Programming allows the an objects to notify (i.e. publish an event) to observers.

    Java programming model has long used the Observer/Listener pattern approach, and also Whiteboard pattern in OSGi environments.

    However, there is no easy way and/or standardized convention to implement this.

    Implementing the Observer pattern requires an object to extend Observable, which is "not POJO". Having listeners also clutter the class and there are a lot of boilerplate code to add:

    private List<ClickListener> clickListeners = new ArrayList<ClickListener>();
    private List<TouchListener> touchListeners = new ArrayList<TouchListener>();
    public boolean addClickListener(ClickListener clickListener);
    public boolean addTouchListener(TouchListener touchListener);
    public List<ClickListener> getClickListeners();
    public List<TouchListener> getTouchListeners();
    public boolean removeClickListener(ClickListener clickListener);
    public boolean removeTouchListener(TouchListener touchListener);
    private void fireClick(ClickEvent clickEvent);
    private void fireTouch(TouchEvent touchEvent);

    See what I mean? That's only for two listeners, and those are only method signatures.

    The implementation are probably trivial, but it's very boring to repeat these over and over again in most of your classes.

    And what if the implementation isn't perfect? Will you change all those?

    Over the next few posts, I'll explore the potential solutions.
     
    Copyright 2009 Spring vs Java EE Web Dev. Powered by Blogger Blogger Templates create by Deluxe Templates. WP by Masterplan