The Guava EventBus on Guice

Guava’s EventBus provides a publish-subscribe event mechanism which allows objects to communicate with each other via the “Observer Pattern”:http://en.wikipedia.org/wiki/Observer_pattern. The EventBus shies away from the traditional “Event Listener” pattern seen in Java where an object implements a particular interface and then explicitly subscribes itself with another object.

In a recent project we chose to use the EventBus in conjunction with “Guice”:http://code.google.com/p/google-guice/ (a dependency injection library) and have had a lot of success with it. Specifically, objects in our system only have to express _what_ events they care about without being required to explicitly register with the EventBus or any other object.

Before we go over how we bootstrapped EventBus with Guice, I think it would be a useful exercise to review the traditional and “non-guice” approaches to subscribing to events in order to illustrate the advantages of the EventBus/Guice partnership.

h2. Traditional

As stated above — the traditional method requires an interface declaration, an explicit subscription, and knowledge of the object that is posting the particular event. Additionally, it forces the object that is posting the event to invent its own method of publishing the event.

public interface ApplicationEvent {
    public void invoke(Object value);
}

public class ApplicationEventListener implements ApplicationEvent {
    private Application application;

    public ApplicationEventListener(Application application) {
        this.application = application;
        this.application.addApplicationEventListener(this);
    }

    public void invoke(Object value) {
        // handle event
    }
}

public class Application {
    private ArrayList<ApplicationEvent> events = new ArrayList<ApplicationEvent>();

    public void addApplicationEventListener(ApplicationEvent event) {
        events.add(event);
    }

    public void postEvent() {
        for (ApplicationEvent event : events) {
            event.invoke(this);
        }
    }
}

h2. Non-Guice EventBus

Using the EventBus eliminates the need for an interface or reference to the object that is posting the event. However, the _ApplicationEventListener_ is still required to reference a global EventBus and register itself with it. The coupling between the global EventBus and the _ApplicationEventListener_ eliminates the possibility of providing a flexible way of swapping out one EventBus with another without having to refactor the _ApplicationEventListener_.

public class ApplicationEvent {
    private final Object value;

    public ApplicationEvent(Object value) {
        this.value = value;
    }
}

public class ApplicationEventListener {
    public ApplicationEventListener() {
        Application.globalEventBus().register(this);
    }
    
    @Subscribe
    public void applicationEvent(ApplicationEvent event) {
        // handle event
    }
}

public class Application {
    private static EventBus eventBus = new EventBus("Default EventBus");


    public void postEvent() {
        eventBus.post(new ApplicationEvent(this));
    }

    public static EventBus globalEventBus() {
        return eventBus;
    }
}

h2. EventBus on Guice

We are almost there! The example below utilizes Guice to _inject_ an instance of the EventBus into the objects that require it — giving us the flexibility to swap one EventBus with another without having to refactor code. Unfortunately, it requires that we _inject_ an EventBus instance into objects that are only going to register with it and never reference it again.

public class ApplicationModule extends AbstractModule {
    private EventBus eventBus = new EventBus("Default EventBus");
    
    @Override
    protected void configure() {
        bind(EventBus.class).toInstance(eventBus);
    }
} 

public class ApplicationEventListener {
    private Application application;

    @Inject
    public ApplicationEventListener(EventBus eventBus) {
        eventBus.register(this);
    }

    @Subscribe
    public void applicationEvent(ApplicationEvent event) {
        // handle event
    }
}

public class Application {
    private EventBus eventBus;

    @Inject
    public Application(EventBus eventBus) {
        this.eventBus = eventBus;
    }
    
    public void postEvent() {
        eventBus.post(new ApplicationEvent(this));
    }
}

h2. EventBus on a lot of Guice

Finally, we have arrived at our destination. Using Guice, we bind a _TypeListener_ to every object that is created and _ensure_ that it is registered with our default EventBus. Objects that subscribe to particular events are no longer required to explicitly subscribe with an EventBus and only need to _express_ what kind of events they are interested in.

public class ApplicationModule extends AbstractModule {
    private final EventBus eventBus = new EventBus("Default EventBus");

    @Override
    protected void configure() {
        bind(EventBus.class).toInstance(eventBus);
        bindListener(Matchers.any(), new TypeListener() {
            public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
                typeEncounter.register(new InjectionListener<I>() {
                    public void afterInjection(I i) {
                        eventBus.register(i);
                    }
                });
            }
        });
    }
}

public class ApplicationEventListener {
    private Application application;

    @Subscribe
    public void applicationEvent(ApplicationEvent event) {
        // handle event
    }
}

public class Application {
    private EventBus eventBus;

    @Inject
    public Application(EventBus eventBus) {
        this.eventBus = eventBus;
    }

    public void postEvent() {
        eventBus.post(new ApplicationEvent(this));
    }
}

Conversation
  • Chris Wash says:

    Nice writeup – I didn’t see your example code for the generic EventBus usage w/o Guice — both of your snippets look like they initialize the EventBus in a Guice bind.

    • Justin DeWind Justin DeWind says:

      Good catch Chris. I must have, at some point, messed up my gist and overwrote it with the example following it. I’ve updated the example.

  • Cliff L. Biffle says:

    Good find — EventBus was specifically designed for your last technique. I wrote EventBus in 2006 down the hall from where Bob Lee was writing what became Guice, and each influenced the other’s design. This is why EventBus.register doesn’t throw if an object lacks @Subscribe annotations, for example.

    Glad you’re enjoying it!

    • Justin DeWind Justin DeWind says:

      We are certainly happy with it and the pattern we’ve been able to pull out of it.

      I would have been pretty disappointed if register threw if an object lacked the @Subscribe annotation. I’m of the school that exceptions should indicate a real problem — as opposed to using it for flow control. So I thank you for not having register throw. :-)

      Thank you again for writing EventBus. The combination of EventBus with Guice (GuiceBus?) has been a real gem.

  • Jan says:

    I want to use Eventbus with Vaadin (Servlet). I have to be extra careful with scopes. How can I use the TypeListener Example with Session-Scopes? I cannot have a Singletone Eventbus inside my modules. Thanks for any hint … Great post, btw!

    • Justin DeWind Justin DeWind says:

      Great question. It should be possible to do this and I’ve provided an example that may do what you are asking for. Because TypeListeners are not privy to the injection context it requires that we use a static proxy — which isn’t particularly nice since global state should be frowned upon.

      Here is what the example does:

      * Binds any requests for an EventBus to a provider that is scoped to a session
      * Creates a public class that implements TypeListener which uses a holder object that is statically injected with the injector
      * For every type that is encountered during injection it is registered with the event bus in that given session scope

      This may be subject to a number of pitfalls and it should be thoroughly tested. :-) The example can be found here.

  • Ray Ryan says:

    This is really slick.

    You’re still on your own for unregistering as objects go out of scope or whatever, right? Although IIRC EventBus uses weak references so its not a catastrophe if someone forgets.

    • Cliff L. Biffle says:

      Ray, EventBus does not use weak references — it’s a feature we’re considering though. (It was really designed with Crash-Only systems in mind, where unregistering isn’t such an issue.)

      • Justin DeWind Justin DeWind says:

        Thanks for answering Cliff,

        As far as I knew it did not use weak references. For objects that have a limited scope it would require an explicit action (or event) to ensure the object unregisters.

  • Extremely nice article Justin, thanks a lot for sharing!

    Is the code shown in this post available for public domain? I mean, is there any issue if that code would be integrated in an OSS project, mentioning the original source in the NOTICE file?

    Many thanks in advance, all the best!
    -Simo

    • Justin DeWind Justin DeWind says:

      This code can be used freely for whatever purpose you see fit. Treat it as MIT licensed source code.

  • Comments are closed.