18.4 The java.rmi Package


The java.rmi package contains the classes that are seen by clients (objects that invoke remote methods ). Both clients and servers should import java.rmi . While servers need a lot more infrastructure than is present in this package, java.rmi is all clients need. This package contains one interface, three classes, and a handful of exceptions.

18.4.1 The Remote Interface

The Remote interface tags objects as remote objects. It doesn't declare any methods; remote objects usually implement a subclass of Remote that does declare some methods. The methods that are declared in the interface are the methods that can be invoked remotely.

Example 18-8 is a database interface that declares a single method, SQLQuery( ) , which accepts a String and returns a String array. A class that implements this interface would include the code to send an SQL query to a database and return the result as a String array.

Example 18-8. A database interface
 import java.rmi.*; public interface SQL extends Remote {   public String[] SQLQuery(String query) throws RemoteException; } 

An SQLImpl class that implemented the SQL interface would probably have more methods, some of which might be public. However, only the SQLQuery() method can be invoked by a client. Because the Remote interface is not a class, a single object can implement multiple Remote subinterfaces. In this case, any method declared in any Remote interface can be invoked by a client.

18.4.2 The Naming Class

The java.rmi.Naming class talks to a registry running on the server in order to map URLs like rmi://login. ibiblio .org/myRemoteObject to particular remote objects on particular hosts . You can think of a registry as a DNS for remote objects. Each entry in the registry has a name and an object reference. Clients give the name (via a URL) and get back a reference to the remote object.

As you've seen, an rmi URL looks exactly like an http URL except that the scheme is rmi instead of http . Furthermore, the path part of the URL is an arbitrary name that the server has bound to a particular remote object, not a filename.

The biggest deficiency of Naming is that for security reasons (avoiding man-in-the-middle attacks), it has to run on the same server as the remote objects. It cannot register multiple objects on several different servers. If this is too restrictive , a Java Naming and Directory Interface (JNDI) context can add an additional layer of indirection so that multiple RMI registries can be presented through a single directory. Clients need only know the address of the main JNDI directory. They do not need to know the addresses of all the individual RMI registries the JNDI context is proxying for.

The Naming class has five public methods: list( ) , to list all the names bound in the registry; lookup( ) , to find a specific remote object given its URL; bind( ) , to bind a name to a specific remote object; rebind( ) , to bind a name to a different remote object; and unbind( ) , to remove a name from the registry. Let's look at these methods in turn . public static String[] list(String url) throws RemoteException, MalformedURLException

The list( ) method returns an array of strings, one for each URL that is currently bound. The url argument is the URL of the Naming registry to query. Only the protocol, host, and port are used. The path part of the URL is ignored. list( ) throws a MalformedURLException if url is not a valid rmi URL. A RemoteException is thrown if anything else goes wrong, such as the registry's not being reachable or refusing to supply the requested information.

Example 18-9 is a simple program that lists all the names currently bound in a particular registry. It's sometimes useful when debugging RMI problems. It allows you to determine whether the names you're using are the names the server expects.

Example 18-9. RegistryLister
 import java.rmi.*; public class RegistryLister {   public static void main(String[] args) {        int port = 1099;          if (args.length == 0) {       System.err.println("Usage: java RegistryLister host port");       return;     }          String host = args[0];          if (args.length > 1) {       try {         port = Integer.parseInt(args[1]);         if (port <1  port > 65535) port = 1099;       }       catch (NumberFormatException ex) {}          }        String url = "rmi://" + host + ":" + port + "/";     try {       String[] remoteObjects = Naming.list(url);       for (int i = 0; i < remoteObjects.length; i++) {         System.out.println(remoteObjects[i]);        }     }     catch (RemoteException ex) {       System.err.println(ex);     }     catch (java.net.MalformedURLException ex) {       System.err.println(ex);     }   } } 

Here's a result from a run against the RMI server I was using to test the examples in this chapter:

 %  java RegistryLister login.ibiblio.org  rmi://login.ibiblio.org:1099/fibonacci rmi://login.ibiblio.org:1099/hello 

You can see that the format for the strings is full rmi URLs rather than just names. It turns out this is a bug; in Java 1.4.1 and later, the bug has been fixed. In these versions, the scheme part of the URI is no longer included. In other words, the output looks like this:

 //login.ibiblio.org:1099/fibonacci //login.ibiblio.org:1099/hello public static Remote lookup(String url) throws RemoteException, NotBoundException, AccessException, MalformedURLException

A client uses the lookup() method to retrieve the remote object associated with the file portion of the name; so, given the URL rmi://login.ibiblio.org:2001/myRemoteObject , it would return the object bound to myRemoteObject from login.ibiblio.org on port 2,001.

This method throws a NotBoundException if the remote server does not recognize the name. It throws a RemoteException if the remote registry can't be reached; for instance, because the network is down or because no registry service is running on the specified port. An AccessException is thrown if the server refuses to look up the name for the particular host. Finally, if the URL is not a proper rmi URL, it throws a MalformedURLException . public static void bind(String url, Remote object) throws RemoteException, AlreadyBoundException, MalformedURLException, AccessException

A server uses the bind( ) method to link a name like myRemoteObject to a remote object. If the binding is successful, clients will be able to retrieve the remote object stub from the registry using a URL like rmi://login.ibiblio.org:2001/myRemoteObject .

