Chapter 6. Naming I—RMI registry

  • Purpose—How it works—Names in the registry—Naming class—Registry interface —registry exceptions—Names and URLs—Setup—Configurations—Utilities—Alternative naming services—Exercises

In this chapter

The RMI registry is a naming service which provides clients with a mechanism to find one or more initial RMI servers. This chapter describes the RMI registry's API and configuration. For alternate naming services see Chapter 13.

Purpose

“The Registry is a remote object that maps names to remote objects.”[1]

The RMI registry is a naming service: it provides a name-to-address lookup service like the white pages in a phone book. An RMI server is listed under a name. The listing for the server contains its RMI address, an equivalent of a phone number. Like the phone book, the registry is a set of {name, address} pairs as illustrated in Figure 6.1

RMI registry

Figure 6.1. RMI registry

You are not obliged to use the registry in an RMI application. You must either use some naming service: either the RMI registry discussed in this chapter, a JNDI or Jini service as discussed in Chapter 13; or else use activatable stubs exclusively, as discussed in Chapter 10, with a MarshalledObject bootstrapping technique.

How it works

Initially, the only remote method available to a client is a lookup in a naming service. In § 1.5 we saw that a remote stub can only be obtained via a return value of another remote method invocation.[2] To close off this apparent circularity, obviously you have to start somewhere. The place you start, the “bootstrap”, is the RMI registry.

An RMI client first looks up the RMI registry to find one or more initial RMI servers; it then obtains subsequent RMI servers from the set of RMI servers already found. Initially, this set only includes the servers found via the registry. Once an initial server has been found in the registry, it can return further unregistered servers as RMI result values. The value returned by a registry lookup is not the RMI server itself, but rather a remote stub for it.

Obviously there only needs to be one RMI server known to the registry: all other servers can be obtained from it or from the servers it returns. In any given application, which RMI servers are bound in the registry is an issue for the application designer. Anything which an application needs to access ab initio should be bound; anything which an application can traverse to need not be bound. Typically, singleton services like login or session establishment services are bound, and the further services that they return are not.

The result of any remote method can be an object which implements java.rmi.Remote. This is a “remote reference”, a reference to a remote server. Any RMI server can return such a result—can return an RMI server. As we will see, the registry lookup operation returns such a remote reference.

This is rather like the access pattern to an object-oriented database. When accessing an OODB, an initial object is located by name, and subsequent objects are obtained by following chains of references.

Bind, unbind, and lookup

The registry provides three essential actions: bind, unbind, and lookup. The bind operation adds an entry—a service-name/address pair—to the registry. The unbind operation removes a service's entry from the registry by name. The lookup operation allows anyone to use the service name to find the service's address.

Rebind and list

The registry also provides a rebind operation, which is like bind except that if the specified name is already bound in the registry, the binding is overridden, rather than an exception being thrown. Finally, it provides a list operation, which lists all the names currently bound in the registry. (The registry's name space is “flat”: all bound names are returned.)

Names in the registry

The name to which an RMI server is bound is an arbitrary string. It can contain any characters whatsoever; these are not interpreted by the registry in any way. In particular, although the name can contain directory separators (like a filename), this does not imply that the registry obeys any hierarchical naming structure—unlike a file system. The registry provides a flat naming space.

Unique naming

Using a naming service presents the problem of finding a unique name for an RMI service: a name which will not be used by another software supplier. Fortunately, there is an extremely simple solution in the case of the registry: use a Java qualified name. You have already solved the uniqueness problem for your package and class names. For example, the RemoteEchoServer is registered as “javarmi.quicktour.RemoteEcho”, the class name of the remote interface javarmi.quicktour.RemoteEcho.

As in this example, we recommend that you use the qualified name of the remote interface implemented by the RMI server. This is a better choice than the qualified name of the server itself, since the client has to know the package and class name of the remote interface, while it has no business to know the package and class name of the remote server proper—which needn't even be installed on clients. You should get the class name from the remote interface (via Class.getName) rather than providing it as a “wild” string in your application code.

There is nothing to stop you binding the same service to more than one name. For example, if your service implements more than one remote interface you could bind it multiple times, once to each interface name.

Of course there are occasions when you want to run multiple instances of the same RMI service and register them all under different names, in which case you need a different solution.

The Naming class

The java.rmi.Naming class provides the simplest means for accessing a remote registry. Name arguments to the methods of Naming all take the form of URLs[3] of the form:

[rmi:][//][host][:port][/name]

where:

  • the optional rmi names the protocol, which may be omitted but must be “rmi” if present

  • the optional host is the host on which the desired registry is running

  • the optional port is the port it is listening on (default 1099)

  • name is the registry name to be bound, unbound, or looked up: name is omitted when calling the list method.[4]

These methods all throw RemoteException: see § 6.7.

The Registry interface

Lower-level control is available via the java.rmi.registry.Registry interface. You can obtain an instance of the Registry interface with the various java.rmi.registry.LocateRegistry.getRegistry methods. These either return an instance of Registry whose bind, lookup, list, rebind, and unbind methods can be executed, or throw an exception indicating that the specified URL is malformed or that a bootstrap stub for the registry cannot be created.

Although the LocateRegistry.getRegistry methods return an instance of Registry which is a remote stub for the specified registry, the returned object is specially constructed at the client, as one would expect of a bootstrapping mechanism. The remote registry may or may not be present: no connection has yet been established to it. The connection is established when you invoke a Registry method on this object, which will fail at that point if the specified registry is not running or not reachable.[5]

One of the LocateRegistry.getRegistry methods takes an RMIClientSocketFactory parameter. This allows you to use a specific client sockets used to communicate with the registry. See Chapter 11 for more information on socket factories.

Like the methods of the Naming class, these methods all throw RemoteException: see § 6.7.

Registry exceptions

All methods of Registry and Naming throw RemoteException on any general RMI failure.

All bind, rebind, or unbind methods throw java.rmi.AccessException, which extends RemoteException: this means that the operation was attempted from a host other than that in which the registry is running; this is not permitted, as an elementary security measure.[6]

The bind method throws AlreadyBoundException if the name is already in use. The lookup and unbind methods throw NotBoundException if the name is not in use.

Names and URLs

All methods of Naming—but not of Registry—throw MalformedURLException if anything is wrong with the formation of the supplied URL. Why the difference?

In the Naming class, the name arguments are URLs as described above. In the Registry interface, the name arguments are not URLs, they are simply the name portion.[7]

The reason for this: once you have obtained an instance of the Registry interface, you already have a remote reference to the registry at the desired host and port.

Similarly, the results of the Registry.list method are just names, not URLs like the results of Naming.list. The way to remember this is to remember that Registry.list returns results suitable for passing to Registry.lookup; Naming.list returns results suitable for passing to Naming.lookup.

Obviously the various Naming methods merely use the host and port information to obtain a stub for the remote registry from LocateRegistry, and then call the corresponding Registry methods using only the remaining name information.

Recommendation

Even though Naming takes URL arguments, the protocol and hostname parts of the URL have default values, so arguments to the Naming.bind, Naming.rebind, and Naming.unbind methods can just be bound-names. Using this technique eliminates a possible source of error. The Naming.bind, Naming.rebind, and Naming.unbind methods can only be executed on the same host as the registry. In other words, the only valid hostname in the Naming URL is the current hostname or localhost, which is also the default host supplied by Naming. The rmi: protocol is also the default.

Registry setup

Starting a registry process

Normally the registry is run as a separate process using the rmiregistry command supplied with the JDK and JRE—see their documentation for details.

Creating a local registry

It is possible to create an instance of the registry in the same virtual machine as your application, by calling one of the various LocateRegistry.createRegistry methods.

You might do this if:

  • you want to run your own registry on a non-standard port, for application-configuration reasons

  • you want an easy way for the registry to share the Java environment of your RMI servers (for example, their codebase)

  • you want the registry to exit along with your application[8]

  • you want to equip the registry with socket factories (see Chapter 11 for more information)

  • you have something else against running the rmiregistry command.

The default port for the registry is 1099.

Deployment

If you are using the RMI registry as the naming service, at least one RMI registry is required per host in which RMI servers are running. The reason for this is that the registry only permits bind, rebind, and unbind calls originating from the same host, for security reasons. Other naming services may require different configurations.

This statement may seem too broad, but it isn't. Without using complicated techniques such as serializing remote objects via MarshalledObjects, only a Java object in the same JVM can obtain a reference to an unbound RMI service, and that object can only bind the reference to a registry in the same host as itself. Usually—but not invariably—the server object binds itself.

Registry configurations

Normally, the registry is configured so as to enable code mobility (discussed in Chapter 9). This is the “standard configuration” discussed in § 6.10.1. Early in the development cycle, or for teaching and learning purposes, you may wish to use a configuration which is initially simpler to set up. This is the “development configuration” discussed in § 6.10.2.

Standard configuration

If you are using a stand-alone RMI registry—the rmiregistry command—you can run it in one of two ways:

  1. Run it in an environment where the java.rmi.server.codebase property is set appropriately.

  2. Run it in an environment where the application's classes are not on the CLASSPATH.

Otherwise the java.rmi.server.codebase property will not be forced into use, and RMI clients will not be able to use RMI's facility for automatic class loading from the codebase.

The requirements for method (2) are:

  • CLASSPATH is non-null and does not contain an entry for your application classes, or

  • CLASSPATH is null and the working directory (“.”) is not the root of your application's package structure.

The reason for the second option is that a null CLASSPATH is interpreted as “.”, i.e. the current directory.

This means that if the application's classes are available directly from the file system rather than in a JAR file, to force the registry to load from the codebase you must run the registry from a directory other than the application's package root directory.

For further information see Chapter 9.

Development configuration

Code mobility complicates the initial RMI setup. Until you understand code mobility, you can use one of the following simpler registry setups:

  1. Use an embedded registry (by calling LocateRegistry.createRegistry in your initial RMI server).

  2. Modify the rmiregistry command's environment so that it can load your application classes, either by running it with a null CLASSPATH in the root directory of your application's package structure, or by running it with its CLASSPATH set to include your application's root directory or JAR file(s).

In either case, the registry will be able to load your application classes directly.

When you set up the registry in this way, you must also make all application classes—including RMI stubs—available via the CLASSPATH of your RMI clients. In development environments this is often not too difficult.

When you have your RMI application at least partly running, you can then implement code mobility, if that is a requirement of your application. It is easier to implement code mobility on a running system than it is to get a system running with the added complications that code mobility brings. Implementing code mobility is a setup task, not a programming task.

However, if you are using Activation (described in Chapter 10) you must set up code mobility during development, otherwise you will be unable to activate your servers.

Utilities

Listing the registry

The sample program ListRegistry shown in Example 6.1 prints out the contents of a specified RMI registry.

Example 6.1. Program to list the registry

import java.rmi.*;
import java.util.*;
/**
** List the names and objects bound in RMI registries.
** Invoke with zero or more registry URLs as command-line arguments,
** e.g. "rmi://localhost", "rmi://localhost:1099".
*/
public class ListRegistry
{
       public static void main(String[] args)
       {
              System.setSecurityManager(new RMISecurityManager());
              for (int i = 0; i < args.length; i++)
              {
                       try
                       {
                              String[]list = Naming.list(args[i]);
                              System.out.println("Contents of registry at "+args[i]);
                              for (int j = 0; j < list.length; j++)
                              {
                                       Remote remote = Naming.lookup(list[j]);
                                       System.out.println((j+1)+".	name="+list[j]
                                             "
	remote="+remote);
                              }
                       }
                       catch (java.net.MalformedURLException e)
                       {
                                      System.err.println(e); // bad argument
                       }
                       catch (NotBoundException e)
                       {
                             // name vanished between list and lookup - ignore
                       }
                       catch (RemoteException e)
                       {
                             System.err.println(e); // General RMI exception
                       }
              }
       }
}

You can test this program by starting the RMI activation daemon rmid and then running ListRegistry with the argument rmi://localhost:1098.

Sun registry utility

Sun have made a registry utility available on a non-supported basis. It was announced in a posting to the RMI Mailing List dated 29 November 1999 entitled “RMI Utilities (reg, rmipm)”.[9] You should also read the document located at this URL. The utility provides a number of useful operations:

  • list the registry, in more detail than the ListRegistry program above

  • unbind a name from the registry (use with care!)

  • probe to see if a registry is running, returning an appropriate exit status (for use in scripts)

  • wait for a named object to appear in the registry (for use in scripts).

Alternative naming services

The Java Naming and Directory Interface (JNDI) and the Jini Discovery and Lookup services can be used as alternate naming services for RMI. A discussion of these topics will be found in Chapter 13.

These services are capable of overcoming the two major limitations of the RMI registry: the “flat” name space, and the lack of persistence.

The RMI registry would be persistent if it contained the same {name, value} pairs on start-up that it did when last stopped. It doesn't. The concept of a persistent registry doesn't make much sense when the services registered are UnicastRemoteObjects, as remote stubs for these are inherently transient—they only have meaning while the object instance to which they refer is active.

The concept of a persistent registry does make sense when the services registered are activatable, because remote stubs for these are conceptually persistent: the objects to which they refer are recreated if necessary. See Chapter 10 for details.

Exercises

1:

Enhance the ListRegistry program to show the codebase associated with each remote object found in the RMI registry. The codebase information is found from the Class of the remote object.

2:

To implement persistence in an RMI registry, we could use two simple Java programs: one to dump a registry, and one to re-load it from the dump. Write a program to dump an RMI registry to a persistent store in a file, using serialization.

3:

Write a program to restore an RMI registry from the file produced by the previous exercise.



[1] Online documentation, JDK 1.3.

[2] or as a saved MarshalledObject.

[3] URL stands for “uniform resource locator”. Lately these have been renamed to URI: “uniform resource identifier”, but the Java API is entirely specified using the URL name and abbreviation: we will follow suit. uris are specified in RFC 2396; see also http://www.ics.uci.edu/pub/ietf/uri/.

[4] Actually all the syntax is optional, as long as some part of it is specified. Try the ListRegistry program presented later in the chapter with the following arguments: (a)“rmi:”, (b)“//”, (c)“:1099”.

[5] You may ask “so why do the getRegistry methods throw RemoteException?” The answer is that these methods construct a special bootstrap stub for the registry “by hand”: this construction process can incur a remote exception for a number of reasons, including not being able to load the required stub class.

[6] This rule is sometimes misunderstood to mean that the registry must be in the same host as the server being bound, instead of the program doing the binding (not necessarily the same thing). Some of the other Registry methods are also declared to throw AccessException, although they don't: this seems to be a relic, or mistake.

[7] All versions of the RMI specification and all JDK implementations agree on this. Up to and including JDK 1.3, the JDK documentation was incorrect on this point.

[8] This behaviour is guaranteed by the RMI specification, § 6.2.

[9] It was at http://developer.jini.org/exchange/users/aecolley/rmiutil as reg.jar at the time of writing, but search for the mailing list item. To access this URL you must be a member of the Jini Sun Community Source Licence programme; directions for joining at no charge appear at the Jini developer site http://developer.jini.org.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.103.183