Java RMI (Remote Method Invocation) is a remote procedure call mechanism integrated within the Java programming environment. Standard features of the Java environment are used both to define the remotely accessible interfaces and to serialize the invocation arguments.
The interface of a remotely accessible object is a standard Java interface, with several simple restrictions. A remotely accessible object must implement the Remote interface, which marks it as an object that can receive remote invocations. All remotely accessible methods must be able to throw the RemoteException
exception. Only serializable types can be passed by value.
public interface Example extends Remote { void printString (String text) throws RemoteException; }
Inheritance used to request passing by reference
Serializable arguments
Remote exception
The stubs are instances of the generic Proxy
class that appear to implement all Remote interfaces of the server implementation. It is possible to cast between multiple remote interfaces of the same remote object. The stubs implement the equals
and hashCode
methods, making proxies that refer to the same remote object appear equal.
To receive invocations, an instance of a remote object must first be exported. Exporting associates the object with a unique object identifier, which is registered by the RMI infrastructure and used by the stubs. A remote object can simply inherit from the UnicastRemoteObject
class, which exports the object in the inherited constructor. Alternatively, a remote object can be registered by calling the exportObject
method and unregistered by calling the unexportObject
method, both provided by the UnicastRemoteObject
class.
public class ExampleImpl extends UnicastRemoteObject implements Example { public ExampleImpl () throws RemoteException { } public void printString (String text) { System.out.println (text); } }
Interface used to mark remotely accessible object
Inheritance used to export the instance
Constructor can return exception
static Remote exportObject (Remote obj, int port) static boolean unexportObject (Remote obj, boolean force)
Passing an object by reference is done by passing its proxy by value. An object is passed by reference only when it is exported, objects that are not exported are passed by value even when they implement the Remote
interface.
The use of the default language serialization mechanism for remotely accessible interfaces poses security risks. A class can implement proprietary variants of the readObject
and writeObject
methods, used by the serialization mechanism to transfer state between object instances and serialization streams. See https://github.com/frohoff/ysoserial for examples of how these methods in common libraries can be tricked to perform remote code execution, and https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290 for more work after input stream filtering was introduced.
The specification explicitly makes no guarantees regarding the threading model. A common implementation requires exclusive use of both a thread and a connection during invocation, threads and connections are created on demand and potentially reused until collected.
The lifecycle of instances is directed by a system of leases. A client that holds a remote reference must possess a lease from the server, the lease is renewed by the client periodically and returned by the client after the object reference is garbage collected locally. To avoid races when passing around references, the reference recipient will first request the lease from the target server, and then acknowledge to the reference sender, who keeps the reference locally alive at least until that moment. The lease duration is configured in system properties, default is 10 minutes. To prevent errors due to network delays, clients renew leases when half expired.
The Unreferenced interface can be used to receive a notification when all leases to an object expire. Exporting an object creates a weak reference. Leasing an object creates a strong reference. A remotely accessible object is therefore subject to garbage collection when it is neither leased remotely nor referenced locally.
Naming uses the rmiregistry server to register object references under string names and to look up the references using the names.
class java.rmi.Naming { static void bind (String name, Remote obj); static void rebind (String name, Remote obj); static void unbind (String name); static Remote lookup (String name); static String [] list (String name); }
Naturally, both the client and the server have to use the same instance of the rmiregistry server.
Oracle: The Java RMI Specification. https://docs.oracle.com/en/java/javase/13/docs/specs/rmi/index.html