This tutorial shows you the steps to follow to create a distributed
version of the classic Hello World program using JavaTM Remote Method Invocation (RMI). While you work
through this example, you will probably come up with a number of
related questions. You may find the answers in the RMI FAQ, or you may wish to look in the email
archives of the rmi-users alias. If you'd like to subscribe to
the rmi-users email alias, click here.
The distributed Hello World example uses an applet to make a remote
method call to an RMI server, running on the host from which the applet
was downloaded. When the applet runs, "Hello World!" is displayed on
the client browser.
HelloImpl.java - a
remote object implementation that implements
examples.hello.Hello
HelloApplet.java - an
applet that invokes the remote method, sayHello
hello.html - the HTML
page that references the applet
Note: For the remainder of this tutorial, the terms
"remote object implementation," "object implementation," and
"implementation" may be used interchangeably to refer to the class,examples.hello.HelloImpl, which implements a remote
interface.
For all of the source code used in this tutorial, you may choose from the following formats:
Because the JavaTM programming language requires a mapping between the
fully-qualified package name of a class and the directory path to that
class, you should decide on package and directory names before you
begin writing any code written in the Java programming language. This
mapping allows the compiler for the Java programming language to know
the directory in which to find the class files mentioned in a program.
For the programs in this tutorial, the package name is
examples.hello and the source directory is
$HOME/mysrc/examples/hello.
To create the directory for your source files in the
SolarisTM operating environment,
execute the command:
mkdir -p $HOME/mysrc/examples/hello
On Microsoft Windows platforms, you would go to the directory of your
choice, and type:
Define the functions of the remote
class as an interface written in the Java programming language
In the Java programming language, a remote object is an instance of a
class that implements a Remote interface. Your remote
interface will declare each of the methods that you would like to call
from other JavaTM virtual machines*
(JVMs). Remote interfaces have the following characteristics:
The remote interface must be declared public.
Otherwise, a client will get an error when attempting to load a
remote object that implements the remote interface, unless that
client is in the same package as the remote interface.
The remote interface extends the java.rmi.Remote
interface.
Each method must declare java.rmi.RemoteException
(or a superclass of RemoteException) in its
throws clause, in addition to any
application-specific exceptions.
The data type of any remote object that is passed as an
argument or return value (either directly or embedded within a
local object) must be declared as the remote interface
type (for example, Hello) not the
implementation class (HelloImpl).
Here is the interface definition for the remote interface,
examples.hello.Hello. The interface contains just one
method, sayHello, which returns a string to the caller:
Because remote method invocations can fail in very different ways from
local method invocations (due to network-related communication problems
and server problems), remote methods will report communication failures
by throwing a java.rmi.RemoteException. If you want
more information on failure and recovery in distributed systems, you
may wish to read A Note on
Distributed Computing.
A "server" class, in this context, is the class which has a
main method that creates an instance of the remote object
implementation, and binds that instance to a name in the
rmiregistry. The class that contains this
main method could be the implementation class itself, or
another class entirely.
In this example, the main method is part of
examples.hello.HelloImpl. The server program needs to:
An explanation of each of the preceding six steps follows the source
for HelloImpl.java:
package examples.hello;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException {
super();
}
public String sayHello() {
return "Hello World!";
}
public static void main(String args[]) {
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
HelloImpl obj = new HelloImpl();
// Bind this object instance to the name "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();
}
}
}
Implement a remote interface
In the Java programming language, when a class declares that it
implements an interface, a contract is formed between the class and the
compiler. By entering into this contract, the class is promising that
it will provide method bodies, or definitions, for each of the method
signatures declared in that interface. Interface methods are implicitly
public and abstract, so if the implementation
class doesn't fulfill its contract, it becomes by definition an
abstract class, and the compiler will point out this fact
if the class was not declared abstract.
The implementation class in this example is
examples.hello.HelloImpl. The implementation class
declares which remote interface(s) it is implementing. Here is the
HelloImpl class declaration:
public class HelloImpl extends UnicastRemoteObject
implements Hello {
As a convenience, the implementation class can extend a remote class,
which in this example is
java.rmi.server.UnicastRemoteObject. By extending
UnicastRemoteObject, the HelloImpl class can
be used to create a remote object that:
Uses RMI's default sockets-based transport for communication
Runs all the time
If you want a remote object that can be activated (created) when a
client requests it, rather than running all the time, after you finish
this tutorial, you can take a look at the RMI Activation tutorial. Also, you
can learn about how to use your own communication protocol, rather than
the TCP sockets that RMI uses by default, in the tutorial on Creating a Custom RMI socket
factory.
Define the constructor for the remote
object
The constructor for a remote class provides the same functionality as
the constructor for a non-remote class: it initializes the variables of
each newly created instance of the class, and returns an instance of
the class to the program which called the constructor.
In addition, the remote object instance will need to be "exported".
Exporting a remote object makes it available to accept incoming remote
method requests, by listening for incoming calls to the remote object
on an anonymous port. When you extend
java.rmi.server.UnicastRemoteObject or
java.rmi.activation.Activatable, your class will be
exported automatically upon creation.
If you choose to extend a remote object from any class other
than UnicastRemoteObject or Activatable,
you will need to explicitly export the remote object by calling either
the UnicastRemoteObject.exportObject method or the
Activatable.exportObject method from your class's
constructor (or another initialization method, as appropriate).
Because the object export could potentially throw a
java.rmi.RemoteException, you must define a
constructor that throws a RemoteException, even if the
constructor does nothing else. If you forget the constructor,
javac will produce the following error message:
HelloImpl.java:13: Exception java.rmi.RemoteException must be caught, or it must be
declared in the throws clause of this method.
super();
^
1 error
To review: The implementation class for a remote object needs
to:
Implement a remote interface
Export the object so that it can accept incoming remote
method calls
Declare its constructor(s) to throw at least a
java.rmi.RemoteException
Here is the constructor for the examples.hello.HelloImpl
class:
public HelloImpl() throws RemoteException {
super();
}
Note the following:
The super method call invokes the no-argument
constructor of
java.rmi.server.UnicastRemoteObject, which exports
the remote object.
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.
If you are interested in why java.rmi.RemoteException
is a checked exception rather than runtime exception, please refer to
the archives of the rmi-users email list:
Although the call to the superclass's no-argument constructor,
super(), occurs by default (even if omitted), it is
included in this example to make clear the fact that the Java virtual
machine (JVM) constructs the superclass before the class.
Provide an implementation for each remote method
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 any data
type for the Java platform, including objects, as long as those objects
implement the interface java.io.Serializable. Most of the
core classes in java.lang and java.util
implement the Serializable interface. In RMI:
By default, local objects are passed by copy, which means that
all data members (or fields) of an object are copied, except
those marked as static or transient.
Please refer to the Java Object
Serialization Specification for information on how to alter
the default serialization behavior.
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.
Create and install a security manager
The main method of the server first needs to create and
install a security manager: either the RMISecurityManager
or one that you have defined yourself. For example:
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
A security manager needs to be running so that it can guarantee that
the classes that get loaded do not perform operations that they are not
allowed to perform. If no security manager is specified no class
loading, by RMI clients or servers, is allowed, aside from what can be
found in the local CLASSPATH.
In this example, a security manager is not installed in the
client code because applets use the security manager already
installed in the client browser. If the client were an
application rather than an applet, however, you would need to use the
same procedure as is used above to install a security manager in the
client. A security manager is required in any JVM that needs to
download code, and RMI clients need to download RMI stubs (as well as
any other custom classes or interfaces needed to communicate with the
RMI server).
Create one or more instances of a remote object
The main method of the server needs to create one or more
instances of the remote object implementation which provides the
service. For example:
HelloImpl obj = new HelloImpl();
The constructor exports the remote object, which means that once created,
the remote object is ready to accept incoming calls.
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 name "HelloServer" to a reference
for the remote object:
Naming.rebind("//myhost/HelloServer", obj);
Note the following about the arguments to the rebind method call:
The first parameter is a URL-formatted
java.lang.String, representing the location and
name of the remote object.
No protocol needs to be specified in the URL-formatted string.
You will need to change the value of
myhost to be the name or IP address of
your server machine; otherwise, the remote object host
defaults to the current host. For example,
"HelloServer" is a valid name string that
refers to a remote object bound to the name
HelloServer, running on the local host.
Optionally, a port number can be supplied in the
URL-formatted string. Specifying the port number is
necessary when the registry that needs to be contacted
is running on a port other than the default port,
1099.
For example, "//myhost:1234/HelloServer"
is a valid name string for the HelloServer
remote object, reachable through an RMI registry that
is running on the host myhost and is
listening for incoming calls on port 1234.
The second parameter is a reference to the object
implementation, on which remote methods will be invoked.
Once an object is exported, the RMI runtime substitutes a
reference to the remote object's stub for the actual remote
object reference specified by the obj argument.
When a client performs a lookup in a server's remote object
registry, a serialized instance of the stub for the
implementation is returned.
For security reasons, an application can bind or unbind only to a
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.
Write a client program that uses the remote service
The applet in this example remotely invokes the
sayHello method in order to get the string "Hello World!"
to display when the applet runs. Here is the code for the applet:
package examples.hello;
import java.applet.Applet;
import java.awt.Graphics;
import java.rmi.Naming;
import java.rmi.RemoteException;
public class HelloApplet extends Applet {
String message = "blank";
// "obj" is the identifier that we'll use to refer
// to the remote object that implements the "Hello"
// interface
Hello obj = null;
public void init() {
try {
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);
}
}
First, the applet gets a reference to the remote object
implementation (advertised as "HelloServer") from the server host's
rmiregistry. Like the Naming.rebind method,
the Naming.lookup method takes a URL-formatted
java.lang.String. In this example, the applet
constructs the URL string by using the getCodeBase method
in conjunction with the getHost method.
Naming.lookup takes care of the following tasks:
Constructing a registry stub instance (to contact the
server's registry) using the hostname and port number
supplied as arguments to Naming.lookup
Using the registry stub to call the remote
lookup method on the registry, using the
URL's name component ("HelloServer")
The registry returns the
HelloImpl_Stub instance bound to
that name
The lookup method receives the
remote object's
(HelloImpl) stub instance and
loads the stub class
(examples.hello.HelloImpl_Stub)
from the CLASSPATH or the applet's codebase
Naming.lookup returns the stub to its
caller (HelloApplet)
The applet invokes the remote sayHello method on
the server's remote object
RMI serializes and returns the reply string "Hello
World!"
RMI deserializes the string and stores it in a variable
named message.
The applet invokes the paint method, causing the
string "Hello World!" to be displayed in the drawing area of
the applet.
The URL-formatted string that is passed as a parameter to the
Naming.lookup method must include the server's
hostname. Otherwise, the applet's lookup attempt will default to the
client, and the AppletSecurityManagerwill throw an
exception because the applet cannot access the local system, but is
instead limited to only communicating with the applet's host.
Here is the HTML code for the web page that references the Hello World
applet:
There needs to be an HTTP server running on the machine
from which you want to download classes.
The codebase in the HTML file specifies a
directory below the directory from which the web page was
itself loaded. Using this kind of relative path is usually a
good idea. For example, if the codebase directory
(where the applet's class files live), referenced by the
applet's HTML, was in the directory above the the HTML
directory, you would use the relative path, "../".
The applet's code attribute specifies the
fully-qualified package name of the applet, in this example
examples.hello.HelloApplet:
code="examples.hello.HelloApplet"
Compile and Deploy Class Files and HTML Files
The source code for this example is now complete and the
$HOME/mysrc/examples/hello directory has four files:
Hello.java contains the source code for the
Hello remote interface
HelloImpl.java contains the source code for the
HelloImpl remote object implementation and the RMI
server for the applet
HelloApplet.java contains the source code for the applet
hello.html is the web page that references
the Hello World applet.
In this section, you compile the .java source files to
create .class files. You then run the rmic
compiler to create stubs and skeletons. A stub is a client-side proxy
for a remote object which forwards RMI calls to the server-side
dispatcher, which in turn forwards the call to the actual remote object
implementation.
When you use the javac and rmic compilers,
you must specify where the resulting class files should reside. For
applets, all files should be in the applet's codebase directory. For
our example, this directory is
$HOME/public_html/myclasses.
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 could use a file URL of the form
"file:/home/username/public_html" for testing, but
this approach will limit you to communicating between a client and
server that have access to the same physical file system. As an
alternative, you can use an HTTP URL by setting up a minimal web server
on your system; we have one available for download here.
Make sure that the deployment directory
$HOME/public_html/myclasses and the development directory
$HOME/mysrc/examples/hello are each accessible through the
local CLASSPATH on the development machine before
attempting to compile.
To compile the source files, run the javac command
as follows:
This command creates the directory examples/hello (if it
does not already exist) in the directory
$HOME/public_html/myclasses. The command then writes to
that directory the files Hello.class,
HelloImpl.class, and HelloApplet.class. These
are the remote interface, the implementation, and the applet
respectively. For an explanation of javac options, you
can refer to the Solaris
javac manual page or the Win32 javac manual
page.
Use rmic to generate skeletons and/or stubs
To create stub and skeleton files, run the rmic compiler
on the fully-qualified package names of compiled class files that
contain remote object implementations, like
my.package.MyImpl. The rmic command takes one
or more class names as an argument and produces class files of the form
MyImpl_Skel.class and MyImpl_Stub.class.
By default, in the Java 2 SDK, v1.2 and later, rmic
runs with the -vcompat flag on, which produces stubs
and skeletons that support access to:
Unicast (not Activatable) remote objects from 1.1
clients and
All types of remote objects from 1.2 (and later) clients
If you will never need support for 1.1 clients, rmic
can be run with the -v1.2 option. For an explanation of
rmic options, you can refer to the Solaris
rmic manual page or the Win32 rmic manual
page.
For example, to create the stub and skeleton for the
HelloImpl remote object implementation, run
rmic like this:
The "-d" option indicates the root directory in which to
place the compiled stub and skeleton class files. So the preceding
command creates the following files in the directory
$HOME/public_html/myclasses/examples/hello:
HelloImpl_Stub.class
HelloImpl_Skel.class
The generated stub class implements exactly the same set of remote
interfaces as the remote object itself. This means that a client can
use the Java programming language's built-in operators for casting and
type checking. It also means that remote objects written for the Java
platform support true object-oriented polymorphism.
Move the HTML file to the deployment directory
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 applet's codebase directory. For example:
The RMI registry is a simple server-side name server that allows remote
clients to get a reference to a remote object. Typically, it is used
only to locate the first remote object an application needs to talk to.
Then that object in turn would provide application-specific support for
finding other objects.
Note: Before you start the
rmiregistry, you must make sure that the shell or
window in which you will run the registry either has no CLASSPATH set
or has a CLASSPATH that does not include the path to any classes that
you want downloaded to your client, including the stubs for your remote
object implementation classes.
If you start the rmiregistry, and it
can find your stub classes in its CLASSPATH, it will ignore the
server's java.rmi.server.codebase property, and as
a result, your client(s) will not be able to download the stub code for
your remote object. For an explanation of how code downloading works in
RMI, please take a look at the tutorial on Dynamic code downloading using RMI.
To start the registry on the server, execute the
rmiregistry command. This command produces no output and
is typically run in the background. For more on the
rmiregistry, you can refer to the Solaris
rmiregistry manual page or the Win32
rmiregistry manual page.
For example, in the Solaris operating environment:
rmiregistry &
For example, on Microsoft Windows 95 systems:
start rmiregistry
(Use javaw if start is not available.)
By default, the registry runs on port 1099. To start the registry on
a different port, specify the port number from the command line. For
example, to start the registry on port 2001 on a Microsoft Windows NT
system:
start rmiregistry 2001
If the registry is running on a port other than 1099, you'll need to
specify the port number in the name handed to the URL-based methods of
the java.rmi.Naming class when making calls to the
registry. For example, if the registry is running on port 2001 in this
example, the call required to bind the name "HelloServer"
to the remote object reference would be:
Naming.rebind("//myhost:2001/HelloServer", obj);
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 type of the object reference bound in the
registry will not match the modified class.
Start the server
When starting the server, the java.rmi.server.codebase
property must be specified, so that the stub class can be dynamically
downloaded to the registry and then to the client. Run the server,
setting the codebase property to be the location of the implementation
stubs. Because the codebase property in this example references a
directory, make sure that any other classes that may need to be
downloaded have also been installed in the directory referenced by
java.rmi.server.codebase. For other command-line examples
of codebase settings, please take a look at the tutorial on Dynamic code downloading using RMI.
For explanations of each of the java.rmi.server
properties, click
here. To see all the available java.rmi.activation
properties, click here.
For an explanation of java options, you can refer to the
Solaris
java manual page or the Win32 java
manual page. If you have problems running the example code,
please take a look at the RMI and Serialization
FAQ.
Note: A stub class is dynamically downloaded to a client's
virtual machine only when the class is not already available locally
and the java.rmi.server.codebaseproperty
has been set properly to specify where the class files are located on
the server.
There are four things that need to go on the same command line: the
"java" command, followed by two property name=value
pairs (for the codebase property, note that there are no
spaces from the "-D" all the way though the last "/") and then the
fully-qualified package name of the server program. There should be a
space just after the word "java", between the two
properties, and just before the word "examples" (which is
very hard to see when you view this as text, in a browser, or on
paper). The following command shows how to start the
HelloImpl server, specifying the
java.rmi.server.codebase and
java.security.policy properties:
In order to run this code on your system, you'll need to change the
location of the policy file to be the location of the
directory on your system, where you've installed the example source
code.
Note: In this example, for simplicity, we will use a policy file that gives global permission to
anyone from anywhere. Do not use this policy file in a production
environment. For more information on how to properly open up
permissions using a java.security.policy file, please
refer to to the following documents:
The codebase property will be resolved to a URL, so it must have the
form of "http://aHost/somesource/" or
"file:/myDirectory/location/" or, due to the requirements
of some operating systems, "file:///myDirectory/location/"
(three slashes after the "file:").
Please note that each of the URL strings above has a trailing
"/". The trailing slash is a requirement for the URL set by the
java.rmi.server.codebase property, so the implementation can
resolve (find) your class definition(s) properly.
If you forget the trailing slash on the codebase
property, or if the class files can't be located at the source (they
aren't really being made available for download) or if you misspell the
property name, you'll get thrown a
java.lang.ClassNotFoundException. This exception will be thrown
when you try to bind your remote object to the
rmiregistry, or when the first client attempts to access
that object's stub. If the latter case occurs, you have another problem
as well because the rmiregistry was finding the stubs in its CLASSPATH.
The output should look like this:
HelloServer bound in registry
Run the applet
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, as shown here: