Check out the new USENIX Web site.
Using javaUSENIX

 

using java

Remote Method Invocation

rao_prithvi

by Prithvi Rao
<prithvi+@kiwilabs.com>

Prithvi Rao is the founder of Kiwilabs, which specializes in software engineering methodology and Java training. He has worked on the development of the MACH OS and a real-time version of MACH, and he holds two patents resulting from his work on mobile robots.



An aim of distributed systems is successful interaction among programs running in different address spaces. An earlier article in this series (;login:, October 1998) discussed RMI, Sun's way of allowing programs written entirely in Java to share information across address boundaries. RMI permits a Java object in one address space to invoke methods contained in a Java object that runs in a separate address space. This can happen in applications in which each object is a thread that is run in its own address space. Another way to run in separate address spaces is to run each Java program on a separate machine. An important feature of RMI is that a method invocation on a local object has the same syntax as that on a remote object.

This article presents a simple RMI example that walks the reader through the steps required to write such applications.

Summary of Java RMI

Remote invocation is nothing new. For instance, C programmers have used Remote Procedure Call (RPC) semantics to execute a C function on a remote host. What makes RMI different is that in Java it is necessary to package both data and methods and ship both across the network. (RPC works on data structures primarily.) This implies that the recipient must be able to interpret the object after receiving it.

RMI at a glance:

The Good

  1. It is very easy to use.
  2. Remoteable interfaces have a special exception.
  3. It supports object-by-value.
  4. Versioning is built into serialization.

The Bad

  1. Java call semantics are changed so that thread identity is not maintained.
  2. Callbacks are blocked in synchronized methods.
  3. It is not always intuitive.
  4. It is not available for use with other languages.

The Ugly

  1. There are limited development tools.
  2. Clients need access to latest stubs.
  3. Performance can be slow as you scale your application.

Although RMI does not directly support other languages, it is possible to use the Java Native Interface (JNI) to create Java wrappers that can be used with RMI. Of course this introduces yet another level of indirection and may further exacerbate performance problems that are due to scaling.

Java RMI Example

The following example demonstrates the use of RMI within applets, which is a typical use of Java and RMI. It is conceivable that an applet (which is a Java program running within the context of a browser such as Netscape, appletviewer, or HotJava) needs to invoke methods on objects that are on other machines. Consider a database application, for instance, with a GUI that is an applet and a data server that is multithreaded and written in Java. The GUI runs on a thin client, and the data server may run on another machine across a network. This is a fairly common scenario and so our example is not atypical.

There are really two Java Virtual Machines (JVMs) involved in this example. The first JVM is the one into which the applet is loaded. The other JVM is the one on which the remote object exists. Let's think of the first JVM (the one running the applet) as the local side. I have avoided the use of "client and server" because there is really no client-server relationship here. It really is a case of one JVM making another JVM do something. The client-server abstraction exists at the level of the application and not at the level of RMI. Another way to look at this is that one JVM is invoking methods on an object running on another JVM.

Let's look at the code for an applet. This is the local side. This file is called HelloApplet.java.

package example.hello;

This means that the Hello class is in a package examples.hello. Remember that this is interpreted as a directory relative to CLASSPATH.

import java.awt.*;

This is necessary because we are writing an applet, and applets are part of the Abstract Windowing Toolkit (AWT). (See my ;login: April 1999 article for an AWT example.)

import java.rmi.*;

This is new. java.rmi is a package that provides support for RMI, and so we must import it.

