CONTENTS | PREV | NEXT Java Remote Method Invocation


7.4 Activation Interfaces

In the RMI activation protocol, there are two guarantees that the activator must make for the system to function properly:

  • like all system daemons, the activator should remain running while the machine is up, and

  • the activator must not reactivate remote objects that are already active.
The activator maintains a database of appropriate information for the groups and objects that it participates in activating.


7.4.1 The Activator Interface

The activator is one of the entities that participates during the activation process. As described earlier, a faulting reference (inside a stub) calls the activator's activate method to obtain a "live" reference to an activatable remote object. Upon receiving a request for activation, the activator looks up the activation descriptor for the activation identifier, id, determines the group in which the object should be activated, and invokes the newInstance method on the activation group's instantiator (the remote interface ActivationGroup is described below). The activator initiates the execution of activation groups as necessary. For example, if an activation group for a specific group descriptor is not already executing, the activator will spawn a child JVM for the activation group to establish the group in the new JVM.

The activator is responsible for monitoring and detecting when activation groups fail so that it can remove stale remote references from its internal tables.

package java.rmi.activation;

public interface Activator extends java.rmi.Remote { java.rmi.MarshalledObject activate(ActivationID id, boolean force) throws UnknownObjectException, ActivationException, java.rmi.RemoteException; }

The activate method activates the object associated with the activation identifier, id. If the activator knows the object to be active already and the force parameter is false, the stub with a "live" reference is returned immediately to the caller; otherwise, if the activator does not know that the corresponding remote object is active or the force parameter is true, the activator uses the activation descriptor information (previously registered to obtain the id) to determine the group (JVM) in which the object should be activated. If an ActivationInstantiator corresponding to the object's group already exists, the activator invokes the activation instantiator's newInstance method, passing it the id and the object's activation descriptor.

If the activation instantiator (group) for the object's group descriptor does not yet exist, the activator starts a new incarnation of an ActivationInstantiator executing (by spawning a child process, for example). When the activator re-creates an ActivationInstantiator for a group, it must increment the group's incarnation number. Note that the incarnation number is zero-based. The activation system uses incarnation numbers to detect late ActivationSystem.activeGroup and ActivationMonitor.inactiveGroup calls. The activation system discards calls with an earlier incarnation number than the current number for the group.


Note - The activator must communicate both the activation group's identifier, descriptor, and incarnation number when it starts up a new activation group. The activator spawns an activation group in a separate JVM (as a separate or child process, for example), and therefore must pass information specifying the information necessary to create the group via the ActivationGroup.createGroup method. How the activator sends this information to the spawned process is unspecified, however, this information could be sent in the form of marshalled objects to the child process's standard input.
When the activator receives the activation group's call back (via the ActivationSystem.activeGroup method) specifying the activation group's reference and incarnation number, the activator can then invoke that activation instantiator's newInstance method to forward each pending activation request to the activation instantiator and return the result (a marshalled remote object reference, a stub) to each caller.

Note that the activator receives a MarshalledObject instead of a Remote object so that the activator does not need to load the code for that object, or participate in distributed garbage collection for that object. If the activator kept a strong reference to the remote object, the activator would then prevent the object from being garbage collected under the normal distributed garbage collection mechanism.

The activate method throws ActivationException if activation fails. Activation may fail for a variety of reasons: the class could not be found, the activation group could not be contacted, etc. The activate method throws UnknownObjectException if no activation descriptor for the activation identifier, id, has been previously registered with this activator. RemoteException is thrown if the remote call to the activator fails.


7.4.2 The ActivationSystem Interface

The ActivationSystem provides a means for registering groups and activatable objects to be activated within those groups. The ActivationSystem works closely with both the Activator, which activates objects registered via the ActivationSystem, and the ActivationMonitor, which obtains information about active and inactive objects and inactive groups.

package java.rmi.activation;

public interface ActivationSystem extends java.rmi.Remote { public static final int SYSTEM_PORT = 1098; ActivationGroupID registerGroup(ActivationGroupDesc desc) throws ActivationException, java.rmi.RemoteException; ActivationMonitor activeGroup(ActivationGroupID id, ActivationInstantiator group, long incarnation) throws UnknownGroupException, ActivationException, java.rmi.RemoteException; void unregisterGroup(ActivationGroupID id) throws ActivationException, UnknownGroupException, java.rmi.RemoteException; ActivationID registerObject(ActivationDesc desc) throws ActivationException, UnknownGroupException, java.rmi.RemoteException; void unregisterObject(ActivationID id) throws ActivationException, UnknownObjectException, java.rmi.RemoteException; void shutdown() throws java.rmi.RemoteException; }


Note - As a security measure, all of the above methods (registerGroup, activeGroup, unregisterGroup, registerObject, unregisterObject, and shutdown) will throw java.rmi.AccessException, a subclass of java.rmi.RemoteException, if called from a client that does not reside on the same host as the activation system.
The registerObject method is used to register an activation descriptor, desc, and obtain an activation identifier for an activatable remote object. The ActivationSystem creates an ActivationID (an activation identifier) for the object specified by the descriptor, desc, and records, in stable storage, the activation descriptor and its associated identifier for later use. When the Activator receives an activate request for a specific identifier, it looks up the activation descriptor (registered previously) for the specified identifier and uses that information to activate the object. If the group referred to in desc is not registered with this system, then the method throws UnknownGroupException. If registration fails (e.g., database update failure, etc), then the method throws ActivationException. If the remote call fails, then RemoteException is thrown.

The unregisterObject method removes the activation identifier, id, and associated descriptor previously registered with the ActivationSystem. After the call completes, the object can no longer be activated via the object's activation id. If the object id is unknown (not registered) the method throws UnknownObjectException. If the unregister operation fails (e.g., database update failure, etc.), then the method throws ActivationException. If the remote call fails, then RemoteException is thrown.

The registerGroup method registers the activation group specified by the group descriptor, desc, with the activation system and returns the ActivationGroupID assigned to that group. An activation group must be registered with the ActivationSystem before objects can be registered within that group. If group registration fails, the method throws ActivationException. If the remote call fails, then RemoteException is thrown.

The activeGroup method is a call back from the ActivationGroup (with the identifier, id), to inform the activation system that group is now active and is the ActivationInstantiator for that JVM. This call is made internally by the ActivationGroup.createGroup method to obtain an ActivationMonitor that the group uses to update the system regarding objects' and the group's status (i.e., that the group or objects within that group have become inactive). If the group is not registered, then the method throws UnknownGroupException. If the group is already active, then ActivationException is thrown. If the remote call to the activation system fails, then RemoteException is thrown.

The unregisterGroup method removes the activation group with identifier, id, from the activation system. An activation group makes this call back to inform the activator that the group should be destroyed. If this call completes successfully, objects can no longer be registered or activated within the group. All information of the group and its associated objects is removed from the system. The method throws UnknownGroupException if the group is not registered. If the remote call fails, then RemoteException is thrown. If the unregister fails, ActivationException is thrown (e.g., database update failure, etc.).

The shutdown method gracefully terminates (asynchronously) the activation system and all related activation processes (activator, monitors and groups). All groups spawned by the activation daemon will be destroyed and the activation daemon will exit. In order to shut down the activation system daemon, rmid, execute the command:

        rmid -stop [-port num]
This command will shut down the activation daemon on the specified port (if no port is specified, the daemon on the default port will be shut down).


7.4.3 The ActivationMonitor Class

An ActivationMonitor is specific to an ActivationGroup and is obtained when a group is reported via a call to ActivationSystem.activeGroup (this is done internally by the ActivationGroup.createGroup method). An activation group is responsible for informing its ActivationMonitor when:

a. its objects become active,

b. its objects become inactive, or

c. the group as a whole becomes inactive.


package java.rmi.activation;

public interface ActivationMonitor extends java.rmi.Remote { public abstract void inactiveObject(ActivationID id) throws UnknownObjectException, RemoteException; public void activeObject(ActivationID id, java.rmi.MarshalledObject mobj) throws UnknownObjectException, java.rmi.RemoteException; public void inactiveGroup(ActivationGroupID id, long incarnation) throws UnknownGroupException, java.rmi.RemoteException; }

An activation group calls its monitor's inactiveObject method when an object in its group becomes inactive (deactivates). An activation group discovers that an object (that it participated in activating) in its JVM is no longer active via a call to the activation group's inactiveObject method.

The inactiveObject call informs the ActivationMonitor that the remote object reference it holds for the object with the activation identifier, id, is no longer valid. The monitor considers the reference associated with id as a stale reference. Since the reference is considered stale, a subsequent activate call for the same activation identifier results in re-activating the remote object. If the object is not known to the ActivationMonitor, the method throws UnknownObjectException. If the remote call fails, then RemoteException is thrown.

The activeObject call informs the ActivationMonitor that the object associated with id is now active. The parameter obj is the marshalled representation of the object's stub. An ActivationGroup must inform its monitor if an object in its group becomes active by other means than being activated directly by the system (i.e., the object is registered and "activated" itself). If the object id is not previously registered, then the method throws UnknownObjectException. If the remote call fails, then RemoteException is thrown.

The inactiveGroup call informs the monitor that the group specified by id and incarnation is now inactive. The group will be re-created with a greater incarnation number upon a subsequent request to activate an object within the group. A group becomes inactive when all objects in the group report that they are inactive. If either the group id is not registered or the incarnation number is smaller than the current incarnation for the group, then the method throws UnknownGroupException. If the remote call fails, then RemoteException is thrown.


7.4.4 The ActivationInstantiator Class

The ActivationInstantiator is responsible for creating instances of activatable objects. A concrete subclass of ActivationGroup implements the newInstance method to handle creating objects within the group.

package java.rmi.activation;

public interface ActivationInstantiator extends java.rmi.Remote { public MarshalledObject newInstance(ActivationID id, ActivationDesc desc) throws ActivationException, java.rmi.RemoteException; }

The activator calls an instantiator's newInstance method in order to re-create in that group an object with the activation identifier, id, and descriptor, desc. The instantiator is responsible for:

  • determining the class for the object using the descriptor's getClassName method,
  • loading the class from the codebase path obtained from the descriptor (using the getLocation method),
  • creating an instance of the class by invoking the special "activation" constructor of the object's class that takes two arguments: the object's ActivationID, and the MarshalledObject containing object-specific initialization data, and
  • returning a MarshalledObject containing the remote object it created.
An instantiator is also responsible for reporting when objects it creates or activates are no longer active, so that it can make the appropriate inactiveObject call to its ActivationMonitor (see the ActivationGroup class for more details).

If object activation fails, then the newInstance method throws ActivationException. If the remote call fails, then the method throws RemoteException.


7.4.5 The ActivationGroupDesc Class

An activation group descriptor (ActivationGroupDesc) contains the information necessary to create or re-create an activation group in which to activate objects in the same JVM.

Such a descriptor contains:

  • the group's class name (a class name of null indicates the default ActivationGroup implementation),
  • the group's codebase path (the location of the group's class), and
  • a "marshalled" object that can contain object-specific initialization data.
The group's class must be a concrete subclass of ActivationGroup. A subclass of ActivationGroup is created or re-created via the ActivationGroup.createGroup static method, which invokes a special constructor that takes two arguments:

  • the group's ActivationGroupID, and
  • the group's initialization data (in a java.rmi.MarshalledObject)

package java.rmi.activation;

public final class ActivationGroupDesc implements java.io.Serializable { public ActivationGroupDesc(java.util.Properties props, CommandEnvironment env);; public ActivationGroupDesc(String className, String codebase, java.rmi.MarshalledObject data, java.util.Properties props, CommandEnvironment env); public String getClassName(); public String getLocation(); public java.rmi.MarshalledObject getData(); public CommandEnvironment getCommandEnvironment(); public java.util.Properties getPropertiesOverrides(); }

The first constructor creates a group descriptor that uses system default for group implementation and code location. Properties specify Java application environment overrides (which will override system properties in the group implementation's JVM). The command environment can control the exact command/options used in starting the child JVM, or can be null to accept rmid's default. This constructor will create an ActivationGroupDesc with a null group class name, which indicates the system's default ActivationGroup implementation.

The second constructor is the same as the first, but allows the specification of Properties and CommandEnvironment.

The getClassName method returns the group's class name (possibly null). A null group class name indicates the system's default ActivationGroup implementation.

The getLocation method returns the codebase path from where the group's class can be loaded.

The getData method returns the group's initialization data in marshalled form.

The getCommandEnvironment method returns the command environment (possibly null).

The getPropertiesOverrides method returns the properties overrides (possibly null) for this descriptor.


7.4.6 The ActivationGroupDesc.CommandEnvironment Class

The CommandEnvironment class allows overriding default system properties and specifying implemention-defined options for an ActivationGroup.

public static class CommandEnvironment 
    implements java.io.Serializable
{
    public CommandEnvironment(String cmdpath, String[] args);
    public boolean equals(java.lang.Object);
    public String[] getCommandOptions();
    public String getCommandPath();
    public int hashCode();


}
The constructor creates a CommandEnvironment with the given command, cmdpath, and additional command line options, args.

The equals implements content equality for command environment objects. The hashCode method is implemented appropriately so that a CommandEnvironment can be stored in a hash table if necessary.

The getCommandOptions method returns the environment object's command line options.

The getCommandPath method returns the environment object's command string.


7.4.7 The ActivationGroupID Class

The identifier for a registered activation group serves several purposes:

  • it identifies the group uniquely within the activation system, and

  • it contains a reference to the group's activation system so that the group can contact its activation system when necessary.
The ActivationGroupID is returned from the call to ActivationSystem.registerGroup and is used to identify the group within the activation system. This group identifier is passed as one of the arguments to the activation group's special constructor when an activation group is created or re-created.

package java.rmi.activation;

public class ActivationGroupID implements java.io.Serializable { public ActivationGroupID(ActivationSystem system); public ActivationSystem getSystem(); public boolean equals(Object obj); public int hashCode(); }

The ActivationGroupID constructor creates a unique group identifier whose ActivationSystem is system.

The getSystem method returns the activation system for the group.

The hashCode method returns a hashcode for the group's identifier. Two group identifiers that refer to the same remote group will have the same hash code.

The equals method compares two group identifiers for content equality. The method returns true if both of the following conditions are true: 1) the unique identifiers are equivalent (by content), and 2) the activation system specified in each refers to the same remote object.


7.4.8 The ActivationGroup Class

An ActivationGroup is responsible for creating new instances of activatable objects in its group, informing its ActivationMonitor when:

a. its objects become active,

b. its objects become inactive, or

c. the group as a whole becomes inactive.

An ActivationGroup is initially created in one of several ways:

  • as a side-effect of creating a "default" ActivationDesc for an object, or
  • by an explicit call to the ActivationGroup.createGroup method, or
  • as a side-effect of activating the first object in a group whose ActivationGroupDesc was only registered.
Only the activator can re-create an ActivationGroup. The activator spawns, as needed, a separate JVM (as a child process, for example) for each registered activation group and directs activation requests to the appropriate group. It is implementation specific how JVMs are spawned. An activation group is created via the ActivationGroup.createGroup static method. The createGroup method has two requirements on the group to be created: 1) the group must be a concrete subclass of ActivationGroup, and 2) the group must have a constructor that takes two arguments:

  • the group's ActivationGroupID, and
  • the group's initialization data (in a MarshalledObject)
