The RM Event Service allows components residing on backend RM hosts and external applications connected to the RM to register interest in the occurrence of some type of event in the RM system, and to receive a notification when an event of that kind occurs.

Overview

The RM Event Service consists of a number of event modules - one in each RM backend host and one in the Remote Access Client component for the external applications using RM. Backend event modules register the com.prosyst.mprm.backend.EventService interface as an OSGi service in the framework of each backend host. If a backend bundle needs to fire an event, it refers to the local EventService and passes the event to it. The backend event module is responsible for broadcasting all received events from applications on the local framework to the event modules running on the rest of the backend server hosts in the RM system. The exchange of events between hosts is done over the RM Connection Framework. When the events are received by the remote backend event modules, they are dispatched to the locally registered backend and external event listeners for the specified type.

External applications/systems can receive events occurring on the backend by registering listeners through the Remote Access Client (RAC). The Remote Access Client, available in each external application connected to RM, contains an event module that dispatches the events passed by backend event modules to its registered listeners. Note, however, that Remote Access Clients cannot generate events, they can only register listeners for events arising on the backend.

The RM Event Service architecture:

By default, event listeners receive events asynchronously. However, the events service enables the optional synchronous delivery of events to backend event listeners. The synchronous or asynchronous mode of delivery is defined by event listeners, not by event originators. The synchronous delivery provides some "feedback" to the event originators on behalf of event listeners. If some exception occurs on the listener during event processing, the exception will be returned to the event originator if the listener has requested synchronous delivery. External event listeners cannot request synchronous event delivery.

Each event has a type and content. The type allows the Event Service to convey the event only to the listeners interested in this kind of "news". The event content holds the information brought by this event.

The RM event mechanism can be used alternatively or complementary to the RPC mechanism. Both mechanisms enable the communication and synchronization among the components of services (by service meaning functionality in this case) running on different backend hosts. In the Event Service, backend applications take the corresponding actions when they receive the event. In the RPC Service, applications remotely invoke the services running on remote backend hosts when they need some action on their behalf. The most significant difference between the two approaches, however, is that the events service is based on the address-less distribution of events (the event originator is not aware of the number of registered listeners, their names or their host IDs/roles), while the RPC service involves addressing a defined service running on defined host(s) or role(s).

The RM Event API is represented by the com.prosyst.mprm.backend.event package. It contains the EventService interface, registered as an OSGi service by backend event modules, and the EventListener interface, which characterizes all applications that need to receive events.

Firing Events


Each backend bundle that needs to notify remote applications of changes through sending events must get the EventService running on its local backend host, and invoke the event method appropriate for the backend bundle's needs.

There are two modifications of the event method - for sending general events, and for sending property-containing events to backend hosts and to specific RAC users.

For sending general events, use the event(String eventType, Object eventData) method or the event(String eventType, byte[] externalizedEventData) one. These methods require two parameters:

  • Event type - Specifies the type of event. It can be an arbitrary String. Note, however, that the type will be used as a filter for the event listeners that will receive the event, so all originators of a common type of events must use the same String for event type.
  • Event data - This is the event's content. This parameter can be an Object of type convenient for the needs of the application. The class of this parameter could be:
    • Wrapper classes of the primitives (Boolean, Integer, etc.)
    • String
    • com.prosyst.util.io.Externalizable - The Externalizable interface defines a lightened structure for object serialization. A bundle developer may implement Externalizable for transferring composite objects representing method results or arguments over a stream.
    • java.io.Serializable
    • Arrays of the above object types.

or it can be in the form of a byte array holding raw serialized data.

For sending property-containing events, use the event(String eventType, Object eventData, Dictionary eventProperties, String userRoleFilter) method. This method requires the following parameters:

  • Event type - Specifies the type of event. Has the same meaning as for general events.
  • Event data - This is the event's content. Has the same meaning and requirements as for general events.
  • Event properties - Provide additional information on the event in the form of key-value pairs. Event listeners might use event properties to additionally filter the incoming events they are subscribed for. See the next sections for more information about filtering events with regard to certain properties.
  • RAC user role - Specifies that the event will be delivered only to those RAC instances whose sessions to RM are established for a user having the specified role. If you will deliver the event to all RAC applications, pass null for this argument.

The EventService will handle delivering the event to all backend hosts available in the system. From then on, the locally running EventService-s redirect the event to all listeners that have subscribed for this kind of events.

The example that follows illustrates the way of originating events. It invokes the local EventService and originates both general and property-enabled events of type "test" and a simple message as content.

Generating general events and property events:

import java.util.Hashtable;
 
import org.osgi.framework.*;
import com.prosyst.mprm.backend.event.*;
 
