Chapter 13. Active Objects: 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 into an Active Object, there are some restrictions that will be detailed 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.

13.1. 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. That's 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, one needs to make sure that the constructor arguments 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. Bear in mind also that a reified object must have a declared empty no-args constructor in order to be properly created.

    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.

13.1.1. 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:

  • As the arguments of the constructor are stored in an array of type Object[], primitive types have to be represented by their wrappers object type. In the example above, we use an Integer object to wrap the int value 26. An ambiguity then arises if two constructor of the same class only differ by converting a primitive type to its corresponding wrapper class. In the example below, an ambiguity exists between the first and the second constructors.

  • If one argument is null, the runtime can obviously not determine its type. This is the second source of ambiguity. In the example below, an ambiguity exists between the third and the fourth constructors if the second element of the array is null.

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

13.1.2. 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);

13.2. 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 beSerializable. 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.

13.3. 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:

  • the initialization of the activity (done only once)

  • the activity itself

  • the end of the activity (done only once)

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.

  • Implementing one or more of the three interfaces directly in the class used to create the active object

  • Passing an object implementing one or more of the three interfaces in parameter to the method newActive or turnActive (parameter active in those methods)

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

13.3.1. Algorithms deciding which 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

13.3.2. Implementing the interfaces directly in the class

Implementing the interfaces directly in the class used to create the active object is the easiest solution when you 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.

  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());
        }
  }

Example 13.1. Custom Init and Run

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();
        }
}

Example 13.2. Start, stop, suspend, restart a simulation algorithm in runActivity method

Even when an AO is busy doing its own work, it can remain reactive to external events (method calls). One just has to program non-blocking services to take into account external inputs.

public class BusyButReactive implements RunActive {
public void  runActivity(Body body) {
Service service = new Service(body);
while ( ! hasToTerminate ) {
        ...    // Do some activity on its own
        ...
        ...    // Non blocking service
        ...
        service.serveOldest("changeParameters", "terminate");   ... 
      }
    }
public void changeParameters () {...   // change computation parameters}
public void terminate (){ hasToTerminate=true;}
}

Example 13.3. Reactive Active Object

It also allows one to specify explicit termination of AOs (there is currently no Distributed Garbage Collector). Of course, the reactivity is up to the length of going around the loop.

13.3.3. Passing an object implementing the interfaces at creation-time

Passing an object implementing the interfaces when creating the active object 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. Following is 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());
    }
  }

13.4. 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:

  • Final classes cannot give birth to active object

  • Same thing for non-public classes

  • Classes without a no-argument constructor cannot be reified. This restriction will be softened in a later release of ProActive

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

  • Final methods cannot be used at all. Calling a final method on an active object leads to inconsistent behavior.

  • Calling a non-public method on an active object raises an exception. This restriction disappeared with JDK 1.2.

13.5. 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.

13.6. Advanced: Customizing the Body of an Active Object

13.6.1. 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 (see code in core/body/MetaObjectFactory.java) able to create factories for each of those MetaObjects. This interface is implemented by ProActiveMetaObjectFactory (see code in core/body/ProActiveMetaObjectFactory.java) 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 (see code in ProActive.java) provides extra newActive and turnActive methods for that:

ProActive.newActive(
  java.lang.String, 
  java.lang.Object[],
  org.objectweb.proactive.core.node.Node, 
  org.objectweb.proactive.Active,  
	org.objectweb.proactive.core.body.MetaObjectFactory)
ProActive.turnActive(
  java.lang.Object,
  org.objectweb.proactive.core.node.Node,
  org.objectweb.proactive.Active,
	org.objectweb.proactive.core.body.MetaObjectFactory)

13.6.2. How to do it

First you have to write a new MetaObject factory that inherits from ProActiveMetaObjectFactory (see code in core/body/ProActiveMetaObjectFactory.java) or directly implements the MetaObjectFactory interface (see code in core/body/MetaObjectFactory.java), 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 (see code in core/body/ProActiveMetaObjectFactory.java) provides to simplify the creation of factories as singleton. More explanations can be found in the javadoc of that class . The use of that 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());

13.7. 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 = {<some parameters for the constructor>};
    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

Figure 13.1. The components of an active object

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

  • a stub (Stub_B)

  • a proxy (BodyProxy)

  • a body (Body)

  • an instance of B

13.7.1. 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 in Chapter 52, MOP: Metaobject Protocol 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:

  • Obviously, Stub_B cannot redefine final methods inherited from class B. Therefore, calls to these methods are not reified but are executed on the stub, which may lead to unexplainable behavior if the programmer does not carefully avoid calling final methods on active objects.

    As there are 6 final methods in the base class Object, one may wonder how to live without them. In fact, 5 out of this 6 methods deal with thread synchronization (notify(), notifyAll() and the 3 versions of wait()). Those method should not be used since an active object provides thread synchronization. Indeed, using the standard thread synchronization mechanism and ProActive thread synchronization mechanism at the same time might conflict and result in an absolute debugger's nightmare.

    The last final method in the class Object is getClass(). When invoked on an active object, getClass() is not reified and therefore performed on the stub object, which returns an object of class Class that represents the class of the stub (Stub_B in our example) and not the class of the active object itself (B in our example). However, this method is seldom used in standard applications and it doesn't prevent the operator instanceof to work thanks to its polymorphic behavior. Therefore the expression (foo instanceof B) has the same value whether B is active or not.

  • Getting or setting instance variables directly (not through a getter or a setter) must be avoided in the case of active objects because it results in getting or setting the value on the stub object and not on the instance of the class B. This problem is usually worked around by using get/set methods for setting or reading attributes. This rule of strict encapsulation may also be found in JavaBeans or in most distributed object systems like RMI or CORBA.

13.7.2. 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.

13.7.3. 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.

13.7.4. 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.

13.8. Asynchronous calls and futures

13.8.1. Creation of a Future Object

Whenever possible a method call on an active object is reified as an asynchronous request. If not possible the call is synchronous and blocks until the reply is received. In case the request is asynchronous, it immediately returns a future object.

This object acts as a placeholder for the result of the not-yet-performed method invocation. As a consequence, the calling thread can go on with executing its code, as long as it doesn't need to invoke methods on the returned object, in which case the calling thread is automatically blocked if the result of the method invocation is not yet available. Below are shown the different cases that can lead to an asynchronous call.

Return type

Can throw checked exception

Creation of a future

Asynchronous

void

-

No

Yes

Non Reifiable Object

-

No

No

Reifiable Object

Yes

No

No

Reifiable Object

No

Yes

Yes

Table 13.1. Future creation, and asynchronous calls depending on return type

As we can see, the creation of a future depends not only on the caller type, but also on the return object type. Creating a future is only possible if the object is reifiable. Note although having a quite similar structure as an active object, a future object is not active. It only has a Stub and a Proxy as shown in figure below:

A future object

Figure 13.2. A future object

During its lifetime, an active object can create many future objects. There are all automatically kept in a FuturePool.

Each time a future is created, it is inserted in the future pool of the corresponding active object. When the result becomes available, the future object is removed from the pool. Although most of the methods of the FuturePool are for internal use only and are directly called by the proactive library we provide a method to wait until a result becomes available. Instead of blocking until a specific future is available, the call to waitForReply() blocks until any of the current futures become available. An application can be found in the FutureList class.

13.8.1.1. HashCode and equals

Any call to a future object is reified in order to be blocked if the future is not yet available and later executed on the result object. However, two methods don't follow this scheme: equals and hashCode. They are often called by other methods from the Java library, like HashTable.add() and so are most of the time out of control from the user. This can lead very easily to deadlocks if they are called on a not yet available object.

13.8.1.2. hashCode()

Instead of returning the hashcode of the object, it returns the hashcode of its proxy. Since there is only one proxy per future object, there is a unique equivalence between them.

13.8.1.3. equals()

The default implementation of equals() in the Object class is to compare the references of two objects. In ProActive it is redefined to compare the hashcode of two proxies. As a consequence it is only possible to compare two future object, and not a future object with a normal object.

There are some drawbacks with this technique, the main one being the impossibility to have a user override the default HashTable and equals() methods.

13.8.1.4. toString()

The toString() method is most of the time called with System.out.println() to turn an object into a printable string. In the current implementation, a call to this method will block on a future object like any other call, thus, one has to be careful when using it. As an example, trying to print a future object for debugging purpose will most of the time lead to a deadlock. Instead of displaying the corresponding string of a future object, you might consider displaying its hashCode.

13.8.2. Asynchronous calls in details

13.8.2.1. The setup

First, let's introduce the example we'll use throughout this section. Let us say that some piece of code in an instance of class A calls method foo on an active instance of class B. This call is asynchronous and returns a future object of class V. Then, possibly after having executed some other code, the same thread that issued the call calls method bar on the future object returned by the call to foo.

13.8.2.2. What would have happened in a sequential world

In a sequential, single-threaded version of the same application, the thread would have executed the code of the calling method in class A up to the call of foo, then the code of foo in class B, then back to the code of the calling method in class A up to the call to bar, then the code of bar in class V, and finally back to the code of the calling method in class A until its end. The sequence diagram below summarizes this execution. You can notice how the single thread successively executes code of different methods in different classes.

Sequence Diagram - single-threaded version of the program

Figure 13.3. Sequence Diagram - single-threaded version of the program

13.8.2.3. Visualizing the graph of objects

Let us first get an idea of what the graph of objects at execution (the objects with their references to each other) looks like at three different moments of the execution:

  • Before calling foo, we have exactly the same setup as after the creation of the active instance of B and summarized in the figure below: an instance of class A and an active instance of class B. As all active objects, the instance of class B is composed of a stub (an instance of class Stub_B, which actually inherits directly from B), a BodyProxy, a Body and the actual instance of B.

    The components of an active object

    Figure 13.4. The components of an active object

  • After the asynchronous call to foo has returned, A now holds a reference onto a future object representing the not-yet-available result of the call. It is actually composed of a Stub_V and a FutureProxy as shown on the figure below.

    The components of a future object before the result is set

    Figure 13.5. The components of a future object before the result is set

  • Right after having executed foo on the instance of B, the thread of the Body sets the result in the future, which results in the FutureProxy having a reference onto a V (see figure below).

    All components of a future object

    Figure 13.6. All components of a future object

13.8.2.4. Sequence Diagram

Let us now concentrate on how and when and by which thread the different methods are called. We have two threads: the thread that belongs to the subsystem A is part of (let's call it the first thread), and the thread that belongs to the subsystem B is part of (the second thread).

The first thread invokes foo on an instance of Stub_B, which builds a MethodCall object and passes it to the BodyProxy as a parameter of the call to reify. The proxy then checks the return type of the call (in this case V) and generates a future object of type V for representing the result of the method invocation. The future object is actually composed of a Stub_V and a FutureProxy. A reference onto this future object is set in the MethodCall object, which will prove useful once the call is executed. Now that the MethodCall object is ready, it is passed as a Request to the Body of the Active Object as a parameter. The body simply appends this request to the queue of pending requests and returns immediately. The call to foo that an A issued now returns a future object of type Stub_V, that is a subclass of V.

At some point, possibly after having served some other requests, the second thread (the active thread) picks up the request issued by the first thread some time ago. It then executes the embedded call by calling foo on the instance of B with the actual parameters stored in the MethodCall object. As specified in its signature, this call returns an object of type V. The second thread is then responsible for setting this object in the future object (which is the reason why MethodCall objects hold a reference on the future object created by the FutureProxy). The execution of the call is now over, and the second thread can select another request to serve in the queue and execute it.

In the meantime, the first thread has continued executing the code of the calling method in class A. At some point, it calls bar on the object of type Stub_V that was returned by the call to foo. This call is reified thanks to the Stub_V and processed by the FutureProxy. If the object the future represents is available (the second thread has already set it in the future object, which is described in figure below, the call is executed on it and returns a value to the calling code in A.

Sequence Diagram

Figure 13.7. Sequence Diagram

If it is not yet available, the first thread is suspended in FutureProxy until the second thread sets the result in the future object (see figure below).

Sequence Diagram

Figure 13.8. Sequence Diagram

13.8.3. Important Notes: Errors to avoid

There are few things to remember with asynchronous method calls and futures, in order to avoid annoying debugging sessions:

  • Constructor with no-args: this constructor will be used either for the Active Objects creation(if not present, an exception might be thrown) or Future creation for a method call (if not present, the method call is synchronous). Avoid to put initialization stuff in this constructor, as it might lead to unexpected behavior. Indeed this constructor is called for the stub creation.

  • Make your classes implement Serializable interface since ProActive deals with objects that cross the network

  • Think to use wrappers instead of primitive types or final classes for methods result type otherwise you will loose the asynchronism capabilities. For instance if one of your object has a method

     int giveSolution(parameter) 
    

    calling this method with ProActive is sychronous. So to keep the asynchronism it is advised to use

     IntWrapper giveSolution(parameter) 
    

    In that case call to this method is asynchronous.

    All wrappers are in the package: org.objectweb.proactive.core.util.wrapper

    ProActive provides more used primitive type wrappers, there are 2 versions of each, one mutable, and the other which is immutable.

    Only the methods return type are concerned not the parameters.

  • Avoid to return null in Active Object methods: on the caller side the test if(result_from_method == null) has no sense. Indeed result_from_method is a couple Stub-FutureProxy as explained above, so even if the method returns null, result_from_method cannot be null:

    public class MyObject{
     public MyObject(){
     //empty constructor with no-args
     }
    
     public Object getObject{
     if(.....) {
     return new Object();
     }
     else {
       return null; --> to avoid in ProActive
      }
     }
    
    } 
    

    On the caller side:

    MyObject o = new MyObject();
    Object result_from_method = o.getObject();
    if(result_from_method == null){
    ......
    }
    

    This test is never true, indeed, result_from_method is Stub-->Proxy-->null if the future is not yet available or the method returns null or Stub-->Proxy-->Object if the future is available, but result_from_method is never null.

13.9. Automatic Continuation in ProActive

13.9.1. Objectives

An Automatic Continuation is due to the propagation of a future outside the activity that has sent the corresponding request.

Automatic Continuations allow to pass in parameter or return as a result future objects(or objects containing a future) without blocking to wait the result object of the future. When the result is available on the object that originated the creation of the future, this object must update the result in all objects to which it passed the future.

13.9.2. Principles

  • Message sending

  • Automatic Continuations can occur when sending a request (parameter of the request is a future or contains a future) or when sending a reply (the result is a future or contains a future).

    Outgoing futures are registered in the FuturePool of the Active Object sending this future(request or reply). Registration for couple(Future,BodyDestination) as an Automatic Continuation occurs when the future is serialized(indeed every request or reply are serialized before being sent, and the future is part of the request or the reply). More precisely, a thread T sending the message(request or reply)---therefore the thread doing the serialization---, keeps in a static table (FuturePool.bodyDestination) a reference of the destination body. Hence when a future F is serialized by the same thread T(since futures are part of request or reply, it is the same thread serializing the request --or reply-- and the future), it looks up in the static table, if there is a destination D registered for the thread T. If true, the future notifies its FuturePool (that it is going to leave), which in turn registers couple (F,D) as an Automatic Continuation

    When value V is available for the future F, V is propagated to all objects that received the fututre F. This Update is realized by a particular thread located in the FuturePool.

  • Message reception

  • When a message is received(request or reply) by an Active Object, this message can contain a future. So the Active Object registers this future in the FuturePool to be able to update it when the value will be available. This registration takes place in two steps:

    • When the future is deserialized, it registers in a static table (FuturePool.incomingFutures

    • In Receive[Request-Reply] method, it is checked if one or many futures are registerd in that table, then, if true these futures are registerd in the FuturePool in a standart way.

13.9.3. Example

The following piece of code shows both cases: passing a future as parameter or as a result.

class C {
....
        public static void main(String[] args){
        ......
        A a = newActive(A);
        A b = newActive(B);
        Result r1 = a.foo();   //r1 is a future
        Result r2 = b.bar(r1); //r1 is passed as parameter
        Result r3 = b.bar2();  // see **
        ........
        }       //end of main
...
}       //end of class C

where

class A {
  ...
        public Result foo(){
        ...
        }
  ...
} //end of class A

and

class B {
...
         public Result bar (Result r) {
        ...
         }

        public Result bar2 () {
         A a = newActive(A);
        return a.foo();     //  ** future is sent as a result
         }
} //end of class B

13.9.4. Illustration of an Automatic Continuation

We will illustrate here how a future is first created, then passed as parameter to a method later on.

Let us say that some piece of code in main method of an object C calls method foo() on an instance of class A.

This call is asynhronous and returns a future object Future_r1 of class Result.

Then method bar() is called on an instance of class B passing future Future_r1 as a parameter to the method

This call is asynhronous and returns a future object Future_r2 of class Result. B needs the value of Future_r1 which is not yet available in order to return the result of method bar(), so it gets the future too.

The value of the result for the call to method foo is now available, so A updates the value of Future_r1

C updates the value of Future_r1 for B

B returns the value for the call to method bar() and updates the value of Future_r2 for C

13.10. The Hello world example

This example implements a very simple client-server application. A client object display a String received from a remote server. We will see how to write classes from which active and remote objects can be created, how to find a remote object and how to invoke methods on remote objects.

13.10.1. The two classes

Only two classes are needed: one for the server object Hello and one for the client that accesses it HelloClient.

13.10.1.1. The Hello class

This class implements server-side functionalities. Its creation involves the following steps:

  • Provide an implementation for the required server-side functionalities

  • Provide an empty, no-arg constructor

  • Write a main method in order to instantiate one server object and register it with an URL.

public class Hello {
  private String name;
  private String hi = "Hello world";
  private java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
  public Hello() {
  }
  public Hello(String name) {
    this.name = name;
  }
  public String sayHello() {
    return hi + " at " + dateFormat.format(new java.util.Date())+
        " from node: " + org.objectweb.proactive.ProActive.getBodyOnThis().getNodeURL();
  }
  public static void main(String[] args) {
    // Registers it with an URL
    try {
      // Creates an active instance of class HelloServer on the local node
      Hello hello = (Hello)org.objectweb.proactive.ProActive.newActive(Hello.class.getName(), 
                             new Object[]{"remote"});
      java.net.InetAddress localhost = java.net.InetAddress.getLocalHost();
      org.objectweb.proactive.ProActive.register(hello, "//" + localhost.getHostName() + "/Hello");
    } catch (Exception e) {
      System.err.println("Error: " + e.getMessage());
      e.printStackTrace();
    }
  }
}

Example 13.4. A possible implementation for the Hello class:

13.10.1.1.1. Implement the required functionalities

Implementing any remotely-accessible functionality is simply done through normal Java methods in a normal Java class, in exactly the same manner it would have been done in a non-distributed version of the same class. This has to be contrasted with the RMI approach, where several more steps are needed:

  • Define a remote interface for declaring the remotely-accessible methods.

  • Rewrite the class so that it inherits from java.rmi.server.UnicastRemoteObject, which is the root class of all remote objects.

  • Add remote exceptions handling to the code.

13.10.1.1.2. Why an empty no-arg constructor?

You may have noticed that class Hello has a constructor with no parameters and an empty implementation. The presence of this empty no-arg constructor is imposed by ProActive and is actually a side-effect of ProActive's transparent implementation of active remote objects (as a matter of fact, this side-effect is caused by ProActive being implemented on top of a 100% Java metaobject protocol). If no such constructor is provided, active objects cannot be created.

If no constructor at all is provided, active objects can still be created because, in this specific case, all Java compilers provide a default no-arg empty constructor. If a no-arg constructor is provided but its implementation is not empty, unwanted behavior may appear because the no-arg constructor is always called when an active object is created, whatever code the user can write.

13.10.1.1.3. Creating the remote Hello object

Now that we know how to write the class that implements the required server-side functionalities, let us see how to create the server object. In ProActive, there is actually no difference between a server and a client object as both are remote objects.Creating the active object is done through instantiation-based creation. We want this active object to be created on the current node, which is why we use newActive with only two parameters. In order for the client to obtain an initial reference onto this remote object, we need to register it in the registry (which is actually the well-known rmiregistry) with a valid RMI URL.

13.10.1.2. The HelloClient Class

The responsibility of this class is first to locate the remote server object, then to invoke a method on it in order to retrieve a message, and finally display that message.

public class HelloClient {
  public static void main(String[] args) {
    Hello myServer;
    String message;
    try {
      // checks for the server's URL
      if (args.length == 0) {
        // There is no url to the server, so create an active server within this VM
        myServer = (Hello)org.objectweb.proactive.ProActive.newActive(Hello.class.getName(), 
                          new Object[]{"local"});
      } else {
        // Lookups the server object
        System.out.println("Using server located on " + args[0]);
        myServer = (Hello)org.objectweb.proactive.ProActive.lookupActive(Hello.class.getName(), 
                          args[0]);
      }
      // Invokes a remote method on this object to get the message
      message = myServer.sayHello();
      // Prints out the message
      System.out.println("The message is: " + message);
    } catch (Exception e) {
      System.err.println("Could not reach/create server object");
      e.printStackTrace();
      System.exit(1);
    }
  }
}

Example 13.5. HelloClient.java

13.10.1.2.1. Looking up a remote object

The operation of lookup simply means obtaining a reference onto an object from the URL it is bound to. The return type of method Proactive.lookupActive() is Object, then we need to cast it down into the type of the variable that holds the reference (Hello here). If no object is found at this URL, the call to Proactive.lookupActive() returns null.

13.10.1.2.2. Invoking a method on a remote object

This is exactly like invoking a method on a local object of the same type. The user does not have to deal with catching distribution related exceptions like, for example, when using RMI or CORBA. Future versions of ProActive will provide an exception handler mechanism in order to process these exceptions in a separate place than the functional code. As class String is final, there cannot be any asynchronism here since the object returned from the call cannot be replaced by a future object (this restriction on final classes is imposed by ProActive's implementation).

13.10.1.2.3. Printing out the message

As already stated, the only modification brought to the code by ProActive is located at the place where active objects are created. All the rest of the code remains the same, which fosters software reuse.

13.10.2. Hello World within the same VM

In order to run both the client and server in the same VM, the client creates an active object in the same VM if it doesn't find the server's URL. The code snippet which instantiates the Server in the same VM is the following:

if (args.length == 0) {
  // There is no url to the server, so create an active server within this VM
  myServer = (Hello)org.objectweb.proactive.ProActive.newActive(
        Hello.class.getName(), new Object[]{"local"});
}

To launch the Client and the Server, just type:

linux> java -Djava.security.policy=scripts/proactive.java.policy
 -Dlog4j.configuration=file:scripts/proactive-log4j
 org.objectweb.proactive.examples.hello.HelloClient
windows> java -Djava.security.policy=scripts\unix\proactive.java.policy
 -Dlog4j.configuration=file:scripts\unix\proactive-log4j
 org.objectweb.proactive.examples.hello.HelloClient &

13.10.3. Hello World from another VM on the same host

13.10.3.1. Starting the server

Just start the main method in the Hello class.

linux> java -Djava.security.policy=scripts/proactive.java.policy
 -Dlog4j.configuration=file:scripts/proactive-log4j
 org.objectweb.proactive.examples.hello.Hello &
windows> java -Djava.security.policy=scripts\proactive.java.policy
   -Dlog4j.configuration=file:scripts\proactive-log4j 
   org.objectweb.proactive.examples.hello.Hello 

13.10.3.2. Launching the client

linux> java -Djava.security.policy=scripts/proactive.java.policy
 -Dlog4j.configuration=file:scripts/proactive-log4j
 org.objectweb.proactive.examples.hello.HelloClient //localhost/Hello &
windows> java -Djava.security.policy=scripts\proactive.java.policy
   -Dlog4j.configuration=file:scripts\proactive-log4j
   org.objectweb.proactive.examples.hello.HelloClient //localhost/Hello

13.10.4. Hello World from abroad: another VM on a different host

13.10.4.1. Starting the server

Log on to the server's host, and launch the Hello class.

linux remoteHost> java -Djava.security.policy=scripts/proactive.java.policy
 -Dlog4j.configuration=file:scripts/proactive-log4j
 org.objectweb.proactive.examples.hello.Hello &
windows remoteHost> java -Djava.security.policy=scripts\proactive.java.policy
  -Dlog4j.configuration=file:scripts\proactive-log4j 
  org.objectweb.proactive.examples.hello.Hello

13.10.4.2. Launching the client

Log on to the client Host, and launch the client

linux clientHost> java -cp $CLASSPATH -Djava.security.policy=scripts/proactive.java.policy
   -Dlog4j.configuration=file:scripts/proactive-log4j
   org.objectweb.proactive.examples.hello.HelloClient //remoteHost/Hello &
windows clientHost> java -cp $CLASSPATH -Djava.security.policy=scripts\proactive.java.policy
  -Dlog4j.configuration=file:scripts\proactive-log4j
  org.objectweb.proactive.examples.hello.HelloClient //remoteHost/Hello
[Note]Note

There is also a Guided Tour section on the Hello world example: Chapter 6, Hands-on programming