[TOC] [Prev] [Next]

# Server Interfaces

When implementing a server, the client interfaces are available and extended with those that allow the definition, creation, and export of remote objects.

### Topics:

- The RemoteObject Class
- The RemoteServer Class
- The UnicastRemoteObject Class
- The Unreferenced Interface
- The RMISecurityManager Class
- The RMIClassLoader Class
- The LoaderHandler Interface
- The RMISocketFactory Class
- The RMIFailureHandler Interface
- The LogStream Class
- Stub and Skeleton Compiler

# The RemoteObject Class

The `java.rmi.server.RemoteObject` class implements the `java.lang.Object` behavior for remote objects. The `hashCode` and `equals` methods are implemented to allow remote object references to be stored in hashtables and compared. The `equals` method returns true if two `Remote` objects refer to the same remote object. It compares the remote object references of the remote objects.

The `toString` method returns a string describing the remote object. The contents and syntax of this string is implementation-specific and can vary.

All of the other methods of `java.lang.Object` retain their original implementations.

    package java.rmi.server;
    public abstract class RemoteObject
    	implements java.rmi.Remote, java.io.Serializable
    {
    	public int hashCode();
    	public boolean equals(Object obj);
    	public String toString();
    }

# The RemoteServer Class

The `java.rmi.server.RemoteServer` class is the common superclass to all server implementations and provides the framework to support a wide range of remote reference semantics. At present the only subclass supported is `UnicastRemoteObject`.

    package java.rmi.server;
    public abstract class RemoteServer
    	extends RemoteObject {

    	public static String getClientHost()
    		throws ServerNotActiveException;

    	public static void setLog(java.io.OutputStream out);

    	public static java.io.PrintStream getLog();
    }

The `getClientHost` method allows an active method to determine the host that initiated the remote method active in the current thread. The `ServerNotActiveException` is thrown if no remote method is active on the current thread. The `setLog` method logs RMI calls to the specified output stream. If the output stream is null, call logging is turned off. The `getLog` method returns the stream for the RMI call log, so that application-specific information can be written to the call log in a synchronized manner.

# The UnicastRemoteObject Class

The `java.rmi.server.UnicastRemoteObject` class provides support for point-to-point active object references using TCP-based streams. The class implements a remote server object with the following characteristics:

- References are valid only for, at most, the life of the process that creates the remote object.
- A TCP connection-based transport is used.
- Invocations, parameters, and results use a stream protocol for communicating between client and server.

      package java.rmi.server;
      public class UnicastRemoteObject extends RemoteServer {

      	protected UnicastRemoteObject()
      		throws java.rmi.RemoteException;

      	public Object clone()
      		throws java.lang.CloneNotSupportedException;

      	public static RemoteStub exportObject(java.rmi.Remote obj)
      		throws java.rmi.RemoteException;
      }

## Constructing a New Remote Object

In a Java virtual machine running as a server, remote objects defined by the developer can be created by the server application. When a remote object class extends `UnicastRemoteObject`, the constructor creates and exports a remote object. The constructor is invoked from the corresponding constructor of the remote object class. The default constructor creates a new unicast remote object using an anonymous port.

The clone method is used to create a unicast remote object with initially the same contents, but is exported to accept remote calls and is distinct from the original object.

## Exporting an Implementation Not Extended From RemoteObject

The `exportObject` method is used to export a simple peer-to-peer remote object that is not implemented by extending the `UnicastRemoteObject` class. The `exportObject` method is called with the object to be exported on an anonymous port. The object must be exported prior to the first time it is passed in an RMI call as either a parameter or return value; otherwise, a `java.rmi.server.``StubNotFoundException` is thrown when a remote call is attempted in which an "unexported" remote object is passed as an argument or return value.

Once exported, the object can be passed as an argument in an RMI call or returned as the result of an RMI call. When a remote object is passed, during marshaling a lookup is performed to find the matching remote stub for the remote object implementation and that stub is passed or returned instead.

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

## Passing a UnicastRemoteObject in an RMI Call

As stated above, when an 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. A 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, a remote object is passed by reference in an RMI call; remote objects cannot be passsed by value.

## 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`, it 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.

# 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 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. When the "reference" set becomes empty, the `Unreferenced.unreferenced` method is invoked (if the server 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 reference. When the reference set becomes empty, `Unreferenced` 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.

