Please refer to lab 10 and the previous RMI lecture while studying the material presented below.


Plan:

Introduction

Remote Method Invocation (RMI) is a pure-Java answer to remote procedure calls (RPC), and in some sense to the Distributed Component Object Model (DCOM) and the Common Object Request Broker Architecture (CORBA). All of these models are abstractions of distributing programs across the memory address space of a single machine, across multiple processors in a single machine, or across many machines on a network.

The RMI model attempts to make designing distributed applications as simple and straightforward as designing nondistributed applications. The programmer doesn't need to worry about sending data or translating data into objects, or any of the ugly stuff that the communication level is responsible for. The only concern is solid application design with minor attention paid to locating objects, as well as to handling some exceptions that are specific to network applications. The communication elements of the program have been abstracted to the point of seeming local. This abstraction frees you from the drudgery of writing low-level protocols and building objects based on data that you are reading over the network, but it can also be a little misleading.

It is simple to write an application that uses objects on remote machines. But although the programming interface makes it seem as if programming and application that uses only local objects, an application that uses remote objects is inherently different from an application that is entirely local. Some of the differences are:

It is left to the programmer to deal with these issues.

Latency can be dealt with, to some degree, by carefully designing an application such that only those calls that must be made to a remote object are remote calls. The problems of full and partial failures can be dealt with only by giving careful consideration to exceptions and possible exceptional conditions that may arise during run time, and by dealing with them appropriately in the design of the application.

RMI was designed to make the design and implementation of network distributed applications almost like that of stand-alone object-oriented programs. No separate interface description language (IDL) is used to glue the parts together, everything is written in Java. In fact, RMI is a Java-specific model for writing distributed applications, unlike CORBA, which is intended to be language-neutral, or DCOM, which was designed with Microsoft's ActiveX architecture in mind.

What you give up in neutrality you make up in simplicity. A call to a remote object using RMI is identical to a call to a local object, with the following exceptions:

Remote Object Structure

A remote method invocation is made through a reference to a remote object. The object is exported via a server application, and a handle to the object is obtained by a remote client either by looking up the object by name with a registry or by checking the return value from another remote method call.

This object must implement at least one interface that extends the java.rmi.Remote interface. All interaction with the remote object will be performed through this interface. Essentially, the interface describes the methods that can be invoked remotely, and the remote object implements them.

When a reference to a remote object is obtained, the object is not sent over the network to the client requesting it. In its place is a proxy object or stub that is the client-side proxy for the remote object. All interactions by the client will be performed with/by this stub class. The stub is responsible for handing off data between the local system and the remote one. Many clients can have references to a single remote object. Each client will have its own stub object that represents the remote object, but the remote object will not be replicated.

On the server side a skeleton class is responsible for handing off the method calls and data to the actual object being referenced. This is the server-side proxy for the object being exported. The complete system can be though of as a four-layer model:

Interface Description

In many distributed programming models, you must define an interface to your remote objects in an interface description language (IDL) and then run a special compiler or generator on the IDL files that generates the files that your objects use to define their remote methods. In the RMI model, all interfaces are described in Java. There is no need for a separate IDL. The necessary files are generated directly from the compiled object code that implements remote interfaces. Namely, the RMI stub compiler (rmic) is used to generate the special objects that are needed to connect remote server objects to clients.

The Application Layer

An application that makes some of its methods available to remote clients must declare the methods in an interface that extends java.rmi.Remote. The java.rmi.Remote interface is an empty interface that declares no methods. Essentially it is used to flag an object as being remotely accessible. When you want to define a set of methods that will be remotely called, they must be declared in one or more interfaces that extend the java.rmi.Remote interface.

You implement a remote interface in the same way that you implement any other Java interface. The only difference is some added exception handling for dealing with RemoteExceptions. These are specific to remote calls and can be thrown if a problem arises in contacting or interacting with a remote application.

Once the methods described in the remote interfaces have been implemented, the object must be exported. This can be done implicitly if the object extends the UnicastRemoteObject class of the java.rmi.server package, or it can be done explicitly with a call to the exportObject() method of the same package.

Finally, the application will normally register itself with a name server, or registry. This is used to make first contact with the application and obtain a reference to its remote objects. Once first contact is made, any other remote object references that the server may want to export may be returned by method calls from the first object. Because of this, name service is generally only necessary upon application start-up.

