Pub/Sub with MBassador, an introduction.

A little while back I wanted to use the pub/sub pattern in one of my Java projects, so I set about searching for a good library to allow me to do this without reinventing the wheel.

Fortunately for me, I came accross MBassador, a super EventBus implementation that was just what I was after. What follows is an introduction to the main features, as well as a few opinions on why I think it is a great little library to keep in your back pocket.

A brief background: What is Pub/Sub?

Before I give an introduction to the technology, let me give a very short intro to the pub/sub pattern, just so we're all on the same page. Pub/Sub (short for publish-subscribe) is a pattern that promotes the modelling of messages into classes, which can then be published and subscribed to (does what it says on the tin, huh?).

The huge benefit of this pattern is that it allows publishers to completely lose the need to know about subscribers, and vice-versa - subscribers need not know anything about publishers. Everything is done using a centralised point of communication. This makes for modular, decoupled software.

However this also means we should choose carefully. A cetralised structure allowing us to communicate between modules and remain loosely coupled is great - but it also gives us a single point of failure that could bring down our whole application. So yes, making the right choice is important.

Introduction to MBassador

MBassador is an open source EventBus implementation, written in Java. It allows the publishing of events and the subscribing to other events. Or the same events, whatever you want.

Here is what that looks like in MBassador:

Create a bus

MBassador eventBus = new MBassador();  

Starting off pretty simple, I know. But now we have a bus to play with!

Let's publish an event

BlogFinishedEvent blogFinished = new BlogFinishedEvent();  
eventPus.publish(blogFinished);  

Again, I don't think there is anything too unexpected here. We can create an event (which is just a class instance), and publish that on our newly created bus. So far so good.

Time to subscribe

The above isn't much use until we have something subscribed. Although this is the pub/sub pattern - our publisher remains blissfully ignorant to it's uselessness, and has no idea no one is listening to it!

class BlogFinishedListener {

    @Handler
    public void handle(BlogFinishedEvent blogFinished) {
        celebrate();
        notifyTwitterFollowers();
        // TODO: Profit?
    }

}

eventBus.subscribe(new BlogFinishedListener());  

Note the @Handler annotation of the handle method. This is the marker for which event is subscribed to, and what will be called when the event is received. This is nice, because it means you can add event-handling code to existing components. It also means you can have a listener that subscribes to multiple different events.

Hands off my references

By default MBassador holds weak references to the listener instance. This is very helpful, but also can be a little gotcha if, like in this example, you have an object that's sole purpose is to handle an incoming event.

It's good because we can subscribe existing instances within a system, and have them perform some actions on some event. And when we no longer need the instance and remove reference within our system? No problem - the GC can run as normal and the instance will get cleaned up, because the event bus is not holding any strong references.

However this is easy to forget, and if we write what I have above, the listener instance will get cleaned up the first time the GC runs. Luckily, MBassador provides the ability to specify strong references. Let's write that listener class definition again:

@Listener(reference = Reference.Strong)
class BlogFinishedListener {

    @Handler
    public void handle(BlogFinishedEvent blogFinished) {
        celebrate();
        notifyTwitterFollowers();
        // TODO: Profit?
    }

}

With the @Listener class annotation the references are kept strong, and while we keep a reference to our MBassador instance, the references to our listeners will remain intact.

Don't care about an event anymore?

No problem. MBassador gives us the ability to dynamically manage our subscriptions. If we no longer care about an event, we can simply unsubscribe our listener.

eventBus.unsubscribe(listenerObject);  

Event hierarchy

Less a specific feature of MBassador, more a general benefit of events-based programming in this way, but still something I would quickly like to mention.

Let's say we have the following event class defintions:

public abstract class ArticleFinishedEvent { }

public class NewsStoryFinished extends ArticleFinishedEvent { }

public class BlogFinished extends ArticleFinishedEvent { }  

This means we could create the following handler, and subscribe to the abstract event:

@Listener(reference = Reference.Strong)
class ArticleFinishedListener {

    @Handler
    public void handle(ArticleFinishedEvent articleFinished) {
        System.out.println("Woohoo!");
    }

}

eventBus.subscribe(new ArticleFinishedListener());  

Fairly clear what would happen, isn't it? The following two lines would both trigger the above handle method to run:

eventBus.publish(new BlogFinished());  
eventBus.publish(new NewsStoryFinished());  

I think this is nice, and an elegant way to handle abstractions within an event-based system.

Don't make me wait

By default a call to eventBus.publish is synchronous. However MBassador also provides eventBus.publishAsync which means an immediate return and no blocking while a handler executes.

Is there more?

There certainly is. However with this post, I just wanted to give a quick example of how to get up and running, as well as a little taste of the MBassador API (which I am a huge fan of).

There are a lot more features, including message filtering, custom error handling and more more that is possible. I am planning to do a couple of blog posts specifically on a few of these features, but until then I recommend you check out the MBassador Wiki for more info.

Let's recap

So here are the reasons I enjoy using MBassador, and think it is a great addition to your Java toolbelt:

  • Well designed API. Reads nicely, is consistent and clear.
  • Easy to just use. Many configuration options are available, but sensible defaulting means little work is needed to just use out of the box.
  • Performant. Here is a report (sure, written by the creator, but still) documenting some performance numbers.
  • It is open source :)