Dynamic Object Activation & Dynamic Class Downloading

1. DYNAMIC OBJECT ACTIVATION

In our previous examples, remote objects run all the time even if no method on them is invoked. Java 2 and later releases allow us to register information about remote object implementations whose instance should be created and executed on demand, instead of running all the time. In fact, the RMI system does not create an instance unless a method invocation request comes. This means, the system acts a lazy activator.

1.1. Basic idea

In the lazy activation scheme, no instance of a remote object is created by the server program in advance. Instead, information about remote object implementation (such as object’s class file name, location from which the class can be loaded, data to be used for object bootstrap etc.) is provided to an application called rmid. At this moment, the object is said to be in inactive (or passive) state. However, a stub of the remote object (which is yet to be created and exported) is obtained from this rmid and bound to the registry so that clients can get a remote reference from this registry. When, method invocation request comes, rmid checks the existences of the underlying object. If the object does not exist, a new instance is created with the help of information provided during the registration procedure. This newly created object is said to be in active state. The process of transforming a passive object into an active object is known as activation. This active object is used for a further method invocation request.

1.2. Implementation

RMI implements lazy activation using a faulting remote reference (also called a fault block). Actually, stub of a remote object contains a faulting remote reference which consists of:

  • an activation identifier for the remote object
  • a transient reference referring to the active remote object.

The activation identifier contains enough information for activating the object. The transient reference is the actual live reference to the active remote object. Initially the live reference to a remote object is null which indicates that the object is not yet active. When the first method invocation request comes, faulting reference checks the live reference and identifies that the object is in passive state. In this case, it initiates an activation protocol (discussed in the next section) to activate the object with the help of activation identifier and sets the live reference to the newly activated remote object. Once the faulting reference obtains the live reference, it forwards method invocations to the underlying remote reference which, in turn, forwards the method invocation to the remote object.

1.3. The activation protocol

This is the protocol that specifies how to activate an activatable passive object. The protocol is executed by the components: faulting reference (discussed in the previous section), activator, activation group, and the object to be activated.

Activator

This component itself does not activate the remote object. Instead, it supervises the activation process. The task of an activator is as follows:

  • It keeps a database of information (such as object’s class file name, location from which the class can be loaded, data to be used for object bootstrap etc.) necessary to activate an object.
  • It forwards requests for object activation (along with the necessary information) to the correct activation group inside a remote JVM.
  • It manages Java virtual machines, that starts up (when necessary).

Activation group

This is the component that actually activates the object with the specified activation information and returns the activated object back to the activator. The object is then said to belong to this group. The object activation occurs in the following way:

A faulting reference calls the activator (an RMI interface) with an activation identifier to activate the object associated with the identifier. The activator consults the object’s activation descriptor consisting of the following information:

  • Group identifier of the object that specifies a JVM where it has to be activated
  • Remote object’s underlying class name
  • A URL path from which the class definition of the object can be loaded
  • A file name containing initialization data for the object in marshalled (packed) form

The activator forwards the activation request to the group (if it already exists) in which this object should reside. If the activation group is not yet created, the activator creates a new activation group and then forwards the activation request to that group. The activation group reads the activation information from the object’s activation descriptor, loads the class for the object and instantiates the object using a special two argument constructor.

After activating the object, the activation group passes a marshalled object reference back to the activator. The activator then records the live reference and activation identifier pair and returns the live (active) reference to the faulting reference. The faulting reference of the stub finally forwards method invocations via the live reference directly to the remote object.

1.4. An example

We shall demonstrate RMI object activation procedure by modifying our calculator application. The following are the three steps we shall follow to modify the application:

  • Modify the implementation class.
  • Modify the Server class.
  • Modify the procedure to run the program.
1.4.1. Writing implementation class

The heart of RMI object activation is java.rmi.activation.Activatabie class that provides support for making remote objects activatable. The implementation class of an activatable remote object usually extends this class. So, let us modify our simpieCaicuiator class as follows:

public class SimpleCalculator extends Activatable implements Calculator {

//…

}

Also write the following import statement:

import java.rmi.activation.*;

Now add a two argument constructor as follows:

public SimpleCalculator(ActivationID id, MarshalledObject data) throws

RemoteException { super(id, 0);

System.out.println(”SimpleCalculator Instantiated.”);

}

The implementation of add() method remains unchanged. Here is the complete source code (written in SimpleCalculator.java) of the modified class:

//SimpleCalculator.java

import java.rmi.*;

import java.rmi.activation.*;

public class SimpleCalculator extends Activatable implements Calculator {

public SimpleCalculator(ActivationID id, MarshalledObject data) throws RemoteException {

super(id, 0);

System.out.println(“SimpleCalculator Instantiated.”);

}

public int add(int a, int b) {

System.out.println(”Received: ” + a + ” and ” + b);

int result = a + b;

System.out.println(”Sent: ” + result);

return result;

}

}

The modified portion has been shown with bold face font.

1.4.2. Writing server class

The server itself does not create an instance of SimpleCalculator. Instead, it gathers necessary information to create an activatable remote object. The server then forwards this information to rmid, gets a reference to an instance of the activatable class’s stub class and finally registers this stub with the rmiregistry. The server then quits silently. The client can consult rmiregistry to get a remote reference.