On the client-side, a client simply requests a remote object from either a registry or a remote object that it has already obtained. The reference to the remote object is cast to one of its Remote interfaces, and any calls to remote methods can be made directly through this interface.

The Stub and Skeleton Layers

The stub and skeleton classes are generated using the RMI stub compiler, rmic. These are simply class files that do the client- and server-side representations of a remote object.

The Stub Class

The stub is the client-side proxy for the remote object. The stub is the applications interface to the remote object; it is responsible for initiating a call to the remote object that it represents by way of the remote reference layer. The stub is also responsible for marshaling method arguments to a marshal stream that is also acquired through the remote reference layer. A marshal stream is simply a stream object that is used to transport parameters, exceptions, and errors needed for method dispatch and returning results. The stub and skeleton classes both use these streams for communicating with each other. The stub unmarshals values that have been returned by the remote object. This is also by way of the marshal stream. Finally, the stub class informs the remote reference layer that a call is complete.
The Skeleton Class
Much like the stub classes, the skeleton classes marshal parameters to and from a marshal stream. The difference is that the skeleton class stays on the server side of the connection and deals directly with the implementation classes of the Remote methods being exported. The skeleton is responsible for sending parameters to the method implementation and for sending return values and exceptions back to the client that made the call. Essentially, the skeleton is responsible for receiving method calls from a stub class, marshalling any necessary parameters, and dispatching the actual methods being exported. This is the server-side proxy for the remote object.

The Remote Reference Layer

The remote reference layer is effectively the abstraction between the stub and skeleton classes and the actual communication protocols that are handled by the transport layer. The remote reference layer expects to get a stream-oriented connection from the transport layer. The actual transport may take place using a non-connection-based protocol, but interaction between the remote reference layer and the transport layer will take place as if it involved a stream, or connection-based protocol.

The remote interface layer will be used to handle replicated objects, once this feature is incorporated in the RMI system. Replicated objects will allow simple dispatch to many programs that are exporting substantially the same Remote objects. This layer will also be responsible for esatblishing persistence semantics and strategies for recovery of lost connections.

The Transport Layer

The transport layer is responsible for handling the actual machine-to-machine communication. Because this communication is abstracted in this way, it allows system implementors to replace the low-level communications protocols with alternatives.

By default, communication will take place through a standard TCP/IP connection. The transport layer creates a stream that is accessed by the remote reference layer to send and receive data to and from other machines. The transport layer sets up connections to remote machines, manages the connections, monitors the connections to make sure that they are all 'live', and listens for connections from other machines.

The transport layer can be modified to handle encrypted streams, compression algorithms, and a number of other security- or performance-related enhancements. Because this layer is independent of the reference layer, the stub/skeleton layer, and the application layer, an RMI application does not need to know the specifics of any changes made to the transport layer.

The Object Hierarchy

Color codes: classes, interfaces.

myRemoteObject 
+-extends----java.rmi.server.UnicastRemoteObject
|            +-extends----java.rmi.server.RemoteServer
|                         +-extends----java.rmi.server.RemoteObject
|                                      +-extends----java.lang.Object
|                                      +-implements-java.rmi.Remote
+-implements myRemoteInterface 
             +-extends java.rmi.Remote

Name Service

In general itis necessary to bootstrap an applicaton that uses RMI. A server will register any remote objects that is exporting with a name server called a registry. When a client wishes to obtain a reference to a remote object, a URL-based lookup is performed on a well-known registry, and a reference to the remote object is returned if the lookup succeeds.

There are two ways of using the registry services. The first is to follow the RPC model and maintain a registry server that is running on a well-known port number. Any number of applications that export objects can register with this registry as long as they are running on the same physical machine. A client can then look up the registered services via URLs passed to certain lookup() and list() methods that query the registry. The second method consists of an application running its own registry services. This allows the application to have complete control over the registry but makes it more difficult to define a well-known port that will be used to access remote objects.

Remote methods can be designed to return other remote objects. Because of this, it is generally necessary to contact a registry server only when making initial contact with an application. Once one of the remote objects on a server has been located, references to others can be obtained via method calls to the first object.

Garbage Collection