# The RMISecurityManager Class

    package java.rmi;

    public class RMISecurityManager extends java.lang.SecurityManager {

    	// Constructor
    	public RMISecurityManager();

    	// Returns implementation specific security context
    	public Object getSecurityContext();

    	// Disallow creating classloaders or execute ClassLoader methods
    	public synchronized void checkCreateClassLoader()
    		throws RMISecurityException;

    	// Disallow thread manipulation
    	public synchronized void checkAccess(Thread t)
    		throws RMISecurityException;

    	// Disallow thread group manipulation.
    	public synchronized void checkAccess(ThreadGroup g)
    		throws RMISecurityException;

    	// Disallow exiting the VM
    	public synchronized void checkExit(int status)
    		throws RMISecurityException;

    	// Disallow forking of processes
    	public synchronized void checkExec(String cmd)
    		throws RMISecurityException;

    	// Disallow linking dynamic libraries
    	public synchronized void checkLink(String lib)
    		throws RMISecurityException;

    	// Disallow accessing of all properties except those labeled OK
    	public synchronized void checkPropertiesAccess()
    		throws RMISecurityException;

    	// Access system property key only if key.stub is set to true
    	public synchronized void checkPropertyAccess(String key)
    		throws RMISecurityException;

    	// Check if a stub can read a particular file.
    	public synchronized void checkRead(String file)
    		throws RMISecurityException;

    	// No file reads are valid from a stub
    	public void checkRead(String file, Object context)
    		throws RMISecurityException;

    	// Check if a Stub can write a particular file.
    	public synchronized void checkWrite(String file)
    		throws RMISecurityException;

    	// Check if the specified system dependent file can be deleted.
    	public void checkDelete(String file)
    		throws RMISecurityException;

    	// Disllow opening file descriptor for reading unless via socket
    	public synchronized void checkRead(FileDescriptor fd)
    		throws RMISecurityException;

    	// Disallow opening file descriptor for writing unless via socket
    	public synchronized void checkWrite(FileDescriptor fd)
    		throws RMISecurityException;

    	// Disallow listening on any port.
    	public synchronized void checkListen(int port)
    		throws RMISecurityException;

    	// Disallow accepting connections on any port.
    	public synchronized void checkAccept(String host, int port)
    		throws RMISecurityException;

    	// Disallow stubs from using IP multicast.
    	public void checkMulticast(InetAddress maddr)
    		throws RMISecurityException;

    	// Disallow stubs from using IP multicast
    	public void checkMulticast(InetAddress maddr, byte ttl)
    		throws RMISecurityException;

    	// Downloaded classes (including stubs) can make connections if
    	// called through the RMI transport.
    	public synchronized void checkConnect(String host, int port)
    		throws RMISecurityException;

    	// Downloaded classes (including stubs) can make connections if
    	// called through the RMI transport.
    	public void checkConnect(String host, int port, Object context)
    		throws RMISecurityException;

    	// Allow caller to create top-level windows.
    	// Allow stubs to create windows with warnings.
    	public synchronized boolean checkTopLevelWindow(Object window)
    		throws RMISecurityException;

    	// Check if a stub can access a package.
    	public synchronized void checkPackageAccess(String pkg)
    		throws RMISecurityException;

    	// Check if a stub can define classes in a package.
    	public synchronized void checkPackageDefinition(String pkg)
    		throws RMISecurityException;

    	// Check if a stub can set a networking-related object factory.
    	public synchronized void checkSetFactory()
    		throws RMISecurityException;

    	// Disallow printing from stubs.
    	public void checkPrintJobAccess()
    		throws RMISecurityException;

    	// Disallow stubs from accessing system clipboard.
    	public void checkSystemClipboardAccess()
    		throws RMISecurityException;

    	// Disallow stubs from accessing AWT event queue.
    	public void checkAwtEventQueueAccess()
    		throws RMISecurityException;

    	// Checks to see if client code can access class members.
    	// Allow access to all public information. Allow non-stubs to
    	// access default, package, and private declarations and data).
    	public void checkMemberAccess(Class clazz, int which)
    		throws RMISecurityException;

    	// Stubs cannot perform security provider operations.
    	public void checkSecurityAccess(String provider)
    		throws RMISecurityException;
    }

The `RMISecurityManager` can be used when the application does not require specialized security functions but does need the protection it provides. This simple security manger disables all functions except class definition and access, so that other classes for remote objects, their arguments, and returns can be loaded as needed. A downloaded class is allowed to make a connection if the connection was initiated via the RMI transport.

If no security manager has been set, stub loading is disabled. This ensures that some security manager is responsible for the actions of loaded stubs and classes as part of any remote method invocation. A security manager is set using `System.setSecurityManager`.

# The RMIClassLoader Class

The`java.rmi.server.RMIClassLoader` is a utility class that can be used by applications to load classes via a URL.

    package java.rmi.server;

    public class RMIClassLoader {

        public static Class loadClass(String name)
    	    throws MalformedURLException, ClassNotFoundException;

        public static synchronized Class loadClass(URL codebase,
    		String name) throws MalformedURLException,
    		ClassNotFoundException;

    	public static Object getSecurityContext(ClassLoader loader);
    }

The first `loadClass` method loads the specified class name via the URL defined by the `java.rmi.server.codebase` property. The class is loaded, defined, and returned.

