Remote methods

Bruce Eckel’s Thinking in Java Contents | Prev | Next

Traditional
approaches to executing code on other machines across a network have been
confusing as well as tedious and error-prone to implement. The nicest way to
think about this problem is that some object happens to live on another
machine, and you can send a message to that object and get a result as if the
object lived on your local machine. This simplification is exactly what Java 1.1
Remote
Method Invocation

(RMI) allows you to do. This section walks you through the steps necessary to
create your own RMI objects.

Remote
interfaces

RMI
makes heavy use of interfaces. When you want to create a remote object, you
mask the underlying implementation by passing around an interface. Thus, when
the client gets a handle to a remote object, what they really get is an
interface handle, which
happens
to connect to some local stub code that talks across the network. But you
don’t think about this, you just send messages via your interface handle.

  1. The
    remote interface must be
    public
    (it cannot have “package access,” that is, it cannot be
    “friendly”). Otherwise, a client will get an error when attempting
    to load a remote object that implements the remote interface.
  2. The
    remote interface must extend the interface
    java.rmi.Remote.
  3. Each
    method in the remote interface must declare
    java.rmi.RemoteException
    in its
    throws
    clause in addition to any application-specific exceptions.
  4. A
    remote object passed as an argument or return value (either directly or
    embedded within a local object) must be declared as the remote interface, not
    the implementation class.
Here’s
a simple remote interface that represents an accurate time service:

//: PerfectTimeI.java
// The PerfectTime remote interface
package c15.ptime;
import java.rmi.*;
 
interface PerfectTimeI extends Remote {
  long getPerfectTime() throws RemoteException;
} ///:~ 

It
looks like any other interface except that it extends
Remote
and all of its methods throw
RemoteException.
Remember that an
interface
and all of its methods are automatically
public.

Implementing
the remote interface

You
must explicitly define the constructor for the remote object even if
you’re only defining a default constructor that calls the base-class
constructor. You must write it out since it must throw
RemoteException.

Here’s
the implementation of the remote interface
PerfectTimeI:

//: PerfectTime.java
// The implementation of the PerfectTime 
// remote object
package c15.ptime;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.net.*;
 
public class PerfectTime
    extends UnicastRemoteObject
    implements PerfectTimeI {
  // Implementation of the interface:
  public long getPerfectTime()
      throws RemoteException {
    return System.currentTimeMillis();
  }
  // Must implement constructor to throw
  // RemoteException:
  public PerfectTime() throws RemoteException {
    // super(); // Called automatically
  }
  // Registration for RMI serving:
  public static void main(String[] args) {
    System.setSecurityManager(
      new RMISecurityManager());
    try {
      PerfectTime pt = new PerfectTime();
      Naming.bind(
        "//colossus:2005/PerfectTime", pt);
      System.out.println("Ready to do time");
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~ 

Here,
main( )
handles all the details of setting up the server. When you’re serving RMI
objects, at some point in your program you must:

  1. Create
    and install a security manager that supports RMI. The only one available for
    RMI as part of the Java distribution is RMISecurityManager.
  2. Create
    one or more instances of a remote object. Here, you can see the creation of the
    PerfectTime
    object.
  3. Register
    at least one of the remote objects with the RMI
    remote
    object registry for bootstrapping purposes. One remote object can have methods
    that produce handles to other remote objects. This allows you to set it up so
    the client must go to the registry only once, to get the first remote object.


Setting
up the registry

start
rmiregistry

to
start it in the background. On Unix, it is:

rmiregistry
&

Like
many network programs, the
rmiregistry
is located at the IP address of whatever machine started it up, but it must
also be listening at a port. If you invoke the
rmiregistry
as above, with no argument, the registry’s port will default to 1099. If
you want it to be at some other port, you add an argument on the command line
to specify the port. For this example, the port will be located at 2005, so the
rmiregistry
should be started like this under 32-bit Windows:

start
rmiregistry 2005

or
for Unix:

rmiregistry
2005 &

  1. localhost
    does
    not work with RMI. Thus, to experiment with RMI on a single machine, you must
    provide the name of the machine. To find out the name of your machine under
    32-bit Windows, go to the control panel and select “Network.”
    Select the “Identification” tab, and you’ll see your computer
    name. In my case, I called my computer “Colossus” (for all the hard
    disks I’ve had to put on to hold all the different development systems).
    It appears that capitalization is ignored.
  2. RMI
    will not work unless your computer has an active
    TCP/IP
    connection, even if all your components are just talking to each other on the
    local machine. This means that you must connect to your Internet service
    provider before trying to run the program or you’ll get some obscure
    exception messages.
Will
all this in mind, the
bind( )
command becomes:

Naming.bind("//colossus:2005/PerfectTime",
pt);

If
you are using the default port 1099, you don’t need to specify a port, so
you could say:

Naming.bind("//colossus/PerfectTime",
pt);

In
a future release of the JDK (after 1.1) when the
localhost
bug is fixed, you will be able to perform local testing by leaving off the IP
address and using only the identifier:

Naming.bind("PerfectTime",
pt);

You
aren’t forced to start up
rmiregistry
as an external process. If you know that your application is the only one
that’s going to use the registry, you can start it up inside your program
with the line:

LocateRegistry.createRegistry(2005);

Creating
stubs and skeletons

rmic
c15.PTime.PerfectTime

You
don’t have to be in the directory containing
PerfectTime.class
when you execute this command, but the results will be placed in the current
directory.

When
rmic
runs successfully, you’ll have two new classes in the directory:

PerfectTime_Stub.class
PerfectTime_Skel.class

Using
the remote object

The
whole point of RMI is to make the use of remote objects simple. The only extra
thing that you must do in your client program is to look up and fetch the
remote interface from the server. From then on, it’s just regular Java
programming: sending messages to objects. Here’s the program that uses
PerfectTime:

//: DisplayPerfectTime.java
// Uses remote object PerfectTime
package c15.ptime;
import java.rmi.*;
import java.rmi.registry.*;
 
public class DisplayPerfectTime {
  public static void main(String[] args) {
    System.setSecurityManager(
      new RMISecurityManager());
    try {
      PerfectTimeI t =
        (PerfectTimeI)Naming.lookup(
          "//colossus:2005/PerfectTime");
      for(int i = 0; i < 10; i++)
        System.out.println("Perfect time = " +
          t.getPerfectTime());
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~ 

The
ID string is the same as the one used to register the object with
Naming,
and the first part represents the URL and port number. Since you’re using
a URL, you can also specify a machine on the Internet.

What
comes back from
Naming.lookup( )
must be cast to the remote interface,
not
to the class. If you use the class instead, you’ll get an exception.

You
can see in the method call

t.getPerfectTime( )

that
once you have a handle to the remote object, programming with it is
indistinguishable from programming with a local object (with one difference:
remote methods throw RemoteException).

Alternatives
to RMI

RMI
is just one way to create objects that can be distributed across a network. It
has the advantage of being a “pure Java” solution, but if you have
a lot of code written in some other language, it might not meet your needs. The
two most compelling alternatives are Microsoft’s
DCOM
(which, according to Microsoft’s plan, will eventually be hosted on
platforms other than Windows) and
CORBA,
which is supported in Java 1.1

and was designed from the start to be cross-platform. You can get an
introduction to distributed objects in Java (albeit with a clear bias towards
CORBA) in
Client/Server
Programming with Java and CORBA

by Orfali & Harkey (John Wiley & Sons, 1997). A more serious treatment
of CORBA is given by
Java
Programming with CORBA

by

Andreas
Vogel

and
Keith Duddy (John Wiley & Sons, 1997).


[69]
Many brain cells died in agony to discover this information.

More by Author

Must Read