The distributed garbage collection algorithm used by the RMI system is a reference-counting algorithm. When a client first receives a reference to a remote object, a "referenced" message is sent to the server that is exporting the object. Every subsequent reference within the client's local machine causes a reference counter to be incremented. As a local reference is finalized, the reference count is decremented, and once the count goes to zero, an 'unreferenced' message is sent to the server. Once the server has no more live references to an object and there are no local references, it is free to be finalized and garbage collected.

If a remote object implements the java.rmi.server.Unreferenced interface, it must implement a body for the unreferenced method. This method will be called once the object is no longer referenced.

It is also important to note that, under certain circumstance, such as network failure, an object may be collected prematurely, because the transport layer may think that a connection to a machine containing a reference to the object is no longer live. If this happens, subsequent attempts by the client to access the remote object will result in a RemoteException being thrown.

Class Loaders

In Java, a class loader is responsible for dynamically loading the classes necessary for an application, as they are needed. This includes locating the files, locally or remotely, and retrieving them.

There are different class loaders for different types of applications. For example a default class loader is used to load locally run files. Applets have special security and class loader needs, so an AppletClassLOader is used to load the classes necessary for an applet to execute. Remote method invocation also has special requirements and normally employs the RMIClassLoader. This class loader is responsible for loading the stub and skeleton classes used by the RMI system, as well as any utility classes necessary for these stubs and skeletons. In general, the RMIClassLoader will first attempt to load classes that can be found on the local file system via the CLASSPATH environment variable. If the classes cannot be found locally, the RMIClassLoader extracts a URL that is included in a marshal stream of serialized objects. This URL is then used as the codebase for locating the necessary classes. In the case of stub and skeleton classes a system property - the java.rmi.server.codebaese property - is used to locate the necessary class files.

Security

The RMI run time requires that a security manager be explicitly set. The RMISecurityManager is a direct descendant of java.lang.SecurityManager. It is used to set the security policies used to givern the behaviour of the stub and skeleton classes.

Performance

If you intend to write video streaming applications, or other applications that require high network performance, RMI is probably not going to satisfy your needs. If you want to rapidly develop network applications and you can deal with increased latency, RMI is the way to go.

Summary

The RMI system is made up of a series of layers.

On the application side, you have a remote interface that is implemented by a class that is exported in one of two ways - either by extending UnicastRemoteObject or by passing a reference of itself to UnicastRemoteObject.exportObject().

The next layer down is the stub/skeleton layer, which is generated from your compiled remote object using the rmic. This generates a stub class to act as a client-side proxy and a skeleton class to act as server-side proxy, handling the work of dispatching the actual method calls. Finally you have an underlying transport layer that uses socket-based communications protocols to send data over the network.

The system works with a registry name server for bootstrapping connections and has various facilities for RMI-specific class-loading and security mechanisms.

Remote method invocation describes a simple method for writing applications that can be distributed locally and over networks. Any object that is to be exported simply implements a Remote interface, subclasses a RemoteObject (or exports itself using the UnicastRemoteObject.exportObject() method) and registers itself with a name service.

A client can access a remote object by looking it up in a name service and then essentially working with it as if it were a local object.

Java API Quick Reference

1. The java.rmi Package contains a Remote interface, a security manager, and a Registry lookup and binding class. The interface, java.rmi.Remote must be implemented by any object that is exportable.

2. The java.rmi.dgc Package contains one interface and one class that are used by the distributed garbage collection system. Garbage collection on distributed systems has some unique needs that separate it from garbage collection in a single memory space.

The interface and class of this package will not generally be used by programmers using the RMI tools, but rather by system implementors who may want to create their own garbage collection algorithms.

3. The java.rmi.registry Package contains two interfaces and one class that, together, are used to create, look up, and query a registry server. The class and interfaces in this package can be used to create your own registry services, or to interact with other registry services.

4. The java.rmi.server Package has most of the core classes and interfaces that are necessary to export remote objects. This includes special class loaders, socket factories, and remote object definitions that are subclassed to create your own remote objects.

The java.rmi.server package also contains most of the classes needed for implementing RMI, such as the stub and skeleton interfaces and the calling mechanism that allow skeletons to communicate with the objects theyr represent. Many of the classes in the server package are not used directly by programmers working with RMI but are part of the low-level RMI system.

The main class in this package that is useful to you as a developer is the UnicastRemoteObject class.

5. The java.io Package (a subset)

6. The java.rmi Exceptions