Distributed Event Notification Using RMI

  Sean Landis is a Senior Staff Engineer at Motorola Labs in Schaumburg, IL. He can be contacted at [email protected].

MANY OF TODAY'S information systems feature distributed processing to achieve performance, parallelism, improved resource utilization, proximity of processing to usage, and integration of legacy systems. Common to most distributed systems is the need for asynchronous, many-to-many communication. A distributed event notifier provides this type of communication where publishers, subscribers, and the event service all reside on distinct machines.

I'll show how to use Remote Method Invocation (RMI) to implement a distributed event notifier. I'll discuss some of the design issues specific to distributed applications and present a sample implementation. Models are presented using the Unified Modeling Language (UML).1

Suchitra Gupta, Jeffrey M. Hartkopf, and Suresh Ramaswamy developed an object behavioral design pattern for general-purpose type-based event notification (see Gupta et al., Java Report, July 1998). They developed the pattern and then presented sample code for an event notification service that is embedded in the same process as its publishers and subscribers. They also discussed a distributed implementation and presented a UML class diagram of that system.

figure 1

Figure 1. Event Proxy structure from Gupta et al.1

They recommended using the Proxy pattern.3 A simplified version of their model is shown in Figure 1 (notes are removed for clarity, and the Subscription class is added as defined in the sample code from the July 1998 article). The Event Notifier participants are:
  • Event: A common ancestor type for all events.
  • ConcreteEvent (FaultEvent): Represents a specific occurrence, possibly containing data about that occurrence.
  • Publisher (Hub, Router): Emits or produces events.
  • Subscriber: Defines an interface for all objects that need to handle events.
  • ConcreteSubscriber (Console, PagingSystem): Registers for events and handles events by implementing the Subscriber interface.
  • EventService: Brokers events between subscriber and publisher.
  • Filter: Responsible for discarding events not of interest to a subscriber.
  • Class: A metaclass (a class whose instances are classes). Event Notifier uses an instance of this class to identify the type of event of interest.
The Distributed Event Notification model has these additional participants:
  • EventServiceProxy: Resides locally and serves as a proxy for the remote service. It is implemented as a singleton.3
  • RemoteEventService: An event service in a distributed environment.
Gupta et al.2 described two ways to implement the proxy. In one approach, the proxy maintains a list of local subscribers and the remote event service maintains a list of proxies. In the second approach, the proxy does not maintain a list but merely delegates to the remote event service, which keeps the subscriber list.

Gupta et al.2 outlined several advantages to the first approach, but I will present the second because it is easier to modify their design using the second approach.

Following are the stated goals for supporting distributed objects in the Java language.4 I have included how each goal can benefit the distributed event notifier.
  • Support seamless remote invocation on objects in different virtual machines (VMs).
The event notifier can be invoked remotely from publishers and subscribers.
  • Support callbacks from servers to clients.
The event server can execute registered subscriber inform methods.
  • Integrate the distributed object model into the Java language in a natural way while retaining most of the Java language's object semantics.
Inheritance, polymorphism, and garbage collection work as expected.
  • Make differences between the distributed object model and local Java object model apparent.
  • Make writing reliable distributed applications as simple as possible.
Eases migration from the embedded model to the distributed model.
  • Preserve the safety provided by the Java runtime environment. Fundamental to distributed applications.
The RMI classes and interfaces of interest are shown in Figure 2. The class diagram is drawn from the perspective of the java.rmi package.

figure 2

Figure 2. Class diagram for some important RMI classes.

A brief description of these RMI classes and interfaces will help to understand how they are used in the distributed event notifier.
  • Naming: This is a bootstrap naming server that allows storage and lookup of remote objects based on Uniform Resource Locator (URL) syntax of the form: rmi://host:port/name, where port is optional.
  • Remote: An interface that all remote objects must extend directly or indirectly.
  • RemoteObject: Provides the semantics of java.lang.Object for remote objects.
  • RemoteServer: Provides abstract functions needed to create and export remote objects.
  • UnicastRemoteObject: Defines a singleton (unicast) remote object whose references are valid only while the server process is alive. It provides support for point-to-point active object references (invocations, parameters, and results) using TCP streams.
  • RMISecurityManager: Defines a default security policy for RMI applications (not applets). For code loaded from a class loader, the security manager disables all functions except class definition and access.
  • RemoteException: The top-level remote exception from which all remote exceptions are derived.
  • java.io.Serializable: Interface for serializing a class to a stream. Classes implementing this interface can be passed as remote method arguments and return values.

figure 3

Figure 3. Remote object pattern.

A name registry must be running on all machines hosting RMI server objects. Java provides a default name registry application called rmiregistry. Remote server objects advertise by creating a name binding in a name registry. A binding associates an URL name with a remote object interface. Clients lookup the server by name using the same registry as the server used. The class diagram for this pattern is shown in Figure 3.

The Naming class is responsible for locating a registry implementation on the server host specified in the URL. The static methods of the Naming class are used to pass server object references to clients.

The distributed event notifier I present here follows as closely as possible the model shown in Figure 1. Some changes are required to utilize RMI.

