Sep 13, 2017

Easy event bus for your Java applications

I'll show you how to use simple event bus, that is suitable for any Java application and can be seamlessly integrated with spring or any other DI framework.

This is MiniBus project: https://github.com/javaplugs/minibus

You may wonder why you should use this event processing bus instead of other implementation from big companies? The answer is, because it follows KISS principle (Keep It Simply, Stupid). It contains only several hundreds lines of code. Every one can read it, understand it and modify it.

If you do not really know why you need event bus, than maybe your app is small and does not require publishing/handling events flow.

Project configuration

It is just simple dependency for maven/gradle without any additional stuff. Just go to the Minibus github page where you will find instruction how to get latest version.

Define events model

In Java world in order to send something you should describe it model first.

Minibus require all your events to have EventBusEvent interface. But you can create any hierarchy you'd like. In this example we will define base event class for our application.
// In many cases you should not do it
// You can just implements EventBusEvent in your DTOs
public abstract class AppEvent implements EventBusEvent {
    // Events should be immutable to all properties better to be declared as final
    protected final int code;
    // In our example we will have common property for all our events
    protected AppEvent(int code) {
        this.code = code;
    }
}
Now we can define our events model.
public class GoodStuff extends AppEvent {
    // Some code here with constructor and super call
    // Additional properties also can be here
}

public class BadStuff extends AppEvent {
    // Some code here with constructor and super call
    // Additional properties also can be here
}

public class SomeStuff extends AppEvent {
    // Some code here with constructor and super call
    // Additional properties also can be here
}

Events handlers (subscribers)

Overall you should consider that each event have to be handled separately, thus for each event you will have one or many separate handlers (subscribers). For our particular case we should create abstract handler class just to force all events to be subclassed from AppEvent.
// No need for this if you do not have your basic event class or interface
public abstract class AppHandler<E extends AppEvent> extends EventBusHandler<E> {
    // Maybe your handlers will have common logic 
    // to deal with AppEvent code property
}
And now we can create handlers for our events.

public abstract class GoodHandler extends AppHandler<GoodStuff> {
    protected void handle(GoodStuff event) {
        // Now you can do something with your event here
    }
}

public abstract class BadHandler extends AppHandler<BadStuff> {
    protected void handle(BadStuff event) {
        // Now you can do something with your event here
    }
}

Initializing event bus

This is last configuration step before we will be ready to publish our events.
// I prefer to wrap event bus in separate service 
// that can be used as Spring bean or any DI instance
@Named
public class EventService extends AppHandler<GoodStuff> {
    private final EventBus<AppEvent, AppHandler<AppEvent>> bus;

    // Spring or other DI framework should pass
    // here all available implementations of AppHandler
    @Inject
    public EventService(List<AppHandler> handlers) {
        this.bus = new EventBusAsync();
        handlers.stream().forEach(this.bus::subscribe);
    }

    // Just a shortcut
    public void publish(AppEvent event) {
        this.bus.publish(event);
    }
}
Main thing you should note here is that EventBus implementations use WeakLink for subscribers. This allows you to unsubscribe from event bus without any direct calls. When there will be no hard links left to subscribed handler instance, it will be automatically unsubscribed and garbage collected.

The downside of this approach is that if you want your handler to be available for whole app lifetime you should have separate long lived hard link to it somewhere in your app. It is not required for Spring environment if your handlers instantiated as beans, because all beans by default treated as singletons, thus Spring would have hard links to each of them.

Publishing and handling events

Only thing you need access from your code is EventService. Just create your event's DTO and publish it.
public class SomewhereInYourCode {
    // Considering that such property exists in your class
    EventService eventSrv;

    public void something() {
        // Publishing events
        ebb.publish(new GoodStuff());
        ebb.publish(new BadStuff());
        // publish method exits immediately 
        // handlers would run in separate threads
    }

    // If you would like to know event handling result 
    // you can add success/failure callbacks
    public void somethingWithCallbacks() {
        // Publishing events
        ebb.publish(new GoodStuff(),
            (event, handler) -> {
            // Will be called for each handler associated with event
            // If there was no exceptions thrown
            },
            (event, handler, throwable) -> {
            // Will be called for each handler associated with event
            // If event processing failed with exception
            }

        );
    }
}

Advanced features

As you would kindly notice we still have no handler for SomeStuff event here. Let's say we want to handle SomeStuff event with other events in one handler for whatever stupid reason.
// It is definitely important to use superclass for event type
// If you want to handle many events types at once you can access them only by common interface
public class AllStuffHandler extends AppHandler<AppEvent> {

    // FIRST you must override this method and return null
    // Event bus will realize that it should not link this handler with event class
    @Override
    protected Class&AppEvent&gt getLinkedClass() {
        return null;
    }

    // SECOND override canHandle method
    // Should return true if you interested in processing particular event type
    @Override
    public boolean canHandle(Class cls) {
        // Always return true -> will handle any event 
        return true;
    }

    @Override
    protected void handle(AppEvent event) {
        // do something with GoodStuff, BadStuff and SomeStuff events
    }
}

Conclusion

As you can see only with several lines of code your application can have event bus with only one tiny single dependency.

I do not want to describe all possible applications here.  But it is extremely handy if you need to link some logic with some modules that are not even implemented yet. Just determine what events you should publish and publish them. Handlers can be added later without touching other parts of code.