Generic, annotation-driven event notification frameworks

13,306

Solution 1

You can already do this today with EventBus.

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.

I'm currently researching about event notification frameworks on Publish-Subscribe Event Driven Programming | Kev's Spring vs Java EE Dev and the followup articles.

Solution 2

I've made http://neoevents.googlecode.com to handle this kind of annotation based event handler.

@actionPerformed
private void onClick() {
    //do something
}

protected void initComponents() {
    JButton button = new JButton("Click me!!!");
    button.addActionListener(new ActionListener(this) );
}

It looks as simple as I was expecting it to be. Annotations are available for every single listener in J2SE.

Solution 3

Don't mistake complicated for clever. It seems to me that this would be:

  1. A nightmare to debug
  2. Difficult to follow (from a maintenance perspective, or someone attempting to change something 6 months down the line)
  3. Full of if (event instanceof NodeCreatedEvent) like code. Why this is better than subclassing an adapter I have no idea!

Solution 4

The main problem I see here are the method parameters, which restrict which methods can actually be used for which events, and there's no compile-time help for that.

This is what makes interfaces attractive to me for observer pattern implementations like the Java event model. Tools like eclipse can autogen method stubs so you can't get the signatures wrong. In your example, it's very easy to use the wrong parameter type and never know it until an event occurs (which might be an error case several months down the line)

One thing you might try are my annotations & processor for implementing observers and null object implementations. Suppose you have

package a.b.c;

public interface SomeListener {
    void fee();
    void fie();
    void fo();
    void fum();
}

and wanted to create a listener instance. You could write

package x.y.z;

import a.b.c.SomeListener;
import com.javadude.annotation.Bean;
import com.javadude.annotation.NullObject;

@Bean(nullObjectImplementations = {@NullObject(type = SomeListener.class) })
public class Foo extends FooGen implements SomeListener {
    @Override
    public void fie() {
        // whatever code you need here
    }
}

To create a source for these events, you can write

package a.b.c;

import com.javadude.annotation.Bean;
import com.javadude.annotation.Observer;

@Bean(observers = {@Observer(type = SomeListener.class)})
public class Source extends SourceGen {
    // SourceGen will have add/remove listener and fire methods
    //   for each method in SomeListener
}

See http://code.google.com/p/javadude/wiki/Annotations if you're interested. Might give you some other ideas as well.

Solution 5

Google Guava v11 has added an EventBus component that uses this style. They also explain why they decided to use annotations rather than interfaces.

Share:
13,306
skaffman
Author by

skaffman

Updated on June 29, 2022

Comments

  • skaffman
    skaffman almost 2 years

    While simple, interface-driven event notification frameworks in Java have been around since pre-Cambrian times (e.g. java.beans.PropertyChangeSupport), it is becoming increasingly popular for frameworks to use annotation-driven event notification instead.

    For an example, see JBossCache 2.2. The listener class has its listener methods annotated, rather than conforming to a rigid interface. This is rather easier to program to, and easier to read, since you don't have to write empty implementations of listener callbacks that you're not interested in (and yes, I know about listener adapter superclasses).

    Here's a sample from the JBossCache docs:

    @CacheListener
    public class MyListener {
       @CacheStarted
       @CacheStopped
       public void cacheStartStopEvent(Event e) {
             switch (e.getType()) {
                case Event.Type.CACHE_STARTED:
                   System.out.println("Cache has started");
                   break;    
                case Event.Type.CACHE_STOPPED:    
                   System.out.println("Cache has stopped");
                   break;    
             }
       }    
    
       @NodeCreated    
       @NodeRemoved
       @NodeVisited
       @NodeModified
       @NodeMoved
       public void logNodeEvent(NodeEvent ne) {
             log("An event on node " + ne.getFqn() + " has occured");
       }
    

    }

    The problem with this, is that it's very much more of an involved process writing the framework to support this sort of thing, due to the annotation-reflection nature of it.

    So, before I charge off down the road of writing a generic framework, I was hoping someone had done it already. Has anyone come across such a thing?

  • skaffman
    skaffman over 15 years
    2. I don't see how. Your IDE will show you references to the annotations just as easily as it will show you references to interfaces.
  • skaffman
    skaffman over 15 years
    3. Because you only get one shot at subclassing, and adapters are poor way of using it. What if you've already subclassed? You either need to take the hit and implement every method with empty stubs, or introduce a delegate object to handle the events.
  • skaffman
    skaffman over 15 years
    You're absolutely correct about the lack of static type checking, but in some cases it's a worthwhile trade-off. Having an untyped mechanism here allows you decouple the notification mechanics (e.g. threading) from the notification itself. Stongly-typed interfaces would limit this decoupling.
  • oxbow_lakes
    oxbow_lakes over 15 years
    It's too easy to change some interception logic (or a method name) and "miss" the fact that all of a sudden your program just does not work as it is supposed to. Like I said, "complicated" and "clever" are not synonymous
  • skaffman
    skaffman over 14 years
    I knew such a thing would exist somewhere :)
  • bennidi
    bennidi over 11 years
    You can also check out MBassador github.com/bennidi/mbassador It is annotation driven, very fast, uses weak references (thus easy to integrate in environments where objects lifecycle management is done by a framework like spring or guice or somethign).