Skip navigation

For a personal project (a 2D scene graph project), I needed to implement some kind of event handling. The usual Java-way to do it is to provide a listener interface, with the appropriate methods. This looks like :

public interface MyEventListener {

  void handleEvent1(MyEvent event);

  void handleEvent2(MyEvent event);

}

When you want to react to an event (a so-called event handler), you would just implement this interface, and add it to the listeners of the object you want to ‘monitor’:

public class MyHandler implements MyEventListener {
    public void handleEvent1(MyEvent event){
      //...
    }

    public void handleEvent2(MyEvent event){
      //...
    }
}

This is a well known pattern.

But during development, I realized I had to add more kinds of events, and thus more kinds of listeners. Each listeners had more and more methods (the same event object could be fired in many situations).
Moreover, my event handlers required to implement many of the listeners, but not all of their methods.
In such situations, you have to implement several interfaces, implement all the methods

public class MyEventHandler implements MyListener1, MyListener2 {
   // implement all the methods...
}

and add an instance to all listener lists:

  MyEventHandler handler = new MyEventHandler();

  objectToMonitor.addListener1(handler);
  objectToMonitor.addListener2(handler);

So, in the process of reinventing the wheel just for fun, I looked for another way to achieve this.

I finally found that an event handler could be easily implemented using Java annotations.

An event handler would be a regular Java object (no listener interface to implement). An annotation would indicate which method should be called (based on the list of event names, but could be more complicated like regular expressions):

public class MyHandler { // No interface to implement!

   // Process 'event1'.
   @HandleEvent("event1")
   public void doSomething(MyEvent event) { ... }

   // Process 'event1' and 'event2' in the same method.
   @HandleEvent({"event1", "event2"})
   public void doSomethingElse(MyEvent event) { ... }
}

The nice part is that the handler object can be added using a single method:

  MyHandler handler = new MyHandler();

  objectToMonitor.addHandler(handler);

That way, all addXXXListener() methods are centralized into a single addHandler() entry point.

The class of ‘objectToMonitor’ must just be provided with the capability to dispatch (named) events to the annotated method of the handler.

Here is the code for such an event dispatcher :

public class EventDispatcher {
    private Collection handlers = new ArrayList();

    /**
     * Adds an event handler.
     */
    public void addHandler(Object handler) {
        this.handlers.add(handler);
    }

    /**
     * Removes an event handler.
     */
    public void removeHandler(Object handler) {
        this.handlers.remove(handler);
    }

    /**
     * Dispatch an event to the registered handlers.
     */
    public void dispatchEvent(Event event) {
        for (Object handler : handlers) {
            dispatchEventTo(event, handler);

        }
    }
    
    protected void dispatchEventTo(Event event, Object handler) {
        Collection methods = findMatchingEventHandlerMethods(handler, event.getName());
        for (Method method : methods) {
            try {
                // Make sure the method is accessible (JDK bug ?)
                method.setAccessible(true);

                if (method.getParameterTypes().length == 0)
                    method.invoke(handler);
                if (method.getParameterTypes().length == 1)
                    method.invoke(handler, event);
                if (method.getParameterTypes().length == 2)
                    method.invoke(handler, this, event);
            } catch (Exception e) {
                System.err.println("Could not invoke event handler!");
                e.printStackTrace(System.err);
            }
        }
    }

    /** 
     * Find all methods from the <em>handler</em> object that must be called, based on the presence
     * of the HandleEvent annotation. 
     */
    private Collection findMatchingEventHandlerMethods(Object handler, String eventName) {
        Method[] methods = handler.getClass().getDeclaredMethods();
        Collection result = new ArrayList();
        for (Method method : methods) {
            if (canHandleEvent(method, eventName)) {
                result.add(method);
            }
        }
        return result;
    }
    
    /**
     * Look for the annotation values.
     */
    private boolean canHandleEvent(Method method, String eventName) {
        HandleEvent handleEventAnnotation = method.getAnnotation(HandleEvent.class);
        if (handleEventAnnotation != null) {
            String[] values = handleEventAnnotation.value();
            return Arrays.asList(values).contains(eventName);
        }
        return false;
    }
}

The code for the annotation is trivial:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandleEvent {
    String[] value();
}

You can find the complete source code
here.

What do you think of it ?

Processing is a wonderful tool for visualization prototyping.

I developped a small applet to implement a ‘Spring layout’ graph algorithm.

This layout is basically a combination of spring forces (Hook’s law) and electrostatic repulsion forces (Coulomb’s law).

Enjoy!

Basically, a rule engine will provide more agility to your project :

  • rules are declarative and more expressive than nested IF/THEN/ELSE of your favorite programming language.
  • rules are more maintainable : adding/removing or modifying a rule is very easy and does not require to change the overall ruleset.
  • rule engines are powerful : rule languages provide powerful constructs and features that cannot be expressed easily in a normal programming language.
  • rule engines are very efficient : even if they do not have native code performance, rule engines provide optimizations so that performances are usually not an issue.

So, as a general rule, a rule engine is a very good way to add decision logic automation to your project if :

  • rules will change ‘often’ (more often than normal patch releases of your application).
  • you will have to deal with a lot of rules.
  • you can afford a rule engine specialist or a consultant in your dev-team.

In the contrary, I would not recommend to adopt rule engines you answer “no” to at least two of the items above.

  • rules will never change or change very rarely: hard coded decision logic could do the job.
  • you have very few rules : no need for a rule engine, you programming language should be enough.
  • You cannot afford a rule engine specialist : don’t risk to lose control over your project and avoid modules that no one can maintain.
Follow

Get every new post delivered to your Inbox.