Table of contents
Open Table of contents
Java RMI
Background
When different JVMs need to call one another, how can a method call from JVM A to JVM B feel like a local call? This is where RMI—Remote Method Invocation—comes in.
The JVMs can be on the same machine or on different machines.
Basic Principles
I find terms such as stub and skeleton hard to remember, and their meaning is not obvious from the names. Instead, I will explain this using the currently popular concept of microservices.
If you are familiar with microservices, the RMI invocation process is quite similar.
- A registry—RMI Registry—is needed to store remote service names and their implementation classes.
- The server provides an interface for remote invocation.
- The server also implements that interface.
- The client looks up the service name in RMI Registry, obtains its instance, and invokes it remotely.
For a detailed understanding, I highly recommend the Proxy Pattern chapter in Head First Design Patterns, which gives a vivid, detailed explanation of RMI’s principles and inner workings.
Example
Provide a Remote Interface on the Server
- Extend the Remote interface.
- Methods exposed for remote invocation must throw RemoteException.
public interface RmiServerInterface extends Remote {
/**
* All methods exposed through RMI must throw RemoteException; otherwise, the following
* Exception "remote object implements illegal remote interface"?
* error occurs.
* @return
* @throws RemoteException
*/
String sayHello() throws RemoteException;
}
Implement the Remote Interface on the Server
- Extend UnicastRemoteObject.
- The no-argument constructor throws RemoteException.
public class RmiInterfaceImpl extends UnicastRemoteObject implements RmiServerInterface{
public RmiInterfaceImpl() throws RemoteException{
}
@Override
public String sayHello() {
return "hello, I'm Remote JVM Server";
}
}
Registry
public class Server {
public static void main(String[] args) throws RemoteException {
// Start the registry
Registry registry = LocateRegistry.createRegistry(2001);
try {
// Bind the service
registry.bind("hello", new RmiInterfaceImpl());
System.err.println("Server ready");
} catch (AlreadyBoundException e) {
System.out.println("Server bind fail" + e.getMessage());
}
}
}
Invoke the Remote Service from the Client
- Look up the service directly in RMI Registry.
public class RmiClient {
public static void main(String[] args) {
try {
// The lookup name must match the name registered in the registry
RmiServerInterface hello = (RmiServerInterface) Naming.lookup("rmi://localhost:2001/hello");
System.out.println("Response from the server interface: " + hello.sayHello() +" "+ hello.getClass().getName());
} catch (RemoteException | NotBoundException | MalformedURLException e) {
System.out.println("Connect Server fail"+ e.getMessage());
}
}
}
- Another way to invoke RMI remotely is to use getRegistry instead of Naming.
Registry registry = LocateRegistry.getRegistry(targetHost, targetPort);
RmiServerInterface hello = (RmiServerInterface)registry.lookup("hello");
System.out.println("Response from the server interface: " + hello.sayHello() +" "+ hello.getClass().getName());
The Difference Between Naming.lookup and Registry.lookup
Answer on Stack Overflow In practice, there is little difference in usage; it comes down to developer preference.
Verification
- Start RMI Registry.
- Start the client and inspect its output. The class received by the client is actually a proxy class.
Other Points
- If the server returns an object, its class must implement the serialization interface because network transmission and I/O are involved.
- Code repository
- RMI brings RPC to mind. How are they related? In my view, RMI is a subset of RPC, customized for Java and its object model, making it convenient for Java developers to invoke methods across JVMs.
Official Documentation
Note: when following the official documentation, I consistently ran into problems configuring codebase.