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.
Consider a standard system of Fractal components:
ProActive/Fractal features distributed components:
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:
The combination of the Fractal model with the ProActive library leverages the Fractal component model and provides an implementation for Grid computing.
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).
ProActive provides a deployment framework for creating a distributed component system. Using a configuration file and the concept of virtual nodes, this framework:
connects to remote hosts using supported protocols, such as rsh, rlogin, ssh, globus, lsf etc...
creates JVMs on these hosts
instantiates components on these newly created JVMs
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.
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.
We address collective interactions (1-to-n and n-to-1 interactions between components) through Chapter 31, Collective interfaces, namely gathercast and multicast interfaces.
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.
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
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
.
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.
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.
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.
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'.
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.
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.
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 ; ; ;
© 2001-2007 INRIA Sophia Antipolis All Rights Reserved