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 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 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.
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.
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.
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
.
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( )
.
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( )
.
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.
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( ));
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.
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 |
---|---|---|---|
|
Can a stub create a |
No |
No |
checkAwtEventQueueAccess |
Can a stub retrieve events from or post events to the AWT event queue? |
No |
No |
|
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. |
|
Can a stub manipulate threads outside its own thread group? |
No |
No |
|
Can a stub force the virtual machine to exit? i.e., can it call
|
No |
No |
|
Can a stub execute system processes? i.e., can it call
|
No |
No |
|
Can a stub link to dynamic libraries? |
No |
No |
|
Can a stub read the properties of the local machine? |
No |
No |
|
Can a stub check a specific property? |
No |
No |
|
Can a stub read a file? |
No |
No |
|
Can a stub write to a file? |
No |
No |
|
Can a stub listen for connections on a port? |
No |
No |
|
Can a stub accept connections on a port? |
No |
No |
|
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. |
|
Can a stub send a datagram to the specified address with the specified TTL? |
Yes |
No |
|
Can the stub create a new window? |
No |
No |
|
Can a stub access the specified package? |
Varies depending on package. |
Varies depending on package. |
|
Can a stub define classes in the specified package? |
Yes, except in the |
Yes, except in the |
|
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.
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 |
---|---|
|
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. This exception could be caused by a corrupted I/O stream, and making the remote method call again may be successful. |
|
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. |
|
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. |
|
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. |
|
An error (that is, an instance of a subclass of
|
|
A |
|
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. |
|
Something unforeseen happened. This is a catchall that should occur only in bizarre situations. |
|
The host cannot be found. This is very similar to
|
|
An object tried to do something that is prohibited by the
stub’s |
|
An unchecked, uncaught runtime exception occurred on the server, such
as |
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( ); }
18.118.126.241