Java Remote Method Invocation: 5 - Server Interfaces


The java.rmi.server package contains interfaces and classes typically used to implement remote objects.

5.1 The RemoteObject Class

See the RemoteObject API documentation.

5.2 The RemoteServer Class

See the RemoteServer API documentation.

5.3 The UnicastRemoteObject Class

The class java.rmi.server.UnicastRemoteObject provides support for creating and exporting remote objects. The class implements a remote server object with the following characteristics:

package java.rmi.server;

public class UnicastRemoteObject extends RemoteServer {

        protected UnicastRemoteObject()
                throws java.rmi.RemoteException {...}
        protected UnicastRemoteObject(int port)
                throws java.rmi.RemoteException {...}
        protected UnicastRemoteObject(int port,
                                      RMIClientSocketFactory csf,
                                      RMIServerSocketFactory ssf)
                throws java.rmi.RemoteException {...}

        public Object clone()
                throws java.lang.CloneNotSupportedException {...}
        public static RemoteStub exportObject(java.rmi.Remote obj)
                throws java.rmi.RemoteException {...}
        public static Remote exportObject(java.rmi.Remote obj, int port)
                throws java.rmi.RemoteException {...}
        public static Remote exportObject(Remote obj, int port,
                                          RMIClientSocketFactory csf,
                                          RMIServerSocketFactory ssf)
                throws java.rmi.RemoteException {...}
        public static boolean unexportObject(java.rmi.Remote obj,
                                             boolean force)
                throws java.rmi.NoSuchObjectException {...}
}

5.3.1 Constructing a New Remote Object

A remote object implementation (one that implements one or more remote interfaces) must be created and exported. Exporting a remote object makes that object available to accept incoming calls from clients. For a remote object implementation that is exported as a UnicastRemoteObject, the exporting involves listening on a TCP port (note that more than one remote object can accept incoming calls on the same port, so listening on a new port is not always necessary). A remote object implementation can extend the class UnicastRemoteObject to make use of its constructors that export the object, or it can extend some other class (or none at all) and export the object via UnicastRemoteObject's exportObject methods.

The constructor that takes no arguments creates and exports a remote object on an anonymous (or arbitrary) port, chosen at runtime. The second form of the constructor takes a single argument, port, that specifies the port number on which the remote object accepts incoming calls. The third constructor creates and exports a remote object that accepts incoming calls on the specified port via a ServerSocket created from the RMIServerSocketFactory; clients will make connections to the remote object via Sockets supplied from the RMIClientSocketFactory.

Note that if you export a remote object without specifying a socket factory, or if you export the object with a version of the method UnicastRemoteObject.exportObject or the constructor UnicastRemoteObject that does not contain parameters of type RMIClientSocketFactory and RMIServerSocketFactory), then the remote object is exported to all local addresses. To export a remote object to a specific address, see the section "RMI Socket Factories".

5.3.2 Exporting an Implementation Not Extended From RemoteObject

An exportObject method (any of the forms) is used to export a simple peer-to-peer remote object that is not implemented by extending the UnicastRemoteObject class. The first form of the exportObject method takes a single parameter, obj, which is the remote object that will accept incoming RMI calls; this exportObject method exports the object on an anonymous (or arbitrary) port, chosen at runtime. The second exportObject method takes two parameters, both the remote object, obj, and port, the port number on which the remote object accepts incoming calls. The third exportObject method exports the object, obj, with the specified RMIClientSocketFactory, csf, and RMIServerSocketFactory, ssf, on the specified port.

The exportObject method returns a Remote stub which is the stub object for the remote object, obj, that is passed in place of the remote object in an RMI call.

5.3.3 Passing a UnicastRemoteObject in an RMI Call

As stated above, when an exported object of type UnicastRemoteObject is passed as a parameter or return value in an RMI call, the object is replaced by the remote object's stub. An exported remote object implementation remains in the virtual machine in which it was created and does not move (even by value) from that virtual machine. In other words, an exported remote object is passed by reference in an RMI call; exported remote object implementations cannot be passed by value.

5.3.4 Serializing a UnicastRemoteObject

Information contained in UnicastRemoteObject is transient and is not saved if an object of that type is written to a user-defined ObjectOutputStream (for example, if the object is written to a file using serialization). An object that is an instance of a user-defined subclass of UnicastRemoteObject, however, may have non-transient data that can be saved when the object is serialized.

When a UnicastRemoteObject is read from an ObjectInputStream using UnicastRemoteObject's readObject method, the remote object is automatically exported to the RMI runtime so that it may receive RMI calls. If exporting the object fails for some reason, deserializing the object will terminate with an exception.

5.3.5 Unexporting a UnicastRemoteObject

The unexportObject method makes the remote object, obj, unavailable for incoming calls. If the force parameter is true, the object is forcibly unexported even if there are pending calls to the remote object or the remote object still has calls in progress. If the force parameter is false, the object is only unexported if there are no pending or in-progress calls to the object. If the object is successfully unexported, the RMI runtime removes the object from its internal tables. Unexporting the object in this forcible manner may leave clients holding stale remote references to the remote object. This method throws java.rmi.NoSuchObjectException if the object was not previously exported to the RMI runtime.

5.3.6 The clone method

Objects are only cloneable using the Java programming language's default mechanism if they support the java.lang.Cloneable interface. The class java.rmi.server.UnicastRemoteObject does not implement this interface, but does implement the clone method so that if subclasses need to implement Cloneable, the remote object will be capable of being cloned properly. The clone method can be used by a subclass to create a cloned remote object with initially the same contents, but is exported to accept remote calls and is distinct from the original object.

5.4 The Unreferenced Interface

package java.rmi.server;

public interface Unreferenced {
        public void unreferenced();
}

The java.rmi.server.Unreferenced interface allows a server object to receive notification that there are no clients holding remote references to it. The distributed garbage collection mechanism maintains for each remote object, the set of client virtual machines that hold references to that remote object. As long as some client holds a remote reference to the remote object, the RMI runtime keeps a local reference to the remote object. Each time the remote object's "reference" set becomes empty (meaning that the number of clients that reference the object becomes zero), the Unreferenced.unreferenced method is invoked (if that remote object implements the Unreferenced interface). A remote object is not required to support the Unreferenced interface.

As long as some local reference to the remote object exists, it may be passed in remote calls or returned to clients. The process that receives the reference is added to the reference set for the remote object. When the reference set becomes empty, the remote object's unreferenced method will be invoked. As such, the unreferenced method can be called more than once (each time the set is newly emptied). Remote objects are only collected when no more references, either local references or those held by clients, still exist.

5.5 The RMISecurityManager Class

See the RMISecurityManager API documentation.

5.6 The RMIClassLoader Class

See the RMIClassLoader API documentation.

5.7 The LoaderHandler Interface

See the LoaderHandler API documentation.

5.8 RMI Socket Factories

When the RMI runtime implementation needs instances of java.net.Socket and java.net.ServerSocket for its connections, instead of instantiating objects of those classes directly, it calls the createSocket and createServerSocket methods on the current RMISocketFactory object, returned by the static method RMISocketFactory.getSocketFactory. This allows the application to have a hook to customize the type of sockets used by the RMI transport, such as alternate subclasses of the java.net.Socket and java.net.ServerSocket classes. The instance of RMISocketFactory to be used can be set once by trusted system code. In JDK1.1, this customization was limited to relatively global decisions about socket type, because the only parameters supplied to the factory's methods were host and port (for createSocket) and just port (for createServerSocket).

In the Java SE platform, the new interfaces RMIServerSocketFactory and RMIClientSocketFactory have been introduced to provide more flexible customization of what protocols are used to communicate with remote objects.

To allow applications using RMI to take advantage of these new socket factory interfaces, several new constructors and exportObject methods, that take the client and server socket factory as additional parameters, have been added to the UnicastRemoteObject class.

Remote objects exported with either of the new constructors or exportObject methods (with RMIClientSocketFactory and RMIServerSocketFactory parameters) will be treated differently by the RMI runtime. For the lifetime of such a remote object, the runtime will use the custom RMIServerSocketFactory to create a ServerSocket to accept incoming calls to the remote object and use the custom RMIClientSocketFactory to create a Socket to connect clients to the remote object.

The implementation of RemoteRef and ServerRef used in the stubs and skeletons for remote objects exported with custom socket factories is UnicastRef2 and UnicastServerRef2, respectively. The wire representation of the UnicastRef2 type contains a different representation of the "endpoint" to contact than the UnicastRef type has (which used just a host name string in UTF format, following by an integer port number). For UnicastRef2, the endpoint's wire representation consists of a format byte specifying the contents of the rest of the endpoint's representation (to allow for future expansion of the endpoint representation) followed by data in the indicated format. Currently, the data may consist of a host name in UTF format, a port number, and optionally (as specified by the endpoint format byte) the serialized representation of an RMIClientSocketFactory object that is used by clients to generate socket connections to remote object at this endpoint. The endpoint representation does not contain the RMIServerSocketFactory object that was specified when the remote object was exported.

