[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 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:

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:

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.



[TOC] [Prev] [Next]

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