Introduction to Java Remote Method Invocation (RMI) 


The description of Java RMI and the "Hello World" program are based on the following tutorials which are accessible from official Java RMI pages at Sun Microsystems, Inc:

This is a brief introduction to Java Remote Method Invocation (RMI). Java RMI is a mechanism that allows one to invoke a method on an object that exists in another address space. The other address space could be on the same machine or a different one. The RMI mechanism is basically an object-oriented RPC mechanism. CORBA is another object-oriented RPC mechanism. CORBA differs from Java RMI in a number of ways:

  1. CORBA is a language-independent standard.
  2. CORBA includes many other mechanisms in its standard (such as a standard for TP monitors) none of which are part of Java RMI.
  3. There is also no notion of an "object request broker" in Java RMI.

Java RMI has recently been evolving toward becoming more compatible with CORBA. In particular, there is now a form of RMI called RMI/IIOP ("RMI over IIOP") that uses the Internet Inter-ORB Protocol (IIOP) of CORBA as the underlying protocol for RMI communication.

The Java Remote Method Invocation (RMI) system allows an object running in one Java Virtual Machine (JVM) to invoke methods on an object (call it remote object) running in another JVM. A typical server application

 A typical client application 


RMI provides the mechanism by which the server and the client communicate and pass information back and forth. Such an application is sometimes referred to as a distributed object application .

Distributed object applications need to

In the following tutorial we describe a simple RMI application -- a distributed "Hello World" program. The program gets  the string " Hello World: I am your remote object!" from a remote machine and prints it out on a local machine.  


The "Hello World" example.

The "Hello World" application consists of two separate programs: a server program and a client program.The client program  contacts the registry, gets a remote reference  to the remote object (created by the server), and uses the reference to invoke the method on the remote object. The method returns the string " Hello World: I am your remote object!" to the client, which can print it out on the client's machine. RMI provides the mechanism by which the server and clients communicate and pass information back and forth.

Because the JavaTM programming language requires a mapping between the fully-qualified package name of a class and the directory path to that class, you should decide on package and directory names before you begin writing any code written in the Java programming language. This mapping allows the compiler for the Java programming language to know the directory in which to find the class files mentioned in a program. For the programs in this tutorial, the package name for both Client and Server's programs is hello and the source directory is $HOME/javarmi/hello in  both Server and Client's machine. 

To create the directory for your source files in the SolarisTM operating environment, execute the command:

    mkdir -p $HOME/javarmi/hello
On Microsoft Windows platforms, you would go to the directory of your choice, and type:
    mkdir javarmi
mkdir javarmi/hello

1 "Hello World" Server Program

A distributed application built using Java RMI is made up of interfaces and classes, as any other Java application. The interfaces define methods, and the classes implement the methods defined in the interfaces. The classes may also define additional methods which will not be accessible to clients.

Below we describe the definition and implementation of a remote interface of the "Hello World" server:

1.1  Defining a Remote Interface

If we want an object to be remote (i.e. accessible from other virtual machines), the class of the object should implement a remote interface, which has the following characteristics:

Here is the definition of the remote interface hello.Hello . The interface contains just one method, sayHello(), which returns a string to the caller.

Below we report the code Hello.java which is supposed to reside in $HOME/javarmi/hello in the server's machine.  
package hello;
import java.rmi.Remote;

import java.rmi.RemoteException;
/**
* Remote Interface for the "Hello, World!" example.
*/
public interface Hello extends Remote {

/**
* Remotely invocable method.
* @return the message of the remote object, such as "Hello, world!".
* @exception RemoteException if the remote invocation fails.
*/
  String sayHello() throws RemoteException;
}
By extending the interface java.rmi.Remote, this interface marks itself as one whose methods can be called from any virtual machine. Any object that implements this interface becomes a remote object. Note that method sayHello() of the interface hello.Hello, as a remote method, must be defined as capable of throwing a java.rmi.RemoteException . This exception can be thrown during a remote method call if either a communication failure or protocol error has occurred. A RemoteException is a checked exception, so any code making a call to a remote method needs to handle this exception by either catching it or declaring it in its throws clause. If you want more information on failure and recovery in distributed systems, you may wish to read A Note on Distributed Computing.

