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.
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_.
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.
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.
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.
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.
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.
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!
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.
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.
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.)
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?
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.
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.
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!
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.
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!
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.
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.
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.)
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
This code can be used freely for whatever purpose you see fit. Treat it as MIT licensed source code.
thanks a lot Justin!