Changes to Original Pattern
Only a few changes to the code presented by Gupta et al.2, are required to support a distributed RMI-based event notifier.
  1. The Filter and Event classes extend java.io.Serializable so instances can be passed as parameters to the remote event service.

  2. The Subscriber class implements an RMI interface class, SubscriberInterface. When constructed, a Subscriber exports itself as an RMI object to enable callbacks from the remote event server.

  3. The EventService class implements an RMI interface class EventServiceInterface. The proxy, to guarantee interface compliance between the two classes, also implements this class.

  4. The EventService methods subscribe and unsubscribe specify a SubscriberInterface parameter instead of a Subscriber to support RMI callbacks.

  5. The EventService methods subscribe, unsubscribe, and publish take an Event parameter instead of a Class because Class can not be made Serializable. This also frees clients from extracting the Class of the Event before calling the event server.

  6. The Subscription class references SubscriberInterface objects instead of Subscriber objects.

  7. Clients of the remote event service (PagingSystem, Router) use a reference to an event proxy instead of a reference to a local EventService object.
These changes affect the class relationships but not the class behaviors. The class diagram for the RMI-based distributed event notifier is shown in Figure 4.

figure 4

Figure 4. RMI-based distributed event notifier class diagram.

New Participants
There are two new interfaces introduced in the RMI-based distributed event notifier that were not present in the model presented by Gupta et al.2 These are required to adapt to the RMI remote object pattern.
  • EventServiceInterface: Provides an RMI remote interface and a common interface for the EventService and the EventServiceProxy.
  • SubscriberInterface: Provides an RMI remote interface for subscribers so that the EventService can call back to them at notification time.
Modeling Events
Publishers now send Event and Filter objects to the distributed event notification server, and the server sends events to subscribers via RMI. Objects to be transported over RMI must implement java.io.Serializable. The new class diagram for events and filters is shown in Figure 5. Note that serializability is transparent to clients.

figure 5

Figure 5. Class diagram for serializable events and filters.

Publisher and Subscriber Clients
Subscriber clients in the distributed version interact with the local proxy in the same fashion as the clients did with the embedded EventService. The class relationships are shown in Figure 6.

figure 6

Figure 6. Class diagram for a paging system subscriber.

In the same fashion, publisher clients refer to a proxy that delegates the operations to the remote event service as shown in Figure 7.

figure 7

Figure 7. Class diagram for a router that publishes events.

The embedded event notifier is now converted to a distributed version using RMI. This distributed event notifier can even operate with the event server collocated with clients.

Location Independence
Although the same server code supports both collocated and remote event notifiers, this approach is inefficient for collocation because it always calls the RMI naming service. Modifying collocated clients to use an EventService instead of an EventServiceProxy would solve this problem but would also expose the location of the server to the clients.

Server location can be hidden from clients by defining an event notifier factory3 that determines whether to use a local or a remote server. Clients call the factory to get an EventServerInterface to interact with. The implementation will be a proxy for remote event services, and an EventService for a collocated server.

Multiple Proxies
The event service proxy described by Gupta et al.2 is defined to be a singleton. But in a large distributed system, there may be several event notifiers. Multiple notifiers can be used to distribute event processing load across machines. Event types may fall into partially ordered sets, each having different quality of service (QoS) requirements. Multiple notifiers can be used to meet the different QoS needs.

The design presented here can support multiple proxies by parameterizing EventServiceProxy objects with the name of the event notification server to lookup. Likewise, the multiple EventService objects must be registered with different names.

The code listings present the code for a simple implementation of the distributed event notifier.

The EventService (see Listing 1) class is implemented as a singleton within the scope of its containing process. It implements EventServiceInterface to be accessible to RMI clients. EventServer.java is a simple main wrapper for an event notification server. The EventService now receives Event objects instead of their Class objects and calls getClass itself.

The EventServiceProxy (see Listing 2) resides in client processes; it simply connects to the remote event notifier and delegates local requests to it.

The Event (see Listing 3) interface must extend java.io.Serializable so that derived event classes can be passed as arguments to the event notification server via RMI.

Classes derived from Filter (see Listing 4) are serializable.

The interface SubscriberInterface (see Listing 5) allows the abstract constructor for Subscriber to export itself as an RMI object using the static method UnicastRemoteObject.exportObject. This is how the EventServer can callback to the concrete PagingSystem subscriber. The Router class (see Listing 6) is an example publisher.

I've shown how easy it is to extend an embedded event notifier to a distributed event notifier using RMI. This implementation can support local or remote event servers, and clients can use multiple remote servers. The skeleton code can be extended to provide fault tolerance, multithreading, and persistent events.

The listings show that RMI is a natural extension of the Java language; it provides distributed inheritance, polymorphism, garbage collection, pass by value, and pass by reference semantics for distributed objects.

Thanks to Suchitra Gupta, Jeffrey M. Hartkopf, and Suresh Ramaswamy for providing the foundation and inspiration for this article.

This article reflects the ideas of the author and does not reflect the views or opinions of Motorola, Inc.

1. Booch, G. et al., Unified Modeling Language User Guide, Addison-Wesley, 1997.
2. Gupta, S., J. M. Hartkopf, and S. Ramaswamy, "Event Notifier: A Pattern for Event Notification," Java Report, Vol. 3, No. 7, July 1998, 19-36.
3. Gamma, E. et al., Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1997.
4. Java Remote Method Invocation Specification, Revision 1.50, JDK 1.2, Oct. 1998, www.javasoft.com/products/jdk/1.2/docs/guide/rmi/spec/rmiTOC.doc.html.

Quantity reprints of this article can be purchased by phone: 717.560.2001, ext.39 or by email: [email protected].