There's a more generic way of passing information between components in Eclipse 4, using the OSGi EventAdmin
service. This is a message bus, like JMS, but operates in memory. There is also an Eclipse-specific EventBroker
, which provides a slightly simpler API to send messages.
com.packtpub.e4.application
project, by double-clicking on the project's META-INF/MANIFEST.MF
file and going to the Dependencies tab:org.eclipse.osgi.services
org.eclipse.e4.core.services
org.eclipse.e4.core.di.extensions
Rainbow
class and obtain an instance of EventBroker
through injection.selectionChanged()
method, so that instead of setting a selection, it uses the EventBroker
to post()
the color asynchronously to the rainbow/colour
topic.public void selectionChanged(SelectionChangedEvent event) { //The following commented line needs to be removed /*selectionService.setSelection(event.getSelection());*/ IStructuredSelection sel = (IStructuredSelection) event.getSelection(); Object colour = sel.getFirstElement(); broker.post("rainbow/colour",colour); }
Hello
class, and add a new method receiveEvent()
. It should be annotated with @Inject
and @Optional
. The parameter should be defined with an annotation @EventTopic
("rainbow/colour"
) to pick up the data from the event. It will look like:@Inject @Optional public void receiveEvent( @EventTopic("rainbow/colour") String data) { label.setText(data); }
!MESSAGE Exception while dispatching event org.osgi.service.event.Event [topic=rainbow/colour] to handler org.eclipse.e4.core.di.internal.extensions. EventObjectSupplier$DIEventHandler@1a91782f !STACK 0 org.eclipse.e4.core.di.InjectionException: org.eclipse.swt.SWTException: Invalid thread access at org.eclipse.e4.core.internal.di.MethodRequestor. execute(MethodRequestor.java:63)
@UIEventTopic
instead of @EventTopic
. Modify the receiveEvent()
as follows:public void receiveEvent(
//The following commented line needs to be removed
/*@EventTopic("rainbow/colour") String data) {*/
@UIEventTopic("rainbow/colour") String data) {
label.setText(data);
}
Events allow components to communicate in a highly decoupled mechanism. Using events to pass data means that the only shared context is the name of the topic.
The OSGi EventAdmin
service is a key component and will always be available in current Eclipse 3.x and E4 applications, since much of the lower-level implementations are based on events. Either the Eclipse EventBroker
wrapper or the EventAdmin
can be used, depending on personal preferences. However, if the code is to be used in other OSGi systems, building directly on top of the EventAdmin
will give the greatest portability.
The Event
object is either created automatically using EventBroker
or can be created manually. Each Event
has an associated topic, which is a String
identifier that allows producers and consumers to coordinate with each other.
Map<String, Object> properties = new HashMap<String,Object>(); properties.put("message","Hello World"); properties.put(IEventBroker.DATA,"E4 Data Object"); eventAdmin.postEvent(new Event("topic/name",properties));
Note, that if the object passed in is a Map
or Dictionary
, it gets passed to the EventAdmin
as is. To pass a Map
and receive it using the E4 tools, another Map
, must be created and passed in with the IEventBroker.DATA
key. Alternatively, the EventAdmin
service can be used directly.
The Event
can be posted synchronously (that is on the same thread as the delivery agent) or asynchronously (on a non-background thread).
sendEvent()
or send()
postEvent()
or post()
Synchronous or Asynchronous?
Generally using asynchronous delivery is recommended, since synchronous delivery will block the calling thread. When delivering events from the UI asynchronous delivery should always be used, since it is not possible to place any bounds on how long the event receivers may take to execute.
To receive an event, a listener needs to be registered with the topic. This can be done via the OSGi EventAdmin
service, or with the @EventTopic
and @UIEventTopic
annotations on a method marked with @Inject @Optional
.
If an Event
needs to be processed on the UI thread, it should not be sent synchronously from the UI thread. Doing so invites delays and blocking the UI, since it is possible for other listeners to pick up on the event and do excessive computation on an unnecessary thread. Instead, send it from a non-UI thread, and in the event hander, delegate it to the UI thread or consume it via the @UIEventTopic
annotation.
The topic name is specified in the annotation (or via the subscription in the EventHandler
interface). Topic names can be any string, but are typically separated with /
characters. This is because the OSGi specification allows for subscription to both topics by exact match and to partial matches. The subscription topic/*
will pick up both topic/name
and topic/another/example
. Note that it is not a regular expression; the topics are explicitly delimited by the /
character, and /*
means "and everything below". So topic/n*e
won't match anything, and nor will topic/*/example
.
To be more selective about the topics subscribed, use the EventAdmin EVENT_FILTER
to specify an LDAP-style query. Subscribe to the highest level that makes sense (such as topic/*
) and then use an LDAP filter to refine it further, using event.filter
with (event.topic=topic/n*e)
.
Currently, the annotations cannot be used to apply an LDAP filter, but it's possible to register an EventHandler
interface which supplies this property.
Finally, it is conventional for the topic name to be constructed from the same kind of reverse domain names used for bundles, with .
replaced with /
for example com/packtpub/e4/app/
.