Java RMI Basic HowTo

This is a very brief intro into Java's RMI API. It is based on a simple example that invokes methods, on a remote machine, which square and cube a number. Some of the commands used assume the use of GNU/Linux so you might need to change a few things if you're using Windows.

1. Define the remote interface:

import java.rmi.*;

public interface TestRMIObject extends Remote
    {
    public int square(int x) throws RemoteException;
    public int cube(int x) throws RemoteException;
    }
				

2. Create a class that extends UnicastRemoteObject AND implements the interface:

import java.rmi.*;
import java.rmi.server.*;

public class TestRMIObjectImpl extends UnicastRemoteObject implements TestRMIObject
    {
    public TestRMIObjectImpl() throws RemoteException
        {
        }

    public int square(int x) throws RemoteException
        {
        return x * x;
        }

    public int cube(int x) throws RemoteException
        {
        return x * x * x;
        }
    }
				

NOTE: A constructor that throws the "RemoteException" seems to be required even if it doesn't contain any code.

3. Create an application that will create and register an instance of the object:

import java.rmi.*;

public class TestRMIObjectServer
    {
    public TestRMIObjectServer()
        {
        try
            {
            TestRMIObject tro = new TestRMIObjectImpl();
            Naming.rebind("rmi://localhost/SquareCube",tro);
            System.out.println("TestRMIObject instance registered.");
            }
        catch(Exception e) {System.out.println("Exception caught: " + e);}
        }

    public static void main(String[] args)
        {
        TestRMIObjectServer tros = new TestRMIObjectServer();
        }
    }
				

The "localhost" in the rebind call above could be any host name. At the moment I have only been able to register a object with an rmiregistry that is running on the same machine. I think that the most likely cause of this is the policy file, i.e. a permissions thing. When I sort that out I will update these notes. The "SquareCube" above could be anything: it is just the name that this object will be known by.

4. Create a file called java.policy:

grant 
    {
    permission java.net.SocketPermission "*:1024-65535","connect,accept";
    permission java.net.SocketPermission "*:80", "connect";
    };
				

At the moment all I can say about this is that it seems to tell the JVM that it is ok for it to accept connections on ports 1024 through 65535.
I do not know all the details of what can or should go into this file to make sure that it gives an implementation that is both usable and secure.
The above works!

5. Compile the server:

This will create the normal class files.

javac TestRMIObjectServer.java

6. Create the proxy stub and skeleton class files.

This is done by running the rmi compiler againt the class file that IMPLEMENTS the remote interface:
rmic TestRMIObjectImpl
NOTE: there is no ".class"

This will generate a further two class files:
TestRMIObjectImpl_stub.class
TestRMIObjectImpl_skel.class

7. Move the class and policy files to the remote machine.

But... make sure you keep a copy of the TestRMIObject_stub.class because the client needs to be compiled against it.

8. On the remote machine:

  • cd to the directory containing the class and policy files.
  • set the CLASSPATH thus:
[me@remote_machine dir_name]$ export CLASSPATH=$(pwd)
  • start the rmiregistry:
[me@remote_machine dir_name]$ rmiregistry &
  • start the server application:
[me@remote_machine dir_name]$ java -Djava.security.policy=java.policy TestRMIObjectServer &
  • With the above code this should display
"TestRMIObject instance registered."

9. Now create a client application:

import java.io.*;
import java.net.*;
import java.rmi.*;

public class TestRMIObjectClient
    {
    private TestRMIObject tro;

    static public void main(String[] args)
        {
        int res;

        TestRMIObjectClient troc = new TestRMIObjectClient();

        try
            {
            res = troc.tro.square(5);
            System.out.println("remote square called, result: " + res);

            res = troc.tro.cube(10);
            System.out.println("remote cube called, result: " + res);
            }
        catch(Exception e)
            {
            System.out.println("Exception caught: " + e);
            }
        }

    public TestRMIObjectClient()
        {
        try
            {
            tro = (TestRMIObject) Naming.lookup ("rmi://laptop/SquareCube");
            System.out.println("Remote object created...");
            }
        catch(Exception e)
            {
            System.out.println ("Exception: " + e);
            }
        }
    }
				
Replace "laptop" in this: "rmi://laptop/SquareCube" with either the name or IP adress of the remote machine.

10. Compile the client application.

NOTE: The client needs access to the file containing the definition of the TestRMIObject interface and the TestRMIObjectImpl_stub.class file.
[me@local_machine dir_name]$ javac TestRMIObjectClient.java

12. Run the client application:

[me@local_machine dir_name]$ java TestRMIObjectClient
The output should be:
25
1000

13. That's it in its very basic form.

Much more can be done with RMI. As and when I find out how to I will update this document.