1.2  Implementing a Remote Interface with a Remote Class

Let's turn now to the task of implementing a remote class  for the "Hello World!" server. In general the implementation class of a remote interface should at least: Note that a Remote Class can have methods that are not in the Remore Interface. These methods can only be invoked locally!

We also need to create the remote object and register it with the RMI remote object registry (or another naming service), for bootstrapping purposes. This setup procedure can be encapsulated in a main method in the remote object implementation class itself (as in our example), or it can be included in another class entirely.

In this example, the main method is part of hello.HelloImpl . The server program needs to:

An explanation of each of the preceding six steps follows the source for HelloImpl.java which is supposed to reside in the server's machine in the directory $HOME/javarmi/hello:

package hello;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
     

public class HelloImpl extends UnicastRemoteObject
                       implements Hello {

  public HelloImpl() throws RemoteException {
       super();
  }

  public String sayHello() {
         return  "Hello World: I am your remote object!";
  }

  public static void main(String args[]) {

    // Create and install a security manager
    if (System.getSecurityManager() == null) {
        System.setSecurityManager(new RMISecurityManager());
    }
    try {
         Hello obj = new HelloImpl();
         // Bind this object instance to the name "HelloServer"
         Naming.rebind("//profs.sci.univr.it:2345/HelloServer", obj);
         System.out.println("HelloServer bound in registry");
    } catch (Exception e) {
           System.out.println("HelloImpl err: " + e.getMessage());
           e.printStackTrace();
      }
   }
 }

   

Below we describe the program in detail.

1.3  Declaring a Remote Object Class

We can declare class  HelloImpl, e.g. as follows:

        public class HelloImpl extends UnicastRemoteObject
            implements Hello {

This declaration states that the class implements the Hello remote interface and extends the class java.rmi.server.UnicastRemoteObject .

By extending UnicastRemoteObject, the HelloImpl class can be used to create a remote object that:

If you want a remote object that can be activated (created) when a client requests it, rather than running all the time, after you finish this tutorial, you can take a look at the RMI Activation tutorial. Also, you can learn about how to use your own communication protocol, rather than the TCP sockets that RMI uses by default, in the tutorial on Creating a Custom RMI socket factory.

1.4  Constructing a Remote Object

  The constructor for a remote class provides the same functionality as the constructor for a non-remote class: it initializes the variables of each newly created instance of the class, and returns an instance of the class to the program which called the constructor.

In addition, the remote object instance will need to be "exported" .  Exporting a remote object makes it available to accept incoming remote method requests, by listening for incoming calls to the remote object on an anonymous port. When you extend java.rmi.server.UnicastRemoteObject or java.rmi.activation.Activatable, your class will be exported automatically upon creation.

If you choose to extend a remote object from any class other than UnicastRemoteObject or Activatable, you will need to explicitly export the remote object by calling either the UnicastRemoteObject.exportObject method or the Activatable.exportObject method from your class's constructor (or another initialization method, as appropriate).

Because the object export could potentially throw a java.rmi.RemoteException , you must define a constructor that throws a RemoteException , even if the constructor does nothing else. If you forget the constructor, javac will produce the following error message:

	HelloImpl.java:13: Exception java.rmi.RemoteException must be caught, or it must be
declared in the throws clause of this method.
super();
^
1 error
To review: The implementation class for a remote object needs to: Here is the constructor for the hello.HelloImpl class:

        public HelloImpl() throws RemoteException {
                super();
            }

  Note the following:

If you are interested in why java.rmi.RemoteException is a checked exception rather than runtime exception, please refer to the archives of the rmi-users email list: http://java.sun.com/products/jdk/rmi/archives/3490.html

Although the call to the superclass's no-argument constructor, super(), occurs by default (even if omitted), it is included in this example to make clear the fact that the Java virtual machine (JVM) constructs the superclass before the class.

1.5  Remote and Local Methods of a Remote Object

The class for a remote object must provide implementations for each of the remote methods declared in the remote interfaces. Here we have a single remote method, sayHello(), which is implemented as follows:

        public String sayHello() {
               return  "Hello World: I am your remote object!";
           }

The method returns string  "Hello World : I am your remote  object! " directly to the caller.

The class HelloImpl can also contain some methods that can be called only locally (actually the constructor defined above is such a method). Below, we define another local method -- a main method, that is used to create an object of class  HelloImpl and make it available to clients.

1.6  Setup Procedure

The server needs to create and install the remote objects. We can encapsulate the setup procedure, e.g. inside the main method of class HelloImpl . A setup procedure should at least:

1.7  Creating and Installing a Security Manager

Here's the code that creates and installs the security manager which is supplied as part of the RMI system:

        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }

A security manager needs to be running so that it can guarantee that the classes that get loaded do not perform operations that they are not allowed to perform.


1.8  Creating Remote Objects

You can create the Hello remote object like this:

        Hello obj = new HelloImpl();

It is now up and running and ready to receive method calls from clients on an anonymous port, one chosen by RMI or the underlying operating system. Notice that the type of the variable obj is Hello, not HelloImpl  -- this is to emphasize that the interface available to clients is actually the Hello interface and its methods, not the HelloImpl class and its methods.

We already said you might want a remote object to be activated when a client actually requests it, rather then running all the time.  The object activation is beyond of the scope of our short introduction to RMI (see the "Remote Object Activation" tutorial at http://java.sun.com/j2se/1.3/docs/guide/rmi/activation.html for details).

1.9  Registering Remote Objects

Before a caller (client, peer, or applet) can invoke a method on a remote object, that caller  must first obtain a reference to the remote object. The reference can be passed as an argument (or returned value) of method calls on other objects, like any other data. The system provides a particular remote object, the RMI registry, which defines a map from string names to remote object references. The java.rmi.Naming interface provides the necessary API for binding, or registering, and looking up remote objects in the registry.

The registry is typically used only for bootstrapping a distributed application; after locating a first object, references to new remote objects can be passed directly between a client and server.

We can register the remote object obj as follows:

        Naming.rebind(locator, obj);

Note the following about the arguments to the rebind method call:


Below is the complete definition of function main.

    public static void main(String args[]) {

      // Create and install a security manager
      if (System.getSecurityManager() == null) {
          System.setSecurityManager(new RMISecurityManager());
      }
      try {
                          / / Create a new remote object
           Hello obj = new HelloImpl();
           // Bind this object instance to the name "HelloServer"
           Naming.rebind("//profs.sci.univr.it:2345/HelloServer", obj);
           System.out.println("Object HelloServer bound in registry");
       } catch (Exception e) {
         System.out.println("HelloImpl err: " + e.getMessage());
         e.printStackTrace();
         }
     }

For security reasons, an application can bind or unbind only to a registry running on the same host. This prevents a client from removing or overwriting any of the entries in a server's remote registry. As a consequence, it is never necessary to specify the name of the machine when one is registering an object. A lookup, however, can be done from any host.

Note that we need to deal with handling RemoteException that might arise during remote object exporting and registering (the rebind method makes a remote call to the RMI registry on the local host). Here, we simply exit after printing an error message.

The main method exits but the RMI system will make sure that the Hello object is available to accept calls and won't be reclaimed until its binding is removed from the registry, and no remote clients hold a remote reference to the Hello object.


2. Compilation and Deployment of the Server's Program.

We assume below that the source code, that is  Hello.java and HelloImpl.java, is in the server's machine in the directory $HOME/javarmi/hello.

The compilation of the server's java source is a two-step process. In the first step you use the javac compiler to compile the source files (i.e. *.java files).

In the second step you use the rmic compiler to create stubs for the remote objects (stubs are needed only for remote object implementation classes). RMI uses a remote object's stub class as a proxy in clients so that clients can contact the remote object.  A stub is a client-side proxy for a remote object which forwards RMI calls to the server-side dispatcher, which in turn forwards the call to the actual remote object implementation.

2.1 Compiling java server's sources

To compile the Java classes, make sure that your local Java CLASSPATH includes the source directory(ies).  Here's how to modify the CLASSPATH environment variable under Unix, e.g. execute

    setenv CLASSPATH $HOME/javarmi   (or  set CLASSPATH $HOME/javarmi)

If the variable has not been defined before, remove ":$CLASSPATH ". 

When you build either server- or client-side classes with the javac and rmic compilers, you generally need to specify where the resulting class files should reside so that they are network accessible . The network accessibility of the class files allows the RMI runtime to download code when needed. Rather than defining its own protocol for code downloading, RMI uses URL protocols supported by the Java platform (e.g., HTTP). In our case, this location is, for UNIX,

    $HOME/public_html/


Most web servers allow accessing a user's public_html directory via an HTTP URL constructed as http://host/~username/, for example http://profs.sci.univr.it/~merro  . If your web server does not support this convention, you could use a file URL of the form "file:/home/username/public_html" (or "file:///home/username/public_html" ) for testing, but this approach will limit you to communicating between a client and server that have access to the same physical file system. As an alternative, you can use an HTTP URL by setting up a minimal web server on your system; we have one available for download here .

To compile  the *.java source files and create the *.class files, you can execute the javac command, e.g. as follows:

       cd $HOME/javarmi/hello
       javac -d  $HOME/public_html/myclasses Hello.java HelloImpl.java

This will compile the source files *.java, create a directory hello in $HOME/public_html/myclasses (if it does not exist yet), and place in that directory the generated files Hello.class and HelloImpl.class. (Make sure you have created a directory myclasses in public_html before trying to compile).

Or, if we can use common file space for deploying our distributed program, simply:

            cd HOME/javarmi/hellocd
     javac Hello.java HelloImpl.java

and the generated files will be created in the directory $HOME/javarmi/hello .


2.2 Generating stubs and skeletons

To create stub (and optionally skeleton files), run the rmic compiler on the fully qualified class names of the remote object implementations that must be found in the class path, such as hello.HelloImpl. The rmic command takes one or more class names as input and produces as output class files of the form, e.g. HelloImpl_Stub.class and HelloImpl_Skel.class.

For example, to create the stub and skeleton for the HelloImpl remote object implementation, run rmic like this:

    cd $HOME/javarmi
    rmic -d  $HOME/public_html/myclasses  hello.HelloImpl

or, if we can use common file space for deploying our distributed program, simply:

    cd $HOME/javarmi
    rmic hello.HelloImpl

The "-d" option indicates the root directory in which to place the compiled stub and skeleton class files. So the preceding command creates the following files in the directory $HOME/public_html/myclasses/hello :

The generated stub class implements exactly the same set of remote interfaces as the remote object itself. This means that a client can use the Java programming language's built-in operators for casting and type checking. It also means that remote objects written for the Java platform support true object-oriented polymorphism.

RMI treats a remote object differently from a nonremote object when the object is passed from one virtual machine to another. Rather than making a copy of the implementation object in the receiving virtual machine, RMI passes a remote stub for a remote object. The stub acts as the local representative, or proxy, for the remote object and basically is, to the caller, the remote reference. The caller invokes a method on the local stub, which is responsible for carrying out the method call on the remote object.

A stub for a remote object implements the same set of remote interfaces that the remote object implements. This allows a stub to be cast to any of the interfaces that the remote object implements. However, this also means that only those methods defined in a remote interface are available to be called in the receiving virtual machine.

Now you can run the server and then the client of our example "Hello World!" program.

2.3 Executing Java RMI programs

If you are using JDK 1.2, you need to specify a policy file when you run your server and client. Here is a policy file that gives global permission to anyone from anywhere (try not to use this policy outside classroom!):

    grant {
            // Allow everything for now
            permission java.security.AllPermission;
    };

Below, we assume that the policy file is named policy and placed in the directory $HOME/java. For information on how to properly define a java.security.policy file which contains the appropriate permissions, please refer to the following documents: " Default Policy Implementation and Policy File Syntax", and " Permissions in the Java 2 SDK" (available at http://java.sun.com/j2se/1.3/docs/guide/security/PolicyFiles.html , and http://java.sun.com/j2se/1.3/docs/guide/security/permissions.html

Before starting the server program, you need to start RMI's registry. The RMI registry is a simple server-side name server that allows remote clients to get a reference to a remote object. Typically, it is used only to locate the first remote object an application needs to talk to. Then that object in turn would provide application-specific support for finding other objects.

Note that before you start the rmiregistry, you must make sure that the shell or window in which you will run rmiregistry either has no CLASSPATH environment variable set or has a CLASSPATH environment variable that does not include the path to any classes, including the stubs for your remote object implementation classes, that you want downloaded to clients of your remote objects. 

If you start the rmiregistry, and it can find your stub classes in its CLASSPATH, it will ignore the server's java.rmi.server.codebase property, and as a result, your client(s) will not be able to download the stub code for your remote object. For an explanation of how code downloading works in RMI, please take a look at the tutorial on Dynamic code downloading using RMI.

So, you can unset the variable CLASSPATH in the following manner

            unsetenv CLASSPATH    (or unset CLASSPATH)

To start the registry on the server, execute the rmiregistry command. This command produces no output and is typically run in the background. For more on the rmiregistry, you can refer to the Solaris rmiregistry manual page or the Win32 rmiregistry manual page.

For example, in the Solaris operating environment:

   rmiregistry &

By default, the registry runs on port 1099. To start the registry on a different port (for istance, 2345), specify the port number on the command line, e.g.

    rmiregistry  2345  &

2.4 Starting the server.

Now  you can start the server. First, you need to make sure that both the interface and the remote object implementation class are in your class path (since that is what you are starting), i.e. in our case the path has been set up as follows:

    setenv CLASSPATH $HOME/public_html/myclasses:$CLASSPATH

When you start the Hello World server, you need to specify, using the java.rmi.server.codebase property, the location, e.g. a code base URL, where the server's classes are made available so that class information for objects sent to other virtual machines will include the location of the class so that a receiver can load it. In this example the server-side classes to be made available for downloading are the HelloImpl's stub and the Hello interface, available in user's public_html/myclasses directory.


There are four things that need to go on the same command line: the " java" command, followed by two property name=value pairs (for the codebase property, note that there are no spaces from the "-D" all the way though the last "/") and then the fully-qualified package name of the server program. There should be a space just after the word " java", between the two properties, and just before the word "hello " (which is very hard to see when you view this as text, in a browser, or on paper). The following command shows how to start the HelloImpl server, specifying the java.rmi.server.codebase and java.security.policy properties

Below, we assume the user "merro"starts the hello server on the current host "profs.sci.univr.it", the same host where he started the registry, by typing in his $HOME/javarmi directory:

java -Djava.rmi.server.codebase=http://profs.sci.univr.it/~merro/myclasses/
     -Djava.rmi.server.hostname=profs.sci.univr.it

     -Djava.security.policy=$HOME/javarmi/policy
      hello.HelloImpl & 

(If you assume shared file space and make the HelloImpl_Stub.class available locally to the client then you can omit the codebase property above.)

The codebase property will be resolved to a URL, so it must have the form of "http://aHost/somesource/" or "file:/myDirectory/location /" or, due to the requirements of some operating systems, "file:///myDirectory/location/ " (three slashes after the "file:").

When the HelloImpl server binds its remote object reference in the RMI registry, the registry downloads the HelloImpl_Stub.class, as well as the Hello interface on which the stub class depends . These classes are downloaded from  the HelloImpl's web server of file system.

Once an object is exported, the RMI runtime substitutes a reference to the remote object's stub for the actual remote object reference specified by the obj argument. When a client performs a lookup in a server's remote object registry, a serialized instance of the stub for the implementation is returned.

Finally, on the screen of the server machine will appear the sentence:

"Object HelloServer bound in registry"

In the exercises, you will execute both the client and server programs as the same user (you might log in on different machines though, but make sure that you don't use the same port numbers for rmiregistry as your neighbour, e.g. choose a port number as your_computer_id + 2000). Also, please don't forget to kill all your running processes after finishing the exercises!

3 "Hello World" Client Program

A remote method invocation can return a remote object as its return value, but one must have a remote object in order to perform a remote method invocation. So to obtain a remote object one must already have one. Accordingly, there must be a separate mechanism for obtaining the first remote object. The RMI Registry fulfills this requirement. It allows one to obtain a remote object using only the name of the remote object.

The name of a remote object includes the following information:

  1. The Internet name (or address) of the machine that is running the RMI Registry with which the remote object is being registered. If the RMI Registry is running on the same machine as the one that is making the request, then the name of the machine can be omitted.
  2. The port to which the RMI Registry is listening. If the RMI Registry is listening to the default port, 1099, then this does not have to be included in the name.
  3. The local name of the remote object within the RMI Registry.

The code that calls a Hello object's methods must obtain a reference to that object, invoke a remote method sayHello, and then print the string received by the remote method call.  The complete code is below.

    package hello;

    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.RMISecurityManager;

    public class HelloClient {

        public static void main(String args[]) {

             // "obj" is the identifier that we'll use to refer
             // to the remote object that implements the "Hello"
             // interface
             Hello obj = null;
             String str = "";

             // Create and install a security manager
             if (System.getSecurityManager() == null) {
                 System.setSecurityManager(new RMISecurityManager());

             try {
                 String name = "//" + args[0] + "/HelloServer";
                 obj = (Hello)Naming.lookup(name);
                 str = obj.sayHello();
                 System.out.println(str);
             } catch (Exception e) {
                 System.out.println("HelloClient exception: " +
                      e.getMessage());
                 e.printStackTrace();
             }
        }
    }

Since RMI could be downloading code to the client (here the HelloImpl 's stub), the client begins by installing a security manager.

Next, the client constructs a name used to look up a Hello remote object (this name has the same URL syntax as the name passed in the rebind call, described earlier). Then it uses the Naming.lookup method to look up the remote object by name in the remote host's registry. To be more precise, the Naming.lookup method obtain  a handle (a reference) to the object stub from the RMI registry running on the remote host at the appropriate port.  The name of the remote host on which the Hello object (and the rmiregistry) runs will be passed as the first command line argument (i.e. args[0]) when running the client code. Note that the result of Naming.lookup must be cast to the type of the Remote Interface (i.e. Hello).

Finally, the remote method invocation in the example Client is sayHello(). It returns a String which is then printed. A remote method invocation can return a String object because String is a Serializable class.

4 Compiling and deploying the java Client's sources

In order to build the client code, you must have the file Hello.java which contains the interface to the remote object that the client can use (note that the interface files must be distributed to the developers of client programs anyway, so they are known locally and can be made available through the class path). Let's say the file is located in the same directory as the client's source program, e.g. $HOME/javarmi/hello, in the client's machine. The client classes can be built, e.g. as follows:

    cd $HOME/javarmi
    javac hello/Hello.java hello/HelloClient.java

Note that also the client must contain in its java directory the file  policy seen above. 

4.1  Start the Client

Once the server is running, the user  can start the client program on another machine, e.g. by typing in his $HOME/java directory:

    java -Djava.security.policy=$HOME/java/policy
          hello.HelloClient remotehost

Where "remotehost" is given by host + port number, where the remote object is running. For istance, "profs.sci.univr.it:2345"
to denote the remote host
"profs.sci.univr.it" and the port 2345.  

Instead of "remotehost" you can use the variable localhost if the client is running in the same machine as the server.

As a result of the Naming.lookup call, the HelloClient program loads the HelloImpl_Stub.class from the HelloImpl 's web server. Since the client has the Hello interface available in its class path, this class is loaded to the HelloClient's virtual machine from the class path, not from the remote location. 

The HelloImpl's stub class will be dynamically loaded into a client's virtual machine only when the class is not already available locally and the java.rmi.server.codebase property has been set properly, to the network-accessible location of the stub class, when the server is started. Once such a stub is loaded, it will not need to be reloaded for additional references to HelloImpl's objects.

The result of the execution of the client program should yield the following sentence:


"Hello World: I am your remote object!".


    These notes are available electronically from  http://profs.sci.univr.it/~merro/javarmi.html.
    Massimo.Merro@univr.it