Activatable Jini services, Part 1: Implement RMI activation

The RMI activation framework can produce self-sufficient, flexible Jini services

1 2 3 Page 2
Page 2 of 3

What activation buys us

In RMI terminology, "an active object is a remote object that is instantiated and exported in a Java VM on some system." (See Resources for the RMI Specification.) Running the main() method of our previous LoanServiceImpl class resulted in an active remote object, since it instantiated and exported an instance of LoanServiceImpl. As stated in the RMI Specification, "A passive object is one that is not yet instantiated (or exported) in a Java VM, but which can be brought into an active state. Transforming a passive object into an active object is a process known as activation."

The main goal, and primary benefit, of object activation is to allow remote object servers to manage their computational resources. According to the RMI Specification, "In RMI, activation allows objects to begin execution on an as-needed basis. When an activatable remote object is accessed (via a method invocation), if that remote object is not currently executing, the system initiates the object's execution inside an appropriate VM."

Using RMI activation, the system can also deactivate an object, by causing it to become passive and freeing up its computational resources. Sometimes the RMI activation can shut down the Java VM that runs the object. When a new method call is made to that object, the system initiates the object to turn active, and possibly creates a Java VM for it to execute in.

In addition to activating and deactivating objects on demand, the RMI activation framework also maintains remote object references over long periods of time. When a service registers its proxy with a lookup service, the proxy contains a reference to an RMI remote object implementation. If that implementation stops execution, the reference inside the RMI stub -- in the Jini service object -- becomes invalid. Even if the remote object restarts, the client can no longer use the stub. The service's only option is to reregister with lookup services.

Clients that have already obtained the service object must request a new copy -- possibly as part of handling the RemoteExceptions thrown at them when they are calling methods on the old, invalid stub. This tedious approach weakens the djinn, even with simple system administration and maintenance requirements. Activation ensures that the references contained in the service object remain valid across remote object implementations. Maintaining such persistent object references is another important benefit of the RMI activation framework.

The activation infrastructure

Developers can rely on the JDK's robust infrastructure to create and maintain highly available services. Activation allows us to register an object with a third-party system component, which will assume responsibility for starting and stopping the service. Once an object registers with this component, remote method calls on the object will not fail, even if the object is idle at that moment. Instead, this third-party component will start the object's execution.

JDK versions 1.2 and above come with such a system utility: the executable program rmid. This program is Sun's implementation of an activation system. (This executable is actually a wrapper around the class sun.rmi.server.Activation.) One activator process per host typically runs. The activation system performs two main responsibilities:

  • Tracking information needed to activate objects
  • Managing Java VMs in which the objects are activated

In addition to rmid, the JDK also provides an API, located in the java.rmi.activation package, that allows the programmer to interact with the activation system.

Figure 3: Classes of the activation API

In Figure 3, the Remote interface is a marker that indicates an object's remoteness. The RMI API provides an abstract class, RemoteObject, that implements this interface and provides correct remote object behavior for the equals(), hashCode(), and toString() methods. Remote reference semantics are supported by a subclass of RemoteObject called RemoteStub. All client stubs in the RMI system are descendants of this class.

On the server side, the API provides the abstract class RemoteServer. This class abstractly provides the semantics of creating and exporting a remote object. Thus, remote object implementations could subclass RemoteServer directly or, more likely, through one of its specified subclasses -- UnicastRemoteObject or Activatable. The former, introduced in JDK 1.1, provides for nonactivatable objects, while the latter, present in the JDK since version 1.2, facilitates the creation of activatable remote objects. It is not absolutely necessary for a unicast or an activatable remote object to extend either of these, though. Java's single-inheritance mechanism might prevent a subtype of another class from extending one of the remote server types. In that case, the programmer must ensure the correct remote semantics for equals() and hashCode(), then use UnicastRemoteObject's or Activatable's exportObject() methods to make the remote object available to the RMI runtime.

There are two distinct phases of using this API:

  1. An object registers with the activation system
  2. The activation system triggers the object in response to a remote method call, and uses the activation API to create an instance of the object

Therefore, in the life cycle of an activatable object, there are two distinct VMs: the setup VM, which registers the object with the activation system, and the server VM, created by rmid to run the activated object. The setup VM exits after performing the following tasks:

  1. Specifying to the activation system the VM in which the object will execute
  2. Specifying the object to the activation system
  3. Registering the object with the activation system, thereby obtaining the object's activation ID
  4. Exporting the object to the RMI runtime

The setup program does not have to create an object's instance to register the object with the activation system. When the setup VM exits, no instance of the activatable object will be running in any VM. It is the job of the activation system to fire up a VM on demand and arrange for it to load an instance of the object.

Activate the loan service

In order to participate in the activation process, an object must meet two requirements:

  1. It must register an activation descriptor with the activation system, or activator
  2. It must provide a special activation constructor

Once an object registers with the activation system, it is ready to participate in the activation protocol.

Figure 4: The activation protocol

Figure 4 illustrates, step by step, the activation protocol outlined in the RMI Specification. The remote object, at right, is initially inactive. The client obtains a service object from the Jini lookup service, which includes the remote server's stub. The client then attempts to make a method call on the stub. The stub checks to see if a live reference to the remote object is null. If it is, the faulting remote reference it contains contacts the activation system on the remote object server. The activation system uses the activation ID passed to it by the stub to access the activation descriptor associated with the object during the registration process.

