Remote Method Invocation (RMI)
Stubs & Skeletons
Home Search Java 2 API C++ Resources

Remote Method Invocation is Java's "objectization" of the usual Remote Procedure Call capablilites of most modern server-capable operating systems such as Unix or Windows NT.    RMI enables a client computer to access an object located in a remote server as if it were a locally instantiated object in the client.

Features:

  1. Both the client and server are pure object-oriented systems.   The interaction between the server and the client is encapsulated in one or more remotely accessible objects.   These objects are instantiated on the server, but the client treats the object as if they were local instantiations.
  2. Once the inital communication between the server and client has been established, all further communications are completely transparent to both the client and the server.
  3. Just as in the methods of ordinary objects, the methods of remote objects can take objects as inputs and return them.   There are a couple of things to note:
    1. Ordinary objects that are passed as arguments or returned by the methods must implent Serializable.   This enables them to be sent as a data stream across the network.   Only the data and not the executable code for objects is passed, thus both the client and server must have a copy of the class file for that object.
    2. Remote objects can be passed from the server to the client as the return value of a method.   In this case, the object is not actually transmitted, but rather, just a stub (see below and the link to the left).
  4. Initial communications between the client and server is established through simple URL-type addressing with minimal references to any underlying communications mechanisms or protocols.

Main Components:

  1. A "client" computer and a "server" computer:  The remote object resides on the server and is accessed by the client.   Note that the roles of client and server are not permanent and may change/reverse depending on which computer accesses a remote object stored on another computer.  It is possible for a single computer to be both a client and a server.
  2. An interface that implements Remote:   This is the view of the remote object by the client.   To the client, the remote object on the server is a local object with the specified interface.    All clients accessing the remote object must have a local copy of the same interface class unless the new dynamic loading schemes in Java 2 are utilized.
  3. An instantiation of a class that extends UnicastRemoteObject and implements the same interface given in #2 above:   This is the remotely accessible object that resides on the server.
  4. A "stub" object created by the rmic program on the above remote object's class file:  This object resides on the server and is transmitted to the client whenever a reference to the remotely accessible object is established.  This is the actual object that the client interacts with.  The stub object simply delegates all methods call across the network to its corresponding skeleton object on the server. 
  5. A "skeleton" object created by the rmic program from the above remote object class:  This object resides on the server and delegates method requests received across the network from the stub object to the actual remote object. 

    Note:  JBuilder 4 will automatically run rmic on those files designated for sub and skeleton creation.   Right click the file and look under Properties/Build.
  6. The RMI Registery:  This is a program on the server called rmiregistry that must be running before any RMI connection is established.   This program associates an instatiation of a remote object with a name that can accessed with a URL-type call.   A name is "bound" into the RMI registry by using Java's built-in method: 

    void Naming.bind(String aName, UnicastRemoteObject aRemoteObject)

    This is how the first connection to the first remote object is established.  Usually that first object is a "factory" design class that has methods to return references to other remote objects.   This recommended "bootstrapping" technique keeps a minimum number of names in the RMI registry.

    Note: JBuilder 4 can run the RMIRegistry directly.  Click on Tools/RMIRegistry. 

    Example (needs to be in a try-catch block): 
    Naming.bind("MRO,new MyRemoteObject()); 

  7. Naming.lookup(): The reference to the remote object can be established either by using Java's built-in method:

    Remote Naming.lookup(String aRMI_URL)

    Or the reference can simply be a return value from a previously established remote object reference's method (e.g. a factory method). 

    Example (needs to be in a try-catch block): 
    IMyRemoteObject mRO = (IMyRemoteObject) Naming.lookup("rmi://foo.cs.oberlin.edu/MRO"); 

  8. Dynamic Class Loading: If the client does not have the class file for the serializable object being sent, it will try to to download the class file from the location specified by the java.rmi.server.codebase system property that is imbedded in the serialized object.  Sun has written a simple class file server that can be used for dynamic class loading.   That code is provided here in a slightly modified version.  It will work properly only if the java.rmi.server.codebase system property is as a URL that points to it.   For example, http://foo.cs.oberlin.edu:2001.    The server also needs to know the exact path to the default Java package directory on the server machine.

    Note:   If both the client and the server have classes with exactly the same name, errors (usually unmarshalling errors) can occur.   It is highly recommended that the names of any serialized classes being sent across the RMI connection be "personalized" in some way--by appending one's initials to the classname for instance.    This problem can be avoided by carefully setting up exactly what directories are visible to the RMIRegistry, class server and application when they are running, but that is beyond the scope of this web page.   See one of the several O'Reilly books that contain sections dealing with RMI for more information. 

Example:


An RMI Chat program

This program uses RMI to establish a connection between two computers and send messages back and forth between them.  The user types in one text area and then clicks "Send."    A copy of their message, echoed from the remote machine, will then appear in the output window.   When the other party sends a message, the message will appear in the output window as well.

The program uses a full Model-View-Controller architecture and includes extra files that could be used to revamp the interface to allow automatic connections and multiple, simultaneous chat sessions. 

See documentation for RMIChat program, including the class server
Get source code for all the RMIChat program pieces

See the documentation for class server needed to support RMI's dynamic class loading capability.

 

Basic Development Procedure:

  1. JBuilder only:  Right-click the files that extend UnicastRemoteObject.  Right click the file and look under Properties/Build.    Select and check off "Generate RMI stub/skeleton".
  2. Compile all code.  Note:  For peer-to-peer connections, the client and server code will be running on separate threads.
  3. Non-JBuilder only: Run rmic [your Remote objects]  to create the stubs and skeletons.
  4. Non-JBuilder:  In Windows, run start rmiregistry to start the RMI registry up in its own window and process.   In Unix, run rmiregistry & to accomplish the same.  Type Ctrl-C in the RMI registry window to kill it when desired.
  5. JBuilder:  Start the rmiregistry by clicking on "Tools/RMIRegistry".
  6. The class server must be started before any objects are bound to the RMIRegistry!   
  7. Peer-to-Peer system: 
    1. On all machines involved, run start rmiregistry (Windows) to start the RMI registry up in its own window and process.   In Unix, run rmiregistry & to accomplish the same.  Type Ctrl-C in the RMI registry window to kill it when desired.
    2. Run your Java program as normal on all machines.

  8. Pure Client-Server system: 
    1. On the server machine only , run start rmiregistry (Windows)to start the RMI registry up in its own window and process.   In Unix, run rmiregistry &to accomplish the same.  Type Ctrl-C in the RMI registry window to kill it when desired. 
    2. Run start java [your server app]( java [your server app] &in Unix) on your server to start your server process.
    3. Run your client  Java program as normal on the client machine(s).

Warnings:

  1. RMI is extremely picky about the JDK being used.  Use the same JDK for all machines, at least ver. 1.1.6 .  Be sure that Sun's JDK is first on the classpath and regular path (before Visual Cafe's for example).
  2. Visual Cafe will not run either rmic nor rmiregistry automatically for programs involving remote objects.   Be sure to manually run these programs when needed.
  3. JBuilder will run rmic automatically only for files whose "Java Source" properties (right click the filename in the project listing) is set to generate the stubs and skeletons.     The rmiregistry can be run from inside of JBuilder (under the Tools menu).
  4. RMI does not wait for the return of a remote void method call to return before proceeding on to the next statement.   This can cause some strange behaviors if the code assumes a certain order of execution based on the assumption that the call to the void method waits for it to finish, as normally happens in a non-RMI, single-threaded situation.  For instance, the following code can cause problems:

    // in ObjectA, initially call method1() below.   remoteObjectB is a RMI stub to a remote ObjectB instance.

    void method1()
    {
         remoteObjectB.doIt();
         System.out.println("the statement after the call to remoteObjectB");
    }
     
    void method2()
    {
        System.out.println("this might come out first or second!");
        // This will mostly likely come out second due to network delays.
       // In a non-RMI, single-threaded situation, this line would print first.
    }


    // The following is in class ObjectB which holds an RMI stub to the above ObjectA instance.

    void doIt()
    {
         remoteObjectA.method2();
    }

Factories and RMI

Factories are one of the most useful techniques to use with RMI.   Usually the server only binds out one object, a factory object, which the client uses to instantiate all the other objects it actually needs.    This makes connecting much simpler, more flexible and more robust.

This technique is important enough that Sun even has a whole web page devoted to it:   http://java.sun.com/j2se/1.3/docs/guide/rmi/Factory.html

 

RMI FAQ

http://java.sun.com/products/jdk/1.2/docs/guide/rmi/faq.html