|
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
BACKGROUND
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. 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.
REMOTE METHOD INVOCATION
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. 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. 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.
DISTRIBUTED EVENT NOTIFIER
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.
- The Filter and Event classes
extend java.io.Serializable so instances can be passed as
parameters to the remote event service.
- 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.
- The EventService class implements an RMI interface
class EventServiceInterface. The proxy, to guarantee interface
compliance between the two classes, also implements this class.
- The EventService methods subscribe
and unsubscribe specify a
SubscriberInterface parameter instead of a
Subscriber to support RMI callbacks.
- 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.
- The Subscription class references
SubscriberInterface objects instead of
Subscriber objects.
- 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. 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. 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. 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. 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.
SAMPLE LISTINGS
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.
SUMMARY
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.
ACKNOWLEDGMENT
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.
REFERENCES
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].
|