Remote Proxy - RMI

Sometimes is useful to use an object as a shield to the access to the real thing, and that is where the Proxy Pattern comes in handy. A typical example is the Remote Proxy, where the proxy is meant to be a local representative to a remote object.

The access to objects on a different JVM could be performed using RMI, Remote Method Invocation. In the RMI context we say that we have an RMI stub on the client JVM that acts as a proxy to a local object. The stub would gain access to its related RMI skeleton on the target JVM, that would resolve the original call to the required object.

An example should help us to understand better what we are talking about.

The connection between the stub and skeleton is provided by an interface that would declare the available methods. Let's call it MyRemote:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface MyRemote extends Remote { // 1
    public String sayHello(String name) throws RemoteException; // 2
}
1. The Remote interface is just a marker, doesn't have any method in it. To be used by RMI an interface has to extend it.
2. Any method in a remote interface is unsafe, since anything could happen in a network. So a requisite for each method in this context is that is should be declare throwing a RemoteException. The input/output parameters in a method should be primitive or Serializable. String is serializable, so it is OK.

The UnicastRemoteObject class helps us to have a simpler job implementing the remote service:
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
    public MyRemoteImpl() throws RemoteException {}

    public String sayHello(String name) throws RemoteException {
        return "Hi " + name + ".";
    }

    public static void main(String[] args) {
        try {
            MyRemote service = new MyRemoteImpl();
            Naming.rebind("RemoteHello", service);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
We have include in the class the bootstrap code to lunch the service. Basically it just register our class in the RMI registry.

We can almost forget about stub and skeleton, since they are managed under the hood of this implementation, we just have to run rmic and let it do the dirty job:
rmic my.package.MyRemoteImpl
Well, "my.package." should be replaced with the actual package name where the class is placed. And rmic should have access to our class hierarchy.

Now we can launch the RMI registry, that would sit and wait for clients wanting to connect to our service:
rmiregistry
But before anyone could actually access the service, it should be in execution. So we start it:
java [-cp ...] my.package.MyRemoteImpl
So, that's it for the server.

The client is even simpler:
import java.rmi.Naming;

public class MyRemoteClient {
    static private final String url = "rmi://localhost/RemoteHello"; // 1
        public void go(String name) {
        try {
            MyRemote svc = (MyRemote) Naming.lookup(url); // 2
            System.out.println(svc.sayHello(name)); // 3
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}
1. OK, we actually have both client and server on the same machine, localhost. But the only thing that we have to change, in case of a real remote method invocation, is the URL that makes us available the service.
2. We need the interface MyRemote available here. There are cooler ways to do that, but in this implementation we have just copied the source file from the service project to the client one.
3. Here is where the magic happens. We are actually working with a stub provided by RMI, but we have the impression of working with the real thing.

In the application main we create an instance of the client and then here we go:
MyRemoteClient rc = new MyRemoteClient();
rc.go("Tom");

Chapter eleven of HFDP - Head First Design Patterns is about proxy, and there you can find also a quick introduction to RMI.

No comments:

Post a Comment