This article is based on Spring Dynamic Modules in Action, published on September, 2010. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks ]
also read:
Introduction
The core of the OSGi technology is a standard that’s described in the OSGi core specification. OSGi also comes with the compendium services specifications that build on the features provided by the core specification. Each compendium service addresses a particular requirement and can be used on any conformant OSGi platform. The Event Admin Service provides a standardized and centralized service to manage events triggered by entities used within the OSGi container. It’s based on an event channel called topic, which makes it possible for entities to subscribe to, receive, and send events. At the moment, Spring DM doesn’t support this service directly, but it’s possible to implement a bridge between Spring and Spring DM events and the Event Admin Service.
We’ll first explain how to install and use the service and the concepts behind linking Spring DM to the service. Then we’ll see how to implement this bridge.
OSGi Event Admin Service
Before using the Event Admin Service with Spring DM, we need to know how to install it, how to reference the corresponding OSGi service, and how to send and receive events.
Installing the Event Admin Service
Table 1 lists the components needed to install the Event Admin Service from the SpringSource EBR.
Adding these two components within your OSGi container will install the Event Admin Service.
Accessing the Event Admin Service using Spring DM
Once the implementation bundle for the Event Admin Service has been installed, a service will appear in the OSGi service registry under the name org.osgi.service.event.EventAdmin corresponding to the service interface.
This service can be referenced using Spring DM just like any other service, by using its reference XML element and then injecting the result into any bean. This is shown in the following snippet:
<osgi:reference id="eventAdminService" interface="org.osgi.service.event.EventAdmin"/> <bean id="eventAdminManager" class="com.manning.sdmia .compendium.event.EventAdminManager"> <property name="eventAdminService" ref="eventAdminService"/> </bean>
In the preceding snippet, the OSGi service for the Event Admin Service is referenced with the identifier org.osgi.service.event.EventAdmin. This service can then be injected in the eventAdminManager bean using its eventAdminService property.
The EventAdminManager class configured in the preceding snippet contains a configured reference to the installed Event Admin Service, enabling the class to interact with it. Listing 1 shows the structure of the class.
Listing 1 Structure of the EventAdminManager class
public class EventAdminManager { private EventAdmin eventAdminService; #A (...) public EventAdmin getEventAdminService() { #B return eventAdminService; } public void setEventAdminService( #B EventAdmin eventAdminService) { this.eventAdminService = eventAdminService; } } #A Attribute for Event Admin Service instance #B Accessor and mutator for service instance
Now that the eventAdminService property is set, all the methods of the EventAdmin interface can be used to interact with the service. Before describing how to implement a bridge between Spring DM and the Event Admin Service, we’ll first see how to exchange events.
Interacting with the Event Admin Service
The Event Admin Service is primarily used to send events. This can be done explicitly using the EventAdmin service that we automatically registered. This service provides a sendEvent method that takes an event object to send an event. The following snippet shows how to send an event.
Event event = new Event(topicName, eventProperties); eventAdmin.sendEvent(event);
The Event Admin Service can also implement event listeners based on the whiteboard pattern. These listeners must implement the EventHandler interface, and they have to be registered as OSGi services. When an event occurs, the Event Admin Service dispatches the event to all the registered implementations by calling their handle-Event method. The following snippet shows a sample implementation of the Event-Handler interface.
public class SampleEventHandler implements EventHandler { public void handleEvent(Event event) { (...) } }
Event data can be accessed using properties contained in the instance of the Event class passed as arguments to the handleEvent method. The class contains the event topic and its associated properties. The event topic has the same value as the topic-Name variable used to send the event.
Now that you know how to use the Event Admin Service to send and receive events, let’s see how we can use it with Spring DM.
Linking Spring DM and the OSGi Event Admin Service
Spring DM and Spring raise a set of events during their normal operation. Applications, and components, in our case, can register event listeners to be notified of these events when they occur. These system elements then use their own strategies for implementing event handling and delivering messages.
In Spring, listeners are implemented as regular classes implementing the ApplicationListener interface. The scope of this event system is restricted to the owning Spring application context, and with this framework events are propagated synchronously.
In Spring DM, events are intended to be consumed by bundles other than the one that triggers them. Event handling is based on the whiteboard pattern, which allows for the decoupling of event producers from their consumers and provides support for registering and unregistering listeners dynamically. In the context of this pattern, delivery has important requirements because OSGi is dynamic and the implementation mustn’t freeze the entire system. For this reason, Spring DM delivers events asynchronously to registered listeners.
In the next sections, we’ll describe how to link Spring DM and Spring events to the Event Admin Service. When events occur in either framework, they’re redirected to this service.
Handling Spring DM events
A dedicated Spring DM listener receives any Spring DM events and forwards them to the Event Admin Service. To do so, the listener builds an event compatible with the Event Admin Service using data contained in the initial Spring DM event and then sends it using the EventAdmin service API.
This listener isn’t provided by Spring DM and needs to be implemented. Figure 1 summarizes the steps for implementing the link between Spring DM events and the Event Admin Service.
First, like every Spring DM event listener, the bridge listener must implement Spring DM’s OsgiBundleApplicationContextListener interface. Spring DM dispatches events to these listeners registered as services following the whiteboard pattern. The bridge listener references the EventAdmin service through the OSGi service registry and sends events to the Event Admin Service when it receives events from Spring DM. Every class registered as a listener on the Event Admin Service will then be notified.
This mechanism allows for third-party bundles to receive events and notifications from Spring DM without having to be tied to its use.
Handling Spring events
With Spring, things are a little different because its event management is completely independent from OSGi, and event listeners must be directly defined within the Spring application context. Having detected all these configured classes, Spring notifies them when an event occurs. For our purposes, we can use a special Spring application listener that receives these events and forwards them to the Event Admin Service.
Figure 2 summarizes the steps for implementing the link between Spring events and the Event Admin Service.
First, like every Spring application event listener, the bridge listener must implement Spring’s ApplicationListener interface. Spring dispatches events to these listeners configured within the Spring container. So far, nothing is specific to OSGi.
The bridge listener then references the EventAdmin service through the OSGi service registry and sends events to the Event Admin Service when it receives events from Spring. Every class registered as a listener on the Event Admin Service will then be notified.
This mechanism allows for third-party bundles to receive events and notifications from Spring without having to be tied to the framework. Note that the bridge listener must deliver events asynchronously.
Now that we’ve described how to have events triggered by Spring and Spring DM dispatched to the Event Admin Service, we’ll focus on how to implement the bridge listener for Spring DM events.
Implementing the bridge between Spring DM and Event Admin Service
Handling Spring DM events consists of creating a listener class that implements Spring DM’s OsgiBundleApplicationContextListener interface. Having registered this listener as an OSGi service, this class will then be able to handle Spring DM events and forward them to the Event Admin Service.
By implementing this interface, the class must also define the onOsgiApplication-Event method. This method is called when an event is triggered and is responsible for handling it correctly.
Because the class provides the link with the Event Admin Service, it has an attribute of type EventAdmin. We’ll configure this attribute later on by using the corresponding EventAdmin OSGi service referenced with Spring DM facilities.
Listing 2 shows the structure of this listener implementation.
Listing 2 A listener implementation linking Spring DM and the Event Admin Service
public class EventAdminAdapterListener implements OsgiBundleApplicationContextListener { #A private EventAdmin eventAdmin; public void onOsgiApplicationEvent( #A OsgiBundleApplicationContextEvent event) { String topicName = buildTopicName(event); #B Properties eventProperties #B = buildEventProperties(event); #B eventAdmin.sendEvent(new Event( #B topicName, eventProperties)); } (...) public void setEventAdmin(EventAdmin eventAdmin) { this.eventAdmin = eventAdmin; } } #A Implements Spring DM listener interface and method #B Creates, configures, and sends event
Building the event for the Event Admin Service requires several steps, which are implemented in listing 2 by the buildTopicName and buildEventProperties methods within the onOsgiApplicationEvent method. We’ll describe the implementation of buildTopicName and buildEventProperties methods shortly.
The first step in building the event is creating the name of the topic on which the event will be published. Topic names allow classification of topics using tokens separated by the slash (/) character. The following snippet shows how to use the class name of the Spring DM event to build the topic name:
private String buildTopicName(OsgiBundleApplicationContextEvent event) { String eventClassName = event.getClass().getName(); return eventClassName.replace(".", "/"); }
Properties for the event then need to be specified—they should contain all the information describing the event. In our context, this information can be obtained from the event instance provided by Spring DM. Listing 3 shows how to build properties for an event compatible with the Event Admin Service.
Listing 3 Implementing buildEventProperties to build event properties
private Properties buildEventProperties( OsgiBundleApplicationContextEvent event) { Properties eventProperties = new Properties(); eventProperties.put("event.timestamp", #A event.getTimestamp()); Object source = event.getSource(); eventProperties.put("event.source.class", #A source.getClass().getName()); Bundle bundle = event.getBundle(); eventProperties.put("event.bundle.id", #B bundle.getSymbolicName()); #B eventProperties.put("event.bundle.state", #B bundle.getState()); (...) return eventProperties; } #A Sets general properties #B Sets properties related to bundle
Implementations of the buildTopicName and buildEventProperties methods provide all of the information necessary to build an Event instance, which is the event class for the Event Admin Service. This instance can then be used when using the sendEvent method of the EventAdmin instance to send the event.
The last step consists of configuring the bridge listener. Because this listener uses the EventAdmin service, it first needs to be referenced using Spring DM’s facilities. The listener itself can then be configured as a bean in the Spring application context and exported as an OSGi service with the same facilities. Once these configurations are done, the listener is ready to receive events from Spring DM and forward them to the Event Admin Service.
Listing 4 shows how to configure the bridge listener.
Listing 4 Configuring the bridge listener
<bean id="eventAdminAdapterListener" #A class="org.springframework.osgi .compendium.event.EventAdminAdapterListener"> <property name="eventAdmin" #A ref="eventAdminService"/> </bean> <osgi:reference id="eventAdminService" interface="org.osgi.service.event.EventAdmin"/> <osgi:service ref="eventAdminAdapterListener" #B interface="org.springframework.osgi .context.event.OsgiBundleApplicationContextListener"/> #1 Defines listener and injects EventAdmin service #2 Registers listener as OSGi service
We saw how to implement and configure a bridge listener that receives and forwards Spring DM events to the Event Admin Service. This listener is primarily a Spring DM listener that builds a new Event Admin Service event from the initial Spring DM event and sends it to interested parties via the EventAdmin service.
Let’s look now at how to implement listeners in Spring DM that can receive and handle events triggered by the Event Admin Service.
Implementing OSGi event handlers
Creating event listeners for the Event Admin Service involves implementing the EventHandler interface, which requires defining a handleEvent method that is called whenever an event occurs. The event is provided as parameter to this method.
Listing 5 shows how to implement such a listener.
Listing 5 An event listener implementation for the Event Admin Service
public class AdminEventHandler implements EventHandler { public void handleEvent(Event event) { String topicName = event.getTopic(); String[] propertyNames = event.getPropertyNames(); for (int cpt=0; cpt<propertyNames.length; cpt++) { String propertyName = propertyNames[cpt]; String propertyValue = (String)event.getProperty(propertyName); } } }
From the event instance passed as a parameter, we can get the topic names and the properties of the event and iterate through them.
Because the Event Admin Service follows the whiteboard pattern for finding all the registered listeners, the listener we just defined must also be defined as an OSGi service under the name org.osgi.service.event.EventHandler. Spring DM can be used to accomplish this. The event.topics service property can also be added when exporting the listener to specify which topic it’s interested in. Wildcards are allowed here.
TIP Topic names support hierarchies and become more specific when going from left to right. You can take advantage of this to define an event handler that receives a set of event kinds. For example, specifying the expression org/springframework/osgi/* in the event.topics property of an event handler service will allow the handler to receive events whose names begin with org/springframework/osgi/. This means using hierarchies when creating event names is very important.
Listing 6 shows how to configure a listener for the Event Admin Service using Spring DM’s facilities.
Listing 6 Configuring the Event Admin Service listener using Spring DM
<bean id="eventAdminHandler" class="org.springframework.osgi.compendium.event.AdminEventHandler"/> <osgi:service ref="eventAdminHandler" interface="org.osgi.service.event.EventHandler"> <osgi:service-properties> <entry key="event.topics" value="org/springframework/osgi/*"/> </osgi:service-properties> </osgi:service>
The listener is configured as bean within the configuration and then registered as an OSGi service with Spring DM’s service XML element. Within this element, the topic names are specified in service properties with the event.topics key.
Given the configuration in listing 6, all events with topic names beginning with org/springframework/osgi are sent to the listener corresponding to the AdminEvent-Handler class. For each event, the handleEvent method is called with the event instance passed as a parameter.
We’ve now seen how to use the Event Admin Service with Spring DM, even though this compendium service isn’t directly supported by Spring DM.
also read:
Summary
The Event Admin Service is an interesting compendium service. It provides a standardized way of exchanging events between components—event producers and consumers don’t need to be tied to a particular API. Although Spring DM doesn’t provide support for this service, a bridge can be implemented to forward events triggered by Spring or Spring DM to the Event Admin Service. This allows components to be notified without having to use the API provided by these two frameworks.