Back to the main page
RMI allows an object in one JVM to call methods on objects located in a separate JVM. The JVMs can be on the same server or different servers. URLs are used to locate the server.
The calling object (client) needs the class definition of a remote object for RMI to work. The remote object class definitions can be deployed to the client server or looked up via a web server and downloaded.
For an object to accept remote calls it needs to implement an interface which extends
java.rmi.Remote. Each method that can be called remotely needs to throw
java.rmi.RemoteException. This object also needs to be exported and registered.
Exporting places a remote object reference to the object in the RMI runtime (part of the JVM). Exporting an object will therefore keep the JVM running, even after the main method completes. The remote object reference can be unexported to remove it from the RMI runtime.
To export, either call
java.rmi.server.UnicastRemoteObject.exportObject() or have the class extend
UnicastRemoteObject. The constructor for
UnicastRemoteObject calls
exportObject for you. If 0 or no port is passed then an anonymous port is used. Note this is the port for which the object is made available and not the port used by the RMIRegistry. If you try to export to a port on which the RMIRegistry is running then you will get a port already in use exception. The exportObject method returns a remote object reference, which can be passed to the registry. If an object extends UnicastRemoteObject it is it's own remote reference which can be passed to the registry.
A separate process called the RMIRegistry provides a lookup for clients to identify a remote object. The RMIRegistry can be run in the same JVM hosting the remote object or in its own JVM. To start an RMIRegistry as a separate process from the command line (unix) run
rmiregistry & for the default port or
rmiregistry portNumber &.
To register an object you pass the remote reference to the relevant RMIRegistry and bind it to a name which is used by clients to lookup this reference. This can be done by first locating or creating a registry then separately calling bind or rebind on the registry object or in a single static helper method Naming.
More then one RMIRegistry can run at the same time on a single machine, they just need different port numbers.
Below is an example of a service that has an RMI wrapper so that it can be called directly or through RMI on a separate JVM
First the service...
// ServiceOperations.java
interface ServiceOperations {
void doOperation();
}
// Service.java
class Service implements ServiceOperations {
public void doOperation() {
System.out.println("Hello");
}
}
Now the RMI service wrapper...
// RMIServiceOperations.java
import java.rmi.Remote;
import java.rmi.RemoteException;
interface RMIServiceOperations extends Remote {
void doOperation() throws RemoteException;
}
// RMIService.java
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.Naming;
import java.net.MalformedURLException;
public class RMIService
extends UnicastRemoteObject
implements RMIServiceOperations {
ServiceOperations delegate;
public RMIService() throws RemoteException {
super(1101); // port to export object with
delegate = new Service();
// If this class didn't extend UnicastRemoteObject then we would need to perform the line below
//RMIServiceOperations remoteRef = (RMIServiceOperations)UnicastRemoteObject.exportObject(this, 1101); // 0 = anonymous port
// If RMIRegistry is already running use LocateRegistry.getRegistry(1100);
Registry registry = LocateRegistry.createRegistry(1100);
try {
registry.bind("TestService", this);
}
catch(AlreadyBoundException e) {
// handle exception
}
// alternatively use Naming. Naming can only be used if the registry already exists
try {
Naming.rebind("//localhost:1100/TestService", this);
}
catch(MalformedURLException e) {
// handle exception
}
}
public void doOperation() throws RemoteException {
// Call the delegate service
delegate.doOperation();
// then shut down the service
// to shutdown we need to unexport all objects from the RMIService
// unbinding from the registry isn't necessary but included here for completeness
Registry registry = LocateRegistry.getRegistry(1100);
try {
registry.unbind("TestService");
}
catch(NotBoundException e) {
// handle exception
}
UnicastRemoteObject.unexportObject(this, true);
}
public static void main(String[] args) {
try {
RMIService service = new RMIService();
}
catch(RemoteException e) {
e.printStackTrace();
}
}
}
The RMIServiceProxy is used by the client instead of calling service directly.
// RMIServiceProxy.java
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
import java.rmi.Naming;
import java.net.MalformedURLException;
class RMIServiceProxy implements ServiceOperations {
public void doOperation() {
try {
Registry registry = LocateRegistry.getRegistry("localhost", 1100);
RMIServiceOperations rmiService = (RMIServiceOperations)registry.lookup("TestService");
// Alternatively use Naming
try {
rmiService = (RMIServiceOperations)Naming.lookup("//localhost:1100/TestService");
}
catch(MalformedURLException e) {
// handle exception
}
rmiService.doOperation();
}
catch(RemoteException e) {
//handle Exception
}
catch(NotBoundException e) {
//handle Exception
}
}
}
Finally the client.
// Client.java
public class Client {
ServiceOperations service;
Client(ServiceOperations s) {
service = s;
}
public static void main(String[] args) {
ServiceOperations localService = new Service();
ServiceOperations remoteService = new RMIServiceProxy();
Client clientWithLocalService = new Client(localService);
Client clientWithRemoteService = new Client(remoteService);
clientWithLocalService.service.doOperation();
clientWithRemoteService.service.doOperation();
}
}
Terminal 1, start the server.
> java RMIService
Terminal 2, run the client.
> java Client
Hello
>
Terminal 1, after the client has run.
> java RMIService
Hello
>
Resources