May 25, 2011

Groovy Goodness: Add Java-style Listener Support with ListenerList Annotation

To implement a bean event pattern in Java a class must be able to register or remove event listeners. Then the list of listeners can be used to fire methods defined by the listeners. Setting up a list of listeners, providing the correct add and remove listener methods, add fire event methods is all boilerplate code we have to write each time. With Groovy we don't have to, we simply apply the @ListenerList annotation to a typed collection field in our class.

Groovy will add the add and remove listener methods, a method to get the assigned listeners and methods to fire event methods defined in the listener interface. All this is done before compilation, so the resulting class file contains all the necessary code and is therefore usable in plain Java code without any Groovy dependencies. Notice the collection field must have generic type, because otherwise the correct code cannot be generated.

// Simple listener interface with spoke() method.
interface TalkListener {
    void spoke(TalkEvent event)

// Event to be passed into listener spoke() method.
class TalkEvent {
    String text, origin

class Speaker {
    List<TalkListener> talkListeners

    String name

    void sayHello() {
        // fireSpoke is added by @ListenerList.
        fireSpoke(new TalkEvent(origin: name, text: 'Hello Groovy world!'))

    // Methods generated by @ListenerList:
    // void addTalkListener(TalkListener)
    // void removeTalkListener(TalkListener)
    // TalkListener[] getTalkListeners()
    // void fireSpoke(TalkEvent)

def s = new Speaker(name: 'mrhaki')
// Add listener interface implementation to simply output
// the data in the event object.
s.addTalkListener([spoke: { event ->
   println "$event.origin says '${event.text.toLowerCase()}'"
}] as TalkListener)
// Second implemenation of listener interface is added.
def shouter = { event -> println "${event.text.toUpperCase()}" } as TalkListener
s.addTalkListener shouter

// Output:
// mrhaki says 'hello groovy world!'