Tutorial: creating a distributed application with Java RMI
Note: Only the Client/Server version of JBuilder ships with distributed applications features.
Remote Method Invocation (RMI) enables you to create
distributed Java-to-Java applications, in which the methods of remote
Java objects can be invoked from other Java virtual machines, possibly
on different hosts. A Java program can make a call on a remote object
once it obtains a reference to the remote object, either by looking up
the remote object in the bootstrap naming service provided by RMI or by
receiving the reference as an argument or a return value. A client can
call a remote object in a server, and that server can also be a client
of other remote objects. RMI uses object serialization to marshal and
unmarshal parameters and does not truncate types, supporting true
object-oriented polymorphism.
To learn more about Java RMI before completing this tutorial, select the following links of interest:
Caffeine is another way to to define interfaces in Java. The java2iiop
compiler allows you to stay in an all-Java environment, if that's what you want. The java2iiop
compiler takes your Java interfaces and generates IIOP-compliant stubs and skeletons. Part of the advantage of using the java2iiop
compiler is that, through the use of extensible structs, you can pass Java serialized objects by value. This feature is unique to VisiBroker for Java. For more information, please read Caffeine: defining CORBA interfaces with Java.
An example for writing a distributed application using RMI and DataSetData may be found in the jbuilder\samples\borland\samples\tutorial\dataset\ directory of your JBuilder installation. This example includes a server application that will take data from the employee sample table and send the data via RMI in the form of DataSetData. A client application will communicate with the server through a custom Provider and a custom Resolver, and simply display the data in a grid.
This chapter shows you the steps to follow to create a distributed version of
the classic "Hello World" program using Java Remote Method Invocation
(RMI). The completed example is located in the jbuilder/doc/java/guide/rmi/examples/hello directory of your JBuilder installation. Other examples using RMI are located in the jbuilder/doc/java/guide/rmi/examples directory.
The distributed "Hello World" example uses an applet to make a remote method
call to the server from which it was downloaded to retrieve the message "Hello
World!". When the applet runs, the message is displayed on the client.
To accomplish this, complete these three lessons:
There are four source files for the "Hello World" server and applet:
- The Java remote interface
- The Java remote object (server) which implements the remote interface
- The Java applet that remotely invokes the server's method
- The HTML code for the Web page that references the applet
Because the Java language requires a mapping between the fully qualified
package name of a class file and the directory path to that class, before you
begin writing Java code you need to decide on package and directory names.
(This mapping allows the Java compiler to know the directory in which to find
the class files mentioned in a Java program.) For the "Hello World" program
developed in this chapter, the package name is examples.hello and the root
directory is $HOME/myprojects/examples/hello. In this case $HOME points to the location of the JBuilder directory, for example, you might enter C:/JBuilder/myclasses/examples/hello.
To do this in JBuilder, create a new project to hold the application, and then set its properties. To create a new project,
- Select File|New Project from the menu. Change the directory and file name from untitledx\untitledx.jpr to examples.hello\hello. Click Finish.
This step creates the development directory and package and provides a container for adding the other pieces of the application.
The following topics in this section create the applicable interfaces and implementations. To skip this topic, you can add the files from the jbuilder/doc/java/guide/rmi/examples/hello directory. To work from existing files,
- Click the + sign in the folder above the Navigation pane (the Add To Project button).
- Choose File|Open/Create, click the File tab. Browse to the jbuilder/doc/java/guide/rmi/examples/hello directory. Select the file Hello.java, and, while holding down the Shift key, press the down arrow to also select HelloApplet.java, and HelloImpl.java. Make sure that the Add To Project option is checked. Click Open. The file names now appear in the Navigation pane.
- You can view the code in the files by selecting the file in the Navigation pane and selecting the Source tab.
- You can now skip to the Compile and deploy class files and HTML files topic.
Remote method invocations can fail in very different ways from local method
invocations, due to network related communication problems and server
problems. To indicate that it is a remote object, an object implements a remote
interface, which has the following characteristics:
- The remote interface must be public. Otherwise, a client will get an error
when attempting to load a remote object that implements the remote
interface.
- The remote interface extends the interface java.rmi.Remote.
- Each method must declare java.rmi.RemoteException in its throws
clause, in addition to any application-specific exceptions.
- 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.
To create the interface definition for "Hello World",
- Select File|Open/Create. In the File Name field, enter Hello.java.
- Check the Add To Project option.
- Click Open. The file Hello.java displays in the Navigation pane.
- Select the file Hello.java in the Navigation pane. Select the Source tab.
- Enter the following code in the Source window:
The interface contains just one
method, sayHello, which returns a string to the caller:
package examples.hello;
public interface Hello extends java.rmi.Remote {
String sayHello() throws java.rmi.RemoteException;
}
To write a remote object, you write a class that implements one or more remote
interfaces. The implementation class needs to
- Specify the remote interface(s) being implemented.
- Define the constructor for the remote object.
- Provide implementations for the methods that can be invoked remotely.
- Create and install a security manager.
- Create one or more instances of a remote object.
- Register at least one of the remote objects with the RMI remote object
registry, for bootstrapping purposes.
To create the file HelloImpl.java, which contains
the code for the "Hello World" server, follow these steps. Following the code is an explanation of
each of the preceding six steps.
- Select File|Open/Create. In the File Name field, enter HelloImpl.java.
- Check the Add To Project option.
- Click Open. The file HelloImpl.java displays in the Navigation pane.
- Select the file HelloImpl.java in the Navigation pane. Select the Source tab.
- Enter the following code in the Source window:
package examples.hello;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl
extends UnicastRemoteObject
implements Hello
{
private String name;
public HelloImpl(String s) throws RemoteException {
super();
name = s;
}
public String sayHello() throws RemoteException {
return "Hello World!";
}
public static void main(String args[])
{
// Create and install a security manager
System.setSecurityManager(new RMISecurityManager());
try {
HelloImpl obj = new HelloImpl("HelloServer");
Naming.rebind("//myhost/HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
The implementation class for the "Hello World" example is HelloImpl. An
implementation class specifies the remote interface(s) it is implementing.
Optionally, it can indicate the remote server that it is extending, which in this
example is java.rmi.server.UnicastRemoteObject. Here is the
HelloImpl class declaration:
public class HelloImpl
implements Hello
extends java.rmi.server.UnicastRemoteObject
Extending UnicastRemoteObject indicates that the HelloImpl class is used
to create a single (nonreplicated) remote object that uses RMI's default sockets-based transport for communication. If you choose to extend a remote object
from a nonremote class, you need to explicitly export the remote object by
calling the method UnicastRemoteObject.exportObject.
The constructor for a remote class is no different than the constructor for a
nonremote class: it initializes the variables of each newly created instance of
the class.
Here is the constructor for the HelloImpl class, which initializes the private
string variable name with the name of the remote object:
private String name;
public HelloImpl(String s) throws java.rmi.RemoteException {
super();
name = s;
}
Note the following:
- The super method call invokes the no-arg constructor of
java.rmi.server.UnicastRemoteObject, which "exports" the remote
object by listening for incoming calls to the remote object on an anonymous
port.
- The constructor must throw java.rmi.RemoteException, because RMI's
attempt to export a remote object during construction might fail if
communication resources are not available.
Although the call to the super no-arg constructor occurs by default if omitted,
it is included in this example to make clear the fact that Java constructs the
superclass before the class.
The implementation class for a remote object contains the code that
implements each of the remote methods specified in the remote interface.
For example, here is the implementation for the sayHello method, which
returns the string Hello World!
to the caller.
public String sayHello() throws RemoteException {
return "Hello World!";
}
Arguments to, or return values from, remote methods can be of any Java type,
including objects, as long as those objects implement the interface
java.io.Serializable. Most of the core Java classes in java.lang and
java.util implement the Serializable interface.
- Local objects are passed by copy, and only the nonstatic and nontransient
fields are copied by default.
- Remote objects are passed by reference. A reference to a remote object is
actually a reference to a stub, which is a client-side proxy for the remote
object. Stubs are described fully in Compile the Java source files.
A class can define methods not specified in the remote interface, but
those methods can only be invoked within the virtual machine running the
service and cannot be invoked remotely.
The main method of the service first needs to create and install a security
manager: either the RMISecurityManager or one that you have defined
yourself. For example:
System.setSecurityManager(new RMISecurityManager());
A security manager needs to be running so that it can guarantee that the
classes loaded do not perform "sensitive" operations. If no security manager is
specified, no class loading for RMI classes, local or otherwise, is allowed.
The main method of the service needs to create one or more instances of the
remote object which provides the service. For example:
HelloImpl obj = new HelloImpl("HelloServer");
The constructor exports the remote object, which means that once created, the
remote object is ready to begin listening for incoming calls.
For a caller (client, peer, or applet) to be able to invoke a method on a remote
object, that caller must first obtain a reference to the remote object. Most of the
time, the reference will be obtained as a parameter to, or a return value from,
another remote method call.
For bootstrapping, the RMI system also provides a URL-based registry that
allows you to bind a URL of the form //host/objectname
to the remote
object, where objectname is a simple string name. Once a remote object is
registered on the server, callers can look up the object by name, obtain a remote
object reference, and then remotely invoke methods on the object.
For example, the following code binds the URL of the remote object named
HelloServer to a reference for the remote object:
Naming.rebind("//myhost/HelloServer", obj);
Note the following about the arguments to the call:
- The host defaults to the current host if omitted from the URL, and no
protocol needs to be specified in the URL.
- The RMI runtime substitutes a reference to the remote object's stub for the
actual remote object reference specified by the obj argument. Remote
implementation objects like instances of HelloImpl never leave the virtual
machine where they are created, so when a client performs a lookup in a
server's remote object registry, a reference to the stub is returned.
- Optionally, a port number can be supplied in the URL: for example
//myhost:1234/HelloServer. The port defaults to 1099. It is necessary to
specify the port number only if a server creates a registry on a port other
than the default 1099.
For security reasons, an application can bind or unbind only in the
registry running on the same host. This prevents a client from removing or
overwriting any of the entries in a server's remote registry. A lookup, however,
can be done from any host.
The applet part of the distributed "Hello World" example remotely invokes the
HelloServer's sayHello method in order to get the string "Hello World!",
which is displayed when the applet runs. To create the applet,
- Select File|Open/Create. In the File Name field, enter HelloApplet.java.
- Check the Add To Project option.
- Click Open. The file HelloApplet.java appears in the Navigation pane.
- Select the file HelloApplet.java in the Navigation pane. Select the Source tab.
- Enter the following code in the Source window:
package examples.hello;
import java.awt.*;
import java.rmi.*;
public class HelloApplet extends java.applet.Applet {
String message = "";
public void init() {
try {
Hello obj = (Hello)Naming.lookup("//" +
getCodeBase().getHost() + "/HelloServer");
message = obj.sayHello();
} catch (Exception e) {
System.out.println("HelloApplet exception: " +
e.getMessage());
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawString(message, 25, 50);
}
}
The applet does the following:
- First gets a reference to the "HelloServer" from the server's
registry, constructing the URL by using the getCodeBase method in
conjunction with the getHost method.
- Remotely invokes the sayHello method of the HelloServer
remote object and stores the return value from the call (the string "Hello
World!") in a variable named message.
- Invokes the paint method to draw the applet on the display,
causing the string "Hello World!" to be displayed.
The constructed URL must include the host. Otherwise, the applet's
lookup will default to the client, and the AppletSecurityManager will
throw an exception since the applet cannot access the local system, but is
instead limited to communicating only with the applet host.
To write the Web page that contains the applet,
- Select the file hello.html in the Navigation pane.
- In the Source pane, enter the following HTML code for the Web page that references the "Hello World"
applet:
<HTML>
<title>Hello World</title>
<center> <h1>Hello World</h1> </center>
The message from the HelloServer is:
<p>
<applet codebase="../.."
code="examples.hello.HelloApplet"
width=500 height=120>
</applet>
</HTML>
Note the following:
- There needs to be an HTTP server running on the machine from which you
want to download classes. The applet's codebase attribute indicates the
URL, as shown here:
codebase="../.."
The codebase in this example specifies a directory two levels above the
directory from which the Web page was itself loaded. Using this kind of
relative path is usually a good idea.
- The applet's code attribute specifies the fully package-qualified name of the
applet, in this example examples.hello.HelloApplet:
code="examples.hello.HelloApplet"
The source code for the "Hello World" example is now complete and the
$HOME/myclasses/examples.hello/hello directory has four files:
- Hello.java, which contains the source code for the Hello remote interface.
- HelloImpl.java, which is the source code for the HelloImpl remote object
implementation, the server for the "Hello World" applet.
- HelloApplet.java, which is the source code for the applet.
- hello.html, which is the Web page that references the "Hello World" applet.
In this section, you compile the .java source files to create .class files and create stubs and skeletons. A stub is a client-side proxy for a remote object, which forwards RMI calls to the server-side
skeleton, which in turn forwards the call to the actual remote object
implementation.
Some Web servers allow accessing a user's public_html directory via an
HTTP URL constructed as "http://host/~username/". If your Web server
does not support this convention, you may use a file URL of the form
"file://home/username/public_html".
To compile the Java source files,
- Right-click on HelloImpl.java in the Navigation pane. Select Java Source Properties from the context menu. Check the Generate RMI stub/skeleton field. Click OK.
- Select Build|Make from the menu.
These steps create the directory examples/hello (if it does not already
exist) in the directory $HOME/myclasses/. The command then
writes to that directory the files Hello.class, HelloImpl.class, and
HelloApplet.class. These are the remote interface, the server, and the
applet respectively.
It also creates stub and skeleton files
in the
directory $HOME/myclasses/examples/hello:
- HelloImpl_Stub.class
- HelloImpl_Skel.class
Note that the generated stub implements exactly the same set of remote
interfaces as the remote object itself. This means that a client can use the Java
language's built-in operators for casting and type checking. It also means that
Java remote objects support true object-oriented polymorphism.
To make the Web page that references the applet visible to clients, the
hello.html file must be moved from the development directory to the
codebase directory. Use Windows Explorer to move the file from $HOME/myprojects/examples/hello/hello.html to $HOME/myclasses/examples/hello
The RMI registry is a simple server-side bootstrap name server that allows
remote clients to get a reference to a remote object. It is typically used only to
locate the first remote object an application needs to talk to. That object in turn
will provide application specific support for finding other objects.
To start the registry on the server, select Tools|RMIRegistry. This
command produces no output and is typically run in the background. The check to the left of the option shows that the RMIRegistry is running.
You must stop and restart the registry any time you modify a remote
interface or use modified/additional remote interfaces in a remote object
implementation. Otherwise, the class bound in the registry will not match the
modified class.
When starting the server, the java.rmi.server.codebase property must be
specified, so that references to the remote objects created by the server can
include the URL from which the stub class can be dynamically downloaded to
the client.
To start the HelloImpl server,
- Right-click on the project file, hello.jpr. Select Properties. Select the Run/Debug tab.
- In the Command Line Parameters box, enter:
-Djava.rmi.server.codebase=http://myhost/~myusrname/myclasses/
The trailing /
in the codebase
URL must be specified. myhost should be replaced with a valid Web server. ~myusrname/myclasses/ should specify the location on the server.
A stub class is dynamically loaded into a client's virtual machine only when
the class is not already available locally.
Once the registry and server are running, the applet can be run. An applet is
run by loading its Web page into a browser or appletviewer. To view the application as an applet, right-click on hello.html and select Run.
After running the appletviewer, you will see output similar to the following on
your display:

This completes the tutorial.
For updates to this document, see http://www.javasoft.com/products/jdk/1.1/docs/guide/rmi/getstart.doc.html.