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 IEventBroker, 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:
    1. org.eclipse.osgi.services
    2. org.eclipse.e4.core.services
    3. org.eclipse.e4.core.di.extensions
  2. Open the Rainbow class and inject an instance of IEventBroker into a private field broker:
    @Inject
    private IEventBroker broker;
  3. Modify the selectionChanged method, so that instead of setting a selection, it uses the IEventBroker to post the color asynchronously to the rainbow/color topic:
    public void selectionChanged(SelectionChangedEvent event) {
      // selectionService.setSelection(event.getSelection());
      IStructuredSelection sel = (IStructuredSelection)
       event.getSelection();
      Object color = sel.getFirstElement();
      broker.post("rainbow/color", color);
    }
  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/color") to pick up the data from the event:
    @Inject
    @Optional
    public void receiveEvent(
     @EventTopic("rainbow/color") 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]
      {org.eclipse.e4.data=Orange} to handler
      org.eclipse.e4.core.di.internal.extensions.
        EventObjectSupplier$DIEventHandler@4ad72dc9
    !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:68)
    
  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(
     // @EventTopic("rainbow/color") String data) {
     @UIEventTopic("rainbow/color") 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 most of the lower level implementations are based on events. Either the Eclipse IEventBroker 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 IEventBroker or can be created manually. Each Event has an associated topic, which is a String identifier that allows producers and consumers to co-ordinate 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

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 the sendEvent or send methods
  • Asynchronously, using the postEvent or post methods

    Note

    Synchronous or asynchronous event delivery?

    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 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 made 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/application/).

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

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