The second form of the `loadClass` method loads the specified class name via the URL parameter codebase.

The `getSecurityContext` method returns the security context of the given class loader, loader. The security context is obtained by querying the `LoaderHandler`'s `getSecurityContext` method.

The RMI runtime uses its own class loader to load stubs, skeletons, and other classes needed by the stubs and skeletons. These classes, and the way they are used, support the safety properties of the Java RMI runtime. This class loader always loads locally-available classes first. Only if a security manager is in force will stubs be loaded from either the local machine or from a network source. The class loader keeps a cache of loaders for individual Uniform Resource Locators (URLs) and the classes that have been loaded from them. When a stub or skeleton has been loaded, any class references that occur as parameters or returns will be loaded (from their originating codebase host) and are subject to the same security restrictions. Server processes must declare to the RMI runtime the location of the classes (stubs and parameters/returns) that will be available to its clients. The `java.rmi.server.codebase` property should be a URL from which stub classes and classes used by stubs will be loaded, using the normal protocols, such as http and ftp.

# The LoaderHandler Interface

    package java.rmi.server;

    public interface LoaderHandler {

        Class loadClass(String name)
    	    throws MalformedURLException, ClassNotFoundException;

        Class loadClass(URL codebase,String name)
    		throws MalformedURLException, ClassNotFoundException;

    	Object getSecurityContext(ClassLoader loader);
    }

The `LoaderHandler` interface must be implemented by a class named `LoaderHandler`. The `LoaderHandler` class must be defined in the package specified by the property `java.rmi.loader.packagePrefix`. The methods of `LoaderHandler` are used by the `java.rmi.server.RMIClassLoader` class to carry out its operations.

# The RMISocketFactory Class

The`java.rmi.server.RMISocketFactory` abstract class provides an interface for specifying how the transport should obtain sockets.

    package java.rmi.server;
    public abstract class RMISocketFactory {

    	public abstract java.net.Socket createSocket(String h,int p)
    		throws IOException;

    	public abstract java.net.ServerSocket createServerSocket(int p)
    		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. Note that the RMISocketFactory may only be set if the current security manager allows setting a socket factory; if setting the socket factory is disallowed, a `SecurityException` will be thrown.

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 default transport's implementation of `RMISocketFactory` provides for transparent RMI through firewalls using HTTP as follows:

- On `createSocket`, the factory automatically attempts HTTP connections to hosts that cannot be contacted with a direct socket.
- On `createServerSocket`, the factory returns a server socket that automatically detects if a newly accepted connection is an HTTP POST request. If so, it returns a socket that will transparently expose only the body of the request to the transport and format its output as an HTTP response.

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.

# 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.

    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.Socket` or `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, creation is not attempted.

# The LogStream Class

The class `LogStream` presents a mechanism for logging errors that are of possible interest to those monitoring the system. This class is used internally for server call logging.

    package java.rmi.server;

    public class LogStream extends java.io.PrintStream {

    	public static LogStream log(String name);

    	public static synchronized PrintStream getDefaultStream();

    	public static synchronized void setDefaultStream(

    		PrintStream newDefault);

    	public synchronized OutputStream getOutputStream();

    	public synchronized void setOutputStream(OutputStream out);

    	public void write(int b);

    	public void write(byte b[], int off, int len);

    	public String toString();

    	public static int parseLevel(String s);

    	// constants for logging levels
    	public static final int SILENT  = 0;
    	public static final int BRIEF   = 10;
    	public static final int VERBOSE = 20;
    }

The method `log` returns the LogStream identified by the given name. If a log corresponding to name does not exist, a log using the default stream is created.

The method `getDefaultStream` returns the current default stream for new logs.

The method `setDefaultStream` sets the default stream for new logs.

The method `getOutputStream` returns current stream to which output from this log is sent.

The method `setOutputStream` sets the stream to which output from this log is sent.

The first form of the method `write` writes a byte of data to the stream. If it is not a new line, then the byte is appended to the internal buffer. If it is a new line, then the currently buffered line is sent to the log's output stream with the appropriate logging prefix. The second form of the method `write` writes a subarray of bytes.

The method `toString` returns log name as string representation.

The method `parseLevel` converts a string name of a logging level to its internal integer representation.

# 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. The compiler is invoked with the package qualified class name of the remote object class. The class must previously have been compiled successfully.

- The location of the imported classes may be specified either with the CLASSPATH environment variable or with the -classpath argument.
- The compiled class files are placed in the current directory unless the -d argument is specified following the same mechanism for writing class files as `javac`.
- The -keepgenerated argument retains the generated java source files for the stubs and skeletons.
- The -show option displays a graphical user interface for the program.
- All other `javac` command line arguments are applicable and can be used with `rmic`.

[TOC] [Prev] [Next]

Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved.