back to API     back to index     prev     next  

Examples

3 examples are presented : code snippets for visualizing the transition between active objects and components, the "hello world", from the Fractal tutorial, and C3D component version. The programming model is Fractal, and one should refer to the Fractal documentation for detailed examples.

From objects to active objects to distributed components

In Java, objects are created by instantiation of classes. With ProActive, one can create active objects from Java classes, while components are created from component definitions. Let us first consider the "A" interface :

public interface A {
public String foo(); // dummy method
}

"AImpl" is the class implementing this interface :

public class AImpl implements A {

public AImpl() {}

public String foo() {
// do something
}
}

The class is then instantiated in a standard way :

A object = new AImpl();

Active objects are instantiated using factory methods from the ProActive class (see the ProActive Hello World example ). It is also possible to specify the activity of the active object, the location (node or virtual node), or a factory for meta-objects, using the appropriate factory method.

A active_object = (A)ProActive.newActive(
AImpl, // signature of the base class
new Object[] {}, // Object[]
aNode, // location, could also be a virtual node
);

As components are also active objects in this implementation, they benefit from the same features, and are configurable in a similar way. Constructor parameters, nodes, activity, or factories, that can be specified for active objects, are also specifiable for components. The definition of a component requires 3 sub-definitions : the type, the description of the content, and the description of the controller.

Type

The type of the component (i.e. the functional interfaces provided and required) is specified in a standard way : (as taken from the Fractal tutorial)

We begin by creating objects that represent the types of the components of the application. In order to do this, we must first get a bootstrap component. The standard way to do this is the following one (this method creates an instance of the class specified in the fractal.provider system property, and uses this instance to get the bootstrap component):

Component boot = Fractal.getBootstrapComponent();

We then get the TypeFactory interface provided by this bootstrap component:

TypeFactory tf = (TypeFactory)boot.getFcInterface("type-factory");

We can then create the type of the first component, which only provides a A server interface named "a":

// type of the a component
ComponentType aType = tf.createFcType(new InterfaceType[] {
tf.createFcItfType("a", "A", false, false, false)
});

Description of the content

The second step in the definition of a component is the definition of its content. In this implementation, this is done through the ContentDescription class :

ContentDescription contentDesc = new ContentDescription(
AImpl, // signature of the base class
new Object[] {}, // Object[]
aNode, // location, could also be a virtual node
);

Description of the controller

Properties relative to the controller can be specified in the ControllerDescription :

ControllerDescription controllerDesc = new ControllerDescription(
"myName", // name of the component
Constants.PRIMITIVE // the hierarchical type of the component
// it could be PRIMITIVE, COMPOSITE, or PARALLEL
);

Eventually, the component definition is instantiated using the standard Fractal API. This component can then be manipulated as any other Fractal component.

Component component = componentFactory.newFcInstance(
componentType, // type of the component (defining the client and server interfaces)
controllerDesc, // implementation-specific description for the controller
contentDesc // implementation-specific description for the content
);

From attributes to client interfaces

There are 2 kinds of interfaces for a component : those that offer services, and those that require services. They are named respectively server and client interfaces.

From a Java class, it is fairly natural to identify server interfaces : they (can) correspond to the Java interfaces implemented by the class. In the above example, "a" is the name of an interface provided by the component, corresponding to the "A" Java interface.

On the other hand, client interfaces usually correspond to attributes of the class, in the case of a primitive component. If the component defined above requires a service from another component, say the one corresponding to the "Service" Java interface, the AImpl class should be modified. As we use the inversion of control pattern, a BindingController is provided, and a binding operation on the "requiredService" interface will actually set the value of the "service" attribute, of type "Service".

First, the type of the component is changed :

// type of the a component
ComponentType aType = tf.createFcType(new InterfaceType[] {
tf.createFcItfType("a", "A", false, false, false),
tf.createFcItfType("requiredService", "A", true, false, false)
});

The Service interface is the following :

