Since RMI is network based, it is essential to understand a little networking terminology. Networking is based on the notion of hosts, servers, and clients. We can treat each computer as a host. Hosts have names ("ieng9") and network address ("132.239.50.9"). Some hosts provide services. These are called servers. These services are identified by either a name ("ftp", "http", etc.) or the associated port numbers. Clients are machines which use these services. The services mentioned above are "standard" services. If new services are added, how do clients come to know of the services? This is done by having a registry service. With a registry service, servers can register services and clients can lookup services and find the corresponding servers.
In RMI framework, each server is an object. The clients can use these objects as if they are local objects. Since the server and client objects can be geographically distributed, they need to communicate through messages. Java provides a good abstraction for this. (The idea of using the network as a software bus for communication between objects is the underlying idea of CORBA for achieving integration of programs written in various languages.)
Suppose you want to provide a compute service. You are willing to take any program and execute it, and return the result. In the language of Java, you are willing to take any object, execute one of its methods, and return the result. To be more specific, we should specify the following.
import java.rmi.*;
public interface ComputeServer extends Remote {
Object compute(Task task) throws RemoteException;
}
Here the class ComputeServer provides a service called
compute which returns an object of type Object.
To run this method, it needs an object of type Task as
input.
compute method is
an object of type Task. We can specify task as
public interface Task {
Object calculate();
}
Task. The server runs its run()
method and returns the result.
import java.rmi.*;
import java.rmi.server.*;
public class CServer
extends UnicastRemoteObject
implements ComputeServer {
public CServer() throws RemoteException { }
public Object compute(Task task) {
return task.calculate();
}
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
ComputeServer server = new CServer();
Naming.rebind("//ieng9:2006/ComputeServer", server);
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(-1);
}
System.out.println("Ready to receive tasks");
return;
}
}
Points to note about this are:
compute method.
ComputeServer at the
registry ieng9:2006.
import java.rmi.*;
import java.rmi.registry.*;
public class RecPower implements Task, java.io.Serializable {
private int m, n;
RecPower(int m, int n) {
this.m = m;
this.n = n;
}
public Object calculate() {
int res = recPower(m, n);
return new Integer(res);
}
int recPower(int m, int n) {
if (0 == n) return m;
int j = recPower(m, n/2);
if (0 == n/2) return j*j;
else return j*j*n;
}
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
RecPower rp = new RecPower(11, 10);
try {
ComputeServer csi
= (ComputeServer) Naming.lookup("//ieng9:2006/ComputeServer");
Integer power = (Integer) csi.compute(rp);
System.out.println(power);
} catch (Exception e) {
e.printStackTrace();
}
}
}
rmic CServer to
create these files.
rmiregistry 2006 &for the above code. (If you find that this results in an error, you should change the port number in the above code, compile it, and start
rmiregistry at the
new port number.)
import java.rmi.*;
interface SquareI extends Remote {
long square(int x) throws RemoteException;
}
The server class implements this method. The main method
starts the server.
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.net.*;
public class Square extends UnicastRemoteObject implements SquareI {
Square() throws RemoteException { }
public long square(int x) throws RemoteException {
return x*x;
}
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
Square s = new Square();
Naming.bind("//ieng9/Square", s);
System.out.println("Ready to compute squares");
} catch(Exception e) {
e.printStackTrace();
}
}
}
The client calculates the squares of first ten numbers using the
remote service.
import java.rmi.*;
import java.rmi.registry.*;
public class SquareClient {
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
SquareI t = (SquareI)Naming.lookup( "//ieng9/Square");
for(int i = 0; i < 10; i++)
System.out.println("Square" +"("+i+") = " + t.square(i));
} catch(Exception e) {
e.printStackTrace();
}
}
}
+--------+ +--------+
| | | | +--------------+
| | | | | RMI REGISTRY |
|Server | | Client | +--------------+
| | | |
+--------+ +--------+
| |
| |
| |
| |
+--------+ +--------+
| | | |
| | | |
|Skel | <-----> | Stub |
| | | |
+--------+ +--------+
The skeletons and stubs are created using rmic.
Naming.lookup() method.
java.io.Serializable. The actual serialization is done by
Java itself.