Monday, August 5, 2013

Using the Event Broker without DI

The new EventBroker in e4 is a very handy tool to send events globally without the need to allow listeners to register. Listeners may subscribe to events on the event bus instead of finding an instance that supports addEventListener().

If you are running an E4 application you may directly inject the IEventBroker. If DI is not available you may consume the IEventBroker as a service, which works nicely for "old school" plug-ins that run within an e4 application.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Creating an event publisher

Lets start with a fresh Plug-in Project named com.example.eventbroker. Then continue by creating a PublisherJob implementation:
package com.example.eventbroker;

import java.util.HashMap;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.ui.PlatformUI;

public class PublisherJob extends Job {

 private static int mCounter = 0;
 
 public PublisherJob() {
  super("Publisher");

  schedule();
 }

 @Override
 protected IStatus run(IProgressMonitor monitor) {
  Object service = PlatformUI.getWorkbench().getService(IEventBroker.class);
  if (service instanceof IEventBroker) {
   ((IEventBroker) service).post("com/example/eventbroker/basic", new Object());

   HashMap<String, Object> data = new HashMap<String, Object>();
   data.put("Event origin", "unknown");
   data.put("Event number", mCounter++);
   ((IEventBroker) service).post("com/example/eventbroker/advanced", data);
   schedule(1000);
   return Status.OK_STATUS;
  }
  
  return Status.OK_STATUS;
 }
}
Each posted event contains a topic and optional event data. Topics use hierarchies separated by slashes. If the data parameter is a Map it will be directly used for the event. If it is something else it will be wrapped inside a Map. So if you want to transport more than a single object use a Map<String, Object> to encapsulate your data.

To resolve IEventBroker you need to add a dependency to org.eclipse.e4.core.services. For the rest we additionally need org.eclipse.core.runtime and org.eclipse.ui.workbench.

The Job in the example will publish 2 events each second.

Step 2: Creating a subscriber

Create the Subscriber class:
package com.example.eventbroker;

import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.ui.PlatformUI;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;

public class Subscriber implements EventHandler {

 public Subscriber() {
  Object service = PlatformUI.getWorkbench().getService(IEventBroker.class);
  if (service instanceof IEventBroker) 
   ((IEventBroker) service).subscribe("com/example/eventbroker/*", this);
 }

 @Override
 public void handleEvent(Event e) {
  System.out.println("-------------------------------------------");
  System.out.println(e.getTopic());
  for (String name: e.getPropertyNames())
   System.out.println("\t" + name + ": " + e.getProperty(name));
 }
}
The subscriber subscribes to dedicated topics. Wildcards (*) may be used to subscribe to event groups. Subscribing to "*" will create notifications on any event.

e.getPropertyNames() may be used to retrieve the keys of the event data.

You need to add an additional dependency to org.eclipse.osgi.services to resolve EventHandler.

Step 3: Autostart our sample code

To run our little example we have to add an Activator:
package com.example.eventbroker;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.ui.PlatformUI;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

 public void start(BundleContext bundleContext) throws Exception {

  Job job = new Job("Starting Publisher/Subscriber") {

   @Override
   protected IStatus run(IProgressMonitor monitor) {
    if (PlatformUI.isWorkbenchRunning()) {

     Object service = PlatformUI.getWorkbench().getService(IEventBroker.class);
     if (service instanceof IEventBroker) {
      // broker available, create publisher
      new PublisherJob();
      new Subscriber();
     }
     
    } else
     // workbench not running yet, try again in 1 second
     schedule(1000);

    return Status.OK_STATUS;
   }
  };

  job.schedule();
 }

 public void stop(BundleContext bundleContext) throws Exception {
 }
}
Basically a job runs continuously until the workbench is up and running. Afterwards we look for the Broker and create publisher and listener.

Register the Activator in your plugin.xml and set Auto-Start to true for com.example.eventbroker in your launch configuration.

1 comment: