The java.rmi.server package is the most complex of all the RMI packages; it contains the scaffolding for building remote objects and thus is used by objects whose methods will be invoked by clients . The package defines 6 exceptions, 9 interfaces, and 10-12 classes (depending on the Java version). Fortunately, you only need to be familiar with a few of these in order to write remote objects. The important classes are the RemoteObject class, which is the basis for all remote objects; the RemoteServer class, which extends RemoteObject ; and the UnicastRemoteObject class, which extends RemoteServer . Any remote objects you write will likely either use or extend UnicastRemoteObject . Clients that call remote methods but are not themselves remote objects don't use these classes and therefore don't need to import java.rmi.server .
18.6.1 The RemoteObject Class
Technically, a remote object is not an instance of the RemoteObject class but an instance of any class that implements a Remote interface. In practice, most remote objects will be instances of a subclass of java.rmi.server.RemoteObject :
public abstract class RemoteObject extends Object implements Remote, Serializable
You can think of this class as a special version of java.lang.Object for remote objects. It provides toString( ) , hashCode( ) , clone( ) , and equals( ) methods that make sense for remote objects. If you create a remote object that does not extend RemoteObject , you need to override these methods yourself.
The equals( ) method compares the remote object references of two RemoteObject s and returns true if they point to the same RemoteObject . As with the equals( ) method in the Object class, you may want to override this method to provide a more meaningful definition of equality.
The toString( ) method returns a String that describes the RemoteObject . Most of the time, toString() returns the hostname and port from which the remote object came as well as a reference number for the object. You can override this method in your own subclasses to provide more meaningful string representations.
The hashCode( ) method maps a presumably unique int to each unique object; this integer may be used as a key in a Hashtable . It returns the same value for all remote references that refer to the same remote object. Thus, if a client has several remote references to the same object on the server, or multiple clients have references to that object, they should all have the same hash code.
The final instance method in this class is getRef() :
public RemoteRef getRef( ) // Java 1.2
This returns a remote reference to the class:
public abstract interface RemoteRef extends Externalizable
There's also one static method, RemoteObject.toStub( ) :
public static Remote toStub(Remote ro) // Java 1.2 throws NoSuchObjectException
RemoteObject.toStub( ) converts a given remote object into the equivalent stub object for use in the client virtual machine, which can help you dynamically generate stubs from within your server without using rmic .
18.6.2 The RemoteServer Class
The RemoteServer class extends RemoteObject ; it is an abstract superclass for server implementations such as UnicastRemoteObject . It provides a few simple utility methods needed by most server objects:
public abstract class RemoteServer extends RemoteObject
UnicastRemoteObject is the most commonly used subclass of RemoteServer included in the core library. Two others, Activatable and ActivationGroup , are found in the java.rmi.activation package. You can add others (for example, a UDP or multicast remote server) by writing your own subclass of RemoteServer .
RemoteServer has two constructors:
protected RemoteServer( ) protected RemoteServer(RemoteRef r)
However, you won't instantiate this class yourself. Instead, you will instantiate a subclass like UnicastRemoteObject . That class's constructor calls one of these protected constructors from the first line of its constructor.
220.127.116.11 Getting information about the client
The RemoteServer class has one method to locate the client with which you're communicating:
public static String getClientHost( ) throws ServerNotActiveException
RemoteServer.getClientHost() returns a String that contains the hostname of the client that invoked the currently running method. This method throws a ServerNotActiveException if the current thread is not running a remote method.
For debugging purposes, it is sometimes useful to see the calls that are being made to a remote object and the object's responses. You get a log for a RemoteServer by passing an OutputStream object to the setLog( ) method:
public static void setLog(OutputStream out)
Passing null turns off logging. For example, to see all the calls on System.err (which sends the log to the Java console), you would write:
Here's some log output I collected while debugging the Fibonacci programs in this chapter:
Sat Apr 29 12:20:36 EDT 2000:RMI:TCP Accept-1:[titan.oit.unc.edu: sun.rmi.transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease dirty(java.rmi.server.ObjID, long, java.rmi.dgc.Lease)] Fibonacci Server ready. Sat Apr 29 12:21:27 EDT 2000:RMI:TCP Accept-2:[macfaq.dialup.cloud9.net: sun.rmi.transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease dirty(java.rmi.server.ObjID, long, java.rmi.dgc.Lease)] Sat Apr 29 12:22:36 EDT 2000:RMI:TCP Accept-3:[macfaq.dialup.cloud9.net: sun.rmi. transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease dirty(java.rmi.server.ObjID, long, java.rmi.dgc.Lease)] Sat Apr 29 12:22:39 EDT 2000:RMI:TCP Accept-3:[macfaq.dialup.cloud9.net: FibonacciImpl: java.math.BigInteger getFibonacci(java.math.BigInteger)] Sat Apr 29 12:22:39 EDT 2000:RMI:TCP Accept-3:[macfaq.dialup.cloud9.net: FibonacciImpl: java.math.BigInteger getFibonacci(java.math.BigInteger)]
If you want to add extra information to the log along with what's provided by the RemoteServer class, you can retrieve the log's PrintStream with the getLog( ) method:
public static PrintStream getLog( )
Once you have the print stream, you can write on it to add your own comments to the log. For example:
PrintStream p = RemoteServer.getLog( ); p.println("There were " + n + " total calls to the remote object.");
18.6.3 The UnicastRemoteObject Class
The UnicastRemoteObject class is a concrete subclass of RemoteServer . To create a remote object, you can extend UnicastRemoteObject and declare that your subclass implements some subinterface of java.rmi.Remote . The methods of the interface provide functionality specific to the class, while the methods of UnicastRemoteObject handle general remote object tasks like marshalling and unmarshalling arguments and return values. All of this happens behind the scenes. As an application programmer, you don't need to worry about it.
A UnicastRemoteObject runs on a single host, uses TCP sockets to communicate, and has remote references that do not remain valid across server restarts. While this is a good general-purpose framework for remote objects, it is worth noting that you can implement other kinds of remote objects. For example, you may want a remote object that uses UDP, or one that remains valid if the server is restarted, or even one that distributes the load across multiple servers. To create remote objects with these properties, extend RemoteServer directly and implement the abstract methods of that class. However, if you don't need anything so esoteric, it's much easier to subclass UnicastRemoteObject .
The UnicastRemoteObject class has three protected constructors:
protected UnicastRemoteObject( ) throws RemoteException protected UnicastRemoteObject(int port) // Java 1.2 throws RemoteException protected UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException // Java 1.2
When you write a subclass of UnicastRemoteObject , you call one of these constructors, either explicitly or implicitly, in the first line of each constructor of your subclass. All three constructors can throw a RemoteException if the remote object cannot be created.
The noargs constructor creates a UnicastRemoteObject that listens on an anonymous port chosen at runtime. By the way, this is an example of an obscure situation I mentioned in Chapter 9 and Chapter 10. The server is listening on an anonymous port. Normally, this situation is next to useless because it is impossible for clients to locate the server. In this case, clients locate servers by using a registry that keeps track of the available servers and the ports they are listening to.
The downside to listening on an anonymous port is that it's not uncommon for a firewall to block connections to that port. The next two constructors listen on specified ports so you can ask the network administrators to allow traffic for those ports through the firewall.
If the network administrators are uncooperative, you'll need to use HTTP tunneling or a proxy server or both. The third constructor also allows you to specify the socket factories used by this UnicastRemoteObject . In particular, you can supply a socket factory that returns sockets that know how to get through the firewall.
The UnicastRemoteObject class has several public methods:
public Object clone( ) throws CloneNotSupportedException public static RemoteStub exportObject(Remote r) throws RemoteException public static Remote exportObject(Remote r, int port) throws RemoteException // Java 1.2 public static Remote exportObject(Remote r, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException // Java 1.2 public static boolean unexportObject(Remote r, boolean force) throws NoSuchObjectException // Java 1.2
The clone( ) method simply creates a clone of the remote object. You call the UnicastRemoteObject.exportObject( ) to use the infrastructure that UnicastRemoteObject provides for an object that can't subclass UnicastRemoteObject . Similarly, you pass an object UnicastRemoteObject.unexportObject( ) to stop a particular remote object from listening for invocations.
The java.rmi.server package defines a few more exceptions. The exceptions and their meanings are listed in Table 18-2. All but java.rmi.server.ServerNotActiveException extend, directly or indirectly, java.rmi.RemoteException . All are checked exceptions that must be caught or declared in a throws clause.
Table 18-2. java.rmi.server exceptions
You're trying to export a remote object on a port that's already in use.
An attempt was made to invoke a method in a remote object that wasn't running.
An attempt to clone a remote object on the server failed.
This subclass of ExportException is thrown when the SecurityManager prevents a remote object from being exported on the requested port.
This chapter has been a fairly quick look at Remote Method Invocation. For a more detailed treatment, see Java RMI , by William Grosso (O'Reilly).