Remote Method Invocation (RMI) is Java's distributed object infrastructure. It relies on serialization and the proxy pattern to allow method calls to be transparently executed on a different machine (more accurately, in a different VM) than the caller.
Applying RMI to simple applications is mostly a painless process. However, more complex applications often take advantage of more advanced features of Java, such as the definition of object equality, object hash codes and garbage collection rules. To the RMI novice, RMI sometimes behaves unexpectedly in these areas.
This page lists some common issues, explaining the root cause, and, where possible, gives a solution or workaround.
Consider an object reference (object
), which is an instance of
a class that extends Remote
, along with a reference to the stub to
that object (stub
), in the same VM. This situation arises, for instance, when
the object reference from VM1 is passed to VM2 (and
therefore represented there as a stub) and then passed back to VM1,
still as a stub.
These two references in principle implement the same interfaces and invoking a method on one is immediately and intrinsically reflected in the state of the other. However, in general, they are not the same object:
stub.equals(object)
is false
stub.hashCode()
does not equal to object.hashCode()
A direct consequence of this is that the Collections Framework (java.util.*) treats these as distinct items. For instance, stub
and object
would represent distinct keys in a HashMap
.
Remember: Don't mix "real" objects and their stubs in collections.
We must make a choice: either keep the real objects or their stubs. Since it's difficult to deduce the real object from its stub (impossible if the object resides in a different VM), we will convert all real object references to stubs before inserting or seeking them in any collection. This can be done easily either at export-time using the return value of UnicastRemoteObject.exportObject
or at any other time using UnicastRemoteObject.toStub
.
If you're thinking about getting clever and discarding the real object reference immediately after construction, keep reading.
Plain and simple: UnicastRemoteObject.exportObject
(and any derivative, such as extending UnicastRemoteObject
) does not increase the reference count of the exported object.
This means that if there is no other reference to the object, the object will be garbage collected, resulting in a stale entry in the RMI export table. The symptom of these is a server-side NoSuchObjectException
when invoking a method on a stub that refers to the stale entry.
To make sure this does not happen, ensure the exported object doesn't get garbage collected in whatever way seems appropriate. In most cases, it's sufficient to keep a reference to the object in some other long-lived object. It sucks, but it works.
Remember: The single-parameter overload of UnicastRemoteObject.exportObject
does not support dynamic stubs. Instead, use the two-parameter overload with port := 0
.
This is documented (somewhere?) but it boils down to the fact that the auto-generated proxies that replace the rmic-generated stubs do not extend the RemoteStub
interface, which is the type of the single-parameter overload. The two overloads otherwise have the same functionality.
Put simply: To test whether two stubs refer to the same remote object, use stub1.equals(stub2)
, not stub1 == stub2
.
This is a consequence of the fact that it is possible for there to exist many stub instances that refer to the same remote object.
This can be the case, for example, when the same object is passed from one VM to another during different method invocations, or possibly even when it is passed twice within the argument list of a single invocation.
This is in contrast to local object references, where testing obj1 == obj2
is sufficient to assert that the two references point to the same object.
The author of this article is Catalin Patulea. Feel free to e-mail me with questions and comments. If you would like to see some of the other things I've done, check out my web site.