Recent from talks
Contribute something
Nothing was collected or created yet.
Java remote method invocation
View on Wikipedia
The Java Remote Method Invocation (Java RMI) is a Java API that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage-collection.
The original implementation depends on Java Virtual Machine (JVM) class-representation mechanisms and it thus only supports making calls from one JVM to another. The protocol underlying this Java-only implementation is known as Java Remote Method Protocol (JRMP). In order to support code running in a non-JVM context, programmers later developed a CORBA version.
Usage of the term RMI may denote solely the programming interface or may signify both the API and JRMP, IIOP, or another implementation, whereas the term RMI-IIOP (read: RMI over IIOP) specifically denotes the RMI interface delegating most of the functionality to the supporting CORBA implementation.
The basic idea of Java RMI, the distributed garbage-collection (DGC) protocol, and much of the architecture underlying the original Sun implementation, come from the "network objects" feature of Modula-3.
Generalized code
[edit]The programmers of the original RMI API generalized the code somewhat to support different implementations, such as a HTTP transport. Additionally, the ability to pass arguments "by value" was added to CORBA in order to be compatible with the RMI interface. Still, the RMI-IIOP and JRMP implementations do not have fully identical interfaces.
RMI functionality comes in the package java.rmi, while most of Sun's implementation is located in the sun.rmi package. Note that with Java versions before Java 5.0, developers had to compile RMI stubs in a separate compilation step using rmic. Version 5.0 of Java and beyond no longer require this step - and static stubs have been deprecated since Java 8.
Jini version
[edit]Jini offers a more advanced version of RMI in Java. It functions similarly but provides more advanced security, object discovery capabilities, and other mechanisms for distributed object applications.[1]
Example
[edit]The following classes implement a simple client-server program using RMI that displays a message.
RmiServerIntfinterface- defines the interface that is used by the client and implemented by the server. This extends the
java.rmi.Remoteinterface, which serves to identify an implementing class as one with remotely-invokable methods.
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RmiServerIntf extends Remote {
String getMessage() throws RemoteException;
}
RmiServerclass- listens to RMI requests and implements the interface which is used by the client to invoke remote methods.
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.*;
public class RmiServer extends UnicastRemoteObject implements RmiServerIntf {
public static final String MESSAGE = "Hello World";
public RmiServer() throws RemoteException {
super(0); // required to avoid the 'rmic' step, see below
}
public String getMessage() {
return MESSAGE;
}
public static void main(String[] args) throws Exception {
System.out.println("RMI server started");
try { //special exception handler for registry creation
LocateRegistry.createRegistry(1099);
System.out.println("java RMI registry created.");
} catch (RemoteException e) {
//do nothing, error means registry already exists
System.out.println("java RMI registry already exists.");
}
//Instantiate RmiServer
RmiServer server = new RmiServer();
// Bind this object instance to the name "RmiServer"
Naming.rebind("//localhost/RmiServer", server);
System.out.println("PeerServer bound in registry");
}
}
RmiClientclass- this is the client which gets the reference (a proxy) to the remote object living on the server and invokes its method to get a message. If the server object implemented java.io.Serializable instead of java.rmi.Remote, it would be serialized and passed to the client as a value.[2]
import java.rmi.Naming;
public class RmiClient {
public static void main(String[] args) throws Exception {
RmiServerIntf server = (RmiServerIntf)Naming.lookup("//localhost/RmiServer");
System.out.println(server.getMessage());
}
}
Before running this example, we need to make a 'stub' file for the interface we used. For this task we have the RMI compiler - 'rmic'
- Note: we make a stub file from the '*.class' file with the implementation of the remote interface, not from the '*.java' file.
rmic RmiServer
Note that since version 5.0 of J2SE, support for dynamically generated stub files has been added, and rmic is only provided for backwards compatibility with earlier runtimes,[3] or for programs that don't provide an explicit port number (or zero) when exporting remote objects, which is required for generated stubs to be possible, as described in the Javadoc for UnicastRemoteObject. See the comment in the constructor above.
References
[edit]- ^ Taylor, Ian J (2005). From P2P to Web Services and Grids : Peers in a Client/Server World. Computer Communications and Networks. London: Springer-Verlag. doi:10.1007/b138333. ISBN 1852338695. OCLC 827073874.[page needed]
- ^ Wilson, M. Jeff (November 10, 2000). "Get smart with proxies and RMI". JavaWorld. Retrieved 2020-07-18.
- ^ "Java RMI Release Notes". Oracle. Retrieved 9 May 2012.
External links
[edit]- "Remote Method Invocation Home". Oracle Technology Network for Java Developers. Redwood Shores, CA, USA: Oracle Corporation. Retrieved 2014-07-14.
- The Java RMI tutorial - a good starting point to learn RMI. Also check the Hello World in RMI (Java 5)
- Java RMI online training (Java 7) at the Wayback Machine (archived 2012-08-12) - Very good for training JavaRMI and as reference
- The RMI page in the JDK 8 docs
java.rmi(Java API Reference for the RMI package)- Ann Wollrath; Roger Riggs; Jim Waldo. "A Distributed Object Model for the Java System" (PDF). Archived (PDF) from the original on 2022-10-10. Retrieved 2009-02-11.
- Programming WebLogic RMI - an introduction to RMI in Oracle Weblogic.
- General Remote Method Invocation
Java remote method invocation
View on Grokipediajava.rmi.Remote, implementing server-side remote objects, and using client-side stubs—dynamically generated proxies that handle the network transport and dispatch calls to the actual remote implementation.[2]
RMI operates through a layered protocol stack, including a stub/skeleton layer for object-oriented semantics, a remote reference layer for managing connections, and a transport layer typically based on Java sockets, with support for custom factories to enable secure communications like SSL/TLS.[2] Key benefits include seamless integration with Java's security model (though the RMI SecurityManager is deprecated since Java 17 in favor of modern practices), dynamic class loading for distributed code, and exception handling that distinguishes between remote and local errors.[1] Over time, RMI has evolved to emphasize security enhancements, such as serialization filtering to prevent vulnerabilities, making it suitable for enterprise distributed systems despite the rise of alternatives like RESTful services.[1]
Introduction
Definition and Purpose
Java Remote Method Invocation (RMI) is a Java application programming interface (API) that enables an object running in one Java Virtual Machine (JVM) to invoke methods on an object running in another JVM, potentially located on a different host, treating the remote object as if it were local.[3] This mechanism provides a distributed object model that preserves the semantics of the Java platform's object-oriented paradigm, allowing seamless method calls across address spaces.[4] The primary purpose of RMI is to support remote procedure calls (RPC) in distributed Java applications by utilizing Java objects for communication, with parameters and return values handled through object serialization to marshal and unmarshal data efficiently.[2] By abstracting low-level networking details, RMI allows developers to build client-server systems where remote invocations appear identical to local ones, fostering the creation of scalable, modular distributed software. Key benefits of RMI include simplifying distributed application development by extending Java's object-oriented features—such as inheritance and polymorphism—across JVM boundaries, enabling true distributed polymorphism without type truncation during data transfer.[2] This approach leverages the Java runtime environment to handle serialization natively, reducing the complexity associated with external data formats like XDR and promoting code reusability in networked environments.[4] As its default transport mechanism, RMI employs the Java Remote Method Protocol (JRMP), which facilitates reliable communication between JVMs over TCP/IP connections.[4]History and Development
Java Remote Method Invocation (RMI) originated in 1996 as part of the development efforts for JDK 1.1, released on February 19, 1997, by Sun Microsystems.[5] It was designed to provide a more seamless object-oriented approach to distributed computing in Java, drawing inspiration from the Modula-3 Network Objects system developed by Andrew Birrell, Greg Nelson, and Susan Owicki at Digital Equipment Corporation's Systems Research Center.[6][7] This influence is evident in RMI's adoption of reference-counting garbage collection for distributed objects and its emphasis on simplicity in network programming.[8] Prior to RMI, Java developers relied on low-level socket-based mechanisms for remote communication, which required manual handling of data serialization, deserialization, and error management, leading to verbose and error-prone code; RMI abstracted these concerns to enable direct method invocation on remote objects as if they were local.[9] A significant evolution occurred with the introduction of RMI over IIOP (Internet Inter-ORB Protocol) in JDK 1.3, released in May 2000, which extended the original Java Remote Method Protocol (JRMP) to support interoperability with CORBA systems.[10] This allowed Java RMI applications to communicate with non-Java CORBA clients and servers, broadening its applicability in heterogeneous environments while maintaining the core RMI programming model. In 2004, JDK 5.0 introduced dynamic stub generation, eliminating the need for pre-compilation of static stubs using the rmic tool and simplifying deployment by generating stubs at runtime.[11] Further refinements came in JDK 8, released in March 2014, when static stubs and the rmic compiler were deprecated in favor of the more efficient dynamic stubs, reflecting a shift toward streamlined RMI usage.[12] RMI has remained integrated into Java SE without major architectural changes since JDK 11 (the previous LTS release in 2018), continuing through JDK 25 (LTS, released September 2025).[13] In JDK 21, RMI Activation was deprecated for removal to simplify the API and reduce maintenance overhead.[14] Concurrently, related technologies like Jini, introduced by Sun Microsystems in July 1998, built upon RMI to add dynamic service discovery and leasing mechanisms for networked devices and services.[15] Although Jini extended RMI's capabilities for spontaneous networking, it has been largely superseded in practice by more modern frameworks for service-oriented architectures.[16]Core Concepts
Remote Objects and Interfaces
In Java Remote Method Invocation (RMI), a remote object is defined as an object whose methods can be invoked from another Java Virtual Machine (JVM), potentially on a different host, and it is described by one or more remote interfaces.[17] Such objects must implement thejava.rmi.Remote interface, either directly or indirectly, which serves to identify interfaces whose methods may be invoked from a non-local virtual machine.[18] This marker interface has no methods itself but enables the RMI system to treat the object as accessible remotely, allowing clients in separate JVMs to perform method invocations as if the object were local.[18]
A remote interface in RMI is an interface that declares a set of methods invocable from a remote JVM and must extend java.rmi.Remote directly or indirectly.[17] Methods within a remote interface must be declared as public and include java.rmi.RemoteException (or one of its superclasses) in their throws clause, in addition to any application-specific checked exceptions that the method may throw.[17] This exception handling accounts for potential remote communication failures, such as network issues or JVM crashes. Remote interfaces can extend other interfaces, but only methods defined in the remote interface (or its remote superinterfaces) are considered remote methods; non-remote methods from other interfaces are not accessible via RMI.[17]
Parameters and return values in remote methods must adhere to specific serialization requirements to facilitate transmission across JVM boundaries. Primitive types and objects implementing java.io.Serializable are passed by value, meaning their serialized state is copied and reconstructed as new objects in the receiving JVM.[17] In contrast, remote objects—those implementing a remote interface—are passed by reference; instead of serializing the object itself, a stub (a client-side proxy) is transmitted, preserving the object's identity and allowing subsequent invocations to target the original instance.[17] This distinction ensures efficient handling of distributed references while avoiding unnecessary deep copies for complex object graphs.
Stubs and Skeletons
In Java Remote Method Invocation (RMI), stubs and skeletons serve as intermediary components that facilitate transparent communication between clients and remote objects, abstracting the underlying network operations. A stub acts as a client-side proxy that implements the same remote interfaces as the actual remote object, allowing clients to invoke methods as if they were local. When a method is called on the stub, it marshals the arguments into a serialized form, transmits them over the network to the server, awaits the response, unmarshals the results or exceptions, and returns them to the client. This process ensures that network details, such as serialization and transport errors (wrapped asRemoteException), remain hidden from the application code.[8]
Skeletons, on the other hand, were server-side entities responsible for receiving incoming requests, unmarshaling the parameters, dispatching the call to the appropriate method on the remote object implementation, marshaling the results, and sending them back to the client. Introduced in early RMI versions, skeletons were generated alongside stubs to handle the server-side dispatch logic. However, skeletons were deprecated since JDK 1.1 and no longer required starting with the Java 2 SDK, v1.2, as the RMI runtime incorporated a new stub protocol that eliminated the need for them in Java 2 and later environments. In modern Java implementations, server-side unmarshaling and dispatching are managed internally by the RMI runtime, simplifying the architecture without requiring explicit skeleton classes.[19][8]
The generation of stubs has evolved significantly across Java versions. Prior to Java 5.0, stubs (and skeletons) were statically generated at compile time using the rmic compiler tool, which processed the remote object implementation class to produce corresponding stub and skeleton class files. This approach required explicit compilation steps and distribution of the generated classes to clients. Starting with Java 5.0, RMI introduced dynamic stub generation at runtime, leveraging the java.lang.reflect.Proxy class in conjunction with a RemoteObjectInvocationHandler to create stubs on demand when exporting remote objects via methods such as exportObject in java.rmi.server.UnicastRemoteObject. If a pregenerated stub class is unavailable or the system property java.rmi.server.ignoreStubClasses is set to true, the JVM automatically generates and uses a dynamic proxy stub, enhancing flexibility and reducing deployment overhead for clients running Java 5.0 or later. Pre-5.0 clients, however, still require static stubs to avoid ClassNotFoundException or StubNotFoundException.[20][8]
Architecture
Communication Protocol
Java Remote Method Invocation (RMI) primarily employs the Java Remote Method Protocol (JRMP) as its default communication protocol, which operates over TCP/IP to facilitate binary data exchange between Java virtual machines (JVMs). JRMP leverages Java object serialization through mechanisms like ObjectOutputStream to encode and transmit method invocations, including parameters and return values, while supporting features such as connection pooling to optimize reuse of established TCP connections for multiple remote calls. This protocol is proprietary to Java and designed specifically for seamless interoperability within JVM environments, ensuring that remote method calls mimic local invocations in terms of semantics.[21][2] For interoperability with CORBA systems, RMI supports the RMI-IIOP protocol, which utilizes the Internet Inter-ORB Protocol (IIOP) as its underlying transport and requires mapping Java remote interfaces to CORBA Interface Definition Language (IDL) via tools like the rmic compiler. RMI-IIOP enables Java clients and servers to communicate with non-Java CORBA implementations by adhering to standard IIOP wire formats, though it demands additional configuration for IDL stubs and ties, preserving Java's object-oriented polymorphism during cross-platform invocations. This alternative is particularly useful in heterogeneous distributed environments but introduces overhead from CORBA compliance.[22][23] The transport layer in RMI is built on TCP sockets by default but can be customized using RMIClientSocketFactory and RMIServerSocketFactory interfaces, allowing developers to implement alternative socket types, such as SSL-enabled connections for encryption, without altering the core protocol semantics. Additionally, RMI historically supported HTTP tunneling to traverse firewalls by encapsulating JRMP messages within HTTP requests, a feature that was deprecated in Java 8 and removed in subsequent versions in favor of more secure alternatives like HTTPS tunneling via custom factories. These customizations enhance flexibility for deployment in varied network topologies while maintaining the protocol's reliability.[24][11] During communication, RMI's marshaling process serializes method signatures, input parameters, and output results into a binary stream using a subclass of ObjectOutputStream that extends standard Java serialization to handle remote object references (via RemoteRef) and annotates streams with codebase information for dynamic class loading. This process also accommodates distributed garbage collection by embedding callbacks, such as Distributed Garbage Collector (DGC) messages, to notify remote JVMs of unreferenced objects, ensuring efficient memory management across distributed boundaries without manual intervention. Stubs initiate this marshaling transparently to clients, abstracting the protocol details.[25][26]Registry and Naming Service
In Java Remote Method Invocation (RMI), the registry serves as a central naming service that enables the binding of names to remote object references, facilitating their discovery in a distributed environment.[27] The registry is implemented as a remote object that adheres to thejava.rmi.registry.Registry interface, which provides methods for storing and retrieving these references using simple string names.[28] Typically, the registry operates on a well-known TCP port, with the default being 1099, allowing clients to bootstrap connections to remote services without prior knowledge of specific object locations.[28]
Servers register their remote objects with the registry through the java.rmi.Naming class, which handles the association of a URL-formatted name to an object reference. The URL follows the format rmi://[host](/page/host):port/name, where host specifies the registry's location (defaulting to localhost if omitted), port indicates the listening port (defaulting to 1099), and name is an uninterpreted string identifier for the object.[29] Binding occurs via Naming.bind() for initial associations or Naming.rebind() to update existing ones, ensuring that only one object is bound per name to avoid conflicts.[29] This process allows remote objects—those implementing the java.rmi.Remote interface—to be made available for invocation across the network.[29]
Clients access these bound objects by performing lookups on the registry using Naming.lookup(name), which returns a stub reference to the remote object if the name exists, or throws a NotBoundException otherwise.[29] The lookup resolves the URL to retrieve the stub, enabling subsequent method calls as if the object were local.[29] Additional registry operations include unbinding names or listing all current bindings, though these are less commonly used for basic bootstrapping.[28]
The RMI registry has inherent limitations, such as supporting only a single instance per host and port combination, which restricts scalability in environments requiring multiple naming contexts on the same machine.[30] For more advanced naming needs, such as hierarchical structures or integration with enterprise directories, the Java Naming and Directory Interface (JNDI) serves as an alternative, providing a unified API that can access RMI registries while offering location transparency and broader federation capabilities.[31] JNDI subsumes RMI's naming functionality and supports binding remote objects into diverse providers like LDAP, making it suitable for complex distributed systems.[31]
Implementation
Creating Remote Objects
Creating a remote object in Java RMI involves implementing a remote interface on the server side and exporting an instance of the implementing class to make it available for remote invocations. The process ensures that the object can handle incoming calls from clients across different Java Virtual Machines (JVMs). To implement the remote interface, the server class must extendjava.rmi.server.UnicastRemoteObject (a subclass of java.rmi.server.RemoteServer) or another appropriate base class, and it must implement the methods defined in the remote interface, which extends java.rmi.Remote. Each remote method in the implementation overrides the interface declaration and includes the server-side logic, while throwing java.rmi.RemoteException to handle communication failures. For example, consider a simple remote interface Hello and its implementation:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
protected HelloImpl() throws RemoteException {
// Constructor calls super() which exports the object
}
public String sayHello() throws RemoteException {
return "Hello, world!";
}
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
protected HelloImpl() throws RemoteException {
// Constructor calls super() which exports the object
}
public String sayHello() throws RemoteException {
return "Hello, world!";
}
}
RemoteServer (via UnicastRemoteObject) provides the necessary infrastructure for remote method dispatching.
Exporting the object activates it within the RMI runtime, allowing it to listen for incoming remote calls on a specified port (or an anonymous port if none is provided). This is typically done by calling UnicastRemoteObject.exportObject() statically or implicitly through the constructor when extending UnicastRemoteObject. For instance, in the main server method:
import [java](/page/Java).rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main([String](/page/String) args[]) {
try {
HelloImpl obj = new HelloImpl(); // Exports via constructor
// Or explicitly: UnicastRemoteObject.exportObject(new HelloImpl(), 1099);
Registry registry = LocateRegistry.getRegistry();
registry.[bind](/page/BIND)("Hello", obj); // Bind for lookup (detailed in Registry and Naming Service section)
} catch (Exception e) {
e.printStackTrace();
}
}
}
import [java](/page/Java).rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main([String](/page/String) args[]) {
try {
HelloImpl obj = new HelloImpl(); // Exports via constructor
// Or explicitly: UnicastRemoteObject.exportObject(new HelloImpl(), 1099);
Registry registry = LocateRegistry.getRegistry();
registry.[bind](/page/BIND)("Hello", obj); // Bind for lookup (detailed in Registry and Naming Service section)
} catch (Exception e) {
e.printStackTrace();
}
}
}
exportObject() (e.g., 0 for anonymous or 1099 for a fixed port) determines the network endpoint for the object's listener.
Client-Server Example
To illustrate the practical use of Java Remote Method Invocation (RMI), consider a simple client-server application implementing an "adder" service. In this scenario, the server exposes a remote method to add two integers, allowing the client to invoke this computation remotely as if it were a local method call. This example demonstrates the core workflow of defining a remote interface, implementing the server, and connecting from the client via the RMI registry. The remote interface, namedAdder, must extend java.rmi.Remote and declare the add method, which throws RemoteException to handle communication failures. Here is the interface code:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Adder extends Remote {
int add(int a, int b) throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Adder extends Remote {
int add(int a, int b) throws RemoteException;
}
AdderServer, implements the Adder interface, creates an instance of the remote object, exports it as a stub using UnicastRemoteObject.exportObject, and binds the stub to the RMI registry under the name "Adder". The main method handles the setup and binds the object for client access.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class AdderServer implements [Adder](/page/Adder) {
public AdderServer() {}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
public static void main(String[] args) {
try {
AdderServer obj = new AdderServer();
[Adder](/page/Adder) stub = ([Adder](/page/Adder)) UnicastRemoteObject.exportObject(obj, 0);
Registry registry = LocateRegistry.createRegistry(1099);
registry.bind("Adder", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class AdderServer implements [Adder](/page/Adder) {
public AdderServer() {}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
public static void main(String[] args) {
try {
AdderServer obj = new AdderServer();
[Adder](/page/Adder) stub = ([Adder](/page/Adder)) UnicastRemoteObject.exportObject(obj, 0);
Registry registry = LocateRegistry.createRegistry(1099);
registry.bind("Adder", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
AdderClient class looks up the stub from the RMI registry using LocateRegistry.getRegistry, casts it to the Adder interface, and invokes the add method on two sample integers. The stub proxies the remote call transparently. As noted in the stubs and skeletons documentation, the client interacts solely with this proxy without direct awareness of the remote location. To handle potential connection issues, the client catches ConnectException (a subclass of RemoteException) for network failures and NotBoundException for registry lookup errors.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.ConnectException;
import java.rmi.NotBoundException;
public class AdderClient {
public static void main(String[] args) {
String host = (args.length < 1) ? "localhost" : args[0];
try {
Registry registry = LocateRegistry.getRegistry(host, 1099);
Adder stub = (Adder) registry.lookup("Adder");
int result = stub.add(5, 3);
System.out.println("Result: " + result);
} catch (ConnectException e) {
System.err.println("Cannot connect to server: " + e.toString());
e.printStackTrace();
} catch (NotBoundException e) {
System.err.println("Service not bound in registry: " + e.toString());
e.printStackTrace();
} catch (RemoteException e) {
System.err.println("Remote exception: " + e.toString());
e.printStackTrace();
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;
import java.rmi.ConnectException;
import java.rmi.NotBoundException;
public class AdderClient {
public static void main(String[] args) {
String host = (args.length < 1) ? "localhost" : args[0];
try {
Registry registry = LocateRegistry.getRegistry(host, 1099);
Adder stub = (Adder) registry.lookup("Adder");
int result = stub.add(5, 3);
System.out.println("Result: " + result);
} catch (ConnectException e) {
System.err.println("Cannot connect to server: " + e.toString());
e.printStackTrace();
} catch (NotBoundException e) {
System.err.println("Service not bound in registry: " + e.toString());
e.printStackTrace();
} catch (RemoteException e) {
System.err.println("Remote exception: " + e.toString());
e.printStackTrace();
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
javac compiler on all source files to generate the class files; no separate stub generation with rmic is required in Java 5 and later versions, as dynamic stubs are generated at runtime. Place all .java files in a directory and run javac Adder.java AdderServer.java AdderClient.java. For pre-Java 5 environments, rmic was used to generate static stubs and skeletons, but this is now legacy and deprecated.
To run the application, launch the server with java AdderServer from the directory containing the class files; the server will create the RMI registry on port 1099. If the registry is already running on port 1099, modify the server code to use LocateRegistry.getRegistry(1099) instead of createRegistry(1099) to avoid a RemoteException. Finally, run the client with java AdderClient (optionally specifying a remote host as an argument). Ensure the classpath includes the class directory if running from another location. This sequence establishes the remote connection and executes the addition remotely.
Advanced Features
Security Considerations
Java Remote Method Invocation (RMI) incorporates security mechanisms to protect against unauthorized access, code execution, and data interception, though careful configuration is required in distributed environments. As of Java 24 (released in 2024), the Java Security Manager—previously used to enforce policies for class loading, file access, and network operations—has been permanently disabled (JEP 486). It cannot be enabled, and all permission checks fail, eliminating traditional policy-based security for RMI.[32] Instead, RMI security relies on built-in features like serialization filtering, restrictions on dynamic behaviors, and support for secure transports.[33] Dynamic class loading from remote codebases, once configurable via properties likejava.rmi.server.codebase and java.rmi.server.useCodebaseOnly, has been completely removed in Java 24. This eliminates risks associated with downloading and executing untrusted code, such as malware injection, without the need for signed JAR files or codebase restrictions.[32] Previously, such loading was disabled by default since Java 7, but the mechanism is now obsolete.[34]
Deserialization vulnerabilities remain a concern, where malicious serialized objects can trigger harmful code execution. These are addressed through serialization filters introduced in Java 9, which validate incoming streams against allowlists or patterns, rejecting unsafe classes. For RMI, built-in filters for the registry (sun.rmi.registry.registryFilter) and distributed garbage collection limit array sizes and depths (e.g., maxarray=1000000; maxdepth=20) to prevent exploits with oversized or nested objects. Administrators can configure additional filters via system properties for enhanced protection.[35]
For authentication and encryption, RMI supports secure transports through custom socket factories, integrating with SSL/TLS protocols. Clients and servers can use RMISocketFactory subclasses, such as javax.rmi.ssl.SslRMIClientSocketFactory and javax.rmi.ssl.SslRMIServerSocketFactory, to create encrypted channels with mutual authentication using keystores (e.g., via javax.net.ssl.keyStore properties). This leverages the Java Secure Socket Extension (JSSE) to guard against man-in-the-middle attacks and data interception. Best practices include restricting RMI to localhost when feasible and applying serialization filters to all endpoints.[33]
Distributed Garbage Collection
Java Remote Method Invocation (RMI) employs distributed garbage collection (DGC) to automatically manage the memory of remote objects by tracking references across JVMs, preventing premature reclamation while enabling timely cleanup. The DGC protocol relies on reference counting, where servers maintain lists of active client references for each exported remote object. This mechanism, implemented via thejava.rmi.dgc package, uses "dirty" and "clean" calls to update these counts dynamically.[36][37]
The protocol begins when a client unmarshals a remote reference, triggering a dirty call to the server's DGC interface. This method, defined in java.rmi.dgc.DGC, accepts an array of object identifiers (ObjID[]), a sequence number for ordering, and a Lease object specifying the client's virtual machine identifier (VMID) and requested duration. The server grants a lease—defaulting to 10 minutes if unspecified—adding the client's VMID to the object's reference list and returning the approved Lease. Sequence numbers ensure detection of late or duplicate calls, maintaining consistency even under network delays.[37][38]
Lease renewal occurs implicitly through the RMI runtime: during remote method invocations, the client-side DGC automatically issues dirty calls if the lease is nearing expiration, extending the reference without developer intervention. Explicit cleanup happens when the client drops the reference, invoking the clean method with the relevant ObjIDs, sequence number, VMID, and a boolean indicating strong or weak cleanup (strong retains sequence for reliability). This removes the VMID from the server's list; if the list empties, the server recognizes the object as unreferenced remotely.[37][39]
Server-side integration with the JVM's garbage collector ensures safe reclamation: remote objects are exported with strong references initially, but the RMI runtime uses weak references to monitor local reachability. The local GC notifies the DGC via phantom references when no strong local references remain. Only after both local unreachability and an empty remote reference list is the object finalized and collected, avoiding distributed inconsistencies. This reference-counting approach draws from the seminal algorithm in Birrell et al.'s work on Modula-3 Network Objects, adapted for Java's environment.[40][41]
Despite its robustness, the DGC has limitations, including potential memory leaks if clean calls fail due to client crashes or network issues, leaving stale leases that block reclamation indefinitely. Proper lease management is critical, as unrenewed or uncleaned references can accumulate, straining server resources over time.[39]
Limitations and Alternatives
Performance and Scalability Issues
Java Remote Method Invocation (RMI) introduces several performance overheads primarily stemming from its reliance on object serialization and deserialization, which can consume 25% to 65% of the total invocation time depending on object complexity.[42] Complex objects exacerbate this cost due to the need to marshal and unmarshal entire object graphs, including references and metadata, before transmission.[43] Additionally, the default Java Remote Method Protocol (JRMP) adds network latency inherent to TCP-based communication, with typical round-trip times for invocations ranging from 0.4 to 2.2 milliseconds on standard hardware.[42] The thread-per-connection model further contributes to resource contention, as each incoming client connection spawns or reuses a dedicated thread, potentially leading to excessive context switching and memory usage under concurrent loads.[44] Scalability in RMI is limited by its design, particularly in high-throughput environments where the single-threaded nature of the RMI registry can create a bottleneck for name bindings and lookups during peak usage. RMI lacks native support for load balancing across multiple server instances, requiring external mechanisms like replicated registries or proxies, which complicates deployment in clustered setups.[45] The maximum number of concurrent connections is constrained by operating system limits on file descriptors, thread pools (configurable via properties likesun.rmi.transport.tcp.maxConnectionThreads), and available JVM memory, often capping practical throughput at hundreds of simultaneous clients without custom tuning.[46]
Benchmarks consistently demonstrate that RMI invocations are substantially slower than local method calls, with overheads making remote calls at least 1,000 times slower due to combined serialization, network traversal, and deserialization steps.[47] For instance, optimized RMI implementations can reduce this gap but still exhibit round-trip latencies in the microsecond to millisecond range on local networks, compared to nanosecond-scale local invocations.[42] Mitigations such as object pooling to reuse serializable instances or explicit batching of multiple operations into single calls can alleviate some overhead, but these require manual implementation outside RMI's core API and do not address fundamental protocol inefficiencies.[48]
Since JDK 8 in 2014, Oracle has deprecated several RMI features, including HTTP proxying and the activation mechanism (fully removed in JDK 17 via JEP 407). Core RMI remains supported in JDK 25 as of 2025 for legacy systems, but Oracle documentation advises against its use for new development in favor of more scalable and interoperable technologies, emphasizing security and performance improvements in web-oriented protocols.[11][49][50][51]
