The E4 Eclipse application uses events internally to manage the state of the user interface. The decoupling allows the user interface mechanisms to be separated from the user interface renderer, which allows different user interfaces to be presented (such as JavaFX).
There is an E4-specific wrapper for the EventAdmin
service called the IEventBroker
. This provides a simple mechanism to post
or send
objects to a particular topic, as well as frontends to register event listeners. It has specific ties to E4 and is present in the UI package. Create a plug-in named com.packtpub.e4.advanced.event.e4
.
The IEventBroker
can be injected into an E4 component using standard injection techniques, and from that, events can be posted synchronously or asynchronously. Create a class named E4Sender
in the com.packtpub.e4.advanced.event.e4
plugin.
Obtaining the service in E4 is done through injection. Since the sender requires this, it will be a non-optional component:
@Inject IEventBroker broker;
Having obtained the broker service, it can be used to send the e-mail event, in the same way as the previous EventAdmin
example:
public void send() { String topic = "smtp/high"; String body = "Sample email sent via event at " + System.currentTimeMillis(); Map<String, String> email = new HashMap<String, String>(); email.put("Subject", "Hello World"); email.put("From", address); email.put("To", address); email.put("DATA", body); broker.send(topic, email); }
The IEventBroker
has the same kind of event delivery as with the EventAdmin
service; it can either be used to send events asynchronously with post
, or synchronously with send
.
There is a subtle difference between EventAdmin
and IEventBroker
. The former only accepts a Map
or Dictionary
, while the latter takes any object. If a Map
or Dictionary
is passed to IEventBroker
, it will be passed straight through to the EventAdmin
without modification. If the object passed is of another type, it will be wrapped in a Map
with the key IEventBroker.DATA
(org.eclipse.e4.data
).
Since the IEventBroker
uses EventAdmin
under the covers, it is possible to process an Event
with the same mechanism as used earlier in the chapter. However, there is an easier way to receive events with E4 using the @EventTopic
and @UIEventTopic
annotations. Create a class named E4Receiver
in the com.packtpub.e4.advanced.event.e4
plugin.
A method in an E4 component can be annotated with the @Inject
and @Optional
annotations, and the argument can be annotated with the @EventTopic
annotation.
If the argument is an OSGi Event
, then it will be passed through as it is. This allows the complete set of properties to be pulled from the Event
object and processed in one call:
@Inject LogService log; @Inject @Optional void receive(@EventTopic("smtp/*") Event event) { log.log(LogService.LOG_INFO, "Received e-mail to " + event.getProperty("To")); }
The event will be delivered on a background thread. If the processing of the event requires UI interactions, then these will need to be remapped to run on the UI thread instead. E4 provides a UISynchronizer
that allows code to run on the UI thread.
There is an alternative annotation that can be used to indicate that the event needs to run on the UI thread. Modifying the annotation to @UIEventTopic
instead of @EventTopic
will result in the code being automatically run on the UI thread:
void receive(@UIEventTopic("smtp/*") Event event) { … }
When setting up an event handler in E4 (whether @UIEventTopic
or @EventTopic
), the documentation suggests using a non-public method. The injector can call non-public methods, and by making the methods non-public, it is ensured that they are not called directly. However, marking them as private
may result in the compiler or IDE eliminating the code. Therefore, a minimum of package-level or protected
access should be used for such handler methods.
It's possible to subscribe event handlers using the EventAdmin
directly, but it's also possible to use the IEventBroker
to subscribe
(and unsubscribe
) event handlers. As with EventAdmin
, the subscription takes a topic and the handler. By default, calls will be run on the UI thread.
@Inject IEventBroker broker; public void subscribeUI(EventHandler handler) { // will be called on the UI thread broker.subscribe("smtp/*", handler); }
This allows a custom-built handler to receive events and guarantee that they will be run on the UI thread. E4 uses an (internal) implementation of EventHandler
, which delegates to the passed handler, optionally wrapping it in a UISynchronizer
.
Create a class named E4Subscriber
in the com.packtpub.e4.advanced.event.e4
plugin. To use the IEventBroker
class without taking the UI thread, or to pass in a filter, there is an alternative method that provides more options:
public void subscribe(EventHandler handler) { broker.subscribe("smtp/*", "(Subject=Hello World)", handler, true); }
The final parameter is whether to run the method headlessly or not (in other words, do not run in the UI). In this case, the true
value says that this event should not be run in the UI.
This API is mainly useful if a handler always needs to be run in the UI. Alternatively, the body of the handler can trivially wrap itself in a UI block, such as a UIJob
or via the UISynchronize
class.
For events that do not need the UI, it is generally more efficient to register them with EventAdmin
directly.
When writing code to handle events, either the EventAdmin
or the IEventBroker
can be used. Although it may appear that the IEventBroker
isolates the caller from having to deal with OSGi classes, if there are several properties that need to be acquired, then casting to an OSGi Event
is often necessary.
The other problem is that the IEventBroker
has direct references to the EventHandler
class in the unsubscribe
and subscribe
methods. So, any dependency on the IEventBroker
will automatically have a dependency on the OSGi EventAdmin
classes.
The IEventBroker
adds two things that are useful in an E4 application:
When writing a handler that needs UI or E4 injection, use the IEventBroker
to register or use the @EventTopic
or @UIEventTopic
annotations.