2.18. Java Remote Method Invocation (RMI)

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.

2.18.1. Interface

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.

Remotely Accessible Type Example

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.

2.18.2. Implementation

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.

Remotely Accessible Object Example

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

exportObject Methods

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.

Serialization Exploits

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.

2.18.3. Threading

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.

2.18.4. Lifecycle

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.

Unreferenced Interface

public interface Unreferenced {
    void unreferenced ();
}
  • Called some time after no client holds reference to remote object

2.18.5. Naming

Naming uses the rmiregistry server to register object references under string names and to look up the references using the names.

Naming Interface

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.

Naming Example

Server Side Registration. 

ExampleImpl obj = new ExampleImpl ();
Naming.rebind ("//localhost/Example", obj);

Client Side Lookup. 

Example object = (Example) Naming.lookup ("//localhost/Example");
object.printString ("Hello RMI !");

2.18.6. References