Event Admin Service specification explained by Example Print E-mail
Written by Valery Abu-Eid   
Wednesday, 19 November 2008 22:07

Event Admin Service specification of the OSGi Compendium Services provides developers with a standard way for working with events in the OSGi Environment using the publish/subscribe model.

Unlike JMS, Event Admin service doesn't persist events, as such, any unprocessed event will be lost after restarting the application.

Working with the Event Admin service you will be Publishing Events and Listening to them.

The example application and its source code are contained in event-admin-example.zip. The example application was tested with Equinox, Felix and Knopflerfish.

Publishing Events

To publish an event using the Event Admin service you will need to specify two parameters: Topic Name on which the event will be published and Event Properties that may contain environment and domain specific info, e.g., for an event that informs about account registration the topic name can has the name "accountRegistered" (please note that such naming for topics is not recommended, proper naming will be covered below) and Event Properties which contain the AccountID property that describes the registered account and a property that describes the publisher (something like the ID of the bundle that published the event).

Topic Naming Rules

Topic Name tokens are separated by '/', moving from left to right the Topic Name should be more specific. The Topic Name 'org/dynamicjava/osgi/dynamic_jpa/EntityManagerFactory/REGISTERED' follows this rule, as you can see it begins with the name of the organization and moving from left to right it becomes more specific to a level where it specifies the status of a domain entity that has been changed, such naming will simplify the lives of people who subscribe to these topics, so if a person needs to subscribe to all events published by DynamicJava.org all he needs to do is to subscribe to the topic 'org/dynamicjava/*', if he is interested only in events that published by the Dynamic-JPA project all he needs to do is to subscribe to the topic 'org/dynamicjava/osgi/dynamic_jpa/*', etc. Topic Names are intended solely for dispatching purposes, any other info like event source, environment or domain specific info should be placed in Event Properties.
Placing non-primitive types (types other than string and and the eight primitive types) as Event Property values is far form being considered a best practice since in cases where the handler of your event exists in a different environment (different VM for example), the handler might not be able to deserialize the property.

How to publish an event?

To publish an event you need to:

  • Acquire a reference to the EventAdmin OSGi service, it implements the org.osgi.service.event.EventAdmin.
  • Pick a topic name for the event and make sure that it follows Topic Naming Conventions mentioned above.
  • Fill Event Properties in a dictionary that will be passed as a parameter to the publish method.
  • Having the Topic Name and Properties, ready invoke one of the following methods of the Event Admin service: postEvent or sendEvent - while postEvent initiates synchronous delivery of the event, sendEvent initiates asynchronous delivery of the event. So by default, your option should be postEvent method unless you have strict requirements to not continue execution until all handlers of the event handle it.

Below are two example classes, the Activator which runs the publisher class and binds the EventAdmin service to it and EventPublisher which publishes events every 20 seconds using the EventAdmin service bound to it.


import org.osgi.service.http.EventAdmin;
import ...;

public class Activator implements BundleActivator {
	
	//@Override
	public void start(BundleContext bundleContext) throws Exception {
		/// OsgiServiceBinder is a utility class that binds OSGi Services
		/// to properties using setter methods. We will use it here to
		/// bind the EventAdmin to the EventPublisher.
		binder = new OsgiServiceBinder(bundleContext);
		binder.bind(getEventPublisher(), "setEventAdmin",
			ServiceFilter.forInterface(EventAdmin.class.getName()));
		
		getEventPublisher().start();
	}
	
	//@Override
	public void stop(BundleContext bundleContext) throws Exception {
		getEventPublisher().stop();
	}
	
	
	private OsgiServiceBinder binder;
	
	private EventPublisher eventPublisher = new EventPublisher();
	public EventPublisher getEventPublisher() {
		return eventPublisher;
	}
	
}


import org.osgi.service.event.Event;
import org.osgi.service.http.EventAdmin;
import ...;

public class EventPublisher {
	
	public void publish() {
		if (getEventAdmin() != null) {			
			getEventAdmin().postEvent(new Event("org/dynamicjava/test1",
					getEventProperties("1")));
			getEventAdmin().postEvent(new Event("org/dynamicjava/test1",
					getEventProperties("2")));
			getEventAdmin().postEvent(new Event("org/dynamicjava/test2",
					getEventProperties("1")));
		}
	}
	
	
	protected Dictionary getEventProperties(String property1Value) {
		Dictionary result = new Hashtable();
		result.put("property1", property1Value);
		return result;
	}
	
	
	private EventAdmin eventAdmin;
	public EventAdmin getEventAdmin() {
		return eventAdmin;
	}
	public void setEventAdmin(EventAdmin eventAdmin) {
		this.eventAdmin = eventAdmin;
	}
	
	/// Other Supportive Code
	/// ...
	
}
	

Handling Events

To handle events using the EventAdmin service you need to register an object that implements the EventHandler interface as an OSGi Service with properties that indicates the kind of events in which the handler is interested.

There are two handler properties that specify the type of events you are interested in: Event Topic and Event Properties filter. The name of the Event Topic property can be retrieved from the constant org.osgi.service.event.EventConstants.EVENT_TOPIC and Event Properties Filter from EventConstants.EVENT_FILTER.

The value of the Topic Name pattern property must be of type String[]. This value contains the names of the topics that this handler is interested in. A wildcard '*' can be used as the last token of a Topic Name, e.g., the value 'org/osgi/framework/*' matches topic name 'org/osgi/framework/BundleEvent/REGISTERED', but the value 'org/osgi/framework/Bundle*' is considered invalid.

The value of the Event Properties filter is of type String and contains an LDAP filter that will be matched against event properties, e.g., The Event Properties Filter '(&(productCategory=books)(price>=80))' filters all events that has the 'books' category and price is more than or equal 80.

EventConstants.TOPIC_NAME property is mandatory!

If the handler service doesn't provide this property it will receive no events. If you want to subscribe to all of the events in the environment then you should specify the value '*' for it.

How to handle events?

To handle events you need to:

  • Specify the topics that you want to subscribe to - Use wilcards when applicable.
  • Specify the filter for your event properties if you have such a requirement.
  • Register an OSGi service that implements the org.osgi.service.event.EventHandler interface and set the values that you specified in the previous two steps in the dictionary object that represents handler service properties.

Below are two example classes, the Activator which registers the event handler as a service, and the ExampleEventHandler which handles the events.


import org.osgi.service.event.EventConstants;
import org.osgi.service.http.EventAdmin;
import ...;

public class Activator implements BundleActivator {
	
	//@Override
	public void start(BundleContext bundleContext) throws Exception {
		bundleContext.registerService(EventHandler.class.getName(),
				new ExampleEventHandler("Handler 1"),
				getHandlerServiceProperties("org/dynamicjava/test1"));
		
		bundleContext.registerService(EventHandler.class.getName(),
				new ExampleEventHandler("Handler 2"),
				getHandlerServiceProperties(
						new String[] { "org/dynamicjava/*" },
						"(property1=2)"));
	}
	
	//@Override
	public void stop(BundleContext bundleContext) throws Exception {
	}
	
	
	protected Dictionary getHandlerServiceProperties(String... topics) {
		Dictionary result = new Hashtable();
		result.put(EventConstants.EVENT_TOPIC, topics);
		return result;
	}
	
	protected Dictionary getHandlerServiceProperties(
			String[] topics, String filter) {
		Dictionary result = new Hashtable();
		result.put(EventConstants.EVENT_TOPIC, topics);
		result.put(EventConstants.EVENT_FILTER, filter);
		return result;
	}
	
}


import org.osgi.service.event.Event;
import org.osgi.service.http.EventAdmin;
import org.osgi.service.event.EventHandler;

public class ExampleEventHandler implements EventHandler {

	public void handleEvent(Event event) {
		System.out.println(String.format(
				"# Event Handler '%s' handled event on topic '%s':"
					+ " Value of 'property1' = %s",
				getHandlerName(),
				event.getProperty(EventConstants.EVENT_TOPIC),
				event.getProperty("property1")));
	}
	
	
	public ExampleEventHandler(String handlerName) {
		this.handlerName = handlerName;
	}
	
	private final String handlerName;
	protected String getHandlerName() {
		return handlerName;
	}
	
}
	

EventAdmin Service specification Implementations

All of the main OSGi Frameworks (Equinox, Felix and Knopflerfish) provide an implementation of the EventAdmin service specification. The implementation that was used by the Example Application is the one provided by Felix since it was the easiest to use and compatible with other frameworks.