Chapter 29. An implementation of the Fractal component model geared at Grid Computing

Fractal defines a general conceptual model, along with a programming application interface (API) in Java. According to the official documentation, the Fractal component model is 'a modular and extensible component model that can be used with various programming languages to design, implement, deploy and reconfigure various systems and applications, from operating systems to middleware platforms and to graphical user interfaces'.

There is a reference implementation, called Julia.

We first tried to use Julia to manipulate active objects (the fundamental entities in ProActive), but we wouldn't have been able to reuse the features of the Proactive library, because of the architectures of the libraries.

Julia manipulates a base class by modifying the bytecode or adding interception objects to it. On the other hand, ProActive is based on a meta-object protocol and provides a reference to an active object through a typed stub. If we wanted to use active objects with Julia, the Julia runtime would try to manipulate the stub, and not the active object itself. And if trying to force Julia to work on the same base object than ProActive, the control flow could not traverse both ProActive and Julia.

Eventually, re-implementing ProActive using Julia could be a solution (a starting point could be the 'protoactive' example of Julia), but this would imply a full refactoring of the library, and therefore quite a few resources...

More generally speaking, Julia is designed to work with standard objects, but not with the active objects of ProActive. Some features (see next section) would not be reusable using Julia with ProActive active objects.

Therefore, we decided to provide our own implementation of Fractal, geared at Grid Computing and based on the ProActive library.

This implementation is different from Julia both in its objectives and in the programming technniques. As previously stated, we target Grid and P2P environments. The programming techniques and the architecture of the implementation is described in a following section.

29.1. Specific features

Consider a standard system of Fractal components:

A system of Fractal components

Figure 29.1. A system of Fractal components

ProActive/Fractal features distributed components:

A system of distributed ProActive/Fractal components (blue, yellow and white represent distinct locations)

Figure 29.2. A system of distributed ProActive/Fractal components (blue, yellow and white represent distinct locations)

Each component is implemented as one (at least) active object:

Match between components and active objects

Figure 29.3. Match between components and active objects

The combination of the Fractal model with the ProActive library leverages the Fractal component model and provides an implementation for Grid computing.

29.1.1. Distribution

Distribution is achieved in a transparent manner over the Java RMI protocol thanks to the use of a stub/proxy pattern. Components are manipulated indifferently of their location (local or on a remote JVM).

29.1.2. Deployment framework

ProActive provides a deployment framework for creating a distributed component system. Using a configuration file and the concept of virtual nodes, this framework:

  1. connects to remote hosts using supported protocols, such as rsh, rlogin, ssh, globus, lsf etc...

  2. creates JVMs on these hosts

  3. instantiates components on these newly created JVMs

29.1.3. Activities

A fundamental concept of the ProActive library is this of Active Objects (see Chapter 12, ProActive Basis, Active Object Definition), where activities can actually be redefined (see also Chapter 13, Active Objects: creation and advanced concepts) to customize their behavior.

29.1.4. Asynchronous method calls with futures

Asynchronous method calls with transparent futures is a core feature of ProActive (Section 13.8, “Asynchronous calls and futures”), and it allows concurrent processing. Indeed, suppose a caller invokes a method on a callee. This method returns a result on a component. With synchronous method calls, the flow of execution of the caller is blocked until the result of the method called is received. In the case of intensive computations, this can be relatively long. With asynchronous method calls, the caller gets a future object and will continue its tasks until it really uses the result of the method call. The process is then blocked (it is called wait-by-necessity) until the result has effectively been calculated.

29.1.5. Collective interactions

We address collective interactions (1-to-n and n-to-1 interactions between components) through Chapter 31, Collective interfaces, namely gathercast and multicast interfaces.

29.1.6. Conformance

The Fractal specification defines conformance levels for implementations of the API (section 7.1. of the Fractal 2 specification). The implementation for ProActive is conformant up to level 3.3. In other words, it is fully compliant with the API. Generic factories (template components) are provided as ADL templates.

We are currently implementing a set of predefined standard conformance tests for the Fractal specification.

29.2. Implementation specific API

29.2.1. fractal.provider

The API is the same for any Fractal implementation, though some classes are implementation-specific:

The fractal provider class, that corresponds to the fractal.provider parameters of the JVM, is org.objectweb.proactive.core.component.Fractive. The Fractive class acts as:

  • a bootstrap component

  • a GenericFactory for instantiating new components

  • a utility class providing static methods to create collective interfaces and retreive references to ComponentParametersController

29.2.2. Content and controller descriptions

The controller description and the content description of the components, as specified in the method public Component newFcInstance(Type type, Object controllerDesc, Object contentDesc) throws InstantiationException of the org.objectweb.fractal.api.factory.Factory class, correspond in this implementation to the classes org.objectweb.proactive.core.component.ControllerDescription and org.proactive.core.component.ContentDescription.

29.2.3. Collective interactions

Collective interactions are an extension to the Fractal model, described in section Chapter 31, Collective interfaces, that relies on collective interfaces.

Collective interfaces are bound using the standard Fractal binding mechanism.

29.2.4. Requirements

As this implementation is based on ProActive, several conditions are required (more in Chapter 13, Active Objects: creation and advanced concepts):

  • the base class for the implementation of a primitive component has to provide an empty, no-args constructor.

  • for asynchronous invocations, return types of the methods provided by the interfaces of the components have to be reifiable and methods must not throw exceptions.

29.3. Architecture and design

The implementation of the Fractal model is achieved by reusing the extensible architecture of ProActive, notably the meta-object protocol and the management of the queue of requests. As a consequence, components are fully compatible with standard active objects and as such, inherit from the features active objects exhibit: mobility, security, deployment etc.

A fundamental idea is to manage the non-functional properties at the meta-level: each component is actually an active object with dedicated meta-objects in charge of the component aspects.

29.3.1. Meta-object protocol

ProActive is based on a meta-object protocol (MOP), that allows the addition of many aspects on top of standard Java objects, such as asynchronism and mobility. Active objects are referenced indirectly through stubs: this allows transparent communications, would the active objects be local or remote.

The following diagram explains this mechanism:

Java objects 'b' and 'a' can be in different virtual machines (the network being represented here between the proxy and the body, though the invocation might be local). Object 'b' has a reference on active object 'a' (of type A) through a stub (of type A because it is generated as a subclass of A) and a proxy. When 'b' invokes a method on 'stub_A', the invocation is forwarded through the communication layer (possibly through a network) to the body of the active object. At this point, the call can be intercepted by meta-objects, possibly resulting in induced actions, and then the call is forwarded to the base object 'a'.

ProActive's Meta-Objects Protocol.

Figure 29.4. ProActive's Meta-Objects Protocol.

The same idea is used to manage components: we just add a set of meta-objects in charge of the component aspects.

The following diagram shows what is changed:

A new set of meta-objects, managing the component aspect (constituting the controller of the component, in the Fractal terminology), is added to the active object 'a'. The standard ProActive stub (that gives a representation of type A on the figure) is not used here, as we manipulate components. In Fractal, a reference on a component is of type Component, and references to interfaces are of type Interface. 'b' can now manipulate the component based on 'a' through a specific stub, called a component representative. This component representative is of type Component, and also offers references to control and functional interfaces, of type Interface. Note that classes representing functional interfaces of components are generated on the fly: they are specific to each component and can be unknown at compile-time.

Method invocations on Fractal interfaces are reified and transmitted (possibly through a network) to the body of the active object corresponding to the component involved. All standard operations of the Fractal API are now accessible.

The ProActive MOP with component meta-objects and component representative

Figure 29.5. The ProActive MOP with component meta-objects and component representative

29.3.2. Components vs active objects

In our implementation, because we make use of the MOP's facilities, all components are constituted of one active object (at least), are they composite or primitive components. If the component is a composite, and if it contains other components, then we can say it is constituted of several active objects. Also, if the component is primitive, but the programmer of this component has put some code within it for creating new active objects, the component is again constituted of several active objects.

As a result, a composite component is an active object built on top of the CompositeComponent class, and a parallel component is built on top of the ParallelComponent class. These classes are empty classes, because for composite and parallel components, all the action takes place in the meta-level. But they are used as a base to build active objects, and their names help to identify them with the IC2D visual monitoring tool.

29.3.3. Method invocations on components interfaces

Invoking a method on an active object means invoking a method on the stub of this active object. What usually happens then is that the method call is reified as a Request object and transferred (possibly through a network) to the body of the active object. It is then redirected towards the queue of requests, and delegated to the base object according to a customizable serving policy (standard is FIFO).

Component requests, on the other hand, are tagged so as to distinguish between functional requests and controller requests. A functional request targets a functional interface of the component, while a controller request targets a controller of the component.

Like in the standard case (without components), requests are served from the request queue. The serving policy has to be FIFO to ensure coherency. This is where the life cycle of the components is controlled: the dispatching of the request is dependent upon the nature of the request, and corresponds to the following algorithm:

  loop
        if componentLifeCycle.isStarted()
                get next request
                // all requests are served
        else if componentLifeCycle.isStopped()
                get next controller request
                // only controller requests are served
        ;
        if gotten request is a component life cycle request
                if request is start --> set component state to started ;
                if request is stop --> set component state to stopped ;
        ;
;