The original page is available at: http://courses.dce.harvard.edu/%7Ecscie160/RmiShakedown.htm

Prepared by Charlie Sawyer

Modified by Umar Kalim

Lab 2


Getting Started Using RMI

[Adapted from the Sun tutorial "Getting Started With RMI". That tutorial makes heavy use of Applets, which we are not using. But some of the content here is extracted directly from that document.]

This document shows you the steps to follow to create a distributed version of the classic Hello World program using Java Remote Method Invocation (RMI).

The distributed Hello World example uses an client to make a remote method call to the server to retrieve the message "Hello World!". When the client runs, the message is displayed on the client.

To accomplish this, you will work through the following three lessons:

Write The Java Source Files

There are three source files that comprise the Hello World server and client:

  1. The Java remote interface.
  2. The Java remote object (server) which implements the remote interface.
  3. The Java client that remotely invokes the server's method.

Because the Java language requires a mapping between the fully qualified package name of a class file and the directory path to that class, before you begin writing Java code you need to decide on package and directory names. (This mapping allows the Java compiler to know the directory in which to find the class files mentioned in a Java program.) For the Hello World program developed here, the package name is examples.hello.

Define a Remote Interface

Remote method invocations can fail in very different ways from local method invocations, due to network related communication problems and server problems. To indicate that it is a remote object, an object implements a remote interface, which has the following characteristics:

Here is the interface definition for Hello World. The interface contains just one method, sayHello, which returns a string to the caller:

package examples.hello;
public interface Hello extends java.rmi.Remote {
	String sayHello() throws java.rmi.RemoteException;
}

Write an Implementation Class

To write a remote object, you write a class that implements one or more remote interfaces. The implementation class needs to:

  1. Specify the remote interface(s) being implemented.
  2. Define the constructor for the remote object.
  3. Provide implementations for the methods that can be invoked remotely.
  4. Create and install a security manager.
  5. Create one or more instances of a remote object.
  6. Register at least one of the remote objects with the RMI remote object registry, for bootstrapping purposes.

For example, here is the source for the HelloImpl.java file, which contains the code for the Hello World server. The code is followed by an explanation of each of the preceding six steps.

package examples.hello;

import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

public class HelloImpl
 extends UnicastRemoteObject implements Hello
{
   private String name;

   public HelloImpl(String s) throws RemoteException {
      super();
      name = s;
   }

   public String sayHello() throws RemoteException {
      return  "Hello World!";
   }
	  public static void main(String args[])
   {
      // Create and install a security manager
      System.setSecurityManager(new RMISecurityManager());
      try {
         HelloImpl obj = new HelloImpl("HelloServer");
         Naming.rebind("//myhost/HelloServer", obj);
         System.out.println("HelloServer bound in registry");
      } catch (Exception e) {
         System.out.println("HelloImpl err: " + e.getMessage());
         e.printStackTrace();
      }
   }
}

Implement a Remote Interface

The implementation class for the Hello World example is HelloImpl. An implementation class specifies the remote interface(s) it is implementing. Optionally, it can indicate the remote server that it is extending, which in this example is java.rmi.server.UnicastRemoteObject. Here is the HelloImpl class declaration:

public class HelloImpl
	implements Hello
	extends java.rmi.server.UnicastRemoteObject

Extending UnicastRemoteObject indicates that the HelloImpl class is used to create a single (nonreplicated) remote object that uses RMI's default sockets- based transport for communication.

Define the Constructor for the Remote Object

The constructor for a remote class is no different than the constructor for a nonremote class: it initializes the variables of each newly created instance of the class.

Here is the constructor for the HelloImpl class, which initializes the private string variable name with the name of the remote object:

private String name;
public HelloImpl(String s) throws java.rmi.RemoteException {
	super();
	name = s;
}

Note the following:

Although the call to the super no-arg constructor occurs by default if omitted, it is included in this example to make clear the fact that Java constructs the superclass before the class.

Provide an Implementation for Each Remote Method

The implementation class for a remote object contains the code that implements each of the remote methods specified in the remote interface.

For example, here is the implementation for the sayHello method, which returns the string Hello World! to the caller.

public String sayHello() throws RemoteException {
	return  "Hello World!";
}

Arguments to, or return values from, remote methods can be of any Java type, including objects, as long as those objects implement the interface java.io.Serializable. Most of the core Java classes in java.lang and java.util implement the Serializable interface.

A class can define methods not specified in the remote interface, but those methods can only be invoked within the virtual machine running the service and cannot be invoked remotely.

Create and Install a Security Manager

The main method of the service first needs to create and install a security manager: either the RMISecurityManager or one that you have defined yourself. For example:

System.setSecurityManager(new RMISecurityManager());

A security manager needs to be running so that it can guarantee that the classes loaded do not perform "sensitive" operations. If no security manager is specified, no class loading for RMI classes, local or otherwise, is allowed.

Create One or More Instances of a Remote Object

The main method of the service needs to create one or more instances of the remote object which provides the service. For example:

HelloImpl obj = new HelloImpl("HelloServer");

The constructor exports the remote object, which means that once created, the remote object is ready to begin listening for incoming calls.

Register a Remote Object

For a caller (client, peer, or applet) to be able to invoke a method on a remote object, that caller must first obtain a reference to the remote object. Most of the time, the reference will be obtained as a parameter to, or a return value from, another remote method call.

For bootstrapping, the RMI system also provides a URL-based registry that allows you to bind a URL of the form //host/objectname to the remote object, where objectname is a simple string name. Once a remote object is registered on the server, callers can look up the object by name, obtain a remote object reference, and then remotely invoke methods on the object.

For example, the following code binds the URL of the remote object named HelloServer to a reference for the remote object:

Naming.rebind("//myhost/HelloServer", obj);

Note the following about the arguments to the call:

For security reasons, an application can bind or unbind only in the registry running on the same host. This prevents a client from removing or overwriting any of the entries in a server's remote registry. A lookup, however, can be done from any host.

Write a Client that Uses the Remote Service

The client part of the distributed Hello World example remotely invokes the HelloServer's sayHello method in order to get the string "Hello World!", which is displayed when the client runs. Here is the code for the client:

package examples.hello;

import java.rmi.*;

public class HelloClient {
   public static void main(String args[])	{
      try {
         Hello obj = (Hello)Naming.lookup("//myhost/HelloServer");
         System.out.println("Server returned : " + obj.sayHello());
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
  1. The client first gets a reference to the "HelloServer" from the server's registry.
  2. The client remotely invokes the sayHello method of the HelloServer remote object and prints out the return value from the call.

The constructed URL optionally includes the host. If you are running both the client and server on the same machine, you can remove the "//myhost/" prfix. Otherwise, you must change the host "myhost" to the machine name that is hosting the server piece of this application.

Compile and Deploy Class Files

The source code for the Hello World example is now complete and the examples/hello directory has three files:

In this section, you compile the .java source files to create .class files. You then run the rmic compiler to create stubs and skeletons. A stub is a client- side proxy for a remote object which forwards RMI calls to the server-side skeleton, which in turn forwards the call to the actual remote object implementation.

When you use the javac and rmic compilers, you must specify where the resulting class files should reside.

Generate Stubs and Skeletons

To create stub and skeleton files, run the rmic compiler on the names of compiled class files that contain remote object implementations. rmic takes one or more class names as input and produces as output class files of the form myImpl_Skel.class and myImpl_Stub.class.

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

rmic examples.hello.HelloImpl

The preceding command creates the following files in the directory examples/hello:

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

Set Paths for Runtime

Make sure that the examples/hello directory is available via the server's local CLASSPATH when you run the HelloImpl server.

Start the Remote Object Registry, Server, and Client

Start the RMI Bootstrap Registry

The RMI registry is a simple server-side bootstrap name server that allows remote clients to get a reference to a remote object. It is typically used only to locate the first remote object an application needs to talk to. That object in turn will provide application specific support for finding other objects.

To start the registry on the server, execute the rmiregistry command. This command produces no output and is typically run in the background. For example, on Windows 95 or Windows NT:

start rmiregistry

(Use javaw if start is not available.)

The registry by default runs on port 1099. To start the registry on a different port, specify the port number in the command. For example, to start the registry on port 2001 on Windows NT:

start rmiregistry 2001

If the registry is running on a port other than the default, you need to specify the port number in the URL-based methods of the java.rmi.Naming class when making calls to the registry. For example, if the registry is running on port 2001 in the Hello World example, here is the call required to bind the URL of the HelloServer to the remote object reference:

Naming.rebind("//myhost:2001/HelloServer", obj);

You must stop and restart the registry any time you modify a remote interface or use modified/additional remote interfaces in a remote object implementation. Otherwise, the class bound in the registry will not match the modified class.

Start the Server

The following command shows how to start the HelloImpl server:

java examples.hello.HelloImpl 

Run the Client

Once the registry and server are running, the client can be run as shown here:

	java examples.hello.HelloClient

The output will look something like the following:

	Server returned : Hello World!