Event Bus¶
Krail uses a publish - subscribe Event Bus in place of the common Event Listener based Observer pattern. The Guava EventBus page gives a good summary of the reasons for choosing this approach (under One Minute Guide). In fact, the idea was originally to use the Guava implementation, but MBassador was chosen in its place because:
- it supports both weak and strong references (weak by default). Guava supports only strong references.
- it supports synchronous and asynchronous buses
Overview¶
Most of the work is done in the EventBusModule
, which binds bus
instances to match the Krail scopes of @Singleton,
@VaadinSessionScope and @UIScope. You don’t have to use all
three, but there is a natural correlation between a bus and a scope.
The 3 implementations are represented by 3 binding annotations @GlobalMessageBus, @SessionBus and @UIBus respectively - each of which also is used to bind implementations for bus configuration, bus configuration error handlers and publication error handlers.
This means that by simple configuration changes in EventBusModule, you have 3 possible bus implementations matching Guice scopes, with each having individual configuration objects, individual error handlers if required and a choice between synchronous and asynchronous messaging.
The EventBusModule also uses Guice AOP to automatically subscribe classes annotated with @Listener.
The @GlobalMessageBus is asynchronous by default, as most of the publishers and subscribers are likely to be @Singletons, and therefore thread-safe. The other 2 buses are synchronous.
All of the Guice configuration can of course be changed by replacing /
sub-classing EventBusModule and updating your BindingManager
in the
usual way.
Publishing Messages¶
Simply inject the @GlobalMessageBus, @SessionBus or @UIBus you want:
public class MyClassWithPublish {
private final eventBus;
@Inject
protected MyClassWithPublish(@SessionBus PubSubSupport eventBus){
this.eventBus=eventBus;
}
}
Use an existing BusMessage
implementation, or create your own
message class - which can be anything which implements the
BusMessage
interface. (There are no methods in the interface, it is
there for type safety and to help identify message classes). At the
appropriate point in your code, publish your message: ` public void
someMethod(){ …. do stuff … eventBus.publish (new MyBusMessage(this,
someMoreInfo)); } `
Subscribing to Messages¶
Annotate the class with @Listener (which can also specify strong references)
Annotate the method within the listener class which will handle the message with @Handler. The method must have a single parameter of the type of message you want to receive
@Handler
public void MyMessageHandlerMethod(MyBusMessage busMessage){
MyClassWithPublish sender = busMessage.getSender();
}
There are some other very useful features such as Filters and Priority .. see the MBassador documentation.
Automatic Subscription¶
During object instantiation, Guice AOP uses an InjectionListener in the
EventBusModule
to intercept all objects whose class is annotated
with @Listener. The rules defining which bus to subscribe to are defined
in an implementation of EventBusAutoSubscriber, which you can of course
replace by binding a different implementation. The default
implementation uses the @SubscribeTo to complement the association
rules:
if a @SubscribeTo annotation is present, the buses defined by the annotation are used, and no others (Services are an exception, see below) if a @SubscribeTo annotation has no arguments, it is effectively the same as saying “do not subscribe to anything even though this class is marked with a @Listener” if there is no @SubscribeTo annotation a Singleton is subscribed to the @GlobalMessageBus anything else is subscribed to @SessionBus Note that @Listener and @SubscribeTo are inherited, so can be used on super-classes, but be overridden if re-defined in a sub-class.
Services and Messages¶
Service implementations make use of the Event Bus to automate starting /
stopping and restarting interdependent services. Many Service
implementations are @Singletons (though they do not have to be), so
the @GlobalMessageBus is used and ALL Service objects are
automatically subscribed though AbstractService
to the
@GlobalBus. It is probably unwise to change that.