It’s a given in any software application there are objects that need to share information in order to get work done. In Java applications, one way of achieving information sharing is to have event listeners, whose sole purpose is to take some action when a desired event occurs. For the most part this process works and most experienced Java developers are used to writing the requisite anonymous inner class that implements some event listener interface. This post is about taking a different approach to handling Java events using Guava’s EventBus. The EventBus allows for objects to subscribe for or publish events, without having explicit knowledge of each other. The EventBus is not meant to be a general purpose publish/subscribe system or support interprocess communication.
EventBus Class
The EventBus is very flexible and can be used as a singleton, or an application can have several instances to accomodate transferring events in different contexts. The EventBus will dispatch all events serially, so it is important to keep the event handling methods lightweight. If you need to do heavier processing in the event handlers, there is another flavor of the EventBus, AsyncEventBus. The AsyncEventBus is identical in functionality, but takes an ExecutorService as a constructor argument to allow for asynchronous dispatching of events.
Subscribing for Events
An object subscribes for events by taking the following steps:
- Define a public method that takes a single argument of the desired event type and place a @Subscribe annotation on that method.
- Register with the EventBus by passing an instance of the object to the EventBus.register method.
Here is a brief example with details omitted for clarity:
public class PurchaseSubscriber {
@Subscribe
public void handlePurchaseEvent(PurchaseEvent event) {
.....
}
.....
EventBus eventBus = new EventBus();
PurchaseSubscriber purchaseSubscriber = new PurchaseSubscriber();
eventBus.register(purchaseSubscriber);
There is another annotation that can be used in conjunction with @Subscribe, and that is @AllowConcurrentEvents. The @AllowConcurrentEvents marks a handler method as thread safe so an EventBus (most likely an AsyncEventBus) could potentially call the event handler from simultaneous threads. One interesting thing I discovered in unit testing is that if a handler method does not have the @AllowConcurrentEvents annotation, it will invoke handlers for an event serially even when using the AsyncEventBus. It’s important to note that @AllowConcurrentEvents will not mark a method as an event handler, the @Subscribe annotation still needs to be present. Finally, the event handling method must have one and only one parameter or an IllegalArgumentException will be thrown when you register the object with the EventBus.
Publishing Events
Publishing events with the EventBus is equally straight forward. In the section of code where you want to send the notification of an event, call EventBus.post and all subscribers registered for that event object will be notified.
public void handleTransaction(){
purchaseService.purchase(item,amount);
eventBus.post(new CashPurchaseEvent(item,amount));
....
}
While it might be obvious, it’s important that the subscribing and publishing classes share the same EventBus instance, and it would make sense to use Guice or Spring to help manage the dependencies.
More About Event Handlers
A very powerful feature of the EventBus is that you can make your handlers as course or fine grained as needed. The EventBus will call the registered subscribers for all subtypes and implemented interfaces of a posted event object. For example, to handle any and all events one could create an event handler that takes a parameter of type Object. To handle only singular events, create a handler that is very type specific. To help illustrate, consider the following simple event hierarchy:
public abstract class PurchaseEvent {
String item;
public PurchaseEvent(String item){
this.item = item;
}
}
public class CashPurchaseEvent extends PurchaseEvent {
int amount;
public CashPurchaseEvent(String item, int amount){
super(item);
this.amount = amount;
}
}
public class CreditPurchaseEvent extends PurchaseEvent {
int amount;
String cardNumber;
public CreditPurchaseEvent(String item,int amount, String cardNumber){
super(item);
this.amount = amount;
this.cardNumber = cardNumber;
}
}
Here are the corresponding event handling classes:
//Would only be notified of Cash purchase events
public class CashPurchaseSubscriber {
@Subscribe
public void handleCashPurchase(CashPurchaseEvent event){
...
}
}
//Would only be notified of credit purchases
public class CreditPurchaseSubscriber {
@Subscribe
public void handleCreditPurchase(CreditPurchaseEvent event) {
....
}
}
//Notified of any purchase event
public class PurchaseSubscriber {
@Subscribe
public void handlePurchaseEvent(PurchaseEvent event) {
.....
}
}
If there is a need to capture a broad range of event types, an alternative would be to have more than one event handling method in a class. Having multiple handlers could be a better solution as you would not have to do any “instanceof” checks on the event object parameter. Here’s a simple example from my unit test:
public class MultiHandlerSubscriber {
List<CashPurchaseEvent> cashEvents = new ArrayList<ashPurchaseEvent>();
List<CreditPurchaseEvent> creditEvents = new ArrayList<CreditPurchaseEvent>();
List<SimpleEvent> simpleEvents = new ArrayList<SimpleEvent>();
public MultiHandlerSubscriber(EventBus eventBus){
eventBus.register(this);
}
@Subscribe
public void handleCashEvents(CashPurchaseEvent event){
cashEvents.add(event);
}
@Subscribe
public void handleCreditEvents(CreditPurchaseEvent event){
creditEvents.add(event);
}
@Subscribe
public void handleSimpleEvents(SimpleEvent event){
simpleEvents.add(event);
}
....
Testing
Since the event handlers are just plain methods, they can easily be tested by instantiating an EventBus in the test case or simulate the EventBus by passing the appropriate event object. When working with the EventBus I found it was very easy to:
- Forget to register the subscribing object with the EventBus
- Neglect to add the @Subscribe annotation.
If it seems that an event handler is not being called, check for those two errors first.
A useful debugging technique is to subscribe to the DeadEvent class. An EventBus will wrap any published event with no handlers in a DeadEvent instance. DeadEvent provides the getEvent method that returns the original event object.
Conclusion
The Guava EventBus class provides an attractive and useful alternative to the standard Java event handling mechanism. It is hoped the reader will find the EventBus as useful as I do. As always comments and suggestions are welcomed.




What supri
If you have ever wondered why google guava uses strong references and this has been a problem for you, check out this message bus:
https://github.com/bennidi/mbassador
It uses weak references, support synchronous and asynchronous message delivery, object filtering etc. Handlers are marked using annotations (pretty much the same as in guava). It’s quite stable and production ready (has been used in production for half a year now). And it’ fast!
I created a performance and feature comparison for a selection of available event bus implementations including Guava, MBassador and some more. The results are quite interesting. Check it out here
http://codeblock.engio.net/?p=37
Bennidi,
Congratulations on your new project. The performance of MBassador looks impressive. Thanks for sharing!
Regards,
Bill
@Bill, I don’t really get the multithreading handling in EventBus http://goo.gl/oRz02 … There is a ThreadLocal isDispatching variable and if you look at dispatchQueuedEvents() method, Cliff Biffle tests whether this thread is dispatching right now. How the hell ONE thread can be dispatching and test if (isDispatching.get()) at the same time
? In general I don’t get how a Boolen ThreadLocal variable of type : “Am I Doing Something” make any sense… It should be : “Is any other thread doing something”, right? I can’t figure it out, could you please have a look ? Just out of curiosity
This is how it should be for it to make sense to me :
private volatile boolean unlocked = true;
private Monitor.Guard available = new Monitor.Guard(monitor) {
public boolean isSatisfied() {
return unlocked;
}
};
protected void dispatchQueuedEvents() {
if (monitor.enterIf(available)) {
unlocked = false;
try {
while (true) {
EventWithHandler eventWithHandler = eventsToDispatch.get().poll();
if (eventWithHandler == null) {
break;
}
dispatch(eventWithHandler.event, eventWithHandler.handler);
}
} finally {
unlocked = true;
}
}
}
Hey, very helpful blog post. Just so you know, Monitor. reevaluateGuards(); method has been removed from Monitor, so it won’t compile.
Joseph,
Thanks for the comments. Also thanks for the heads up on the code change. I’ll need to go back and fix my examples to match up with the latest version of Guava.
Cheers,
Bill
I am currently using eventBus heavily in my app, sync and async.
And i blame you for this.
Hopefully you can find more time to share other guava stuffs.
Thanks a lot !
Albert,
Thanks for the comments. I’m glad to hear that the EventBus is working for you. I do plan on getting back to some more Guava posts at some point.
Cheers,
Bill
Nice explanation of the event bus. I’ve, coincidentally, followed up with a blog post showing how to use the EventBus with Guice.
http://spin.atomicobject.com/2012/01/13/the-guava-eventbus-on-guice/
Justin, Thanks for the comments. Using the EventBus with Guice is a great topic. I’ll be sure to check it out.
Hello,
From what I see in your article, @Subscribe annotation is mandatory. Then, annotation @AllowConcurrentEvents supports the async mode. I think that this annotation should be removed and the characteristic “allow concurrent events” should be just a property of the @Subscribe.
Anyway, thanks for your explanation!
@Ngoc
I did not think about it before, but now that you point that out, I agree with you.
I believe it is the case but I was not certain from the documentation. The publishing supports inheritance of message type, yes? E.g.,
@Subscribe
public void smorgasbord(Object o) {
System.out.println(o);
}
Will print every published message to the console?
@binkley
Yes messages are delivered to any handler that the event object is assignable to. So from the example you presented here, every published message would be printed to the console
Great explanation, that’s exactly what the Javadocs are missing. Also thanks for your other Guava articles!
Matt,
Thanks for the comments and you’re welcome!
Guava developer here — if you’re using EventBus with some kind of dependency injection, you may be able to set up your injector to register _every_ created object with the EventBus. This can avoid issues with forgetting to register objects explicitly.
This would be pretty neat. Any idea how to do it?
A little late, but here’s a good Spring example:
http://pmeade.blogspot.com/2012/06/using-guava-eventbus-with-spring.html
I am curious how you find the performance. Event driven programming seems like a good fit for many aspects of game programming if it were performant enough.
Wow, I didn’t even know that there was an eventbus in the guava libs !
Thank you for that code !
@Martin
You’re welcome!