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.*;An SQLImpl class that implemented the
public interface SQL extends Remote {
public String[] SQLQuery(String query) throws RemoteException;
}
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.
18.4.2.1 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.*;Here's a result from a run against the RMI server I
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);
}
}
}
was using to test the examples in this chapter:
% java RegistryLister login.ibiblio.orgYou can see that the format for the strings is full
rmi://login.ibiblio.org:1099/fibonacci
rmi://login.ibiblio.org:1099/hello
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
18.4.2.2 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.
18.4.2.3 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.
18.4.2.4 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().
18.4.2.5 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 SecurityManagerIn 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 andrespond accordingly.
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. A corrupted I/O stream could cause this exception; making the remote method call again might be successful. |
UnmarshalException | 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. |
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 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 | Despite the name, this is indeed an exception, not an error. It indicates that the server threw an error while executing the remote method. |
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, or the client could have requested the wrong URL. |
UnexpectedException | Something unforeseen happened. This is a catchall that occurs only in bizarre situations. |
UnknownHostException | The host cannot be found. This is very similar to java.net.UnknownHostException. |
field called detail:
public Throwable detailThis 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( );
}
• Table of Contents• Index• Reviews• Reader Reviews• Errata• AcademicJava Network Programming, 3rd EditionBy
Elliotte Rusty Harold Publisher: O'ReillyPub Date: October 2004ISBN: 0-596-00721-3Pages: 706
Thoroughly revised to cover all the 100+ significant updates
to Java Developers Kit (JDK) 1.5, Java Network
Programming is a complete introduction to
developing network programs (both applets and applications)
using Java, covering everything from networking fundamentals
to remote method invocation (RMI). It includes chapters on
TCP and UDP sockets, multicasting protocol and content
handlers, servlets, and the new I/O API. This is the
essential resource for any serious Java developer.