public class HelloApplet extends java.applet.Applet {

We are extending applet, except that because we did not import java.applet we must provide the fully qualified name (fqn) for the applet class we wish to extend.

String message = "";

Field for the message that will be received from the remote object.

public void init() {

The init method for the applet.

try {
 Hello obj = (Hello)
  Naming.Lookup("//" + getCodeBase().getHost() +
    "/HelloServer");

This creates an instance of the remote object inside the remote JVM. We will say more about this later. It is important to note that this method returns an instance of a class Hello. The argument it takes is a URL, or so it seems because of the '//'.

message = obj.sayHello();

This statement invokes the method sayHello() of the remote object.

} catch (Exception e) {
 System.out.println("HelloApplet: an exception occurred:");
 e.printStackTrace();

Naming.lookup throws three exceptions, so we must catch them.

}
public void paint(Graphics g) {
 g.drawString(message, 25, 50);
}
}

Draw the string that was received from the remote object on the screen.

Now let's take a look at the remote side.

There are two files associated with the remote side. The first is Hello.java.

package examples.hello;

Put this in the same package as the applet.

public interface Hello extends java.rmi.Remote {

Define an interface called Hello that extends java.rmi.Remote. We will explain this later. Basically, if an object wants to be a remote object—that is, it wants to be able to be invoked by some other object—then it must implement the interface java.rmi.Remote.

{
 String sayHello throws java.rmi.RemoteException;
}
}

It must also specify those methods that can be invoked remotely. In this case there is only one such method, sayHello. Recall that the applet HelloApplet calls a method by this name. This is the method that will be invoked. At this point it is merely a method of an interface and has only a signature but no body.

Now the second of the two files for the remote side. This is called HelloImpl.java.

import examples.hello.*;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

All servers must be subclasses of this if they want to be remote.

public class HelloImpl extends UnicastRemoteObject implements Hello {

This implements the interface Hello, and since Hello extends the Remote interface, this makes HelloImpl a class of type Remote.

private String name;

This is the name by which this object is known to the other objects that wish to invoke it.

public HelloImpl(String s) throws java.rmi.RemoteException {
super();
name = s;
}

The constructor for this class.

public String sayHello() throws RemoteException {
 return("Hello World");
}

Recall that the sayHello method is part of the interface Hello. Since this class implements that interface, it must provide a definition for it. The definition simply returns the string "Hello World."

public static void main(String args[])

This application's main function. Recall that applets do not need main.

System.setSecurityManager(new RMIsecurityManager());

Remember that applications can set their security manager (;login:, August 1998). The object that invokes methods on this JVM should not be allowed to roam free on the host machine on which this JVM is executing. The RMI security manager enforces a suitable security policy. Recall that the applet invoking a method on this object might send it data that might be bytecode that is capable of being executed. For this reason it is necessary to ensure the presence of the security manager.

try {
 HelloImpl obj = new HelloImpl("HelloServer");

Instantiate a HelloImpl and call it HelloServer. Recall that the applet used Naming.lookup().

Naming.rebind("HelloServer", obj);
System.out.println("HelloImpl created and bound in " +
        the registry to name HelloServer");

Register this object as existing and print out some diagnostics.

 } catch (Exception e) {
  System.out.println("HelloImpl.main: exception occurred:");
  e.printStackTrace();
 }
}
}

Running the Example

  1. Compile the applet and create an HTML page for it (;login:, April 1999). The applet runs on the local machine.

    > javac HelloApplet.java

  2. Compile the Java classes on the "remote" machine.

    > javac Hello.java

  3.  Make sure that this file is in a directory examples/hello relative to where the file HelloImpl.java exists.

    > javac HelloImpl.java

  4. Generate the stubs and skeletons on the remote machine using the rmi compiler (rmic).

    > rmic HelloImpl

    This creates two files called HelloImpl_Skel.class and HelloImpl_stub.class.

  5. Start the RMI registry on the server.

    > rmiregistry

    The registry is used to let the two Java objects locate each other and therefore establish contact. Notice that nowhere in any of this code is any direct reference to low-level networking interfaces such as sockets. This is transparent to the user. The registry runs on the remote machine.

    The important thing to remember is that the stub is sent from the remote object to the object that invoked it when the local object uses the lookup method of the class Naming.

  6. Start the applet.

    Start a browser and load the HTML page for this applet.

Conclusion

The use of distributed objects is fairly common in many IT application domains. Two examples are the health-care industry and the stock market. In most cases where distributed objects are used, it is necessary to create an infrastructure in which Java and non-Java objects can invoke methods on each other. In these cases Java RMI cannot be used without first writing some kind of Java "wrapper" for the non-Java code. These implementations therefore use CORBA or DCOM. If a pure Java application is envisaged, then Java RMI is a good choice for its ease of use and its ability to facilitate the rapid prototyping of the application.

In future articles we will demonstrate the capability of Java with other middleware packages. There is no substitute for being well informed in order to make intelligent decisions, and Java RMI is only one piece of the puzzle.


 

?Need help? Use our Contacts page.
Last changed: 16 Nov. 1999 mc
Using Java index
Publications index
USENIX home