|
|
 |  |
|
|
The goal of RMI is to provide services to remote clients. A remote
client obtains and uses a proxy object that implements an
interface. The interface is the contract for the service, it is the
definition of the methods that the service provides.
Because the client is using a proxy object, the actual execution of code
occurs on the server. A proxy object is placeholder that the client uses
to cause execution of code on a server.
The RMI registry is used to store a list of available services. A client uses
the registry to make it's proxy object, and the Registry is responsible for
giving appropriate information to the client so that it can hook up with the
server that implements the service.
In many scenarios, the Registry and the server for the services are in the same
JVM. It is possible, however, for the Registry to run in a different
JVM or even on a different machine than the server or servers that implement
the services.
A registry has a TCP port that it uses to listen to incoming requests,
typically this is port 1099. The RMI registry is a global resource, each JVM
can have only one Registry on a particular port. This has important
ramifications for the naming of services.
If you are considering RMI as a mechanism for publishing services, you may
want to consider using Hessian instead. Hessian offers the following
advantages:
- it does not have a global namespace, separate web-app's can provide
services with the same name without conflict
- it supports the use of clients written in languages other than Java
- it does not require the manual generation of stubs
- it does not require a security-manager
More information is available in the
Hessian section of the documentation.
The JDK requires that a security manager be in place for the use of RMI.
This is true for both clients and servers.
A security manager is enabled in Resin using the configuration:
<resin xmlns="http://caucho.com/ns/resin"
xmlns:resin="http://caucho.com/ns/resin/core">
<security-manager/>
...
For clients that are applets, the developer does not need to enable the
security manager; the browser provides the security manager.
More information is available in the
Security
section of the documentation.
Resin provides the resource class
to define an RMI Registry and register services with it.
If the Registry is on the `localhost' server, then Resin will also start the
RMI Registry if needed.
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>localhost</server>
<port>1099</port>
<rmi-service service-name="HelloWorld"
service-class="example.HelloWorldImpl"/>
<rmi-service .... >
</init>
</resource>
</web-app>
| server | the ip address of the server with the Registry | localhost
|
| port | the port of the Registry | 1099
|
| rmi-service | an rmi service (see below) |
|
RmiRegistry is used to define the location of an RMI Registry to
use. If server is `localhost', then the Registry will be started
on the specified port, if it has not already been started.
If server is something other than `localhost', then it is assumed
that the Registry has been started by some other JVM , and is treated as remote Registry to register any services defined with rmi-server.
com.caucho.resources.rmi.RmiRegistry
Each RmiRegistry can have rmi-service children, which
causes the service to be instantiated and registered with the RMI Registry
defined by the containing RmiRegistry.
| service-name | the name of the service, used for registration in the Registry and also used by clients to locate the service. | required
|
| server-class | the name of the implementation class for the service | required
|
An RMI service requires the developer to create two classes - an interface
and an implementation. The interface defines the contract for the service, it
is given to the client and it is the client view of the service. The
implementation class implements the functionality; it implements
the interface and is used on on the server.
The following is a simple hello world example.
package example;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloWorld extends Remote
{
public String sayHello()
throws RemoteException;
}
package example;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld
{
public HelloWorldImpl()
throws RemoteException
{
super();
}
public String sayHello()
throws RemoteException
{
return "Hello, World";
}
}
When the client uses a service, it uses a proxy object. The proxy
object is a placeholder, it implements the interface defined for the service,
and call's through to the server so that the code is executed on the
server.
RMI calls proxy objects Stubs, and the stubs must be manually
generated. The generation of stubs is done using the rmic
tool.
rmic -v1.2 -d WEB-INF/classes/ example.HelloWorldImpl
This call to rmic will use the file
WEB-INF/classes/example/HelloWorldImpl.class to generate the class
file WEB-INF/classes/example/HelloWorldImpl_Stub.class.
It is tedious to perform this step manually, an ant build script (as shown
in a later section) can be used to expediate the process.
Once the work of making an interface, an implementation, and generating a stub
is complete, it is a simple process to deploy the service in Resin.
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>localhost</server>
<port>1099</port>
<rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
</init>
</resource>
</web-app>
More than once service is easily deployed
with the use of multiple rmi-service tags:
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>localhost</server>
<port>1099</port>
<rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
<rmi-service service-name="HelloAgainWorld" service-class="example.HelloAgainWorldImpl"/>
</init>
</resource>
</web-app>
By convention, the name chosen for the service often matches the name of the
interface class. For example, if the interface name is "example.HelloWorld"
then service-name is "HelloWorld" or even "example.HelloWorld" to match.
The RMI Registry has a global namespace. If two different web-app's try to
publish the same service, with the same name, there will be conflicts.
An ant build file is useful for completing the rmic step, and for
preparing a jar for use by the client. The client jar contains the interfaces
and the stubs.
The following build file, placed in /WEB-INF/build, creates
the jar file /rmiclient.jar.
<project name="rmiexample" default="dist" basedir=".">
<property file="local.properties"/>
<property file="build.properties"/>
<property environment="env"/>
<property name="build.compiler.emacs" value="true"/>
<property name="resin.home" value="${env.RESIN_HOME}"/>
<property name="rmiclient.jar" value="../rmiclient.jar"/>
<!-- NOTE: new RMI interfaces must have corresponding entries addeed
- in the rmiclient.jar taget
-->
<path id="compile.classpath">
<fileset dir="${resin.home}/lib">
<include name="**/*.jar" />
</fileset>
</path>
<target name="init">
<tstamp/>
</target>
<target name="compile" depends="init">
<mkdir dir="classes"/>
<javac classpathref="compile.classpath"
destdir="classes"
debug="true">
<src path="classes"/>
</javac>
</target>
<target name="rmic" depends="init,compile">
<rmic base="classes"
classpathref="compile.classpath"
includes="**/*Impl.class"/>
</target>
<target name="rmiclient.jar" depends="init,rmic">
<jar destfile="${rmiclient.jar}">
<fileset dir="classes">
<patternset>
<include name="**/HelloWorld.class"/>
<include name="**/*_Stub.class"/>
</patternset>
</fileset>
</jar>
</target>
<target name="dist" depends="rmiclient.jar"/>
</project>
The client is usually on a different machine, or at least in a different JVM, than the server. That is the point of RMI, it enables the execution of code on a remote machine.
In order to use the RMI service, the client needs the interface classes and the
Stubs. The easiest way to provide these to the client is to provide a jar; the
ant build file above provides an example of using ant to automate the creation
of the jar file for the client.
Once the jar file is available to the client, using the RMI service id fairly simple.
String server = "//server-with-registry.com:1099/";
HelloWorld remote = (HelloWorld) Naming.lookup(server + "HelloWorld");
System.out.println(remote.sayHello());
In the most common scenario, the Resin server provides both the RMI Registry
and the RMI services. When the registry server is defined as `localhost',
Resin will start the rmi registry if has not been started already.
This provides a simple method of using RMI, you don't have to worry about
the (somewhat tricky) process of starting the rmi registry yourself.
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>localhost</server>
<port>1099</port>
<rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
<rmi-service .... >
</init>
</resource>
</web-app>
When the Resin server starts, it will start the rmi registry on port 1099 and register the `HelloWorld' service with it.
In this scenario, the rmi registry is located on the machine
services.hogwarts.com. The registry is started with a custom (not
Resin) server implemented by Hogwarts.
The requirement is for the HelloWorld service, implemented within a Resin
server, to be registered with the remote Registry.
In this scenario, the Resin resource RmiRegistry is used to attach to the
existing RMI registry running on services.hogwarts.com.
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>services.hogwarts.com</server>
<port>1099</port>
<rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
<rmi-service .... >
</init>
</resource>
</web-app>
When the Resin server starts, it will register the `HelloWorld' service with
the RMI Registry on services.hogwarts.com. Since the server is on
a remote machine, Resin will not create a registry on the local machine. When
the REsin server shuts down, or is restarted, the `HelloWorld' service will be
removed from the remote registry.
In this scenario, the rmi registry is located on the same machine
as the Resin server, but is started with a custom (not
Resin) server implemented by Hogwarts.
This is essentially the same scenario as having a Registry on a different
szerver. The server name cannot be provided as `localhost', however, because
Resin will try to create the RMI registry.
The solution is to use an IP address of `127.0.0.1' as the address of the
server. Because the server name is not `localhost', the RMI registry will not
be created.
<web-app>
<resource type="com.caucho.resources.rmi.RmiRegistry">
<init>
<server>127.0.0.1</server>
<port>1099</port>
<rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
<rmi-service .... >
</init>
</resource>
</web-app>
When the Resin server starts, it will register the `HelloWorld' service with
the RMI Registry on the local machine. Since the server is not `localhost',
Resin will not create a registry on the local machine. When
the Resin server shuts down, or is restarted, the `HelloWorld' service will be
removed from the remote registry.
Copyright (c) 1998-2009 Caucho Technology, Inc. All rights reserved. caucho® ,
resin® and
quercus®
are registered trademarks of Caucho Technology, Inc.
|
Copyright (c) 1998-2009 Caucho Technology, Inc. All rights reserved. caucho® ,
resin® and
quercus®
are registered trademarks of Caucho Technology, Inc.
|