Intro
I've been fooling around with RMI today for fun and learning and got some serious headbanging when I couldn't get any of the examples I have in books by respected authors to work with JDK 1.5 (a.k.a. Tiger). All tutorials and examples on RMI say you should run rmic on the serverside objects you register in order to create stub and skeleton classes.
Yet if you do this with the 1.5 version of rmic you will
- not get a Skeleton class
- your server will not run, with an obscure error that the stub class (which IS generated) can not be found
Some serious delving into the JDK release notes told me that the skeleton class is indeed no longer created by rmic using the default options. It also hinted at the stub classes now being created by the JVM on the fly when you run the server and client applications. From this I theorised that my fault had been following the printed examples (all written for J2SDK 1.4)...
Time to put theory to the test
I deleted the generated _Stub class, crossed fingers, and started my server. Surprise surprise, it worked just fine. Next came the client (for the occasion moved to a different directory). It too worked as expected and called the server to get a response which arrived and was printed.
Now for some code :D
Step 1: define the remote interface
This can be any interface at all, except it MUST extend java.rmi.Remote. The only additional condition is that all method parameters and return types MUST be serializable (if not the data can't be flattened over the network, a logical restriction).
import java.rmi.*;
public interface SomeRemoteInterface extends Remote
{
public String sayHi() throws RemoteException;
}
Step 2: define the server class
There are many ways to accomplish this, here is a simple yet effective solution for a small server.
The class you bind to the RMI registry MUST have a no-argument constructor which throws a RemoteException, this constructor may be empty.
import java.rmi.*;
import java.rmi.server.*;
public class RemoteServer extends UnicastRemoteObject implements SomeRemoteInterface
{
public String sayHello()
{
return "Server says Hi";
}
public RemoteServer() throws RemoteException {}
public static void main(String[] args) throws Exception
{
Naming.rebind("Hello", new RemoteServer());
}
}
Step 3: the client
import java.rmi.*;
public class RemoteClient
{
public static void main(String[] args) throws Exception
{
MyRemote service = (SomeRemoteInterface)Naming.lookup("rmi://localhost/Hello");
System.out.println(service.sayHi());
}
}
I've deliberately not done any exception handling here, instead just dumping when an error occurs. To run all this, first of course compile server and client (the interface gets compiled automatically when you compile either).
>javac RemoteServer
>javac RemoteClient
Then start the RMI registry.
>start rmiregistry
(or >rmiregistry & on Unix)
Next start the server
>start java RemoteServer
Last but not least start the client and marvel at the friendly message you get
>java RemoteClient
Of course in a real scenario the client would run on a different machine from the registry and the server. In that case the interface must be available to both.