Many things can go wrong with the binding process. bind() throws a MalformedURLException if url is not a valid rmi URL. It throws a RemoteException if the registry cannot be reached. It throws an AccessException , a subclass of RemoteException , if the client is not allowed to bind objects in this registry. If the URL is already bound to a local object, it throws an AlreadyBoundException . public static void unbind(String url) throws RemoteException, NotBoundException, AlreadyBoundException, MalformedURLException, AccessException // Java 1.2

The unbind( ) method removes the object with the given URL from the registry. It's the opposite of the bind( ) method. What bind( ) has bound, unbind( ) releases. unbind( ) throws a NotBoundException if url was not bound to an object in the first place. Otherwise, this method can throw the same exceptions for the same reasons as bind() . public static void rebind(String url, Remote object) throws RemoteException, AccessException, MalformedURLException

The rebind( ) method is just like the bind( ) method, except that it binds the URL to the object, even if the URL is already bound. If the URL is already bound to an object, the old binding is lost. Thus, this method does not throw an AlreadyBoundException . It can still throw RemoteException , AccessException , or MalformedURLException , which have the same meanings as they do when thrown by bind() .

18.4.3 The RMISecurityManager Class

A client loads stubs from a potentially untrustworthy server; in this sense, the relationship between a client and a stub is somewhat like the relationship between a browser and an applet. Although a stub is only supposed to marshal arguments and unmarshal return values and send them across the network, from the standpoint of the virtual machine, a stub is just another class with methods that can do just about anything. Stubs produced by rmic shouldn't misbehave; but there's no reason someone couldn't handcraft a stub that would do all sorts of nasty things, such as reading files or erasing data. The Java virtual machine does not allow stub classes to be loaded across the network unless there's some SecurityManager object in place. (Like other classes, stub classes can always be loaded from the local class path.) For applets, the standard AppletSecurityManager fills this need. Applications can use the RMISecurityManager class to protect themselves from miscreant stubs:

 public class RMISecurityManager extends SecurityManager 

In Java 1.1, this class implements a policy that allows classes to be loaded from the server's codebase (which is not necessarily the same as the server itself) and allows the necessary network communications between the client, the server, and the codebase. In Java 1.2 and later, the RMISecurityManager doesn't allow even that, and this class is so restrictive, it's essentially useless. In the Java 1.5 documentation, Sun finally admitted the problem: " RMISecurityManager implements a policy that is no different than the policy implemented by SecurityManager . Therefore an RMI application should use the SecurityManager class or another application-specific SecurityManager implementation instead of this class."

18.4.4 Remote Exceptions

The java.rmi package defines 16 exceptions, listed in Table 18-1. Most extend java.rmi.RemoteException . java.rmi.RemoteException extends java.io.IOException . AlreadyBoundException and NotBoundException extend java.lang.Exception . Thus, all are checked exceptions that must be enclosed in a try block or declared in a throws clause. There's also one runtime exception, RMISecurityException , a subclass of SecurityException .

Remote methods depend on many things that are not under your control: for example, the state of the network and other necessary services such as DNS. Therefore, any remote method can fail: there's no guarantee that the network won't be down when the method is called. Consequently, all remote methods must be declared to throw the generic RemoteException and all calls to remote methods should be wrapped in a try block. When you just want to get a program working, it's simplest to catch RemoteException :

 try {   // call remote methods... } catch (RemoteException ex) {   System.err.println(ex); } 

More robust programs should try to catch more specific exceptions and respond accordingly .

Table 18-1. Remote exceptions




A client tried to do something that only local objects are allowed to do.


The URL is already bound to another object.


The server refused the connection.


An I/O error occurred while trying to make the connection between the local and the remote host.


An I/O error occurred while attempting to marshal (serialize) arguments to a remote method. A corrupted I/O stream could cause this exception; making the remote method call again might be successful.


An I/O error occurred while attempting to unmarshal ( deserialize ) the value returned by a remote method. A corrupted I/O stream could cause this exception; making the remote method call again might be successful.


The object reference is invalid or obsolete. This might occur if the remote host becomes unreachable while the program is running, perhaps because of network congestion, system crash, or other malfunction.


The URL is not bound to an object. This might be thrown when you try to reference an object whose URL was rebound out from under it.


The generic superclass for all exceptions having to do with remote methods.


Despite the name, this is indeed an exception, not an error. It indicates that the server threw an error while executing the remote method.


A RemoteException was thrown while the remote method was executing.


The stub for a class could not be found. The stub file may be in the wrong directory on the server, there could be a namespace collision between the class that the stub substitutes for and some other class, or the client could have requested the wrong URL.


Something unforeseen happened . This is a catchall that occurs only in bizarre situations.


The host cannot be found. This is very similar to java.net.UnknownHostException .

The RemoteException class contains a single public field called detail :

 public Throwable detail 

This field may contain the actual exception thrown on the server side, so it gives you further information about what went wrong. For example:

 try {   // call remote methods... } catch (RemoteException ex) {   System.err.println(ex.detail);   ex.detail.printStackTrace( ); } 

In Java 1.4 and later, use the standard getCause() method to return the nested exception instead:

 try {   // call remote methods... } catch (RemoteException ex) {   System.err.println(ex.getCause( ));   ex.getCause( ).printStackTrace( ); } 

Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net