RMI

We've seen method calls between instances in the same JVM so far. Now, let's look at how to call methods of objects in other JVMs.

We already know about sockets. However, sockets require the programmer to define a protocol between the two objects. Suppose you are writing a banking program that uses sockets. An interbank transfer requires a message:

Transfer|Bank Account|SWIFT|Bank Account|Amount

RMI allows you to directly call methods on objects in other JVMs without these protocols. RMI eliminates the need to define a protocol and interpret messages according to a protocol.

RMI Programming Procedures

  1. Defining a remote interface
  2. Implementing a server
  3. Implementing a client
  4. Starting the Java RMI Registry, Starting the server, Starting the client

RMI Example

  • Hello.java - Remote Interface
  • Server.java - Remote interface implementation
  • Client.java - Clients that call remote methods

1. Defining a remote interface: Hello.java

A remote interface defines methods called remotely from the client. A remote interface must extend the Remote. Remote methods on the remote interface must declare throws RemoteException. In addition to RemoteException -- adding additional exceptions are available --

Hello.java
package example.hello;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {
  String sayHello() throws RemoteException;
}

2. Remote interface implementation: Server.java

Server implements the remote interface Hello.
When implementing the sayHello(), you don't need "throws RemoteException" in the method declaration because the remote object's sayHello() does not throw a RemoteException.

Server.java
package example.hello;

import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

public class Server implements Hello {

  public Server(){}
	
  public String sayHello() {
    return "Hello, World!";
  }
	
  public static void main(String[] args) {
    Server obj = new Server();
    try {
      Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
      //Bind the remote object's stub in the registry
      Registry registry = LocateRegistry.getRegistry();
      registry.bind("Hello", stub);
      System.out.println("Server ready");
    } catch (Exception e) {
      System.out.println("Server exception: " + e.toString());
    }
  }
}

The server's method main creates a remote object that supplies the service and exports the remote object to the Java RMI runtime.

Server obj = new Server();
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);

The following code registers the server-side stub created with the above code in the Java RMI registry so that their clients can find it:

Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);

If the getRegistry() method of the LocateRegistry without arguments runs, the LocateRegistry uses the default port of 1099. So, it may be necessary to open the 1099 port.

3. Implementing a Client

The client finds the registered stub on the server-side with the registered name and downloads it to the client-side JVM. It then calls the stub's sayHello() method.

package example.hello;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {

  private Client() {}

  public static void main(String[] args) {
    String host = (args.length < 1) ? null : args[0];
    try {
      Registry registry = LocateRegistry.getRegistry(host);
      Hello stub = (Hello) registry.lookup("Hello");
      String response = stub.sayHello();
      System.out.println("response: " + response);
    } catch (Exception e) {
      System.err.println("Client exception: " + e.toString());
      e.printStackTrace();
    }
  }
}

4. Starting the Java RMI Registry, Starting the server, Starting the client

Test on Windows systems

C:\ Command Prompt
start rmiregistry

start java -classpath c:/java/rmi/bin ^
-Djava.rmi.server.codebase=file:c:/java/rmi/bin ^
example.hello.Server

^ Is a newline character in a Windows command.
Launch a new command prompt and run the client.

C:\ Command Prompt
java -classpath c:\java\rmi\bin example.hello.Client

Test on Linux Systems

rmiregistry &

java -classpath /home/kim/java/rmi/bin \
-Djava.rmi.server.codebase=file:/home/kim/java/rmi/bin \
example.hello.Server &

The backslash is the newline character in Linux bash.
Launch a new terminal and run the client.

java -classpath /home/John/java/rmi/bin example.hello.Client

Test on two or more computers

If the server IP is 192.168.0.8, run the following on the client:

java -classpath /home/John/java/rmi/bin example.hello.Client 192.168.0.8

Place the bytecodes of Hello and Client on the client machine and the bytecodes of Hello and Server on the server machine.

Test Failure Checklist

  1. If the test fails on a Windows system, try disabling the loopback adapter and try again.
  2. If the test fails when running the server on Windows, open port 1099 (RMI default port) and try again.
  3. If the test using Linux as a server in an environment using a router fails, open the /etc/hosts file, modify 127.0.0.1 to the router-assigned private IP, and test again.
References