// The Service interface
public interface Service {
public String bar();
}

And the AImpl class is :

// The modified AImpl class
public class AImpl implements A, BindingController {
Service service; // attribute corresponding to a client interface

public AImpl() {}

// implementation of the A interface
public String foo() {
return s.bar(); // for example
}

// implementation of BindingController
public Object lookupFc (final String cItf) {
if (cItf.equals("requiredService")) {
return service;
}
return null;
}

// implementation of BindingController
public void bindFc (final String cItf, final Object sItf) {
if (cItf.equals("requiredService")) {
service = (Service)sItf;
}
}

// implementation of BindingController
public void unbindFc (final String cItf) {
if (cItf.equals("requiredService")) {
service = null;
}
}
}

The HelloWorld example

The mandatory helloworld example (from the Fractal tutorial) shows the different ways of creating a component system (programmatically and using the ADL), and it can easily be implemented using ProActive.

Set-up

You can find the code for this example in the package org.objectweb.proactive.examples.components.helloworld of the ProActive distribution.
The code is almost identical to the Fractal tutorial's example.

The differences are the following :

Architecture

The helloworld example is a simple client-server application, where the client (c) and the server (s) are components, and they are both contained in the same root component (root).

Another configuration is also possible, where client and server are wrapped around composite components (C and S). The goal was initially to show the interception shortcut mechanism in Julia. In the current ProActive implementation, there are no such shortcuts, as the different components can be distributed, and all invocations are intercepted. The exercise is still of interest, as it involves composite components.

helloworld-example

Distributed deployment

This section is specific to the ProActive implementation, as it uses the deployment framework of this library.

If the application is started with (only) the parameter "distributed", the ADL used is "helloworld-distributed.fractal", where virtualNode of the client and server components are exported as VN1 and VN2. Exported virtual node names from the ADL match those defined in the deployment descriptor "deployment.xml".

One can of course customize the deployment descriptor and deploy components onto virtually any computer, provided it is connectable by supported protocols. Supported protocols include LAN, clusters and Grid protocols (see deployment descriptors documentation).

Have a look at the ADL files "helloworld-distributed.fractal" and "helloworld-distributed-wrappers.fractal". In a nutshell, they say : "the primitive components of the application (client and server) will run on given exported virtual nodes, whereas the other components (wrappers, root component) will run on the current JVM.

Therefore, we have the two following configurations :

1. The one without wrappers, where the primitive components are distributed.

distributed-components, no wrappers

2. The one with wrappers, where again, only the primitive components are distributed.

distributed-components, wrappers

Currently, bindings are not optimized. For example, in the configuration with wrappers, there is an indirection that can be costly, between the client and the server. We are currently working on optimizations that would allow to shortcut communications, while still allowing coherent dynamic reconfiguration. It is the same idea than in Julia, but we are dealing here with distributed components. It could imply compromises between dynamicity and performance issues.

Execution

You can either compile and run the code yourself, or follow the instructions for preparing the examples and use the script helloworld_fractal.sh (or .bat). If you choose the first solution, do not forget to set the fractal.provider system property.

If you run the program with no arguments (i.e. not using the parser, no wrapper composite components, and local deployment) , you should get something like this:

01 --> This ClassFileServer is reading resources from classpath
02 Jini enabled
03 Ibis enabled
04 Created a new registry on port 1099
05 //crusoe.inria.fr/Node363257273 successfully bound in registry at //crusoe.inria.fr/Node363257273
06 Generating class : pa.stub.org.objectweb.proactive.core.component.type.Stub_Composite
07 Generating class : pa.stub.org.objectweb.proactive.examples.components.helloworld.Stub_ClientImpl
08 Generating class : pa.stub.org.objectweb.proactive.examples.components.helloworld.Stub_ServerImpl

You can see :

Then you have (the exception that pops out is actually the expected result, and is intended to show the execution path) :

01 Server: print method called
02 at org.objectweb.proactive.examples.components.helloworld.ServerImpl.print(ServerImpl.java:37)
03 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
04 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
05 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
06 at java.lang.reflect.Method.invoke(Method.java:324)
07 at org.objectweb.proactive.core.mop.MethodCall.execute(MethodCall.java:373)
08 at org.objectweb.proactive.core.component.request.ComponentRequestImpl.serveInternal(ComponentRequestImpl.java:163)
09 at org.objectweb.proactive.core.body.request.RequestImpl.serve(RequestImpl.java:108)
10 at org.objectweb.proactive.core.body.BodyImpl$ActiveLocalBodyStrategy.serve(BodyImpl.java:297)
11 at org.objectweb.proactive.core.body.AbstractBody.serve(AbstractBody.java:799)
12 at org.objectweb.proactive.core.body.ActiveBody$FIFORunActive.runActivity(ActiveBody.java:230)
13 at org.objectweb.proactive.core.body.ActiveBody.run(ActiveBody.java:145)
14 at java.lang.Thread.run(Thread.java:534)
15 Server: begin printing...
16 --------> hello world
17 Server: print done.

What can be seen is very different from the output you would get with the Julia implementation. Here is what happens (from bottom to top of the stack):

Now let us have a look at the distributed deployment : execute the program with the parameters "distributed parser". You should get something similar to the following :

01 --> This ClassFileServer is reading resources from classpath
02 Jini enabled
03 Ibis enabled
04 Created a new registry on port 1099
05 ************* Reading deployment descriptor: file:/0/user/mmorel/ProActive/classes/org/objectweb/proactive/examplescomponents/helloworld/deployment.xml ********************
06 created VirtualNode name=VN1
07 created VirtualNode name=VN2
08 created VirtualNode name=VN3
09 **** Starting jvm on crusoe.inria.fr
10 --> This ClassFileServer is reading resources from classpath
11 Jini enabled
12 Ibis enabled
13 Detected an existing RMI Registry on port 1099
14 //crusoe.inria.fr/VN1462549848 successfully bound in registry at //crusoe.inria.fr/VN1462549848
15 **** Mapping VirtualNode VN1 with Node: //crusoe.inria.fr/VN1462549848 done
16 Generating class : pa.stub.org.objectweb.proactive.examples.components.helloworld.Stub_ClientImpl
17 **** Starting jvm on crusoe.inria.fr
18 --> This ClassFileServer is reading resources from classpath
19 Jini enabled
20 Ibis enabled
21 Detected an existing RMI Registry on port 1099
22 //crusoe.inria.fr/VN21334775605 successfully bound in registry at //crusoe.inria.fr/VN21334775605
23 **** Mapping VirtualNode VN2 with Node: //crusoe.inria.fr/VN21334775605 done
24 Generating class : pa.stub.org.objectweb.proactive.examples.components.helloworld.Stub_ServerImpl
25 //crusoe.inria.fr/Node1145479146 successfully bound in registry at //crusoe.inria.fr/Node1145479146
26 Generating class : pa.stub.org.objectweb.proactive.core.component.type.Stub_Composite
27 MOPClassLoader: class not found, trying to generate it
28 ClassServer sent class Generated_java_lang_Runnable_r_representative successfully
39 MOPClassLoader: class not found, trying to generate it
30 ClassServer sent class Generated_java_lang_Runnable_r_representative successfully
31 MOPClassLoader: class not found, trying to generate it
32 ClassServer sent class Generated_org_objectweb_proactive_examples_components_helloworld_Service_s_representative successfully
33 MOPClassLoader: class not found, trying to generate it
34 ClassServer sent class Generated_org_objectweb_proactive_examples_components_helloworld_ServiceAttributes_attribute_controller_representative successfully
35 ClassServer sent class pa.stub.org.objectweb.proactive.examples.components.helloworld.Stub_ServerImpl successfully

What is new is :

Then we get the same output than for a local deployment, the activity of active objects is independent from its location.

01 Server: print method called
02 at org.objectweb.proactive.examples.components.helloworld.ServerImpl.print(ServerImpl.java:37)
03 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
04 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
05 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
06 at java.lang.reflect.Method.invoke(Method.java:324)
07 at org.objectweb.proactive.core.mop.MethodCall.execute(MethodCall.java:373)
08 at org.objectweb.proactive.core.component.request.ComponentRequestImpl.serveInternal(ComponentRequestImpl.java:163)
09 at org.objectweb.proactive.core.body.request.RequestImpl.serve(RequestImpl.java:108)
10 at org.objectweb.proactive.core.body.BodyImpl$ActiveLocalBodyStrategy.serve(BodyImpl.java:297)
11 at org.objectweb.proactive.core.body.AbstractBody.serve(AbstractBody.java:799)
12 at org.objectweb.proactive.core.body.ActiveBody$FIFORunActive.runActivity(ActiveBody.java:230)
13 at org.objectweb.proactive.core.body.ActiveBody.run(ActiveBody.java:145)
14 at java.lang.Thread.run(Thread.java:534)
15 Server: begin printing...
16 ->hello world
17 Server: print done.

The Comanche example

The Comanche example is a nice introduction to component based development with Fractal. It explains how to design applications using components, and how to implement these applications using the Fractal API.

You will notice that the example presented in this tutorial is based on Comanche, a simplistic http server. However, this example extensively uses reference passing through components. For example Request objects are passed by reference. This is incompatible with the ProActive programming model, where, to avoid shared passive objects, all passive objects passed to active objects are actually passed by copy(see ProActive basis). As active objects are themselves passed by reference, one could argue that we could turn some passive object into active objects. This would allow remote referencing through stubs. Unfortunately, for reasons specific to the Sockets and Streams implementations, (Socket streams implementations do not provide empty no-arg constructors), it is not easily possible to encapsulate some of the needed resource classes into active objects.

C3D - from Active Objects to Components

Reason for this example

This is an example of an application that is refactored to fit the components dogma. The standard C3D example has been taken as a basis, and component wrappers have been created. This way, one can see what is needed to transform an application into component-oriented code.

You may find the code in the examples/components/c3d directory of the proactive source.

Using working C3D code with components

C3D Components simplified UML

We consider the working C3D application. It's nice, and has a sleak GUI, but we now want to add component power to it! What we do is shown on the image to the right: add wrappers around the original object classes (C3D*) and instead of linking the classes together by setting fields through the initial methods, do that in the binding methods. In other words, we have to spot exactly where C3DRenderingEngine, C3DUser and C3DDispatcher are used by a class other than itself, and make theses references component bindings. Of course, we also have to expose the interfaces that we are goining to use, hence the Dispatcher, Engine and User interface that have to be implemented.


How the application is written

First of all, have a look at the doc on C3D to remember how this application is written. Most important is the class diagram, showing C3DUser, C3DDispatcher and C3DRederingEngine. We decided that the only objects worth wrapping in components were those three. The rest is too small to be worth the hassle.

Creating the interfaces

What we need to do is to extract the interfaces of the Objects, ie the which methods are going to be called on the components. This means find out what methods are called from outside the Active Object. You can do that by searching in the classes where the calls are made on active objects. For this, you have to know in detail which classes are going to be turned into component. We have done that, and those exposed methods are put in the interfaces User, Engine and Dispatcher.

Tricky part: whatever way you look at components, you'll have to modify the initial code if these interfaces were not created at first go. You have to replace all the classes by their interface, when you use them in other files. If we had not already used interfaces in the C3D Objecct code, we would have had to replace all occurrences of C3DDispatcher by occurrences of Dispatcher.

Why do we have to do that, replacing classes by interfaces? That's due to the way components work. When the components are going to be bound, you're not binding the class themselves, but proxies to these classes. And these proxies implement the interfaces, and do not extend the classes.

Creating the Component Wrappers

You now have to create a class that englobes the previous Active Objects, and which is a composent representing the same functionality. How do you do that? Pretty simple. All you need to do is extend the Active Object class, and add to it the non-functional interfaces which go with the component. You have the binding interfaces to create, which basically say how to put together two Components, tell who is already attached, and how to separate them. These are the lookupFc, bindFc, unbindFc, and listFc methods.

This has been done in the *Impl files. Have a peek, for example, at UserImpl.java. What you have here are those component methods. Be even more careful with this bindFc method. In fact, it really binds the protected Dispatcher variable c3ddispatcher. This way, the C3DUser code can now use this varaible as if it was addressing the real Active Object. Just to be precise, we have to point out that you're going through proxies before reaching the Component, then the Active Object. But this is hidden by the ProActive layer, all you should know is you're addressing a Dispatcher, and you're fine!

Discarding direct reference acknowledgment

If you're out of luck, the code contains instructions to retain references to objects that call methods on the current Object. These methods have a signature ressembling method(..., ActiveObject ao, ...). This is called, in ProActive, with a ProActive.getStubOnThis() (if you don't, and instead use 'this', the code won't work correctly on remote hosts!). If the local object uses this ProActive.getStubOnThis(), you're going to have trouble with components. The problem is that this design does not fit the component paradigm : you should be using declared interfaces bound with the bind methods, not be passing along references to self. So you have to remove these from the code, and make it component-oriented. But remember, you should be using bind methods to attach other components.

ADL

You may be wanting to see how we have bound the components together, now. Since the design is pretty simple, there is not much to it. We have used the fractal ADL, to avoid hard-coding bindings. So all of the information here is in the components/c3d/fractal/ directory. There are the components (which interfaces they propose), and a "distributed.fractal" file, which is where the bindings are made. It includes the creation of a Composite component, just for the fun. You may want to explore it with the Fractal GUI provided with IC2D, it's easier to understand graphically. Here's the code, nevertheless, for you curiosity :

<?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.c3d.fractal.distributed">
  <interface signature="java.lang.Runnable" role="server" name="r"/>
  <component definition="org.objectweb.proactive.examples.components.c3d.fractal.UserImpl" name="user" />
  <component name="Composite">
    <interface signature="org.objectweb.proactive.examples.c3d.Dispatcher" role="server" name="dispatch"/>
    <component definition="org.objectweb.proactive.examples.components.c3d.fractal.EngineImpl" name="engine2"/>
    <component definition="org.objectweb.proactive.examples.components.c3d.fractal.EngineImpl" name="engine1"/>
    <component definition="org.objectweb.proactive.examples.components.c3d.fractal.DispatcherImpl" name="disp"/>
    <binding client="this.dispatch" server="disp.user2dispatcher"/>
    <binding client="disp.dispatcher2engine00" server="engine1.dispatcher2engine"/>
    <binding client="disp.dispatcher2engine01" server="engine2.dispatcher2engine"/>
  </component>
  <binding client="this.r" server="user.r"/>
  <binding client="user.user2dispatcher" server="Composite.dispatch"/>
  <controller desc="composite"/>
  <coordinates color="-73" y0="0.11" x1="0.30" y1="0.33" name="user" x0="0.03"/>
  <coordinates color="-73" y0="0.18" x1="0.99" y1="0.98" name="Composite" x0="0.32">
    <coordinates color="-73" y0="0.60" x1="0.84" y1="0.89" name="engine2" x0="0.23"/>
    <coordinates color="-73" y0="0.15" x1="0.99" y1="0.53" name="engine1" x0="0.72"/>
    <coordinates color="-73" y0="0.12" x1="0.67" y1="0.42" name="disp" x0="0.09"/>
  </coordinates>
</definition>

Here's what it looks like when you explore it through the IC2D Component explorer C3D Components, as seen with the Fractal GUI

Source Code

You may find the source code of this application at the following locations :



Copyright © April 2005 INRIA All Rights Reserved.