Lab 8 : RMI

Distributed Programming using RMI

RMI is Java-oriented middleware for Java distributed applications.
In RMI an object invokes methods on another remote object. The remote object is called the server. RMI tries to make remote invocation as local invocation.
Setting up RMI is not a one step process but a series of steps.
1. Define the interface for the remote object
2. Implement the remote interface in a class, an object of this class will be the remote
object (the server)
3. Implement the client
4. Compile the server and the client
5. Generate stub and skeletons using rmic, the rmi compiler that comes with the JDK
6. Start the registry (if it is not running already)
7. Run the server class from step 2
8. Run one or more clients from step 3

Example: Running the DateServer example from last week's lecture

Step 1: Defining the interface for the remote object

The interface for the remote object must implement the java.rmi.Remote interface and all
method definitions inside the interface must throw a RemoteException

import java.rmi.*;
import java.rmi.server.*;
import java.util.Date;
public interface DateServer extends Remote{
    public Date getDate() throws RemoteException;
}

Step 2: Implement the Remote interface

A remote server class must extend the UnicastRemoteObject and implement the remote
interface from step 1

import java.rmi.*;
import java.rmi.server.*;
import java.util.Date;
import java.net.*;
public class DateServerImpl extends UnicastRemoteObject
implements DateServer{
    public DateServerImpl() throws RemoteException{ }
    public Date getDate(){
        return new Date();
    }
 
    public static void main(String args[]){
        try{
            //create an instance of the remote object
            DateServerImpl dateServer = new DateServerImpl();
            // register this remote object with the registry as DateServer
            Naming.bind("DateServer", dateServer);
        }catch(AlreadyBoundException abex){
            abex.printStackTrace();
        }catch(RemoteException rex){
            rex.printStackTrace();
        }catch(MalformedURLException mex){
            mex.printStackTrace();
        }
    }
} // end DateServerImpl class

Step 3: Implement the client

The client first locates the registry or the name server. It uses only the interface of the
remote object not the implementation class.

import java.rmi.*;
import java.net.*;
import java.util.Date;
public class DateClient{
    public static void main(String args[]) {
        try{
            //get a reference to the remote object from the registry
            DateServer dateServer =
            (DateServer)Naming.lookup("rmi://localhost/DateServer");
            //Invoke the remote method on the remote object
            Date d = dateServer.getDate();
            System.out.println(d);
        }catch(NotBoundException nbex){
            nbex.printStackTrace();
        }catch(RemoteException rex){
            rex.printStackTrace();
        }catch(MalformedURLException mex){
            mex.printStackTrace();
        }
    }
}

Step 4: Compile the server and client classes

Put the DateServer, DateServerImpl and DateClient class in a directory called rmi_date.
Compile the classes using javac *.java
At this point you need to generate the stub and skeleton classes for the client and the
remote object respectively.

Step 5: Generating the stub and skeleton

Use the rmi compiler to generate the stub and skeleton classes as shown below
$ rmic DateServerImpl

Step 6: Start the RMI registry

Before a the remote object can be running it must be registered with the RMI registry, this
allows clients to find remote objects in a transparent way.

$ rmiregistry

If the registry is already running, you can choose to do one of the following:
Skip this step, move to step 7 and run the server.
OR
Start your own instance of registry on a different port. RMI registry by default listens
on port 1099. Start the registry on a different port :

$ rmiregistry 3000

Change the remote server so that it binds to your registry on port 3000 or whatever number you choose. In DateServerImpl class replace the following line

Naming.bind("DateServer", dateServer);

with

Naming.bind("rmi://localhost:3000/DateServer", dateServer);

Save, compile, generate stubs and skeletons with rmic and run the server.

Now change the DateClient to point to your RMI Registry. Change the following line in DateClient:

DateServer dateServer = (DateServer)Naming.lookup("rmi://localhost/DateServer");

to

DateServer dateServer = (DateServer)Naming.lookup("rmi://localhost:3000/DateServer");

Step 7: Run the server

Before running the server make sure that the skeleton class is present on the server
Run the server class as shown below:

$ java DateServerImpl

If you want to run the server in background:

$ java DateServerImpl &

If another remote object has bound itself to the registry with the same name, change
the name of the remote server in step 2 in the DateServerImpl class:
Naming.bind("DateServer", dateServer);
becomes
Naming.bind("DateServer2", dateServer); //give a new name to the remote object

Compile and run the DateServerImpl class. Also generate new stub and skeleton using rmic as shown in step 5
Also change the name of remote reference in DateClient (step 3):
DateServer dateServer = (DateServer)Naming.lookup("rmi://localhost/DateServer2");

Step 8: Run the client

Before running the client make sure that the stub class is present on the client
Run the client class as shown below
$ java DateClient

Exercises:

Write the SortServer and SortClient using RMI. Start by defining the interface for the SortServer.