Time for action – dealing with events

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.

  1. Add the following bundles as dependencies to the 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
  2. Open the Rainbow class and obtain an instance of EventBroker through injection.
  3. Modify the 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);
    }
  4. Open the 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);
    }
  5. Run the application, and switch to the Rainbow tab. Select an item in the list, and go back to the Hello tab. Nothing will be displayed. Open the Console view in the host Eclipse, and an error will be visible:
    !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)
  6. This happens because the dispatched event runs on a non-UI thread by default. There are two ways of solving this problem; either re-dispatch the call to the UI thread, or use @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);
    }
  7. Run the application, go to the Rainbow tab and select a color. Switch back to the Hello tab and the text of the label will be updated appropriately.

What just happened?

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).

  • Synchronously, using sendEvent() or send()
  • Asynchronously, using postEvent() or post()

Tip

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/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset