Chapter 1. Principles

GRID computing is now a key aspect, from scientific to business applications, from large scale simulations to everyday-life enterprise IT, including telcos and embedded domains. We are just entering the era of Ubiquitous Computing with many computers at hand of every single individual - after the old days of mainframes and servers, hundreds of persons sharing the same machines, and the quite current days of PCs, one person/one computer. Potentially spanning all over the world, involving several thousands or several hundred thousands of nodes, the programming of Grid applications call for a new paradigms. The ProActive Grid solution relies on systematic asynchronous method calls, allowing to master both complexity and efficiency.

Overall, ProActive promotes a few basic and simple principles:

ProActive takes advantage of this sound programming model, to further propose advanced features such as groups, mobility, and components. In the framework of a formal calculus, ASP (Asynchronous Sequential processes), confluence and determinism have been proved for this programming model: CH05 and CHS04.

Asynchronous method calls with returns lead to an emerging abstraction: futures, the expected result of a given asynchronous method call. Futures turn out to be a very effective abstraction for large distributed systems, preserving both low coupling and high structuring.

Asynchronous method calls and first-class futures are provided in the unifying framework of an Active Object.

1.1. Seamless sequential, multithreaded and distributed

Most of the time, activities and distribution are not known at the beginning, and change over time. Seamless implies reuse, smooth and incremental transitions.

Different computing deployment paradigms

Figure 1.1. Different computing deployment paradigms

A huge gap still exists between multithreaded and distributed Java applications which impedes code reuse in order to build distributed applications from multithreaded applications. Both JavaRMI and JavaIDL, as examples of distributed object libraries in Java, put a heavy burden on the programmer because they require deep modifications of existing code in order to turn local objects into remotely accessible ones. In these systems, remote objects need to be accessed through some specific interfaces. As a consequence, these distributed objects libraries do not allow polymorphism between local and remote objects. This feature is our first requirement for a Grid Computing framework. It is strongly required in order to let the programmer concentrate first on modeling and algorithmic issues rather than lower-level tasks such as object distribution, mapping and load balancing.

1.2. Active objects: Unifying threads and remote objects

Active Objects are the core of the ProActive computing concept. An Active Object is both a Remote Object (which allows to deploy it on a distant host) and a Thread (which gives it its own activity, its own independant behaviour and in concurrency with other Active Objects deployed). Given a standard object, turning it into an Active Objects provides:

  • location transparency

  • activity transparency

  • synchronization

Communications to an active object are by default asynchronous. So, an active object is: a main object + a single thread + a queue of pending requests. As such, a reference to a remote object is equivalent to a reference to a remote activity. An activity is an object ; but being in a non-uniform model, not all objects are active objects, the majority remaining standard Java objects. As there cannot be any sharing, an active object is also a unit of computational mobility (see Chapter 16, Active Object Migration).

[Note]Note

The Active Object concept only requires modification of the instanciation code !

On activation, an object becomes a remotely accessible entity with its own thread of control: an active object. Here are given as example three ways to transform a standard Object into an Active Object:

Object[] params = new Object[] { new Integer (26), "astring" };
A a = (A) ProActive.newActive("example.A", params, node); 

Example 1.1. Class-based Active Object

public class AA extends A implements Active {}
Object[] params = new Object[] { new Integer (26), "astring" };
A a = (A) ProActive.newActive("example.AA", params, node); 

Example 1.2. Instantiation-based Active Object

Object-based Active Objects Allows to turn active and set remote objects for which you do not have the source code; this is a necessary feature in the context of code mobility.

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

Example 1.3. Object-based Active Object

[Note]Note

Nodes allow to control the mapping to the hosts. See Section 13.1.2, “Using a Node” for an example use of a Node, and Section 21.2, “Principles” for a definition.

1.3. Model of Computation

Here is a summary of the computation model being used by ProActive:

  • Heterogeneous model both passive and active objects

  • Systematic asynchronous communications towards active objects

  • No shared passive object , Call-by-value between active objects

  • Automatic continuations , a transparent delegation mechanism

  • wait-by-necessity , automatic and transparent futures

  • Centralized and explicit control , libraries of abstractions

To compare to Java RMI, a Java remote object is not by essence an activity. The fact that several threads can execute several remote method calls simultaneously within a remote object does reveal that facet. When writing ro.foo(p);, what ro identifies is not a remote activity, but just a remote object. This has several consequences, along with the presence of sharing between remote objects that prevents them from being a unit of computational migration.

1.4. Reusablilty and Seamless interface: why and how do we achieve it?

Two key features:

  • Wait-by-necessity: inter-objects synchronization. Systematic, implicit and transparent futures. Ease the programming of synchronization and reuse of existing methods

  • Polymorphism between standard and active objects

    • Type compatibility for classes and not just for interfaces

    • Needed and done for the future objects as well

    • Dynamic mechanism (dynamically achieved if needed)

Polymorphism

Figure 1.2. Polymorphism

1.5. Hello world ! (tiny example)

This example implements the smallest program in ProActive. This is the easiest program you could write, using the Active Object concept. This is just to show quickly how code can be written, with minimal knowledge of the API.

You can get a more complete 'hello world' example, with deployment on a remote host, further on in the manual (Section 13.10, “The Hello world example”).

A client object displays a String received from elsewhere (the original VM). This illustrates the creation of an Active Object.

Only one class is needed: we have put the main method inside the class, which when deployed will be an Active Object.

1.5.1. The TinyHello class

This class can be used as an Active Object, serving requests. 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.

            public class TinyHello implements java.io.Serializable {
    static Logger logger = ProActiveLogger.getLogger(Loggers.EXAMPLES);
    private final String message = "Hello World!";

    /** ProActive compulsory no-args constructor */
    public TinyHello() {
    }

    /** The Active Object creates and returns information on its location
     * @return a StringWrapper which is a Serialized version, for asynchrony */
    public StringMutableWrapper sayHello() {
        return new StringMutableWrapper(
            this.message + "\n from " + getHostName() + "\n at " +
            new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new java.util.Date()));
    }

    /** finds the name of the local machine */
    static String getHostName() {
        try {
            return java.net.InetAddress.getLocalHost().toString();
        } catch (UnknownHostException e) {
            return "unknown";
        }
    }

    /** The call that starts the Acive Objects, and displays results.
     * @param args must contain the name of an xml descriptor */
    public static void main(String[] args)
        throws Exception {
        // Creates an active instance of class Tiny on the local node
        TinyHello tiny = (TinyHello) ProActive.newActive(
                TinyHello.class.getName(), // the class to deploy
                null // the arguments to pass to the constructor, here none
            ); // which jvm should be used to hold the Active Object

        // get and display a value 
        StringMutableWrapper received = tiny.sayHello(); // possibly remote call
        logger.info("On " + getHostName() + ", a message was received: " + received); // potential
 wait-by-necessity
        // quitting
        
        ProActive.exitSuccess();
    }
}

          

Example 1.4. A possible implementation for the TinyHello class

1.5.2. Implement the required functionality

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. Here, the only method is sayHello

1.5.3. Creating the Hello Active 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. We want this active object to be created on the current node, which is why we use newActive with only two parameters (done in the main method).

The code snippet which instantiates the TinyHello in the same VM is the following (in the main method):

        TinyHello tiny = (TinyHello) ProActive.newActive(
                TinyHello.class.getName(), // the class to deploy
                null // the arguments to pass to the constructor, here none
            ); // which jvm should be used to hold the Active Object

1.5.4. Invoking a method on a remote object and printing out the message

This is exactly like invoking a method on a local object of the same type. The user does not have to deal with catching exceptions related to the distant deployment.

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. So the way to call the sayHello method in this example is the following (in the main method):

        StringMutableWrapper received = tiny.sayHello(); // possibly remote call
        logger.info("On " + getHostName() + ", a message was received: " + received); // potential
 wait-by-necessity

1.5.5. Launching

To launch the example, you may type:

linux> java -cp $CLASSPATH -Djava.security.policy=scripts/proactive.java.policy 
            -Dlog4j.configuration=file:scripts/proactive-log4j 
            org.objectweb.proactive.examples.hello.TinyHello
windows> java -cp $CLASSPATH -Djava.security.policy=scripts\proactive.java.policy 
              -Dlog4j.configuration=file:scripts\proactive-log4j 
               org.objectweb.proactive.examples.hello.TinyHello

There are also scripts in the scripts directory:

linux> cd scripts/unix/ 
linux> tinyHello.sh
windows> cd scripts/windows 
windows> tinyHello.bat

1.5.5.1. The output

[apple unix]tinyhello.sh
--- Hello World tiny example ---------------------------------
> This ClassFileServer is reading resources from classpath
ProActive Security Policy (proactive.runtime.security) not set. Runtime Security disabled
Created a new registry on port 1099
//apple.inria.fr/Node628280013 successfully bound in registry at //apple.inria.fr/Node628280013
Generating class: pa.stub.org.objectweb.proactive.examples.hello.Stub_TinyHello
Generating class: pa.stub.org.objectweb.proactive.core.util.wrapper.Stub_StringMutableWrapper
On apple/138.96.218.62, a message was received: Hello World!
  from apple/138.96.218.62
  at 03/11/2005 14:25:32