This is a feature of JBuilder Professional and Enterprise.
Note that RMI functionality is available to you in every version of JBuilder, because the tools you need to create an RMI application ship with Java. The steps in this tutorial will need to be modified only slightly when using the RMI command-line tools instead of the JBuilder development environment.
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 unmarshall parameters and does not truncate types, supporting true object-oriented polymorphism.
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 is a feature of JBuilder Enterprise.
There may be some sample RMI applications installed in the /jbuilder/samples/rmi
directory of your JBuilder installation. See their accompanying HTML files for a description of the sample application.
An example of writing a distributed database application using RMI and DataSetData
may be found in the /jbuilder/samples/DataExpress/StreamableDataSets
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 display the data in a grid.
This chapter first shows you the steps to follow to create a distributed version of the classic "Hello World" program using Java Remote Method Invocation (RMI). Other examples using RMI may be located on the Java Remote Method Invocation (RMI) Home Page, at http://java.sun.com/products/jdk/rmi/index.html.
The distributed "Hello World" example uses an applet to make a remote method call to the server from which the applet was downloaded to retrieve the message "Hello World!". When the applet runs, "Hello World!" is displayed on the client browser.
This tutorial contains these three lessons:
The source code used in this tutorial can be downloaded from the http://java.sun.com Web site, or copied from this tutorial into the appropriate files in JBuilder. The files are:
Hello.java
HelloImpl.java
, which implements
examples.hello.Hello
HelloApplet.java
, that invokes
the remote method, sayHello
examples.hello.HelloApplet.html
, that references the applet
examples.hello.HelloImpl
, which implements a
remote interface.
examples.hello
and the source
directory is $HOME/jbproject/examples/hello
. In this case $HOME points to the location of the user's home directory, so for the default installation on Solaris, the directory would be something like usr/home/<username>/jbproject/examples/hello
. On Windows NT, the directory would be something like c:/WINNT/profiles/<username>/jbproject/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,
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.
java.rmi.Remote
interface.
java.rmi.RemoteException
(or a superclass of RemoteException
) in its
throws clause, in addition to any application-specific
exceptions.
Hello
) not the implementation class
(HelloImpl
).
Hello.jpx
.
Hello.java
. It displays, with the Source tab selected.
package examples.hello; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; }
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, at http://www.sunlabs.com/techrep/1994/abstract-29.html.
At a minimum, a remote object implementation class must:
main
method that creates an instance of the remote object
implementation, and binds that instance to a name in the
RMI Registry. 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:
HelloImpl.java
.
To create the file HelloImpl.java
, which contains
the code for the "Hello World" server, follow these steps:
Hello.jpx
.
HelloImpl.java
. It displays, with the Source tab selected.
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("//localhost/HelloServer", obj); System.out.println("HelloServer bound in registry"); } catch (Exception e) { System.out.println("HelloImpl err: " + e.getMessage()); e.printStackTrace(); } } }
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 the interface that it is implementing. Interface methods are implicitly public and abstract, so if the implementation class doesn't fulfill it's 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:
In addition, your 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
automatically exported 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,
compiling 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
java.rmi.RemoteException
examples.hello.HelloImpl
class:
public HelloImpl() throws RemoteException { super(); }Note the following:
super
method call invokes the no-argument
constructor of
java.rmi.server.UnicastRemoteObject
, which exports
the remote object.
java.rmi.RemoteException
, because RMI's attempt to
export a remote object during construction might fail if
communication resources are not available.
super()
, occurs by default (even if omitted), it is
included in this example to make clear the fact that the Java virtual
machine (VM) constructs the superclass before the class.
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:
static
or transient
.
Please refer to the Java Object
Serialization Specification, at http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/serialTOC.doc.html, for information on how to alter
the default serialization behavior.
RMI
Specification
, at http://java.sun.com/j2se/1.3/docs/guide/rmi/spec/rmiTOC.html. We'll create them later in this tutorial in
the section "Compile the Java source files".
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.
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.
For bootstrapping, the RMI system provides a remote object registry
that allows you to bind a URL-formatted name of the form "//host/objectname"
to the remote object, where objectname
is a simple string name.
The RMI registry is a simple server-side 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 RMI client needs to talk to. Then that first object in turn, would provide application-specific support for finding other objects.
For example, the reference can be obtained as a parameter to, or a return value from, another remote method call. For a discussion on how this works, please take a look at Applying the Factory Pattern to RMI, at http://java.sun.com/j2se/1.3/docs/guide/rmi/Factory.html.
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("//localhost/HelloServer", obj);Note the following about the arguments to the
rebind
method call:
java.lang.String
representing the location and
name of the remote object.
HelloServer
.".
"/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.
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, an object that
contains the stub for the implementation is returned.
sayHello
method in order to get the string "Hello World!",
which is displayed when the applet runs. To create the applet,
The file HelloApplet.java
displays in the Project pane.
package examples.hello; import java.awt.*; import java.awt.event.*; import java.applet.*; import java.rmi.Naming; import java.rmi.RemoteException; public class HelloApplet extends Applet { boolean isStandalone = false; 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; //Get a parameter value public String getParameter(String key, String def) { return isStandalone ? System.getProperty(key, def) : (getParameter(key) != null ? getParameter(key) : def); } //Construct the applet public HelloApplet() { } //Initialize the applet public void init() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } //Component initialization private void jbInit() throws Exception { obj = (Hello)Naming.lookup("//" + getCodeBase().getHost() + "/HelloServer"); message = obj.sayHello(); } public void paint(Graphics g){ g.drawString(message, 25, 50); } //Get Applet information public String getAppletInfo() { return "Applet Information"; } //Get parameter info public String[][] getParameterInfo() { return null; } }
The applet does the following:
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:
Naming.lookup
.
lookup
method on the registry, using the
URL's name component ("HelloServer
").
HelloImpl_Stub
instance bound to
that name
HelloImpl
) stub instance and
loads the stub class
(examples.hello.HelloImpl_Stub
)
from the CLASSPATH or the stub's codebase
Naming.lookup
returns the stub to its
caller (HelloApplet
)
sayHello
method on
the server's remote object.
message
.
paint
method, causing the
string "Hello World!" to be displayed in the drawing area of
the applet.
Naming.lookup
method must include the server's
host name. Otherwise, the applet's lookup attempt 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's host.
HelloApplet.html
. The generated code looks like this:
<HTML> <head> <title>Hello World</title> </head> <center> <h1>Hello World</h1> </center> <body>examples.hello.HelloApplet will appear below in a Java enabled browser. <applet CODEBASE = "." CODE = "examples.hello.HelloApplet.class" NAME = "TestApplet" WIDTH = 400 HEIGHT = 300 HSPACE = 0 VSPACE = 0 ALIGN = middle > </applet> </body> </HTML>
Note the following:
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 want to use a
relative path like "../".
code
attribute specifies the
fully-qualified package name of the applet, in this example
examples.hello.HelloApplet
:
code="examples.hello.HelloApplet.class"
In order to run this code on your system, you'll need to create a policy
file in the
directory on your system where you've installed the example source
code.
java.security.policy
file, please
refer to to the following documents:
To create the policy file,
Hello.jpx
.
policy.java
. It displays, with the Source tab selected.
grant { // Allow everything for now permission java.security.AllPermission; };
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.
examples.hello.HelloApplet.html
, which is the Web page that references the "Hello World" applet.
policy.java
, which contains the permissions for the applet.
In this section, you compile the .java
source files to create .class
files, stubs, and skeletons. A stub is a client-side proxy for a remote object, which forwards RMI calls to the server-side
dispatcher (skeleton), which in turn forwards the call to the actual remote object implementation.
When you use the compiler,
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
jbproject/examples/hello/classes
. When the Applet wizard generates the HTML file, it puts the file in the correct location for your system setup.
Some Web servers, such as Java Web Server, 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,
HelloImpl.java
in the project pane. Select Properties from the context menu. Check the Generate RMI Stub/Skeleton field on the RMI/JNI tab of the Build page. Click OK.
Hello.jpx
, select Make.
These steps create the directory examples/hello
(if it
does not already exist) in the directory
jbproject/examples/hello/classes
. 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.
The stub and skeleton files are also generated to the same directory. These files are HelloImpl_Stub.class
and HelloImpl_Skel.class
.
The previous step is akin to running the javac and rmic compilers on the files.
If you need to produce stubs and skeletons that support access to the following, run rmic -vcompat from the command line.
Activatable
) remote objects from 1.1
clients and
For an explanation of
rmic
options, refer to the Solaris
rmic
manual page at http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/rmic.html, or the Win32 rmic
manual
page at http://java.sun.com/j2se/1.3/docs/tooldocs/win32/rmic.html.
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 remote objects written for the Java platform support true object-oriented polymorphism.
If you start the RMI Registry, 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.
If JBuilder was installed to a directory other than the default directory (running Windows NT), you may need to modify your CLASSPATH by editing the System Environment properties from the Control Panel.
To start the registry on the server
This step produces no output and is typically run in the background.
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. To stop the registry,
RMI and
Serialization FAQ
at http://java.sun.com/j2se/1.3/docs/guide/rmi/faq.html.
java.rmi.server.codebase
property
has been set properly to where the class files live on the
server. JBuilder takes care of setting the codebase property when the server is run.
The following steps show how to start the
HelloImpl
server, specifying the
java.security.policy
properties:
-Djava.security.policy=<usr/home><username>/jbproject/examples/hello/policy.java
Note that there are no spaces from the "-D" all the way though.
HelloImpl.java
, select Run.
The server is started. "HelloServer bound in registry" displays in the Message pane.
HelloApplet.html
.
The Tomcat Web server starts, with the "Hello World!" applet running inside the Web View pane. If it is running correctly, 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/j2se/1.3/docs/guide/rmi/getstart.doc.html.
To learn more about Java RMI, start with these sites: