This chapter introduces the ProActive/GCM implementation and presents a short user guide which explains how to use it. The chapter will not explain how to program with components but instead focus on the particularities of the GCM implementation for ProActive.
Creation/usage of primitive and composite components
Client, server and non-functional interfaces
In addition to single/collection cardinalities, the ProActive/GCM implementation also provides multicast and gathercast cardinalities.
ADL support
Deployment descriptor files describing components deployment
Several components in an assembly can be distributed on different nodes on several computers using transparent remote communication. Such an arhitecture is presented in the figure below.
The GCM component model is hierarchical, so components can be either primitives or composites. A composite can contain one or many inner components (primitive or composite). Non functional and functional interfaces can be defined.
ProActive/GCM provides the following defaults non functional interfaces thanks to the following controllers: binding-controller, name-controller, lifecycle-controller, super-controller, content-controller as specified by the GCM specification. In addition, there are other specific controllers in our implementation, namely migration-controller, multicast-controller, gathercast-controller and component-parameters-controller. In the current implementation, the non functional part of a component can also be customized and extended.
Furthermore, ProActive/GCM allows developers to define what a component needs and provides with its client and server interfaces.
By using collective interfaces, component systems designers are able to specify parallelism, synchronization and data distribution. Collective communications refer to multipoint interactions between software entities. Collective interfaces have two types of cardinalities, multicast and gathercast.
A collective interface gives the possibility to manage a group of interfaces as a single entity. This entity is itself an interface and viewed likewise. The role and usage of multicast and gathercast interfaces are complementary.
Multicast interfaces are used for parallel invocations, parameters dispatching and results gathering. A multicast interface is an abstraction for 1-to-n communications. When a single invocation is transformed into a set of invocations, these invocations are forwarded to a set of connected server interfaces. Both the propagation of the invocation and the distribution of the invocation parameters are customizable (through Java annotations in the Java interfaces definitions). The result of an invocation, if it isn’t a void return type, on a multicast interface will be a list of results wherever applicable.
If some parameters in a given method of a multicast interface are lists of values, these values can be distributed in various ways through method invocations to the server interfaces connected to the multicast interface (see Figure 2). The default behaviour –namely broadcast – is to send the same parameters to each of the connected server interfaces (see Figure 2.a). In case some parameters are list of values, copies of the lists are sent to each receiver. However, similar to what SPMD programming model offers, it may be adequate to strip some of the parameters so that the bounded components will work on different data. In MPI, for instance, this can be explicitly specified by stripping a data buffer and using the scatter primitive. In this case, you will use scatter or one-to-one dispatch mode (see Figure 2.b).
GCM also specify result aggregation, but in the current state of the ProActive/GCM implementation the only available behaviour is the aggregation of results as list.
Gathercast interfaces are used for synchronization, parameter gathering and result dispatching. A gathercast interface is an abstraction for n-to-1 communications. It handles data aggregation for invocation parameters, as well as process coordination. It gathers incoming data, and can also coordinate incoming invocations before continuing the invocation flow, by defining synchronization barriers. Invocation parameters are simply gathered into lists of parameters as showed in the Figure 3.
The following table summarizes the possible distribution policies based on parameters and return type.
The Architecture Description Language (ADL) is used to define component type, configure and deploy component systems. You can define component and interface types, describe component membrane, and declare on which Virtual Node and you want deploy each component. The architecture of the system is described in one or many XML file.
Components are defined in files ending with the .fractal
extension.
The syntax of the document is validated against a DTD retrieved from
the classpath classpath://org/objectweb/proactive/core/component/adl/xml/proactive.dtd
.
This DTD extends the Fractal ADL DTD and the implementation reuses
and extends the FractalADL project
[2]. You can find tutorial and document on
ADL in this project1. The GCM specification [1]
adds some keywords in the ADL definition which are supported by ProActive/GCM.
The following section illustrates how you can use these files
to describe a component assembly, and how you can use it with the Fractal/GCM API.
Along this short user guide, we will show how to use concretely the features described previously: how to create primitive and composite components, how to assemble them using Fractal/GCM API and Fractal API files, how to interoperate with components, and then how to describe the deployment of components using deployment descriptor file.
The first step of this user guide explains how to create a single primitive component. Next, we will use an assembly of two primitive components in a composite one.
We want to create a primitive component, called PrimitiveComputer. It exposes one server interface called computer-itf which provides the two following methods: compute and doNothing. To do that, we need to write the two following classes.
package org.objectweb.proactive.examples.components.userguide.primitive; public interface ComputeItf { int compute(int a); void doNothing(); }
package org.objectweb.proactive.examples.components.userguide.primitive; import java.io.Serializable; public class PrimitiveComputer implements ComputeItf, Serializable { public PrimitiveComputer() { } public int compute(int a) { int result = a * 2; System.err.println(" PrimitiveComputer-->compute(" + a + "): " + result); return result; } public void doNothing() { System.err.println(" PrimitiveComputer-->doNothing()"); } }
Now, we will discuss on the different ways to use this component. First, we must create the component with the ProActive/GCM framework. Two kinds of component instantiation are shown. In the first case, we can do all these steps in the application. However, in the second case, we will show how we can use the ADL files to simplify the application and create it in a simpler way.
In order to illustrate these different ways, a new class, Main, containing the possible main method of our application (see the source code below), is written. In this main method, four different methods are called and will be described in the following parts of this document, launchFirstPrimitive, launchWithoutADL, launchWithADL, and finally the last launchAndDeployWithADL. To launch this class, you must put in your classpath all the libraries contained in the lib directory and subdirectories and the ProActive jar. And finally, you must set the three Java properties (fractal.provider, java.security.policy, log4j.configuration) as shown in the command line:
java -Dfractal.provider=org.objectweb.proactive.core.component.Fractive -Djava.security.policy=file:<change_this_part>/ ProActive/dist/proactive.java.policy -Dlog4j.configuration=file:<change_this_part>/ ProActive/dist/proactive-log4j org.objectweb.proactive.examples.components.userguide.Main
package org.objectweb.proactive.examples.components.userguide; import java.util.HashMap; import java.util.Map; import org.objectweb.fractal.adl.Factory; import org.objectweb.fractal.api.Component; import org.objectweb.fractal.api.control.LifeCycleController; import org.objectweb.fractal.api.factory.GenericFactory; import org.objectweb.fractal.api.type.ComponentType; import org.objectweb.fractal.api.type.InterfaceType; import org.objectweb.fractal.api.type.TypeFactory; import org.objectweb.fractal.util.Fractal; import org.objectweb.proactive.api.PADeployment; import org.objectweb.proactive.core.component.Constants; import org.objectweb.proactive.core.component.ContentDescription; import org.objectweb.proactive.core.component.ControllerDescription; import org.objectweb.proactive.core.component.factory.ProActiveGenericFactory; import org.objectweb.proactive.core.descriptor.data.ProActiveDescriptor; import org.objectweb.proactive.core.descriptor.data.VirtualNode; import org.objectweb.proactive.core.node.Node; import org.objectweb.proactive.examples.components.userguide.primitive.ComputeItf; import org.objectweb.proactive.examples.components.userguide.primitive.PrimitiveComputer; import org.objectweb.proactive.examples.components.userguide.primitive.PrimitiveMaster; public class Main { public static void main(String[] args) { // System.out.println("Launch primitive component example"); // Main.launchFirstPrimitive(); System.out.println("Launch component assembly example"); Main.launchWithoutADL(); // System.out.println("Launch and deploy component assembly example"); // Main.launchAndDeployWithoutADL(); // System.out.println("Launch component assembly example with ADL"); // Main.launchOneWithADL(); // // System.out.println("Launch and deploy component assembly example with ADL"); // Main.launchAndDeployWithADL(); //System.err.println("The END..."); //System.exit(0); }
If we want to create and call components in a standard Java application, we need to use the GCM API [1]. The method launchFirstPrimitive shows all the steps to create and use our first primitive component. Firstly, define the type of the component. Secondly, create component using a factory. Thirdly, start the component. And finally, retrieve the component’s interface and use it as a standard Java object to access our component.
private static void launchFirstPrimitive() { try { Component boot = Fractal.getBootstrapComponent(); TypeFactory typeFact = Fractal.getTypeFactory(boot); GenericFactory genericFact = Fractal.getGenericFactory(boot); Component primitiveComputer = null; // type of PrimitiveComputer component ComponentType computerType = typeFact.createFcType(new InterfaceType[] { typeFact .createFcItfType("compute-itf", ComputeItf.class.getName(), TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE) }); // component creation primitiveComputer = genericFact.newFcInstance(computerType, new ControllerDescription( "root", Constants.PRIMITIVE), new ContentDescription(PrimitiveComputer.class.getName())); // start PrimitiveComputer component Fractal.getLifeCycleController(primitiveComputer).startFc(); ((LifeCycleController) primitiveComputer.getFcInterface("lifecycle-controller" )).startFc(); // get the compute-itf interface ComputeItf itf = ((ComputeItf) primitiveComputer.getFcInterface("compute-itf")); ; // call component itf.doNothing(); int result = itf.compute(5); System.out.println("Result of computation whith 5 is: " + result); //display 10 } catch (Exception e) { e.printStackTrace(); } }
Uncomment the line calling the launchFirstPrimitive
method in the main method, launch it and see
below the expected output. The first lines are ProActive
log, and at the end, information printed in the component and in the Main class is visible.
Now that we succeeded to create and use a primitive component, we will learn how to use it in a component assembly. First of all, we want use the previous shown primitive component with another primitive component to explain how to define, implement and use client interfaces. Moreover, in order to use composite component, we put the two primitive components in a composite. The Figure 7.5, “Component assembly ” shows this assembly.
To implement this assembly we need one more class, PrimitiveMaster. This class implements the following Java interfaces: java.lang.Runnable and moreover the BindindController to allow binding on the compute-itf client interface. In the run method we put the call to the PrimitiveComputer component, we don’t need to retrieve the compute-itf interface since the assembling it’s done in the launchWithoutADL method or in the following part using ADL.
package org.objectweb.proactive.examples.components.userguide.primitive; import java.io.Serializable; import org.objectweb.fractal.api.NoSuchInterfaceException; import org.objectweb.fractal.api.control.BindingController; import org.objectweb.fractal.api.control.IllegalBindingException; import org.objectweb.fractal.api.control.IllegalLifeCycleException; public class PrimitiveMaster implements Runnable, Serializable, BindingController { private static final String COMPUTER_CLIENT_ITF = "compute-itf"; private ComputeItf computer; public PrimitiveMaster() { } public void run() { computer.doNothing(); int result = computer.compute(5); System.out.println(" PrimitiveMaster-->run(): " + "Result of computation whith 5 is: " + result); //display 10 } //BINDING CONTROLLER implementation public void bindFc(String myClientItf, Object serverItf) throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException { if (myClientItf.equals(COMPUTER_CLIENT_ITF)) { computer = (ComputeItf) serverItf; } } public String[] listFc() { return new String[] { COMPUTER_CLIENT_ITF }; } public Object lookupFc(String itf) throws NoSuchInterfaceException { if (itf.equals(COMPUTER_CLIENT_ITF)) { return computer; } return null; } public void unbindFc(String itf) throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException { if (itf.equals(COMPUTER_CLIENT_ITF)) { computer = null; } } }
In the launchWithoutADL method, we extend component type definition and component creation parts. And we add one more part, the component assembling. In this part, at first we put the two primitives, PrimitiveComputer and PrimitiveMaster in the composite component. Next, we make the binding between each component interfaces.
private static void launchWithoutADL() { try { Component boot = Fractal.getBootstrapComponent(); TypeFactory typeFact = Fractal.getTypeFactory(boot); GenericFactory genericFact = Fractal.getGenericFactory(boot); // component types: PrimitiveComputer, PrimitiveMaster, CompositeWrapper ComponentType computerType = typeFact.createFcType(new InterfaceType[] { typeFact .createFcItfType("compute-itf", ComputeItf.class.getName(), TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE) }); ComponentType masterType = typeFact.createFcType(new InterfaceType[] { typeFact.createFcItfType("run", Runnable.class.getName(), TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE), typeFact.createFcItfType("compute-itf", ComputeItf.class.getName(), TypeFactory.CLIENT, TypeFactory.MANDATORY, TypeFactory.SINGLE) }); ComponentType wrapperType = typeFact.createFcType(new InterfaceType[] { typeFact.createFcItfType( "run", Runnable.class.getName(), TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE) }); // components creation Component primitiveComputer = genericFact.newFcInstance(computerType, new ControllerDescription( "PrimitiveComputer", Constants.PRIMITIVE), new ContentDescription(PrimitiveComputer. class .getName())); Component primitiveMaster = genericFact.newFcInstance(masterType, new ControllerDescription( "PrimitiveMaster", Constants.PRIMITIVE), new ContentDescription(PrimitiveMaster. class .getName())); Component compositeWrapper = genericFact.newFcInstance(wrapperType, new ControllerDescription( "CompositeWrapper", Constants.COMPOSITE), null); // component assembling Fractal.getContentController(compositeWrapper).addFcSubComponent(primitiveComputer); Fractal.getContentController(compositeWrapper).addFcSubComponent(primitiveMaster); Fractal.getBindingController(compositeWrapper).bindFc("run", primitiveMaster.getFcInterface("run")); Fractal.getBindingController(primitiveMaster).bindFc("compute-itf", primitiveComputer.getFcInterface("compute-itf")); // start CompositeWrapper component Fractal.getLifeCycleController(compositeWrapper).startFc(); // get the run interface Runnable itf = ((Runnable) compositeWrapper.getFcInterface("run")); // call component itf.run(); } catch (Exception e) { e.printStackTrace(); } }
This way isn’t the simplest one to create and use component. There is a lot of code to write, that could introduce mistakes or errors in an assembly. We will show an easier one next.
We want create the same component directly using ADL capabilities. The source code of the method launchWithADL shows how to use it. Another factory is used, and we can create directly the component without defining at first its type. Utilization of the created component is still the same. You can see that we don’t need to define and assemble parts any more. Moreover, we need to create only one component, the other ones are automatically created.
private static void launchWithADL() { try { Factory f = org.objectweb.proactive.core.component.adl.FactoryFactory.getFactory(); Map<String, Object> context = new HashMap<String, Object>(); // component creation Component compositeWrapper = (Component) f.newComponent( "org.objectweb.proactive.examples.components.userguide.adl.CompositeWrapper", context); // start PrimitiveComputer component Fractal.getLifeCycleController(compositeWrapper).startFc(); // get the run interface Runnable itf = ((Runnable) compositeWrapper.getFcInterface("run")); // call component itf.run(); } catch (Exception e) { e.printStackTrace(); } }
ADL allows describing a component assembly through a text
file. In our case, we have defined fives files. These
files need to be in the classpath of
the application, for instance the PrimitiveComputer.fractal
file needs to be in the org/objectweb/proactive/examples/components/userguide/adl
directory in the classpath. The first one, PrimitiveComputerType.fractal,
describes the component type, in particular the interface and
the membrane with the tags interface and controller. The second one,
PrimitiveComputer.fractal, adds two necessary information: the
implementation class with the content tag and a virtual node
with the virtual-node tag. These tags are explained in the following section.
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE definition PUBLIC "-//objectweb.org//DTD Fractal ADL 2.0//EN" "classpath://org/objectweb/proactive/core/component/adl/xml/proactive.dtd"> <definition name="org.objectweb.proactive.examples.components.userguide.adl.PrimitiveComputerType"> <interface signature="org.objectweb.proactive.examples.components.userguide.primitive.ComputeItf" role="server" name="compute-itf" /> <controller desc="primitive" /> </definition>
It is quite the same for the PrimitiveMaster component; just the name and definition class change, and there is one more interface, a client one.
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE definition PUBLIC "-//objectweb.org//DTD Fractal ADL 2.0//EN" "classpath://org/objectweb/proactive/core/component/adl/xml/proactive.dtd"> <definition name="org.objectweb.proactive.examples.components.userguide.adl.PrimitiveMasterType"> <interface signature="org.objectweb.proactive.examples.components.userguide.primitive.ComputeItf" role="client" name="compute-itf" /> <interface signature="java.lang.Runnable" role="server" name="run" /> <controller desc="primitive" /> </definition>
And finally, there is the composite one. It defines one interface, and include the two primitive described previously. The binding tag is new; it describes the binding between the interface from composite and inner components.
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE definition PUBLIC "-//objectweb.org//DTD Fractal ADL 2.0//EN" "classpath://org/objectweb/proactive/core/component/adl/xml/proactive.dtd"> <definition extends="org.objectweb.proactive.examples.components.userguide.adl.PrimitiveMasterType" name="org.objectweb.proactive.examples.components.userguide.adl.PrimitiveMaster"> <content class="org.objectweb.proactive.examples.components.userguide.primitive.PrimitiveMaster" /> <virtual-node name="primitive-node" cardinality="single" /> </definition>
Now, we can run the example; uncomment the line calling the launchWithADL method in the main and then you can see the same output as in the previous section.
To deploy components on a specific virtual node, we need
to use ADL files. Just before we saw that the tag
virtual-node allows to specify which virtual node to
use for a component. The virtual node is defined in a separate
file: a deployment descriptor. You can find more information on how to write a
deployment descriptor file in the ProActive documentation,
chapter 21, XML Deployment Descriptors.
The deployment descriptor file used in this example is in the Appendix: deploymentDescriptor.xml.
Furthermore, we need to inform the factory how to use this deployment descriptor; we do this in the launchPrimitiveADLAndDeployment method :
We create a ProActiveDescriptor object
We put this object in the context HashMap
We give this HashMap to the factory
Thus, the factory can retrieve the virtual node defined, and use it as described in the ADL files.
There is another specific point in the
end of this method with the
deploymentDescriptor.killall(false);
call.
This method kills all the JVM deployed using the
original deployment descriptor file. Before this call, we need
to suspend the program since the method calls in GCM
are asynchronous, in order to not kill JVM before the end of the component execution.
private static void launchAndDeployWithADL() { try { // get the component Factory allowing component creation from ADL Factory f = org.objectweb.proactive.core.component.adl.FactoryFactory.getFactory(); Map<String, Object> context = new HashMap<String, Object>(); // retrieve the deployment descriptor ProActiveDescriptor deploymentDescriptor = PADeployment.getProactiveDescriptor(Main. class .getResource("deploymentDescriptor.xml").getPath()); context.put("deployment-descriptor", deploymentDescriptor); deploymentDescriptor.activateMappings(); // component creation Component compositeWrapper = (Component) f.newComponent( "org.objectweb.proactive.examples.components.userguide.adl.CompositeWrapper", context); // start PrimitiveComputer component Fractal.getLifeCycleController(compositeWrapper).startFc(); // get the compute-itf interface Runnable itf = ((Runnable) compositeWrapper.getFcInterface("run")); // call component itf.run(); Thread.sleep(1000); // wait for the end of execution // and kill JVM created with the deployment descriptor deploymentDescriptor.killall(false); } catch (Exception e) { e.printStackTrace(); } }
Now we can run this example; uncomment the line calling the launchPrimitiveADLAndDeployment method, launch it and see the output. The first lines are ProActive log; it’s more verbose than during previous execution because we deploy the two JVMs defined in the deployment descriptor file. After that, you can see information printed from the component and the Main class . And finally, the ProActive log again when the created JVMs are killed.
Client and server also support multicast and gathercast interface cardinality. The GCM [1] explains which constraints the server and client interfaces must respect and we discuss previously in the section 2.2 on their principles.
For multicast interfaces you can specify the parameter dispatching mode thanks to Java annotations available in the org.objectweb.proactive.core.component.type.annotations.multicast package.
Two component applications are included in ProActive the HelloWorld and C3D example.
A Hello World example is provided. It shows the
different ways of creating a component system programmatically and using
ADL. You can find the code for this example in the package
org.objectweb.proactive.examples.components.helloworld
of the CFI prototype distribution.
The example code can either be compiled and run manually or using scripts (hello-world_fractal.sh (or .bat) in the scripts/unix/components directory) can be used to launch it. If you choose the first solution, do not forget to set the fractal.provider system property.
The other example, C3D application — a parallel, distributed and collaborative 3D renderer, is in the org.objectweb.proactive.examples.components.c3d package.
<?xml version="1.0" encoding="UTF-8"?> <ProActiveDescriptor xmlns="urn:proactive:deployment:3.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:proactive:deployment:3.3 http://www-sop.inria.fr/oasis/ProActive/schemas/deployment/3.3/deployment.xsd"> <variables> <descriptorVariable name="PROACTIVE_HOME" value="/user/cdalmass/home/workspace/ProActiveLatest" /> <!--CHANGE ME!!!! --> <descriptorVariable name="JAVA_HOME" value="/user/vcave/home/bin/jdk1.6.0_03" /> <!-- /user/cdalmass/home/pub/local/jdk1.5.0_09 Path of the remote JVM , CHANGE ME!!!! --> </variables> <componentDefinition> <virtualNodesDefinition> <virtualNode name="primitive-node" /> <virtualNode name="composite-node" /> </virtualNodesDefinition> </componentDefinition> <deployment> <mapping> <map virtualNode="primitive-node"> <jvmSet> <!-- <currentJVM />--> <vmName value="jvm1" /> </jvmSet> </map> <map virtualNode="composite-node"> <jvmSet> <!-- <currentJVM />--> <vmName value="jvm2" /> </jvmSet> </map> </mapping> <jvms> <jvm name="jvm1"> <creation> <processReference refid="rshProcess" /> </creation> </jvm> <jvm name="jvm2"> <creation> <processReference refid="rshProcess" /> </creation> </jvm> </jvms> </deployment> <infrastructure> <processes> <processDefinition id="jvmProcess"> <jvmProcess class="org.objectweb.proactive.core.process.JVMNodeProcess"> <classpath> <!-- <absolutePath value="${PROACTIVE_HOME}/bin" />--> <absolutePath value="${PROACTIVE_HOME}/classes/Core" /> <absolutePath value="${PROACTIVE_HOME}/classes/Examples" /> <absolutePath value="${PROACTIVE_HOME}/classes/Extensions" /> <absolutePath value="${PROACTIVE_HOME}/classes/Extra" /> <absolutePath value="${PROACTIVE_HOME}/classesGCMTests" /> <absolutePath value="${PROACTIVE_HOME}/classes/Tests" /> <absolutePath value="${PROACTIVE_HOME}/classes/Utils" /> <absolutePath value="${PROACTIVE_HOME}/lib/javassist.jar" /> <absolutePath value="${PROACTIVE_HOME}/lib/bouncycastle.jar" /> <absolutePath value="${PROACTIVE_HOME}/lib/fractal.jar" /> <absolutePath value="${PROACTIVE_HOME}/lib/log4j.jar" /> <absolutePath value="${PROACTIVE_HOME}/lib/xercesImpl.jar" /> </classpath> <javaPath> <absolutePath value="${JAVA_HOME}/bin/java" /> </javaPath> <policyFile> <absolutePath value="${PROACTIVE_HOME}/dist/proactive.java.policy" /> </policyFile> <log4jpropertiesFile> <absolutePath value="${PROACTIVE_HOME}/dist/proactive-log4j" /> </log4jpropertiesFile> </jvmProcess> </processDefinition> <processDefinition id='rshProcess'> <sshProcess class='org.objectweb.proactive.core.process.ssh.SSHProcess' hostname='nyx.inria.fr'> <processReference refid='jvmProcess' /> </sshProcess> </processDefinition> </processes> </infrastructure> </ProActiveDescriptor>
© 1997-2008 INRIA Sophia Antipolis All Rights Reserved