Each activatable object is said to belong to a group called activation group. This activation group is responsible for activating the object on demand. So, we have to get an ID of an activation group. The activation group may be an existing one or a new one (if desired one does not exist). For that reason, we create an activation group descriptor. The task of the activation group descriptor is to provide all the information to rmid so that it can contact the appropriate existing JVM or spawn a new JVM for the activatable object. It contains the following necessary information:

  • The class name of the group to be created
  • Location from which the group’s class definition may be loaded
  • A “marshalled” object that contains group-specific bootstrap data.

An activation group descriptor is created using ActivationGroupDesc class. It has two constructors as follows:

ActivationGroupDesc(Properties overrides,

ActivationGroupDesc.CommandEnvironment cmd)

ActivationGroupDesc(String className, String location, MarshalledObject<?> data,

Properties overrides, ActivationGroupDesc.CommandEnvironment cmd)

The first constructor (which we shall use in our application) constructs a group descriptor using the default group implementation and code location. Properties are used to override system properties in the group implementation’s VM. The command environment controls the command/options used to start a child VM, or can be null if rmid’s default is accepted. An activation group descriptor is created as follows:

Properties props = new Properties();

props.put(”java.security.policy”, “policy”);

ActivationGroupDesc aGroup = new ActivationGroupDesc(props, null);

It constructs a group descriptor that uses the system defaults for group implementation, code location and command environment. Since rmid runs under a security sandbox, we have specified security policy stored in a file “policy”. For simplicity, we use a policy file that gives global permission to anyone from anywhere as follows:

grant {

// Allow everything for now

permission java.security.AllPermission;

}

This group activation descriptor is then forwarded to the rmid as follows:

ActivationGroupID id = ActivationGroup.getSystem().registerGroup(aGroup);

The getSystem() method returns a reference to the ActivationSystem (rmid), which then registers the specified activation group descriptor. The rmid then contacts the appropriate existing JVM or spawn a new JVM for the activatable object and returns an id representing the group. This id is then used to create an activation descriptor of the activatable object as follows:

ActivationDesc desc = new ActivationDesc (id, ”SimpleCalculator”, null, null);

The task of an activation descriptor is to provide all the information that rmid requires to create a new instance of the implementation class. The second argument is the name of the implementation class (simpieCaicuiator in our case) for the activatable object. We assume that the location of simpieCaicuiator’s class is same as rmid’s start location. That’s why the third argument is specified as nuii. In case the location of the class definition is different, specify it using a URL. We also do not want to pass activatable object’s initialization parameter. So, the last argument is also null.

Finally, register the activation descriptor with the rmid as follows:

Caicuiator stub = (Caicuiator)Activatabie.register(desc);

The static register () method of Activatabie class registers an activation descriptor and returns a reference to the stub of the object to be activated on demand. This stub can then be registered to the rmiregistry as follows:

Naming.rebind(“caicuiator”, stub );

The complete source code for the server (stored in server.java) is given below:

import java.util.*;

import java.rmi.*;

import java.rmi.activation.*;

public class Server {

public static void main( String args[] ) throws Exception     {

Properties props = new Properties();

props.put(”java.security.policy”, “policy”);

ActivationGroupDesc aGroup = new ActivationGroupDesc(props, null);

System.out.print(”Registering activation group descriptor…”);

ActivationGroupID id = ActivationGroup.getSystem().registerGroup(aGroup);

System.out.println(”[ OK ]”);

ActivationDesc desc = new ActivationDesc (id, ”SimpleCalculator”, null,

null);

System.out.print(”Registering activation descriptor…      ”);

Calculator stub = (Calculator)Activatable.register(desc);

System.out.println(”[ OK ]”);

System.out.println(”Obtained stub for the SimpleCalculator”);

Naming.rebind(”calculator”, stub );

System.out.println(”Stub for SimpleCalculator bound in registry”);

}

}

1.4.3. Compiling and running the program

Put all the four files Calculator.java, SimpleCalculator.java, Server.java and policy in a directory. In our computer, we have put these files in the directory E:\Net\rmi\activation. Now, go to the directory and compile these programs as follows:

javac *.java

To run the application, we have to start the Java’s rmid application. Go to the directory and use the following command to start rmid:

start rmid -J-Djava.security.policy=policy

Now start the rmiregistry as follows:

start rmiregistry

Now run the server as follows:

java Server

A sample result is shown in Figure 14.14:

Assume that rmid and rmiregistry are running in a computer having IP address 172.16.5.81. Our previous calculator client program can now be started from a remote computer as follows:

java CalculatorClient 172.16.5.81

A sample result is shown in Figure 14.15: (i). A sample output of rmid terminal is shown in Figure 15.14: (ii)

2. DYNAMIC CLASS DOWNLOADING

One of the important features of Java RMI is that it allows us to dynamically download the definition of the object’s class if the same is not available in the receiver’s JVM. Figure 14.16: illustrates how classes are downloaded from a client to a server and from a server to a client using the URL protocol .

It is possible to transmit all of the fields and methods of an object to another, possibly remote, Java virtual machine. The behaviour of the objects remain unchanged when they are transmitted to another JVM as RMI passes objects by their actual classes. This facility enables new types and functionalities to be migrated into a remote JVM, thus dynamically enhancing the behaviour of an RMI application.

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *