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 what’s present in this package, java.rmi is all that’s needed by clients. This package contains one interface, three classes, and a handful of exceptions.

The Remote Interface

The Remote interface tags objects as being 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.9 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-9. 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.

The Naming Class

The java.rmi.Naming class talks to a registry running on the server in order to map URLs like rmi://metalab.unc.edu/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.

Naming is the only means of communicating with the registry included with RMI at this time; others may be included in the future or may be provided by other vendors. The biggest deficiency of Naming is that it requires the client to know the server on which the remote object lives; you might also complain that Naming implements a flat (i.e., nonhierarchical) namespace. As you’ve seen, an rmi URL looks exactly like an http URL except that the protocol field is rmi instead of http. Furthermore, the file part of the URL is an arbitrary name that the server has bound to a particular remote object, not a filename.

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 all the URLs that are currently bound as an array of strings. The url argument is the URL of the Naming registry to query. Only the protocol, host, and port are used. The file 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.10 is a simple program to list all the names currently bound in a particular registry. It’s sometimes useful when debugging RMI programs. It allows you to determine whether the names you’re using are the names the server expects.

Example 18-10. 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 e) {}
    
    }
  
    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 e) {
      System.err.println(e);
    }
    catch (java.net.MalformedURLException e) {
      System.err.println(e);
    }
  }
}

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

% java RegistryLister login.metalab.unc.edu
rmi://login.metalab.unc.edu:1099/fibonacci
rmi://login.metalab.unc.edu:1099/hello

You can see that the format for the strings is full rmi URLs rather than just names.

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://metalab.unc.edu:2001/myRemoteObject, it would return the object bound to myRemoteObject from metalab.unc.edu on port 2,001.

This method throws a NotBoundException if the name is not recognized by the remote server. A RemoteException is thrown 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, a MalformedURLException is thrown.

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, then clients will be able to retrieve the remote object stub from the registry using a URL like rmi://metalab.unc.edu:2001/myRemoteObject.

Many things can go wrong with the binding process. bind( ) throws a MalformedURLException if url is not a valid rmi URL. A RemoteException is thrown if the registry cannot be reached. An AccessException, a subclass of RemoteException, is thrown if the client is not allowed to bind objects in this registry. If the URL is already bound to a local object, bind( ) 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 is 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 exact same meanings as they do when thrown by bind( ).

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) and allows the necessary network communications between the client, the server, and the codebase. In Java 1.2 and later, the RMISecruity Manager doesn’t allow even that, and this class is so restrictive, it’s essentially useless.

public RMISecurityManager( )

RMISecurityManager has a single constructor that takes no arguments. To set the security manager, use the static System.setSecurityManager( ) method. Most often, you create a new SecurityManager directly inside this method. For example:

System.setSecurityManager(new RMISecurityManager(  ));

public Object getSecurityContext( )

The getSecurityContext( ) method determines the environment for certain operations. An RMISecurityManager allows more operations by a class loaded from a local host than from a network host. Eventually (though not as of this writing), public key signatures will be used to grant different classes different trust levels.

Checking operations

There are 23 methods that check various operations to see whether they’re allowed. All are public synchronized, and void, except for checkTopLevelWindow( ), which returns a boolean. Each one throws a StubSecurityException if the action is forbidden; if the action is allowed, the method just returns. These methods check only actions perfomed by stubs, not by other classes in the application. Since only one SecurityManager can be installed in an application, you may have to replace this class with a class of your own if you want to check both actions performed by stubs and actions performed by other classes such as applets.

Table 18.1 lists the methods of the RMISecurityManager, what they check for, and the circumstances under which the operation will be allowed for Java 1.1. (Java 1.2 and later uses a completely different setup that doesn’t allow remote stubs to do anything interesting.) It is unlikely that you will need to call these methods yourself. However, knowledge of these methods is required to understand what a stub can and cannot do.

Table 18-1. RMISecurityManager Permissions in Java 1.1

Method

Checks For

If Stub is Local

If Stub is Remote

checkCreateClass Loader( )

Can a stub create a ClassLoader?

No

No

checkAwtEventQueueAccess( )

Can a stub retrieve events from or post events to the AWT event queue?

No

No

checkMemberAccess(Class c, int which)

Can a stub use the Reflection API to access members of other classes?

Only of classes loaded by the same class loader.

Only of classes loaded by the same class loader.

checkAccess (Thread t)checkAccess (ThreadGroup g)

Can a stub manipulate threads outside its own thread group?

No

No

checkExit(int status)

Can a stub force the virtual machine to exit? i.e., can it call System.exit( )?

No

No

checkExec(String cmd)

Can a stub execute system processes? i.e., can it call System.exec( )?

No

No

checkLink(String lib)

Can a stub link to dynamic libraries?

No

No

checkProperties Access( )

Can a stub read the properties of the local machine?

No

No

checkProperty Access(String key)

Can a stub check a specific property?

No

No

checkRead(String file)checkRead(String file, Object context)checkRead(File Descriptor fd)

Can a stub read a file?

No

No

checkWrite (String file)checkWrite(File Descriptor fd)

Can a stub write to a file?

No

No

checkListen(int port)

Can a stub listen for connections on a port?

No

No

checkAccept (String host, int port)

Can a stub accept connections on a port?

No

No

checkConnect (String host, int port)checkConnect (String host, int port, Object context)

Can a stub open a connection to this host on this port?

Yes

Yes, if the host is the one from which the stub was downloaded; otherwise, no.

checkMulticast (InetAddress address)checkMulticast (InetAddress address, byte ttl)

Can a stub send a datagram to the specified address with the specified TTL?

Yes

No

checkTopLevelWindow(Object window)

Can the stub create a new window?

No

No

checkPackage Access(String pkg)

Can a stub access the specified package?

Varies depending on package.

Varies depending on package.

checkPackageDefinition(String pkg)

Can a stub define classes in the specified package?

Yes, except in the java and sun packages.

Yes, except in the java and sun packages.

checkSetFactory( )

Can a stub set a network factory?

No.

No.

Except for checkPackageAccess( ), these methods have all been removed from the RMISecurityManager class in Java 1.2 This doesn’t break any existing code, however, because RMISecurityManager still inherits them from its SecurityManager superclass.

Remote Exceptions

The java.rmi package defines 16 exceptions, listed in Table 18.2. All except RemoteException extend java.rmi.RemoteException; java.rmi.Remote Exception extends java.lang.Exception. Thus, all are checked exceptions that must be enclosed in a try block or declared in a throws clause.

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 e) {
  System.err.println(e);
}

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

Table 18-2. Remote Exceptions

Exception

Meaning

AccessException

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

AlreadyBoundException

The URL is already bound to another object.

ConnectException

The server refused the connection.

ConnectIOException

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

MarshalException

An I/O error occurred while attempting to marshal (serialize) arguments to a remote method. This exception could be caused by a corrupted I/O stream, and making the remote method call again may be successful.

UnmarshalException

An I/O error occurred while attempting to unmarshal (deserialize) the value returned by a remote method. This exception could be caused by a corrupted I/O stream, and making the remote method call again may be successful.

NoSuchObjectException

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 some other malfunction.

NotBoundException

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.

RemoteException

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

ServerError

An error (that is, an instance of a subclass of java.lang.Error) was thrown while the remote method was executing.

ServerException

A RemoteException was thrown while the remote method was executing.

StubNotFoundException

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. The client could have requested the wrong URL.

UnexpectedException

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

UnknownHostException

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

RMISecurityException

An object tried to do something that is prohibited by the stub’s SecurityManager. This class is deprecated and no longer used starting in Java 1.2.

ServerRuntimeException

An unchecked, uncaught runtime exception occurred on the server, such as ArrayIndexOutOfBoundsException. This class is deprecated and no longer used starting in Java 1.2.

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 may give you further information about what went wrong. For example:

try {
  // call remote methods...
}
catch (RemoteException e) {
  System.err.println(e.detail);
  e.detail.printStackTrace(  );
}
..................Content has been hidden....................

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