public class EventsActivator implements BundleActivator {
 
private ServiceReference eventRef;
private EventService eventService;
private EventThread customizedEventThread;
private EventThread generalEventThread;
 
public void start(BundleContext bc) throws Exception {
// Getting the Event Service
eventRef = bc.getServiceReference(EventService.class.getName());
if (eventRef != null) {
eventService = (EventService) bc.getService(eventRef);
// Starting generation of general events
sendGeneralEvent();
// Starting generation of property-enabled events
sendCustomizedEvent();
} else {
throw new Exception("The Event Service is unobtainable:-(");
}
}
 
public void stop(BundleContext bc) throws Exception {
// Killing event threads
customizedEventThread.stop();
customizedEventThread = null;
generalEventThread.stop();
generalEventThread = null;
// Ungetting the Event Service
if (eventRef != null) {
bc.ungetService(eventRef);
eventService = null;
eventRef = null;
}
}
 
private void sendGeneralEvent() throws EventListenerException {
// Launching the thread generating general events
generalEventThread = new EventThread(false);
new Thread(generalEventThread, "My General Event Thread").start();
}
 
private void sendCustomizedEvent() {
// Launching the thread generating property-enabled events
customizedEventThread = new EventThread(true);
new Thread(customizedEventThread, "My Customized Event Thread").start();
}
 
// A thread which generates events by using the RM Event Service
private class EventThread implements Runnable {
 
private static final String RAC_USER_NAME = "RAC User";
private static final String TEST_EVENT_TYPE = "test";
private static final String MY_APP_EVENT_PROPERTY = "my.app.event.property";
 
boolean running = false;
boolean isFalse = false;
private Object monitor;
private boolean isCustomized;
 
EventThread(boolean isCustomized) {
monitor = new Object();
running = true;
this.isCustomized = isCustomized;
}
 
public void run() {
Hashtable eventTargetProps = new Hashtable();
try {
while (running) {
if (isCustomized) {
// Generating events with properties
eventTargetProps.put(MY_APP_EVENT_PROPERTY, Boolean.valueOf(isFalse));
eventService.event(TEST_EVENT_TYPE, "test event",
eventTargetProps, RAC_USER_NAME);
System.out.println("[EventThread] Customized event sent!");
// Inverting the value of the event property
// "my.app.event.property"
isFalse = !isFalse;
} else {
// Generating general events
eventService.event(TEST_EVENT_TYPE, "test event");
System.out.println("[EventThread] General event sent!");
}
// Waiting for 100 seconds till next event generation
synchronized (monitor) {
monitor.wait(10 * 1000L);
}
}
} catch (EventListenerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
// Kills the thread
private void stop() {
synchronized (monitor) {
monitor.notifyAll();
}
running = false;
}
}
}


Subscribing for Events

If you want to subscribe your applications for receiving events, you must:

  1. Create an implementation of the EventListener interface.
  2. Register the event listener implementation.

Creating the EventListener Implementation

The event(Event event) method of the EventListener interface must be implemented to handle the processing of incoming events. This method will be invoked by the EventService each time a new event suitable for this listener is received.

The following listing illustrates creating a very simple event listener implementation. We assume that the event content is always String, so we just print the received event in the system output.
Implementing the EventListener interface.

import com.prosyst.mprm.backend.event.Event;
import com.prosyst.mprm.backend.event.EventListener;
import com.prosyst.mprm.backend.event.EventListenerException;
public class EventsConsumer implements EventListener {
 
public void event(Event event) throws EventListenerException {
 
String eventContent = (String) event.getEventData();
System.out.println("The following event is received: " + eventContent);
}
}

Registering a Backend Event Listener

For backend bundles, the event listener implementation must be registered as an OSGi service with the backend framework. The service must be registered with a Dictionary of listener properties. At least the EventListener.EVENT_TYPE_FILTER property must be set in the Dictionary. Its value will show the event types this listener will receive. It corresponds to the event type parameter passed to the EventService.event method when called by event-creators.

Besides the event type property, the registration Dictionary can contain additional properties tuning the performance of the event listener. Optionally, the event listener can be adjusted to:

  • Request synchronous delivery of events
  • Restrict the receiving of events only to events originating at a specified scope: the localhost only, remote hosts only, the MS this host belongs to, all hosts external to the current MS, etc.
  • Restrict the receiving events only to events with certain properties. For property-enabled events only.
  • Ensure an incoming event is processed only once within the RM system or the current MS cluster.

The tuning of these optional event listener characteristics is done by adding the following properties to the service registration Dictionary (all of them available as constants in the EventListener interface):

  • HOST_FILTER - This property allows you to filter events depending on whether they come from the local host or from a remote one. If EventListener.LOCAL_HOST is passed as value to this property, only local events will be received. If EventListener.REMOTE_HOST is passed as value, local events will be ignored and only events originating from remote hosts will be received. If this property is not present in the registration Dictionary, events from any host will be received.

  • MS_FILTER - This property can be used on backend hosts with MS role. It allows you to filter events according to their origin in terms of their management server. If this property has value EventListener.MS_INTERNAL, then only events coming from hosts in the same management server will be received. If the value is EventListener.MS_EXTERNAL, then the events from the current management server will be ignored, and only the ones from external management servers will be received. If the property is missing, the events from any MS will be received.

