Generic, annotation-driven event notification frameworks
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:
- A nightmare to debug
- Difficult to follow (from a maintenance perspective, or someone attempting to change something 6 months down the line)
- Full of
if (event instanceof NodeCreatedEvent)
like code. Why this is better than subclassing anadapter
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.
skaffman
Updated on June 29, 2022Comments
-
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 over 15 years2. 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 over 15 years3. 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 over 15 yearsYou'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 over 15 yearsIt'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 over 14 yearsI knew such a thing would exist somewhere :)
-
bennidi over 11 yearsYou 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).