When calls are made through references of the UnicastRef2 type, the runtime uses the createSocket method of the RMIClientSocketFactory object in the endpoint when creating sockets for connections to the referent remote object. Also, when the runtime makes DGC "dirty" and "clean" calls for a particular remote object, it must call the DGC on the remote JVM using a connection generated from the same RMIClientSocketFactory object as specified in the remote reference, and the DGC implementation on the server side should verify that this was done correctly.

Remote objects exported with the older constructor or method on UnicastRemoteObject that do not take custom socket factories as arguments will have RemoteRef and ServerRef of type UnicastRef and UnicastServerRef as before and use the old wire representation for their endpoints, i.e. a host string in UTF format followed by an integer specifying the port number. This is so that RMI servers that do not use new 1.2 features will interoperate with older RMI clients.

If you export a remote object without specifying a socket factory, or if you export the object with a version of the method UnicastRemoteObject.exportObject or the constructor UnicastRemoteObject that does not contain parameters of type RMIClientSocketFactory and RMIServerSocketFactory, then the Java runtime uses the system's default RMI socket factory, which opens a socket on a wildcard address, which listens on all interfaces. Consequently, the remote object is exported to all local addresses. To export a remote object to a specific address, do one of the following:

5.8.1 The RMISocketFactory Class

The java.rmi.server.RMISocketFactory abstract class provides an interface for specifying how the transport should obtain sockets. Note that the class below uses Socket and ServerSocket from the java.net package.

package java.rmi.server;

public abstract class RMISocketFactory
        implements RMIClientSocketFactory, RMIServerSocketFactory
{

        public abstract Socket createSocket(String host, int port)
                throws IOException;
   public abstract ServerSocket createServerSocket(int port)
                throws IOException;
        public static void setSocketFactory(RMISocketFactory fac)
                throws IOException {...}
        public static RMISocketFactory getSocketFactory() {...}
        public static void setFailureHandler(RMIFailureHandler fh) {...}
        public static RMIFailureHandler getFailureHandler() {...}
}

The static method setSocketFactory is used to set the socket factory from which RMI obtains sockets. The application may invoke this method with its own RMISocketFactory instance only once. An application-defined implementation of RMISocketFactory could, for example, do preliminary filtering on the requested connection and throw exceptions, or return its own extension of the java.net.Socket or java.net.ServerSocket classes, such as ones that provide a secure communication channel.

The static method getSocketFactory returns the socket factory used by RMI. The method returns null if the socket factory is not set.

The transport layer invokes the createSocket and createServerSocket methods on the RMISocketFactory returned by the getSocketFactory method when the transport needs to create sockets. For example:

RMISocketFactory.getSocketFactory().createSocket(myhost, myport)

The method createSocket should create a client socket connected to the specified host and port. The method createServerSocket should create a server socket on the specified port.

The method setFailureHandler sets the failure handler to be called by the RMI runtime if the creation of a server socket fails. The failure handler returns a boolean to indicate if retry should occur. The default failure handler returns false, meaning that by default recreation of sockets is not attempted by the runtime.

The method getFailureHandler returns the current handler for socket creation failure, or null if the failure handler is not set.

5.8.2 The RMIServerSocketFactory Interface

See the RMIServerSocketFactory API documentation.

5.8.3 The RMIClientSocketFactory Interface

See the RMIClientSocketFactory API documentation.

5.9 The RMIFailureHandler Interface

The java.rmi.server.RMIFailureHandler interface provides a method for specifying how the RMI runtime should respond when server socket creation fails (except during object export).

package java.rmi.server;

public interface RMIFailureHandler {

        public boolean failure(Exception ex);
}

The failure method is invoked with the exception that prevented the RMI runtime from creating a java.net.ServerSocket. The method returns true if the runtime should attempt to retry and false otherwise.

Before this method can be invoked, a failure handler needs to be registered via the RMISocketFactory.setFailureHandler call. If the failure handler is not set, the RMI runtime attempts to re-create the ServerSocket after waiting for a short period of time.

Note that the RMIFailureHandler is not called when ServerSocket creation fails upon initial export of the object. The RMIFailureHandler will be called when there is an attempt to create a ServerSocket after a failed accept on that ServerSocket.

5.10 The LogStream Class

See the LogStream API documentation.

5.11 Stub and Skeleton Compiler

The rmic stub and skeleton compiler is used to compile the appropriate stubs and skeletons for a specific remote object implementation.

Please see the following URLs for further information on rmic:

    https://docs.oracle.com/javase/8/docs/technotes/tools/unix/rmic.html
    https://docs.oracle.com/javase/8/docs/technotes/tools/windows/rmic.html