  • BROADCAST_FILTER - This property is used when we are listening for events that must be processed by only one listener when they appear in the system or in the cluster. This property guarantees that the event will be processed by only one listener instance of all listeners registered with the same listener name (EVENT_LISTENER_NAME property) in the specified scope. The listener instance that will process the event is selected internally by the EventService. If the value of this property is EventListener.UNICAST_LISTENER, the event will be processed by only one listener in the entire system. If the value is EventListener.CLUSTER_UNICAST_LISTENER, only one listener for each MS or RAS cluster will receive the event.

If you decide to apply this property, you must also include the EventListener.EVENT_LISTENER_NAME property in the registration Dictionary. 


  • EVENT_LISTENER_NAME - This property is obligatory if the BROADCAST_FILTER property is available (see above). This registration property specifies the name of the listener. This name is used to identify identical listeners running on different backend hosts. This is needed for resolving multicast filters.
  • EVENT_LISTENER_SYNC - This registration property specifies if the event must be delivered synchronously to this listener. This can be ensured by setting this property with value true. By default, events are delivered asynchronously.
  • EVENT_CUSTOM_FILTER - This property is used for filtering incoming property-containing events and its value is the filtering criteria in LDAP format based on the properties of events of the relevant type. If this property has a non-null value, the event listener will receive only those events which satisfy this filter.

The following example illustrates registering the event listener implementation from the above listing "Implementing the EventListener interface" on the backend. It subscribes for events of type "test", indicated by the value of its registration property EventListener.EVENT_TYPE_FILTER, and whose "my.app.event.property" should be true, indicated by the value of its registration property EventListener.EVENT_CUSTOM_FILTER. The example also requests synchronous delivery of events, which is indicated by the true value of its EventListener.EVENT_LISTENER_SYNC property.
Registering a simple backend event listener:

import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import com.prosyst.mprm.backend.event.EventListener;
 
 
public class MyEventsListener implements BundleActivator {
private static final String MY_APP_EVENT_PROPERTY = "my.app.event.property";
static String eventFilter = "(" + MY_APP_EVENT_PROPERTY + "=true)";
private ServiceRegistration eventListenerReg;
private EventsConsumer listener;
 
public void start(BundleContext bc) throws Exception {
// Specifying the parameters of event receiving
Dictionary listenerProps = new Hashtable(3);
listenerProps.put(EventListener.EVENT_CUSTOM_FILTER, eventFilter);
listenerProps.put(EventListener.EVENT_TYPE_FILTER, "test");
listenerProps.put(EventListener.EVENT_LISTENER_SYNC, "true");
listener = new EventsConsumer();
// Registering the backend event listener
eventListenerReg = bc.registerService(EventListener.class.getName(), listener, listenerProps);
}
 
public void stop(BundleContext bc) throws Exception {
if (eventListenerReg != null) {
eventListenerReg.unregister();
eventListenerReg = null;
}


Registering an Event Listener through a Remote Access Client

Just like backend bundles, non-backend applications can receive backend events by registering event listeners. However, remote applications need not register the listener as a service. The registration of an event listener is done by calling the addEventListener(String type, java.util.Dictionary properties, EventListener listener)  or addEventListener(String type, EventListener listener) method of com.prosyst.mprm.rac.RemoteAccessClient. The parameters of these methods are:

  • The type parameter shows the type of events this listener will subscribe for. It is the analogue of the EventListener.EVENT_TYPE_FILTER registration property of backend listener services.
  • The properties parameter indicates the additional listener properties. The properties that can optionally be included in this parameter are as follows:
    • RemoteAccessClient.EVENTS_REMOTE_RAS_ONLY - If it is set to true, the event listener registered by this RAC instance will receive only events originating on the host the RAC is connected to. If set to false or unavailable, all incoming events of the specified type will be received.
    • EventListener.EVENT_CUSTOM_FILTER - Has the same meaning as for backend event listeners. This property is used for filtering incoming property-containing events and its value is the filtering criteria in LDAP format based on the properties of events of the relevant type. If this property has a non-null value, the event listener will receive only those events which satisfy this filter.
  • The listener parameter is the listener object itself. It is implemented in the same way as with backend listeners.

The following listing illustrates registering the event listener from the listing "Implementing the EventListener interface" above through the Remote Access Client. It will receive the events of the same type as the listener in the above listing "Registering a simple backend event listener", but it is interested in the events occurring on the connected host only.
Registering the event listener through the RAC:

import com.prosyst.mprm.backend.event.EventListener;
import com.prosyst.mprm.rac.RemoteAccessClient;
import java.util.Hashtable;
. . .
 
private RemoteAccessClient rac;
private static final String MY_APP_EVENT_PROPERTY = "my.app.event.property";
static String eventFilter = "(" + MY_APP_EVENT_PROPERTY + "=true)";
. . .
// Obtaining the RAC instance in the appropriate way
. . .
 
Hashtable props = new Hashtable();
props.put(RemoteAccessClient.EVENTS_REMOTE_RAS, "true");
props.put(EventListener.EVENT_CUSTOM_FILTER, eventFilter);
EventsConsumer listener = new EventsConsumer();
rac.addEventListener("test", props, listener);
// Start waiting for incoming events
. . .

Working with the Remote Access Client is described in the Remote Access to RM document.