Implementation
Even though the implementation of the core functionalitythat is, the storage and retrieval of the <name, reference, owner IP> mappingsis rather trivial, the overall registry implementation is not, as the following considerations show:
- A remote RMI registry instance is itself an RMI server object (that provides the Registry remote interface). An (ordinary) RMI server or client that needs a reference to the registry uses the getRegistry method of the standard class LocateRegistry. This bootstrapping method is the only one that synthesizes a stub for a remote object, rather than getting it from the remote object itself.
- Each stub, however, contains the ObjID of the remote object that is created when the object is exported. LocateRegistry assumes the registry to have a special value, REGISTRY ID, as the ObjID. Unfortunately, the Java API does not let you specify the ObjID when exporting a remote server object. As a consequence, we had to take advantage of two classes of Sun's JRE implementationsun.rmi.server.UnicastServerRef and sun.rmi.transport.LiveRef to assign the required ObjID to the exported remote registry object.
- Class RemoteRegistry contains our custom implementation of the standard Registry remote interface. Listing One, the constructor, shows how a newly instantiated RemoteRegistry object is exported by means of the two aforementioned classes.
- In the first step, a LiveRef object with the required ObjID and the listener port is created. As can be seen, the special value REGISTRY ID is defined as a constant in class ObjID. The port can be set when the remote registry is started; by default, the well-defined registry port 1099 is used. In a second step, the LiveRef is cast to the type UnicastServerRef, then used to export the newly created RemoteRegistry object. The second parameter to the exportObject call is not used in the UnicastServerRef implementation, and can thus be set to null.
- In pre-5.0 Java versions, the rmic tool had to be used to generate the client stub and server skeleton classes for a given remote object implementation. When the server object was exported, both classes were instantiated and the server skeleton was registered with the remote reference layer of the Java virtual machine.
- Since Java 5.0, generating specific stub and skeleton classes has become obsolete. Now, client stubs are instances of class java.lang.reflect.Proxy, and on the server side, the virtual machine can delegate incoming remote invocations directly to the RMI server object without the need for a skeleton at all.
- The aforementioned method getRegistry of class LocateRegistry, however, synthesizes an old style stub of type RegistryImpl_Stub by default. The reason is backward compatibility; pre-5.0 clients cannot deal with Proxy stubs but would throw an exception when they encounter one. Only if the Java property java.rmi.server.ignoreStubClasses is set to True, LocateRegistry synthesizes a new style Proxy stub. Again, to achieve full transparency for our custom registry implementation (as well as for the same backward compatibility reason), we do not require the ignoreStubClasses property to be set. Instead, we used the rmic tool to generate a server skeleton, because a pre-5.0 stub expects to communicate with a server skeleton on the server side.
- When an RMI server object registers with a registry, the fact that the registry maintains a reference to it prevents the RMI server object from being garbage collected. Our custom RMI registry, however, does not register itself with a registry and thus must be explicitly protected from garbage collection. This is achieved by holding a static reference to the remote registry in the registry implementation class itself.
- In order to garbage collect outdated bindings, the validity of each binding is periodically checked by connecting with the RMI object's server socket. If a registered stub contains a custom client socket factory, then this factory must be used for the attempt to set up a connection to the registered RMI server object. If the connection attempt fails, the respective binding is removed from the list of valid bindings.
- Because a remote registry must be able to dynamically download the implementation classes of the RMI objects to be registered, it must be run under the control of a security manager. We have included the policy file (Listing Two) in the executable jar file that contains the remote registry (available online; see www.ddj.com/code/).
- The SocketPermission is necessary for clients to be able to contact the remote registry. The RuntimePemission lets the remote registry dynamically download code; the FilePermission is needed for the persistent storage of the bindings in file .rrrbindings in the current directory.
public RemoteRegistry (int port) throws RemoteException { ... LiveRef lref = new LiveRef (new ObjID(ObjID.REGISTRY_ID),port); new UnicastServerRef (lref).exportObject (this,null); }
grant { permission java.net.SocketPermission "*:*", "connect,accept,resolve,listen"; permission java.lang.RuntimePermission "accessClassInPackage.sun.rmi.*"; permission java.io.FilePermission".rrrbindings", "read write,delete"; };