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.
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: 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 turn these references into component bindings. Of course, we also have to expose the interfaces that we are going to use, hence the Dispatcher, Engine and User interface that have to be implemented.
First of all, have a look at the doc on C3D to remember how this application is written, in Section 5.2, “C3D: a parallel, distributed and collaborative 3D renderer”. 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.
What we need to do is to extract the interfaces of the Objects,
ie find 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.
If you have a code base which closely follows Object Oriented
Programming rules, the interfaces are already there. Indeed, when a
class is written, it should always go with one or more interfaces,
which present to the world what the class abilities are. In C3D
(Active Object version), these interfaces already exist: they are
called User
, Engine
and
Dispatcher
.
Note | |
---|---|
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 class references by their interface, when you use them in other files. For example, if we had not already used interfaces in the C3D Object 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 classes themselves (ie the container which performs operations), but [proxies to] the interfaces presenting the behaviour available. And these proxies implement the interfaces, and do not extend the classes. What is highlighted here is that components enforce good code design by separating behaviours.
You now have to create a class that englobes the previous Active
Objects, and which is a component 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
, listFc
, bindFc
and unbindFc
methods.
This has been done in the *Impl
files. Let's
consider, for example, the UserImpl class (it is shown below).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 variable 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. This is hidden by the ProActive
layer, all you should know is you're addressing a
Dispatcher
, and you're fine! The
findDispatcher
method has been overridden because
component lookup doesn't work like standard Active Object lookup.
public class UserImpl extends C3DUser implements BindingController, User { /** Mandatory ProActive empty no-arg constructor */ public UserImpl() { } /** Tells what are the operations to perform before starting the activity of the AO. * Registering the component and some empty fields filling in is done here. * We also state that if migration asked, procedure is : saveData, migrate, rebuild */ public void initActivity(Body body) { // Maybe 'binding to dispatcher' has been done before if (this.c3ddispatcher == null) { logger.error( "User component could not find a dispatcher. Performing lookup"); // ask user through Dialog for userName & host NameAndHostDialog userAndHostNameDialog = new NameAndHostDialogForComponent(); this.c3ddispatcher = userAndHostNameDialog.getValidatedDispatcher(); setUserName(userAndHostNameDialog.getValidatedUserName()); if (this.c3ddispatcher == null) { logger.error("Could not find a dispatcher. Closing."); System.exit(-1); } } if (getUserName() == null) { // just in case it was not yet set. setUserName("Bob"); } // Register the User in the Registry. try { Fractive.register(Fractive.getComponentRepresentativeOnThis(), UrlBuilder.buildUrlFromProperties("localhost", "User")); } catch (IOException e) { logger.error("Registering 'User' for future lookup failed"); e.printStackTrace(); } super.initActivity(body); } /** returns all the possible bindings, here just user2dispatcher . * @return the only posible binding "user2dispatcher" */ public String[] listFc() { return new String[] { "user2dispatcher" }; } /** Returns the dispatcher currently bound to the client interface of this component * @return null if no component bound, otherwise returns the bound component */ public Object lookupFc(final String interfaceName) { if (interfaceName.equals("user2dispatcher")) { return c3ddispatcher; } return null; } /** Binds to this UserImpl component the dispatcher which should be used. */ public void bindFc(final String interfaceName, final Object serverInterface) { if (interfaceName.equals("user2dispatcher")) { c3ddispatcher = (org.objectweb.proactive.examples.c3d.Dispatcher) serverInterface; // Registering back to the dispatcher is done in the go() method } } /** Detaches the user from its dispatcher. * Notice how it has not been called in terminate() ? * This is due to the fact that unbinding only sets a reference to null, * and does no cleaning up. */ public void unbindFc(final String interfaceName) { if (interfaceName.equals("user2dispatcher")) { c3ddispatcher = null; } } }
Example 10.1. The UserImpl class, a component wrapper
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.
Note | |
---|---|
If you really have to keep these
|
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
examples/components/c3d/adl/
directory. There are the
components, called '...Impl
' (you can see there which
interfaces they propose), and a
'userAndComposite.fractal
' file, which is where the
bindings are made. It includes the use of a Composite component, just
for the fun. Specifically, it links one user to a composite made of a
dispatcher and two renderers. You may want to explore these files 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"> <!-- This is an example of binding a complete application. In the code below, a user component is attached to a composite, which englobes a dispatcher and 2 renderers. --> <definition name="org.objectweb.proactive.examples.components.c3d.adl.userAndComposite"> <!-- Creating one user component --> <component definition="org.objectweb.proactive.examples.components.c3d.adl.UserImpl" name= "user"/> <component definition="org.objectweb.proactive.examples.components.c3d.adl.compositeOfDispRend" name="composite"/> <!-- binding together the user and the composite --> <binding client="user.user2dispatcher" server="composite.dispatch"/> <controller desc="composite"/> <!-- coordinates added by the fractal GUI of IC2D. --> <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.53" x1="1.00" y1="0.82" name="engine2" x0="0.57"/> <coordinates color="-73" y0="0.10" x1="0.90" y1="0.48" name="engine1" x0="0.63"/> <coordinates color="-73" y0="0.10" x1="0.50" y1="0.90" name="dispatcher" x0="0.1"/> </coordinates> </definition>
Example 10.2. userAndComposite.fractal, a component ADL file
Here's what it looks like when you explore it through the IC2D Component explorer
One feature given by the component architecture is the
possiblity to rename Virtual Nodes
. Let's see how
this can be used:
Suppose you are only dealing with packaged software. That means
you may not modify the source code of some part of your application,
for instance because it is kindly given to you by some other company,
which wants to keep parts of its codebase secret. Let's say that the
deployment descriptor you're using does not reference the proper
VirtualNodes
. How can you still deploy your
application in this case? Well, you have to
rename those Nodes into the names that are
fitting to your application. You should do that after the definition
of the interfaces that are defined inside the component.
Here's an example of how to do that, renaming the externally provided
name 'UserVirtualNode' to the name internally used by UserImpl 'User':
In the main ADL file (userAndComposite.fractal
)
<component ... /> <!-- mapping the node names in the descriptor file to others referenced in the component's adl files. --> <exportedVirtualNodes> <exportedVirtualNode name="UserVirtualNode"> <composedFrom> <composingVirtualNode component="user" name="User"/> </composedFrom> </exportedVirtualNode> </exportedVirtualNodes> <!-- Creating one user component -->
In the User ADL file (UserImpl.fractal
)
<content class="org.objectweb.proactive.examples.components.c3d.UserImpl"/> <!-- Recalling a renamed Virtual Node --> <exportedVirtualNodes> <exportedVirtualNode name="User"> <composedFrom> <composingVirtualNode component="this" name="User"/> </composedFrom> </exportedVirtualNode> </exportedVirtualNodes> <controller desc="primitive"/>
Example 10.3. How to rename Virtual Nodes in ADL files
If you add this code into the
adl, you are saying that the VirtualNode
called
UserVirtualNode
(found in the deployment
descriptor file the application is using) should be recognized by
the application as if it was called
User
.
Note | |
---|---|
Above has been described the way to rename a
|
When running the User Component alone, you are prompted for an address on which to lookup a Dispatcher Component. Then the two components are bound through a lookup mechanism. This is very simple to use. Here's the code to do that:
The component Registration
Fractive.register(Fractive.getComponentRepresentativeOnThis(), UrlBuilder.buildUrlFromProperties("localhost", "Dispatcher"));
The Component lookup
ProActiveComponentRepresentative a = Fractive.lookup( UrlBuilder.buildUrl(this.hostName, "Dispatcher", protocol, this.portNumber)); this.c3dDispatcher = (Dispatcher) a.getFcInterface("user2dispatcher");
Example 10.4. Component Lookup and Register
For the registeration, you only need a reference on the component you want to register, and build a url containing the name of the host, containing an alias for the Component.
The Fractive.lookup
method uses a Url to find
the host which holds the component. This Url contains the machine name
of the host, communication protocl and portNumber, but also the lookup
name under which the desired Component has been registered under ,
here "Dispatcher". The last operation consists only in retreiving the
correct interface to which to connect to. If the interface is not
known at compile-time, it can be discovered at run-time with the
getFcInterfaces()
method, which lists all the
interfaces available.
There is only one access point for this example in the scripts directory:
scripts/unix/components$ ./c3d.sh --- Fractal C3D example --------------------------------------------- Parameters : descriptor_file [fractal_ADL_file] The first file describes your deployment of computing nodes. You may want to try ../../../descriptors/components/C3D_all.xml The second file describes your components layout. Default is org.objectweb.proactive.examples.components.c3d.adl.userAndComposite ---------------------------------------------------------
You have there the way to start this example. If you only want to start the Composite (Dispatcher + Renderer), try this (don't insert the new lines):
scripts/unix/components$ ./c3d.sh ../../../descriptors/components/C3D_all.xml \ org.objectweb.proactive.examples.components.c3d.adl.compositeOfDispRend
If you want to start only a User, you will be asked for the address of a Dispatcher to which to connect to:
scripts/unix/components$ ./c3d.sh ../../../descriptors/components/C3D_all.xml \ org.objectweb.proactive.examples.components.c3d.adl.UserImpl
© 2001-2007 INRIA Sophia Antipolis All Rights Reserved