back to API     back to index     prev     next  

Active Objects : creation and advanced concepts

Active Object: creation and advanced concepts

Active objects are created on a per-object basis: an application can contain active as well as passive instances of a given class. In the remaining part of this section, we will consider that we want to create an active instance of class example.A. Although almost any object can be turned in an Active Object, there are some restrictions that we will detail below.

Any method call m done on a given instance a of A would result in the invocation of the method m on a by the caller thread. By contrast, the same call done on the active object aa created from A would result into placing a request embedding the method call for m in the request queue of the active object aa. Then, later on, the active thread of aa would eventually pick-up and serve the request for the method m. That would result in the invocation of m on the reified object a by the active thread.

The code for creating a passive instance of A could be :

    A a = new A(26, "astring");

In ProActive there are two ways to create active objects. One way is to use ProActive.newActive and is based on the instantiation of a new object, the other is to use ProActive.turnActive and is based on the use of an existing object.

Instantiation-Based Creation

When using instantiation based creation, any argument passed to the constructor of the reified object through ProActive.newActive is serialized and passed by copy to the object. This is because the model behind ProActive is uniform whether the active object is instantiated locally or remotely. The parameters are therefore guaranteed to be passed by copy to the constructor. When using ProActive.newActive you must make sure that the arguments of the constructor are Serializable. On the other hand, the class used to create the active object does not need to be Serializable even in the case of remotely created active objects. Remind also that to be created properly, the reified object must have a declared empty no-args constructor.

    A a;
    Object[] params = new Object[] { new Integer (26), "astring" };
    try {
      a = (A) ProActive.newActive("example.A", params);
    } catch (ActiveObjectCreationException e) {
      // creation of ActiveObject failed
      e.printStackTrace();
    }
    catch(NodeException ex){
    	ex.printStackTrace();
    }

This code creates an active object of class A in the local JVM. If the invocation of the constructor of class A throws an exception, it is placed inside an exception of type ActiveObjectCreationException. When the call to newActive returns, the active object has been created and its active thread is started.

Possible ambiguities on the constructor

The first parameter of newActive is a string containing the fully-qualified name of the class we want to make active. Parameters to the constructor have to be passed as an array of Object. Then, according to the type of the elements of this array, the ProActive runtime determines which constructor of class A to call. Nevertheless, there is still room for some ambiguity in resolving the constructor because :

    public A (int i) {
        //
    }

    public A (Integer i) {
        //
    }

    public A (int i, String s) {
        //
    }

    public A (int i, Vector v) {
        //
    }

Using a Node

It is possible to pass a third parameter to the call to newActive in order to create the new active object on a specific JVM, possibly remote. The JVM is identified using a Node object that offers the minimum services ProActive needs on a given JVM to communicate with this JVM. If that parameter is not given, the active object is created in the current JVM and is attached to a default Node.

A node is identified by a node URL which is formed using the protocol, the hostname hosting the JVM where is the node located and the name of the node. The NodeFactory allows to create or lookup nodes. The method newActive can take in parameter a nodeURL as a String or a Node object that points to an existing node. Here an example :

      a = (A) ProActive.newActive("example.A", params, "rmi://pluto.inria.fr/aNode");
  or
      Node node = NodeFactory.getNode("rmi://pluto.inria.fr/aNode");
      a = (A) ProActive.newActive("example.A", params, node);

Object-Based Creation

Object-based creation is used for turning an existing passive object instance into an active one. It has been introduced in ProActive as an answer to the following problem. Consider, for example, that an instance of class A is created inside a library and returned as the result of a method call. As a consequence, we do not have access to the source code where the object is created, which prevents us for modifying it for creating an active instance of A. Even if it were possible, it may not be likely since we do not want to get an active instance of A for every call on this method.

When using object based creation, you create the object that is going to be reified as an active object before hand. Therefore there is no serialization involved when you create the object. When you invoke ProActive.turnActive on the object two cases are possible. If you create the active object locally (on a local node), it will not be serialized. If you create the active object remotely (on a remote node), the reified object will be serialized. Therefore, if the turnActive is done on a remote node, the class used to create the active object this way has to be Serializable. In addition, when using turnActive, care must be taken that no other references to the originating object are kept by other objects after the call to turnActive. A direct call to a method of the originating object without passing by a ProActive stub on this object will break the model.

Code for object-based creation looks like this :

    A a = new A (26, "astring");
    a = (A) ProActive.turnActive(a);

As for newActive, the second parameter of turnActive if given is the location of the active object to be created. No parameter or null means that the active object is created locally in the current node.

When using this method, the programmer has to make sure that no other reference on the passive object a exist after the call to turnActive. If such references were used for calling methods directly on the passive A (without going through its body), the model would no more be consistent and specialization of synchronization could no more be guaranteed.

Specifying the activity of an active object

Customizing the activity of the active object is at the core of ProActive because it allows to specify fully the behavior of an active object. By default, an object turned into an active object serves its incoming requests in a FIFO manner. In order to specify another policy for serving the requests or to specify any other behaviors one can implement interfaces defining methods that will be automatically called by ProActive.

It is possible to specify what to do before the activity starts, what the activity is and what to do after it ends. The three steps are :

Three interfaces are used to define and implement each step :

In case of a migration, an active object stops and restarts its activity automatically without invoking the init or ending phases. Only the activity itself is restarted.

Two ways are possible to define each of the three phases of an active object.

Note that the methods defined by those 3 interfaces are guaranted to be called by the active thread of the active object.

Algorithms for deciding what activity to invoke

The algorithms that decide for each phase what to do are the following (activity is the eventual object passed as a parameter to newActive or turnActive) :

InitActive
if activity is non null and implements InitActive
  we invoke the method initActivity defined in the object activity
else if the class of the reified object implements InitActive
  we invoke the method initActivity of the reified object
else
  we don't do any initialization
RunActive
if activity is non null and implements RunActive
  we invoke the method runActivity defined in the object activity
else if the class of the reified object implements RunActive
  we invoke the method runActivity of the reified object
else
  we run the standard FIFO activity
EndActive
if activity is non null and implements EndActive
  we invoke the method endActivity defined in the object activity
else if the class of the reified object implements EndActive
  we invoke the method endActivity of the reified object
else
  we don't do any cleanup

Implementing the interfaces directly in the class used to create the active object

This is the easiest solution when you do control the class that you make active. Depending on which phase in the life of the active object you want to customize, you implement the corresponding interface (one or more) amongst InitActive, RunActive and EndActive. Here is an example that has a custom initialization and activity.

Example1:

  import org.objectweb.proactive.*;

  public class A implements InitActive, RunActive {
    	private String myName;

   	 public String getName() {
     	 return myName;
    	}

    	// -- implements InitActive
    	public void initActivity(Body body) {
      	myName = body.getName();
    	}

    	// -- implements RunActive for serving request in a LIFO fashion
   	 public void runActivity(Body body) {
      	Service service = new Service(Body);
      	while (body.isActive()) {
       	 service.blockingServeYoungest();
      	}
    	}

    	public static void main(String[] args) throws Exception {
      	A a = (A) ProActive.newActive("A",null);
      	System.out.println("Name = "+a.getName());
    	}
  }

Example2: start, stop, suspend, restart a simulation algorithm in runActivity method

import org.objectweb.proactive.*;

public class Simulation implements RunActive {

      private boolean stoppedSimulation=false;
	private boolean startedSimulation=false
	private boolean suspendedSimulation=false;
	private boolean notStarted = true;

	public void startSimulation(){
	//Simulation starts
	notStarted = false;
	startedSimulation=true;
	}
	public void restartSimulation(){
	//Simulation is restarted
	startedSimulation=true;
	suspendedSimulation=false;
	}

	public void suspendSimulation(){
	//Simulation is suspended
	suspendedSimulation=true;
	startedSimulation = false;
	}

	public void stoppedSimulation(){
	//Simulation is stopped
	stoppedSimulation=true;
	}

	public void runActivity(Body body) {
     	 Service service = new Service(Body);
      	while (body.isActive()) {
      	//If the simulation is not yet started wait until startSimulation method
   	if(notStarted) service.blockingServeOldest(startSimulation());
       // If the simulation is started serve request with FIFO
     	 if(startedSimulation) service.blockingServeOldest();
      	// If simulation is suspended wait until restartSimulation method
      	if(suspendedSimulation) service.blockingServeOldest(restartSimulation());
      	// If simulation is stopped, exit
      	if(stoppedSimulation) exit();
	}
}

Passing an object implementing the interfaces when creating the active object

This is the solution to use when you do not control the class that you make active or when you want to write generic activities policy and reused them with several active objects. Depending on which phase in the life of the active object you want to customize, you implement the corresponding interface (one or more) amongst InitActive, RunActive and EndActive. Here an example that has a custom activity.

Comparing to the solution above where interfaces are directly implemented in the reified class, there is one restriction here : you cannot access the internal state of the reified object. Using an external object should therefore be used when the implementation of the activity is generic enough not to have to access the member variables of the reified object.

  import org.objectweb.proactive.*;
  public class LIFOActivity implements RunActive {
    // -- implements RunActive for serving request in a LIFO fashion
    public void runActivity(Body body) {
      Service service = new Service(Body);
      while (body.isActive()) {
        service.blockingServeYoungest();
      }
    }
  }

  import org.objectweb.proactive.*;
  public class A implements InitActive {
    private String myName;

    public String getName() {
      return myName;
    }

    // -- implements InitActive
    public void initActivity(Body body) {
      myName = body.getName();
    }

    public static void main(String[] args) throws Exception {
      // newActive(classname, constructor parameter (null = none), 
      //           node (null = local), active, MetaObjectFactory (null = default)
      A a = (A) ProActive.newActive("A", null, null, new LIFOActivity(), null);
      System.out.println("Name = "+a.getName());
    }
  }

Restrictions on reifiable objects

Not all classes can give birth to active objects. There exist some restrictions, most of them caused by the 100% Java compliance, which forbids modifying the Java Virtual Machine or the compiler.

Some of these restrictions work at class-level :

Some other happen at the level of a method in a specific class:

Using the Factory Method Design Pattern

Creating an active object using ProActive might be a little bit cumbersome and requires more lines of code that for creating a regular object. A nice solution to this problem is through the use of the factory pattern. This mainly applies to class-based creation. It consists in adding a static method to class pA that takes care of instantiating the active object and returns it. The code is :

  public class AA extends A {
    public static A createActiveA (int i, String s, Node node) {
      Object[] params = new Object[] {new Integer (i), s};
      try {
        return (A) ProActive.newActive("A", params, node);
      } catch (Exception e) {
        System.err.println ("The creation of an active instance of A raised an exception: "+e);
        return null;
      }
    }
  }

It is up to the programmer to decide whether this method has to throw exceptions or not. We recommend that this method only throws exceptions that appear in the signature of the reified constructor (none here as the constructor of A that we call doesn't throw any exception). But the non functional exceptions induced by the creation of the active object have to be dealt with somewhere in the code.

Advanced : Customizing the Body of an Active Object

Motivations

There are many cases where you may want to customize the body used when creating an active object. For instance, one may want to add some debug messages or some timing behavior when sending or receiving requests. The body is a non changeable object that delegates most of its tasks to helper objects called MetaObjects. Standard MetaObjects are already used by default in ProActive but one can easily replace any of those MetaObjects by a custom one.

We have defined the MetaObjectFactory interface able to create factories for each of those MetaObjects. This interface is implemented by ProActiveMetaObjectFactory which provides all the default factories used in ProActive.

When creating an active object, as we saw above, it is possible to specify which MetaObjectFactory to use for that particular instance of active object being created. The class ProActive provides extra newActive and turnActive methods for that.

How to do it

First you have to write a new MetaObject factory that inherits from ProActiveMetaObjectFactory or directly implements MetaObjectFactory in order to redefine everything. Inheriting from ProActiveMetaObjectFactory is a great time saver as you only redefine what you really need to. Here is an example :

public class MyMetaObjectFactory extends ProActiveMetaObjectFactory {

  private static final MetaObjectFactory instance = new MyMetaObjectFactory();

  protected MyMetaObjectFactory() {
    super();
  }

  public static MetaObjectFactory newInstance() {
    return instance;
  }

  //
  // -- PROTECTED METHODS -----------------------------------------------
  //

  protected RequestFactory newRequestFactorySingleton() {
    return new MyRequestFactory();
  }

  //
  // -- INNER CLASSES -----------------------------------------------
  //

  protected class MyRequestFactory implements RequestFactory, java.io.Serializable {
    public Request newRequest(MethodCall methodCall, UniversalBody sourceBody, boolean isOneWay, long sequenceID) {
      return new MyRequest(methodCall, sourceBody, isOneWay, sequenceID, server);
    }
  } // end inner class MyRequestFactory
}

The factory above simply redefines the RequestFactory in order to make the body use a new type of request. The method protected RequestFactory newRequestFactorySingleton() is one convenience method that ProActiveMetaObjectFactory to simplify the creation of factories as singleton. More explanations can be found in the javadoc of that class. The use of that new factory is fairly simple. All you have to do is to pass an instance of the factory when creating a new active object. If we take the same example as before we have :

  Object[] params = new Object[] {new Integer (26), "astring"};
  try {
    A a = (A) ProActive.newActive("example.AA", params, null, null, MyMetaObjectFactory.newInstance());
  } catch (Exception e) {
    e.printStackTrace() ;
  }
In the case of a turnActive we would have :
  A a = new A(26, "astring");
  a = (A) ProActive.turnActive(a, null, null, MyMetaObjectFactory.newInstance());

Advanced : Role of the elements of an active object

In this section, we'll have a very close look at what happens when an active object is created. This section aims at providing a better understanding of how the library works and where the restrictions of Proactive come from.

Consider that some code in an instance of class A creates an active object of class B using a piece of code like this :

    B b;
    Object[] params = {};
    try {
      // We create an active instance of B on the current node
      b = (B) ProActive.newActive("B", params);
    } catch (Exception e) {
      e.printStackTrace () ;
    }

If the creation of the active instance of B is successful, the graph of objects is as described in figure below (with arrows denoting references).



The components of an active object

The active instance of B is actually composed of 4 objects :

Role of the stub

The role of the class Stub_B is to reify all method calls that can be performed through a reference of type B, and only these as calling a method declared in a subclass of B through downcasting would result in a runtime error). Reifying a call simply means constructing an object (in our case, all reified calls are instance of class MethodCall) that represents the call, so that it can be manipulated as any other object. This reified call is then processed by the other components of the active object in order to achieve the behavior we expect from an active object.

The idea of using a standard object for representing elements of the language that are not normally objects (such as method calls, constructor calls, references, types,...) is what metaobject programming is all about. The metaobject protocol (MOP) ProActive is built on is described here but it is not a prerequisite for understanding and using ProActive.

As one of our objectives is to provide transparent active objects, references to active objects of class B need to be of the same type as references to passive instances of B (this feature is called polymorphism between passive and active instances of the same class). This is why, by construction, Stub_B is a subclass of class B, therefore allowing instances of class Stub_B to be assigned to variables of type B.

Class Stub_B redefines each of the methods inherited from its superclasses. The code of each method of class Stub_B actually builds an instance of class MethodCall in order to represent the call to this method. This object is then passed to the BodyProxy, which returns an object that is returned as the result of the method call. From the caller's point of view, everything looks like if the call had been performed on an instance of B.

Now that we know how stubs work, we can understand some of the limitations of ProActive :

Role of the proxy

The role of the proxy is to handle asynchronism in calls to active object. More specifically, it creates future objects if possible and needed, forwards calls to bodies and returns future objects to the stubs. As this class operates on MethodCall objects, it is absolutely generic and does not depend at all on the type of the stub that feeds calls in through its reify method.

Role of the body

The body is responsible for storing calls (actually, Request objects) in a queue of pending requests and processing these request according to a given synchronization policy, whose default behavior is FIFO. The Body has its own thread, which alternatively chooses a request in the queue of pending ones and executes the associated call.

Role of the instance of class B

This is a standard instance of class B. It may contain some synchronized information in its live method, if any. As the body executes calls one by one, there cannot be any concurrent execution of two portions of code of this object by two different threads. This enables the use of pre- and post-conditions and class invariants. As a consequence, the use of the keyword synchronized in class B should not be necessary. Any synchronization scheme that can be expressed through monitors and synchronized statements can be expressed using ProActive's high-level synchronization mechanism in a much more natural and user-friendly way.



Copyright © April 2004 INRIA All Rights Reserved.