When created, the default implementation of ActivationGroup will set the system properties to the system properties in force when the ActivationGroupDesc was created, and will set the security manager to the java.rmi.RMISecurityManager. If your application requires some specific properties to be set when objects are activated in the group, the application should set the properties before creating any ActivationDescs (before the default ActivationGroupDesc is created).

package java.rmi.activation;

public abstract class ActivationGroup extends UnicastRemoteObject implements ActivationInstantiator { protected ActivationGroup(ActivationGroupID groupID) throws java.rmi.RemoteException; public abstract MarshalledObject newInstance(ActivationID id, ActivationDesc desc) throws ActivationException, java.rmi.RemoteException; public abstract boolean inactiveObject(ActivationID id) throws ActivationException, UnknownObjectException, java.rmi.RemoteException; public static ActivationGroup createGroup(ActivationGroupID id, ActivationGroupDesc desc, long incarnation) throws ActivationException; public static ActivationGroupID currentGroupID(); public static void setSystem(ActivationSystem system) throws ActivationException; public static ActivationSystem getSystem() throws ActivationException; protected void activeObject(ActivationID id, java.rmi.MarshalledObject mobj) throws ActivationException, UnknownObjectException, java.rmi.RemoteException; protected void inactiveGroup() throws UnknownGroupException, java.rmi.RemoteException; }

The activator calls an activation group's newInstance method in order to activate an object with the activation descriptor, desc. The activation group is responsible for:

  • determining the class for the object using the descriptor's getClassName method,
  • loading the class from the URL path obtained from the descriptor (using the getLocation method),
  • creating an instance of the class by invoking the special constructor of the object's class that takes two arguments: the object's ActivationID, and a MarshalledObject containing the object's initialization data, and
  • returning a serialized version of the remote object it just created to the activator.
The method throws ActivationException if the instance for the given descriptor could not be created.

The group's inactiveObject method is called indirectly via a call to the Activatable.inactive method. A remote object implementation must call Activatable's inactive method when that object deactivates (the object deems that it is no longer active). If the object does not call Activatable.inactive when it deactivates, the object will never be garbage collected since the group keeps strong references to the objects it creates.

The group's inactiveObject method unexports the remote object, associated with id (only if there are no pending or executing calls to the remote object) from the RMI runtime so that the object can no longer receive incoming RMI calls. If the object currently has pending or executing calls, inactiveObject returns false and no action is taken.

