Activating an object that does not extend java.rmi.activation.Activatable
This tutorial shows you the steps for enabling an existing class to become activatable. If you are interested in finding out about Creating an Activatable Object or Making a UnicastRemoteObject Activatable, these are also available as tutorials.
Prior to the release of the Java 2 SDK, 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 Java virtual machine* (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.ClassName, which implements a remote interface and is activatable.
This tutorial is organized as follows:
The files needed for this tutorial are:
- The steps to create a remote interface
- The steps to modify the implementation class
- The steps to create the setup class
- The steps to compile and run the example code
- Client3.java, the class which will invoke a method on an activatable object
- AnotherRemoteInterface.java, the interface that extends java.rmi.Remote, implemented by:
- MyClass.java, the class which is being modified to be activatable
- Setup3.java, the class which registers information about the activatable class with the RMI registry and the RMI daemon
You may notice that while the client code is included, it is not discussed in a step-by-step manner, like the implementation and setup classes. The reason for this omission, is that the client code for activatable objects is no different than the RMI client code for accessing non-activatable remote objects. Activation is strictly a server-side implementation decision.
For all of the source code used in the activation tutorials, you may choose from these formats:
- activation.zip
- tar">activation.tar
- activation.tar.Z
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.
- Make the appropriate imports in the interface
- Extend java.rmi.Remote
- Declare each of the methods that may be called remotely
Step 1:
Make the appropriate imports in your interfaceimport java.rmi.*;Step 2:
Extend java.rmi.Remotepublic interface AnotherRemoteInterface extends Remote {Step 3:
Declare each of the methods that may be called remotelypublic 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:
- Make the appropriate imports in the implementation class
- Modify the class declaration so that the class now implements an interface that extends from java.rmi.Remote
- Declare a two-argument constructor in the implementation class
Step 1:
Make the appropriate imports in the implementation classimport java.rmi.*; import java.rmi.activation.*;Step 2:
Modify the class declaration so that the class implements an interface that extends from java.rmi.Remotepublic class ClassName implements examples.activation.AnotherRemoteInterface {Step 3:
Add a new two-argument constructor to the implementation classpublic ClassName(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 seven steps to create a setup class:
- Make the appropriate imports
- Install a SecurityManager
- Create an ActivationGroup instance
- Create an ActivationDesc instance
- Remove the reference to the implementation class creation, and register with rmid
- Bind the stub to a name in the rmiregistry
- Quit the setup application
Step 1:
Make the appropriate imports in the setup classimport java.rmi.*; import java.rmi.activation.* import java.util.Properties;Step 2:
Install a SecurityManagerSystem.setSecurityManager(new RMISecurityManager());Step 3:
Create an ActivationGroup instanceNote: 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:
Default Policy Implementation and Policy File Syntax
In the setup application, the job of the activation group descriptor is to provide all the information that rmid will require to contact the appropriate existing JVM or spawn a new JVM for the activatable object.
Note: In order to run this code on your system, you'll need to change the policy file location to be the absolute path to where you've installed the example policy file that came with the source code.
// Because of the Java 2 security model, a security policy should // be specified for the ActivationGroup VM. The first argument // to the Properties put method, inherited from Hashtable, is // the key and the second is the value // Properties props = new Properties(); props.put("java.security.policy", "/home/rmi_tutorial/activation/policy"); ActivationGroupDesc.CommandEnvironment ace = null; ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace); // Once the ActivationGroupDesc has been created, register it // with the activation system to obtain its ID // ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(exampleGroup);Step 4:
Create an ActivationDesc instanceIn the setup application, 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 // String location = "file:/home/rmi_tutorial/activation/"; // Create the rest of the parameters that will be passed to // the ActivationDesc constructor // MarshalledObject data = null; // The location argument to the ActivationDesc constructor will be used // to uniquely identify this class; it's location is relative to the // URL-formatted String, location. // ActivationDesc desc = new ActivationDesc (agi, "examples.activation.ActivatableImplementation", location, data);Step 5:
Declare an instance of your remote interface and register the activation descriptor with rmidAnotherRemoteInterface ari = (AnotherRemoteInterface)Activatable.register(desc); System.out.println("Got the stub for ClassName");Step 6:
Bind the stub, that was returned by the Activatable.register method, to a name in the rmiregistryNaming.rebind("ClassName", ari); System.out.println("Exported ClassName");Step 7:
Quit the setup applicationSystem.exit(0);
There are six steps to compile and run the code:
- Compile the remote interface, implementation, client and setup classes
- Run rmic on the implementation class
- Start the rmiregistry
- Start the activation daemon, rmid
- Run the setup program
- Run the client
Step 1:
Compile the remote interface, implementation, client and setup classes% javac -d . AnotherRemoteInterface.java % javac -d . ClassName.java % javac -d . Client3.java % javac -d . Setup3.javaStep 2:
Run rmic on the implementation class% rmic -d . examples.activation.ClassName% rmiregistry &Note: Before you start the rmiregistry, 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.
Step 4:
Start the activation daemon, rmid% rmid -J-Djava.security.policy=rmid.policy &Where rmid.policy is the name of the security policy file for rmid.Note: By default, rmid now requires a security policy file, that is used to verify whether or not the information in each ActivationGroupDescriptor is allowed to be used to launch a JVM for an activation group. For complete details, please refer to the rmid man page for the Solaris operating environment and the rmid man page for the Microsoft Windows platform.
Run the setup, setting the codebase property to be the location of the implementation stubs. There are four things that need to go on the same command line:
- The "java" command
- A property name=value pair that specifies the location of the security policy file
- A property to specify where the stub code lives (no spaces from the "-D" all the way though the last "/")
- The fully-qualified package name of the setup program.
There should be one space just after the word "java", one between the two properties, and a third one 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.security.policy=/home/rmi_tutorial/activation/policy -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://aHost/somesource/" or "file:/myDirectory/location/" or, due to the requirements of some operating systems, "file:///myDirectory/location/" (three slashes after the "file:").
While a file: URL is sometimes easier to use for running example code, using the file: URL will mean that the only clients that will be able to access the server are those that can access the same files system as the server (either by virtue of running on the same machine as the server or by using a shared filesystem, such as NFS). If you wish to use an HTTP server, but don't have one available to you, please feel free to download our HTTP server.
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. For more information on setting the java.rmi.server.codebase property from the command line, please take a look at our tutorial on dynamic code downloading using the java.rmi.server.codebase property.
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. 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 server output should look like this:
Got the stub for ClassName Exported ClassNameStep 6:
Run the clientThe argument to the client program is the hostname of the implementation server, in this case, "vector".
% java -Djava.security.policy=/home/rmi_tutorial/activation/policy examples.activation.Client3 vectorThe client output should look like this:
Got a remote reference to the class ClassName Called the remote method Result: Watson are you there? I'm here!
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; } }
We write the same interface, 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; }
But rather than editing 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.*; 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 implementation // result = mnrc.calltheServer(takeThis); return result; } }
Now the class we'll run rmic on and the class referenced by the setup program will be the adapter class. The setup program examples.activation.Setup3alt looks like this now:
package examples.activation; import java.rmi.*; import java.rmi.activation.*; import java.util.Properties; public class Setup3alt { // This class registers information about the ClassName // 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 // String location = "file:/home/rmi_tutorial/activation/"; // Create the rest of the parameters that will be passed to // the ActivationDesc constructor // MarshalledObject data = null; // The second argument to the ActivationDesc constructor // will be used to uniquely identify this class; it's // location is relative to theURL-formatted String, location. // ActivationDesc desc = new ActivationDesc ("examples.activation.MyNonRemoteClassAdapter", location, 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); } }*As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.