Prior to the release of the JDK1.2, an instance of a class that did
not extend from UnicastRemoteObject
could be accessed from a server
program that (1) created an instance of the remote object, and (2) ran
all the time. This functionality was provided by creating a constructor
which exported the object by calling the static method UnicastRemoteObject.exportObject.
The exportObject
method accepts as an argument an object that
implements an interface that extends from java.rmi.Remote
.
Now with the introduction of the class java.rmi.activation.Activatable
and the RMI daemon, rmid
, programs can be written to register
information about remote object implementations that should be created
and execute "on demand", rather than running all the time. The RMI daemon,
rmid
, provides a JVM from which other JVM instances may be spawned.
As in the UnicastRemoteObject.exportObject
case, activatable objects
may be exported using a static method, Activatable.exportObject
,
and these objects must implement a remote interface.
Note: For the remainder of this tutorial, the terms
"activatable object implementation", "activatable object," and "implementation"
may be used interchangeably to refer to the class, examples.activation.MyClass
,
which implements a remote interface and is activatable.
This tutorial is organized as follows:
Client3.java
,
the class which will invoke a method on an activatable objectAnotherRemoteInterface.java
,
the interface that extends java.rmi.Remote
, implemented by:MyClass.java
,
the class which is being modified to be activatableSetup3.java
,
the class which registers information about the activatableFor the source code used in all of the activation tutorials, click here.
Create an interface that describes each of the methods that you would
like to call remotely. For this example, the remote interface will be examples.activation.AnotherRemoteInterface
.
There are three steps to create a remote interface.
Step 1:
Make the appropriate imports in your interface
import java.rmi.*;
Step 2:
Extend java.rmi.Remote
public interface AnotherRemoteInterface extends Remote {
Step 3:
Declare each of the methods that may be called remotely
public String calltheServer(String s) throws RemoteException;
Creating the implementation class
For this example, the implementation class will be examples.activation.MyClass
.
There are three steps to migrate a class which does not extend Activatable
or UnicastRemoteObject
to become activatable:
java.rmi.Remote
import java.rmi.*;
import java.rmi.activation.*;
Step 2:
Modify the class declaration so that the class implements
an interface that extends from java.rmi.Remote
public class MyClass implements examples.activation.AnotherRemoteInterface
{
public MyClass(ActivationID id, MarshalledObject data)
throws
RemoteException {
// Register the object with the activation
system
// then export it on an anonymous port
Activatable.exportObject(this, id, 0);
}
The job of the "setup" class is to create all the information necessary
for the activatable class, without necessarily creating an instance of
the remote object. For this example, the setup class will be examples.activation.Setup3
.
The setup class passes information about the activatable class to rmid
,
registers a remote reference (an instance of the activatable class's stub
class) and an identifier (name) with the rmiregistry
, and then
the setup class may exit. There are six steps to create a setup class:
SecurityManager
ActivationDesc
instancermid
rmiregistry
import java.rmi.*;
import java.rmi.activation.*;
import java.security.CodeSource;
import java.util.Properties;
System.setSecurityManager(new RMISecurityManager());
Step 3:
Create an ActivationDesc
instance
In the setup application, create a Properties
instance, an
ActivationGroupID
instance, a CodeSource
instance and
a MarshalledObject
instance, as these will be needed for the constructor
of the ActivationDesc
. The job of the activation descriptor is
to provide all the information that rmid
will require to create
a new instance of the implementation class.
Note: In order to run this code on your system, you'll need to change the file URL location to be the location of the directory on your system, where you've installed the example source code.
// The "location" URL specifies where the class definition
// will come from when this object is requested (activated).
// Don't forget the trailing slash at the end of the URL
// or your classes won't be found.
//
java.net.URL location = new
java.net.URL("file:/home/rmi-tutorial/activation/");
// The second parameter to the CodeSource constructor
// is an object that implements java.security.PublicKey
//
CodeSource source = new CodeSource(location, null);
Properties props = (Properties)System.getProperties().clone();
ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(
new ActivationGroupDesc(props));
MarshalledObject data = null;
// The second argument to the ActivationDesc constructor will be
used
// to uniquely identify this class; it's location is relative to
the
// URL referenced by the CodeSource
//
ActivationDesc desc = new ActivationDesc
(agi, "examples.activation.MyClass",
source, data);
Step 4:
Declare an instance of your remote interface and register
the activation descriptor with rmid
AnotherRemoteInterface ari =
(AnotherRemoteInterface)Activatable.register(desc);
System.out.println("Got the stub for MyClass");
Step 5:
Bind the stub, that was returned by the Activatable.register
method, to a name in the rmiregistry
Naming.rebind("MyClass", ari);
System.out.println("Exported MyClass");
Step 6:
Quit the setup application
System.exit(0);
There are six steps to compile and run the code:
rmic
on the implementation classrmiregistry
rmid
Step 1:
Compile the remote interface, implementation, client
and setup classes
% javac -d . AnotherRemoteInterface.java
% javac -d . MyClass.java
% javac -d . Client3.java
% javac -d . Setup3.java
Step 2:
Run rmic
on the implementation class
% rmic -d . examples.activation.MyClass
java
" command, followed by the property
name=value pair (no spaces from the "-D" all the way thought
the last "/") and then the fully-qualified package name of the setup program.
There should be a space just after the word "java
" and just before
the word "examples
" (which is very hard to see when you view this
as text, in a browser, or on paper).
% java -Djava.rmi.server.codebase=file:/home/rmi-tutorial/activation/
examples.activation.Setup3
The codebase property will be resolved to a URL, so it must have the
form of "http://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 these sample URL strings 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 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
.
The server output should look like this:
Got the stub for MyClass
Exported MyClass
The argument to the client program is the hostname of the implementation
server, in this case, "vector
".
% java examples.activation.Client3 vector
The client output should look like this:
Got a remote reference to the class MyClass
Called the remote method
Result: Watson are you there? I'm here!
rmid
and the registry by the setup program, and which then creates
the object instance and forwards the remote method to that instance. The
benefit to this approach is that you don't have to make a change to the
original non-remote class.
So, let's assume that the original, non-remote class, looks like this:
package examples.activation;
public class MyNonRemoteClass {
private String result = null;
// Here's the original class, which concatenates
two strings
//
public String calltheServer(String takeThis)
{
result = takeThis + "I'm
here!";
return result;
}
}
examples.activation.AnotherRemoteInterface
,
to describe the methods we'd like to call remotely:
package examples.activation;
import java.rmi.*;
public interface AnotherRemoteInterface extends Remote {
public String calltheServer(String s) throws
RemoteException;
}
MyNonRemoteClass.java
, which we may not
always have the source code to do anyway, we'll create a new class examples.activation.MyNonRemoteClassAdapter
,
which (like an event adapter) implements the specified interface, and then
takes appropriate action. What's new in MyNonRemoteClassAdapter
from what we did earlier, is that it creates an instance of MyNonRemoteClass
in it's constructor and it calls the MyNonRemoteClass.calltheServer
method.
package examples.activation;
import java.rmi.*;
import java.rmi.activation.*;
import java.security.CodeSource;
public class MyNonRemoteClassAdapter implements examples.activation.AnotherRemoteInterface
{
private String result = null;
private MyNonRemoteClass mnrc;
// The constructor for activation and export;
this constructor is
// called by the method ActivationInstantiator.newInstance
during
// activation, to construct the object.
//
public MyNonRemoteClassAdapter(ActivationID
id, MarshalledObject data)
throws RemoteException
{
// Register the object
with the activation system
// then export it on
an anonymous port
//
Activatable.exportObject(this,
id, 0);
// Create an instance
of the class MyNonRemoteClass
//
mnrc = new MyNonRemoteClass();
}
// Define the method declared in AnotherRemoteInterface
// to accept a String, modify it, and return
it to the client
//
public String calltheServer(String takeThis)
throws RemoteException {
// Rather than modify
the String here, forward
// it on to the non-remote
object impleemntation
//
result = mnrc.calltheServer(takeThis);
return result;
}
}
rmic
on and the class referenced by the
setup program will be the adapter class. The
examples.activation.Setup3alt
looks like this now:
package examples.activation;
import java.rmi.*;
import java.rmi.activation.*;
import java.security.CodeSource;
import java.util.Properties;
public class Setup3alt {
// This class registers information about the
MyClass
// class with rmid and the rmiregistry
//
public static void main(String[] args) throws
Exception {
System.setSecurityManager(new
RMISecurityManager());
AnotherRemoteInterface
ari;
// Don't forget the trailing
slash at the end of the URL
// or your classes won't
be found
//
java.net.URL location
=
new java.net.URL("file:/home/rmi_tutorial/activation/");
// The second parameter
to the CodeSource constructor
// is an object that
implements java.security.PublicKey
//
CodeSource source =
new CodeSource(location, null);
// Create the rest of
the parameters that will be passed to
// the ActivationDesc
constructor
//
Properties props = (Properties)System.getProperties().clone();
ActivationGroupID agi
=
ActivationGroup.getSystem().registerGroup(
new ActivationGroupDesc(props));
MarshalledObject data
= null;
// The second argument
to the ActivationDesc constructor will
// be used to uniquely
identify this class; it's location is
// relative to the URL
referenced by the CodeSource
//
ActivationDesc desc
= new ActivationDesc (agi,
"examples.activation.MyNonRemoteClassAdapter",
source, data);
ari = (AnotherRemoteInterface)Activatable.register(desc);
System.out.println("Got
the stub for MyNonRemoteClassAdapter");
// Bind the stub to a
name in the registry running on 1099
//
Naming.rebind("MyNonRemoteClassAdapter",
ari);
System.out.println("Exported
MyNonRemoteClassAdapter");
System.exit(0);
}
}