Java RMI Tutorial

Goal

The goal of this tutorial is to provide hands-on experience with client-server programming using the RPC paradigm. A simple client-server application will be developed using Java RMI.

System Information Application

The application developed in this tutorial allows clients to obtain information about a remote server's configuration that is encoded in its Java Properties (look in the Java Tutorial under System -> getProperties).

The syntax for calling the client is "java SystemInfoClient [server] property". If no server is specified, the local host is assumed.

Writing the Remote Service Interface

Write an interface for the service specified by the remote service implementation below. The file name of the interface should be SystemInfo.java.

Writing the Server Program

The file SystemInfoServer.java contains the implementation of the server. This consists of a constructor for the remote service, the implementation of the remote service and the main method of the server.

Writing the Remote Service Implementation

The server must provide an implementation of all the methods in the interface of the remote service. The SystemInfo service is quite simple and may be implemented by a single method, which accepts a string with the name of the property as input and returns a string with the local value of that property to the client. One way to implement this is shown below:
    public String getSystemInfo(String systemProperty) {
	return System.getProperty(systemProperty);
    }
  

Create the remote service object

The main method first calls the constructor for the class that implements the remote service interface (SystemInfoServer) this returns an object that provides the remote service. This remote object is made available to the clients by exporting to the Java RMI runtime. The creation of the remote object and exporting it to the Java RMI runtime can be done as follows:
	    SystemInfoServer obj = new SystemInfoServer();
	    SystemInfo stub = (SystemInfo) UnicastRemoteObject.exportObject(obj, 0);
  

The static method UnicastRemoteObject.exportObject exports the supplied remote object to receive incoming remote method invocations on an anonymous TCP port and returns the stub for the remote object to pass to clients. As a result of the exportObject call, the runtime may begin to listen on a new server socket or may use a shared server socket to accept incoming remote calls for the remote object. The returned stub implements the same set of remote interfaces as the remote object's class and contains the host name and port over which the remote object can be contacted.

Register the remote object with the Java RMI registry

In order to invoke a remote service, the client needs to be able to locate the service and download a client stub for the remote object. Java RMI provides registry service that allows a client to look up remote objects by name and to bind a name to a remote object's stub.

The Java RMI registry is a simplified name service that allows clients to get a reference (a stub) to a remote object. In general, the registry is used (if at all) only to locate the first remote object a client needs to use. Then, typically, that first object would in turn provide application-specific support for finding other objects. The server must therefore register the exported service with the RMI registry in order for the client to be able to locate the service and download the stub. This may be done as follows:

	    Registry registry = LocateRegistry.getRegistry();
	    registry.bind("SystemInfo", stub);
  
The static method LocateRegistry.getRegistry that takes no arguments returns a stub that implements the remote interface java.rmi.registry.Registry and sends invocations to the registry on server's local host on the default registry port of 1099. The bind method is then invoked on the registry stub in order to bind the remote object's stub to the name "SystemInfo" in the registry.

Note: The call to LocateRegistry.getRegistry simply returns an appropriate stub for a registry. The call does not check to see if a registry is actually running. If no registry is running on TCP port 1099 of the local host when the bind method is invoked, the server will fail with a RemoteException.

Writing the Client Program

The file SystemInfoClient.java contains the main class (the program) of the client. It first parses the arguments to determine the name of the (remote) host that the service is running on and the name of the system property that it should ask for. This may be done as follows:
	String serverHost = (args.length < 2) ? null : args[0];
	String systemProperty = (args.length < 2) ? args[0] : args[1];
  
It then locates the RMI registry on the (remote) host and asks it for a stub for the named service. This may be done as follows:
	Registry registry = LocateRegistry.getRegistry(serverHost);
	SystemInfo systemInfo = (SystemInfo) registry.lookup("SystemInfo");
  
Finally it invokes the remote service and prints the response on the monitor. This may be done as follows:
	String response = systemInfo.getSystemInfo(systemProperty);
	System.out.println("\nServer answer: " + systemProperty + " is " + response + "\n");
  

Putting it All Together

Putting the application together requires three simple steps:
  1. Download all the code into the same directory, and change your current directory to that directory.
    The code for the example client and server applications can be downloaded from the course webpage:
  2. Write the interface definition for the remote service
  3. Compile all sources by typing javac *.java

Running the Application

You should now be able to run the application. First, you need to start the rmiregistry, then start the SystemInfoServer and finally start the SystemInfoClient. On Unix, these three steps are required:
  1. Start the rmiregistry
    rmiregistry &
    	
    By default, the registry runs on TCP port 1099. To start a registry on a different port, specify the port number from the command line. For example, to start the registry on port 2001:
      
    rmiregistry 2001 &
            
    If the registry will be running on a port other than 1099, you'll need to specify the port number in the calls to LocateRegistry.getRegistry in the SystemInfoClient and SystemInfoServer classes. For example, if the registry is running on port 2001 in this example, the call to getRegistry in the SystemInfoClient would be:
    Registry registry = LocateRegistry.getRegistry(serverHost, 2001);
    	  
  2. Start the server
     
    java SystemInfoServer
    	
  3. Start the client
    java SystemInfoClient [host_name] <property>
    	

Useful Links