This descriptor allows the activation system to check for a preexisting activation group for the object. If not, it will create one using the group's descriptor; this might involve forking a new Java VM for the group. The activation system delegates the object's creation to the activation group, which uses the activation descriptor to create the object, including possible bootstrap data contained in a MarshalledObject. Finally, the group returns a live new object reference to the activation group, which passes this live reference back to the remote stub. The activation group notes that the object is now in active state, so subsequent method calls to it will simply return the live reference.

Register with the activation system

The purpose of registering with the activation system is to provide enough information to activate or recreate the object. The abstraction for providing information about the object is the activation descriptor, ActivationDesc. It specifies the following information:

  • The object's class name and code location are specified as strings.
  • Data the object needs to bootstrap itself must be encapsulated into a java.rmi.MarshalledObject. An instance of MarshalledObject is created by passing any serializable objects to its constructor. A marshalled object differs from a plain serialized object because it contains the location from where the object's class file can be retrieved; it's annotated with the codebase URL. When the java.rmi.server.codebase property is set for the VM that creates the MarshalledObject instance, that property's value will be annotated into the MarshalledObject. The loan service, for instance, needs to initialize the bank property file's location on the filesystem.
  • The object's restart mode specifies whether the object should be set in motion as soon as the activation system comes up (eager activation) or on an on-demand basis, when the object receives the first remote method (lazy activation). The default is lazy activation.

ActivationDesc also specifies the object's activation group. The activation protocol uses the activation group description to determine which VM to activate the object in.

VMs on demand

The RMI activation specification states that "the system initiates the object's execution inside an appropriate VM." The abstraction for this VM is the activation group or ActivationGroup; objects in the same activation group activate in the same VM. When a new activation group is created, the activator process running on the host will spawn a new VM. That VM will run as a child process, allowing the activator to notice if the VM crashes, for instance. When the next method call to an object belonging to that VM arrives, the activator will restart the VM. Remember the command line we used to fire up our original service:

      /usr/java1.3/bin/java             -Djava.security.policy=/export/services/loanservice/loanservice.policy             -Djava.rmi.server.codebase=http://objectserver:8080/             LoanServiceImpl             /export/services/loanservice/bank.property

The activation system must specify similar information to any new VM that it spawns. The ActivationGroupDesc class provides a means to specify this material. This class offers immense flexibility: you can tell the activation system to fire up a virtual machine of your choice for each group, and to pass different properties, such as security policies, to each VM.

The ActivationGroupDesc allows us to specify the VM properties, typically passed via the -D command line option with a java.util.Property object:

      Properties vmProperties = new Properties();
      vmProperties.put("java.security.policy",
            "/export/services/loanservice/loanservice.policy");
      vmProperties.put("java.rmi.server.codebase", 
            "http://objectserver:8080/");

ActivationGroupDesc.CommandEnvironment also allows us to specify the command environment needed for the group's Java VM. For instance, we can specify the executable for the Java VM and command parameters (other than properties):

      ActivationGroupDesc.CommandEnvironment commandEnv = 
            new ActivationGroupDesc.CommandEnvironment(
                  "/usr/java1.3/bin/java", 
                  new String[] {"-Xmx48"});

We are now ready to register our group with the activation system:

      ActivationGroupDesc groupDesc = 
            new ActivationGroupDesc(vmProperties, commandEnv);
      ActivationGroupID groupID = 
            ActivationGroup.getSystem().registerGroup(groupDesc);

The registration returns ActivationGroupID, which uniquely identifies this group to the activation system. To run multiple Jini services in the same VM, we could save the group's ID to disk, so other activatable objects could use it when registering. The following shows the new main() method of the loan service implementation class:

  public static void main(String[] argv) throws Exception 
  {
    //collect directory path for bank property file on command line
    String path = argv[0];
       //specify group properties
       Properties vmProperties = new Properties();
       vmProperties.put("java.security.policy",
            "/export/services/loanservice/loanservice.policy");
       vmProperties.put("java.rmi.server.codebase", 
            "http://objectserver:8080/");
       //use default command environment 
       ActivationGroupDesc groupDesc = 
            new ActivationGroupDesc(vmProperties, null);
       ActivationGroupID groupID = 
            ActivationGroup.getSystem().registerGroup(groupDesc);
       //class name and location for object
       String className = "LoanServiceImpl";
       String codeLocation = "http://objectserver:8080/";
                         
       //bootstrap data for object
       MarshalledObject data = 
            new MarshalledObject("/export/services/loanservice/bank.property");
       //this constructor will also create the group
       ActivationDesc activationDesc = 
            new ActivationDesc(groupID, className, codeLocation, data);
       RemoteLoanService service = 
             (RemoteLoanService)Activatable.register(activationDesc);
   //Join Jini lookup service. Same as before.
      //We will change this later to delegate lease management, 
      //lookup discovery, and event handling.
      JoinManager joinManager = new JoinManager(
            service, 
            null, 
            serviceIDListener, 
            null, 
            null);
      }
  } 

As I mentioned earlier, one of the activation system's responsibilities is to serve as a database of activation information. Given an activation ID, it can determine if the object is active; if not, the activation system can retrieve enough information from the object's activation description to recreate the object. The activation system saves this registration information to stable storage.

1 2 3 Page 2
Page 2 of 3