Java RMI
Remote Method Invocation for distributed applications
🔄 What is Java RMI?
RMI (Remote Method Invocation) enables Java objects to call methods on objects running on different JVMs across networks. It provides distributed computing capabilities for building scalable enterprise applications.
// RMI interface example
public interface Calculator extends Remote {
int add(int a, int b) throws RemoteException;
int multiply(int a, int b) throws RemoteException;
}
RMI Components
Remote Interface
Defines methods for remote access
interface MyService extends Remote { }
Remote Object
Implementation of remote interface
class MyServiceImpl extends UnicastRemoteObject
RMI Registry
Naming service for remote objects
Registry registry = LocateRegistry.createRegistry(1099);
RMI Client
Calls methods on remote objects
MyService service = (MyService) registry.lookup("MyService");
🔹 Remote Interface
Define the interface for remote method calls:
import java.rmi.Remote;
import java.rmi.RemoteException;
// Remote interface must extend Remote
public interface Calculator extends Remote {
// All methods must throw RemoteException
int add(int a, int b) throws RemoteException;
int subtract(int a, int b) throws RemoteException;
int multiply(int a, int b) throws RemoteException;
double divide(int a, int b) throws RemoteException;
String getServerInfo() throws RemoteException;
}
🔹 Remote Object Implementation
Implement the remote interface:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// Extend UnicastRemoteObject for automatic stub generation
public class CalculatorImpl extends UnicastRemoteObject implements Calculator {
// Constructor must handle RemoteException
public CalculatorImpl() throws RemoteException {
super();
}
@Override
public int add(int a, int b) throws RemoteException {
System.out.println("Server: Adding " + a + " + " + b);
return a + b;
}
@Override
public int subtract(int a, int b) throws RemoteException {
System.out.println("Server: Subtracting " + a + " - " + b);
return a - b;
}
@Override
public int multiply(int a, int b) throws RemoteException {
System.out.println("Server: Multiplying " + a + " * " + b);
return a * b;
}
@Override
public double divide(int a, int b) throws RemoteException {
if (b == 0) {
throw new RemoteException("Division by zero!");
}
System.out.println("Server: Dividing " + a + " / " + b);
return (double) a / b;
}
@Override
public String getServerInfo() throws RemoteException {
return "Calculator Server v1.0 - Running on " +
System.getProperty("os.name");
}
}
🔹 RMI Server
Create and register the remote object:
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
public class CalculatorServer {
public static void main(String[] args) {
try {
// Create the remote object
Calculator calculator = new CalculatorImpl();
// Create RMI registry on port 1099
Registry registry = LocateRegistry.createRegistry(1099);
// Bind the remote object to a name
registry.rebind("Calculator", calculator);
System.out.println("Calculator Server is running...");
System.out.println("Server ready on port 1099");
// Keep server running
while (true) {
Thread.sleep(1000);
}
} catch (RemoteException e) {
System.err.println("Server error: " + e.getMessage());
} catch (InterruptedException e) {
System.err.println("Server interrupted: " + e.getMessage());
}
}
}
Server Output:
Calculator Server is running...
Server ready on port 1099
🔹 RMI Client
Connect to and use the remote object:
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
public class CalculatorClient {
public static void main(String[] args) {
try {
// Get reference to RMI registry
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
// Look up the remote object
Calculator calculator = (Calculator) registry.lookup("Calculator");
// Call remote methods
System.out.println("Connected to Calculator Server");
int result1 = calculator.add(10, 5);
System.out.println("10 + 5 = " + result1);
int result2 = calculator.multiply(7, 8);
System.out.println("7 * 8 = " + result2);
double result3 = calculator.divide(20, 4);
System.out.println("20 / 4 = " + result3);
String serverInfo = calculator.getServerInfo();
System.out.println("Server Info: " + serverInfo);
// Test error handling
try {
calculator.divide(10, 0);
} catch (RemoteException e) {
System.out.println("Error: " + e.getMessage());
}
} catch (RemoteException e) {
System.err.println("Client error: " + e.getMessage());
} catch (NotBoundException e) {
System.err.println("Service not found: " + e.getMessage());
}
}
}
Client Output:
Connected to Calculator Server
10 + 5 = 15
7 * 8 = 56
20 / 4 = 5.0
Server Info: Calculator Server v1.0 - Running on Windows 10
🔹 RMI with Custom Objects
Pass custom objects between client and server:
import java.io.Serializable;
// Custom class must implement Serializable
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", email='" + email + "'}";
}
}
// Updated interface with custom object methods
public interface PersonService extends Remote {
void addPerson(Person person) throws RemoteException;
Person getPerson(String name) throws RemoteException;
java.util.List getAllPersons() throws RemoteException;
}
🔹 RMI Security
Configure security for RMI applications:
import java.rmi.RMISecurityManager;
public class SecureRMIServer {
public static void main(String[] args) {
// Set security manager (optional but recommended)
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
// Set system properties for RMI
System.setProperty("java.rmi.server.hostname", "localhost");
System.setProperty("java.security.policy", "server.policy");
try {
Calculator calculator = new CalculatorImpl();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("Calculator", calculator);
System.out.println("Secure RMI Server started");
} catch (Exception e) {
System.err.println("Server error: " + e.getMessage());
}
}
}
RMI Security Policy (server.policy):
grant {
permission java.security.AllPermission;
};
Note: In production, use more restrictive permissions.
🔹 RMI Best Practices
Important guidelines for RMI development:
Design Guidelines:
- Interface Design: Keep remote interfaces simple and focused
- Exception Handling: Always handle RemoteException
- Serialization: Ensure all parameter/return types are Serializable
- Performance: Minimize remote method calls
Common Pitfalls:
- Firewall Issues: RMI uses dynamic ports
- ClassPath Problems: Ensure all classes are available
- Registry Location: Client must know server location
- Object Lifecycle: Manage remote object references properly