If the unexportObject operation was successful (meaning that the object has no pending or executing calls), the group informs its ActivationMonitor (via the monitor's inactiveObject method) that the remote object is not currently active so that the remote object will be reactivated by the activator upon a subsequent activation request. If the operation was successful, inactiveObject returns true. The operation may still succeed if the object is considered active by the ActivationGroup but has already been unexported.

The inactiveObject method throws an UnknownObjectException if the activation group has no knowledge of this object (e.g., the object was previously reported as inactive, or the object was never activated via the activation group). If the inactive operation fails (e.g., if the remote call to the activator or activation group fails), RemoteException is thrown.

The createGroup method creates and sets the activation group for the current JVM. The activation group can only be set if it is not currently set. An activation group is set using the createGroup method when the Activator initiates the re-creation of an activation group in order to carry out incoming activate requests. A group must first register a group descriptor with the ActivationSystem before it can be created via this method (passing it the ActivationID obtained from previous registration).

The group specified by the ActivationGroupDesc, desc, must be a concrete subclass of ActivationGroup and have a public constructor that takes two arguments; the ActivationGroupID for the group and a MarshalledObject containing the group's initialization data (obtained from its ActivationGroupDesc). If the ActivationGroupDesc.getClassName method returns null, the system's default group implementation is used. Note: if your application creates its own custom activation group, the group must set a security manager in the constructor, or objects cannot be activated in the group.

After the group is created, the ActivationSystem is informed that the group is active by calling the activeGroup method, which returns the ActivationMonitor for the group. The application need not call activeGroup independently since that callback is taken care of by the createGroup method.

Once a group is created, subsequent calls to the currentGroupID method will return the identifier for this group until the group becomes inactive, at which point the currentGroupID method will return null.

The parameter incarnation indicates the current group incarnation, i.e., the number of times the group has been activated. The incarnation number is used as a parameter to the activeGroup method, once the group has been successfully created. The incarnation number is zero-based. If the group already exists, or if an error occurs during group creation, the createGroup method throws ActivationException.

The setSystem method sets the ActivationSystem, system, for the JVM. The activation system can only be set if no group is currently active. If the activation system is not set via an explicit call to setSystem, then the getSystem method will attempt to obtain a reference to the ActivationSystem by looking up the name java.rmi.activation.ActivationSystem in the Activator's registry. By default, the port number used to look up the activation system is defined by ActivationSystem.SYSTEM_PORT. This port can be overridden by setting the property java.rmi.activation.port. If the activation system is already set when setSystem is called, the method throws ActivationException.

The getSystem method returns the activation system for the JVM. The activation system may be set by the setSystem method (described above).

The activeObject method is a protected method used by subclasses to make the activeObject call back to the group's monitor to inform the monitor that the remote object with the specified activation id and whose stub is contained in mobj is now active. The call is simply forwarded to the group's ActivationMonitor.

The inactiveGroup method is a protected method used by subclasses to inform the group's monitor that the group has become inactive. A subclass makes this call when each object the group participated in activating in the JVM has become inactive.


7.4.9 The MarshalledObject Class

A MarshalledObject is a container for an object that allows that object to be passed as a parameter in an RMI call, but postpones deserializing the object at the receiver until the application explicitly requests the object (via a call to the container object). The Serializable object contained in the MarshalledObject is serialized and deserialized (when requested) with the same semantics as parameters passed in RMI calls, which means that any remote object in the MarshalledObject is represented by a serialized instance of its stub. The object contained by the MarshalledObject may be a remote object, a non-remote object, or an entire graph of remote and non-remote objects.

When an object is placed inside the MarshalledObject wrapper, the serialized form of the object is annotated with the codebase URL (where the class can be loaded); likewise, when the contained object is retrieved from its MarshalledObject wrapper, if the code for the object is not available locally, the URL (annotated during serialization) is used to locate and load the bytecodes for the object's class.

package java.rmi;

public final class MarshalledObject implements java.io.Serializable { public MarshalledObject(Object obj) throws java.io.IOException; public Object get() throws java.io.IOException, ClassNotFoundException; public int hashCode(); public boolean equals(); }

MarshalledObject's constructor takes a serializable object, obj, as its single argument and holds the marshalled representation of the object in a byte stream. The marshalled representation of the object preserves the semantics of objects that are passed in RMI calls:

  • each class in the stream is annotated with its codebase URL so that when the object is reconstructed (by a call to the get method), the bytecodes for each class can be located and loaded, and
  • remote objects are replaced with their proxy stubs.
When an instance of the class MarshalledObject is written to a java.io.ObjectOutputStream, the contained object's marshalled form (created during construction) is written to the stream; thus, only the byte stream is serialized.

When a MarshalledObject is read from a java.io.ObjectInputStream, the contained object is not deserialized into a concrete object; the object remains in its marshalled representation until the marshalled object's get method is called.

The get method always reconstructs a new copy of the contained object from its marshalled form. The internal representation is deserialized with the semantics used for unmarshalling parameters for RMI calls. So, the deserialization of the object's representation loads class code (if not available locally) using the URL annotation embedded in the serialized stream for the object.

The hashCode of the marshalled representation of the object is the same as the object passed to the constructor. The equals method will return true if the marshalled representation of the objects being compared are equivalent. The comparison that equals uses ignores a class's codebase annotation, meaning that two objects are equivalent if they have the same serialized representation except for the codebase of each class in the serialized representation.





CONTENTS | PREV | NEXT