ObjectWeb Consortium
Print

Advanced Search - Powered by Google

  Consortium     Activities     Projects     Forge     Events 

Fractal


Project Links
· Home
· Documentation
· Download
· License
· What's New
· Wiki

Developers' Corner
· Workplan
· OW2 GitLab Fractal

About
· Team
· Users
· Partners
· Mailing List Archive




Fractal Tutorial



AUTHOR :       
E. Bruneton          (France Telecom R&D)

Version   1.1
Released    September 12, 2003




This tutorial explains how to program a Fractal component based application in Java, and how to deploy it. This tutorial is independent of any specific Java implementation of the Fractal component model, provided it is compliant with the 3.3 conformance level (cf. the Fractal specification).

The example used throughout this tutorial is a very simple application made of two primitive components inside a composite component (see the figure below). The first primitive component is a "server" component that provides an interface to print messages on the console. It can be parameterized thanks to two attributes: a "header" attribute to configure the header printed in front of each message, and a "count" attribute to configure the number of times each message should be printed. The other primitive component is a "client" component that uses the previous component to print some messages.

The server component provides a server interface named "s" of type Service, which provides a print method. It also has an AttributeController interface of type ServiceAttributes, which provides four methods to get and set the two attributes of the server component.

The client component provides a server interface named "m" of type Main, which provides a main method, called when the application is launched. It also has a client interface named "s" of type Service.

The code of the classes and interfaces described here can be found in the examples/helloworld directory of the Fractal distribution.

1 Implementation

The application can be programmed in two steps, by creating the component interfaces first, and then implementing these interfaces in the component classes.

1.1 Creating the component interfaces

Three interfaces must be implemented: the two "functional" interfaces Service and Main, and the attribute controller interface ServiceAttributes.

The functional interfaces are programmed "normally", i.e., the Fractal model does not impose any constraints on the Fractal functional component interfaces, except the fact that they must be public. The Service and Main interfaces are therefore very simple to implement:

public interface Service {
  void print (String msg);
}

public interface Main {
  void main (String[] args);
}

On the other hand, the attribute controller interfaces must extend the AttributeController interface, and must only have getter and setter method pairs (and they must be public too). The ServiceAttributes interface is therefore the following:

public interface ServiceAttributes extends AttributeController {
  String getHeader ();
  void setHeader (String header);
  int getCount ();
  void setCount (int count);
}

1.2 Implementing the component classes

The component classes must implement the previous interfaces, as well as some Fractal control interfaces.

The server component class, called ServerImpl, must implement the Service and ServiceAttributes interfaces. It may also implement the LifeCycleController interface, in order to be notified when it is started and stopped, but this is not mandatory. Since the server component does not have client interfaces, the ServerImpl class does not need to implement the BindingController interface. The ServerImpl class is therefore the following:

public class ServerImpl implements Service, ServiceAttributes {

  private String header = "";

  private int count = 0;

  public void print (final String msg) {
    for (int i = 0; i < count; ++i) {
      System.err.println(header + msg);
    }
  }

  public String getHeader () {
    return header;
  }

  public void setHeader (final String header) {
    this.header = header;
  }

  public int getCount () {
    return count;
  }

  public void setCount (final int count) {
    this.count = count;
  }
}

The client component class, called ClientImpl, must implement the Main interface. Since the client component has client interfaces, ClientImpl must also implement the BindingController interface. It may also implement the LifeCycleController interface, in order to be notified when the component is started and stopped, but this is not mandatory. The ClientImpl class is therefore the following:

public class ClientImpl implements Main, BindingController {

  private Service service;

  public void main (final String[] args) {
    service.print("hello world");
  }

  public String[] listFc () {
    return new String[] { "s" };
  }

  public Object lookupFc (final String cItf) {
    if (cItf.equals("s")) {
      return service;
    }
    return null;
  }

  public void bindFc (final String cItf, final Object sItf) {
    if (cItf.equals("s")) {
      service = (Service)sItf;
    }
  }

  public void unbindFc (final String cItf) {
    if (cItf.equals("s")) {
      service = null;
    }
  }
}

If "s" were a collection of client interfaces, i.e., if the client component could be bound to several server components, the ClientImpl class would be the following:

public class ClientImpl implements Main, BindingController {

  private Map services = new HashMap();

  public void main (final String[] args) {
    Iterator i = services.values().iterator();
    while (i.hasNext()) {
      ((Service)i.next()).print("hello world");
    }
  }

  public String[] listFc () {
    return (String[])services.keySet().toArray(new String[services.size()]);
  }

  public Object lookupFc (final String cItf) {
    if (cItf.startsWith("s")) {
      return services.get(cItf);
    }
    return null;
  }

  public void bindFc (final String cItf, final Object sItf) {
    if (cItf.startsWith("s")) {
      services.put(cItf, (Service)sItf);
    }
  }

  public void unbindFc (final String cItf) {
    if (cItf.startsWith("s")) {
      services.remove(cItf);
    }
  }
}

Note: in the current Julia version, the component classes must be public and must have a public constructor without arguments.

2 Deployment

The simplest method to deploy an application is to describe the architecture of the application in a (possibly XML based) Architecture Description Language (ADL), and to use a Fractal deployment tool to automatically instantiate the application (see the Fractal ADL tutorial). However, in order to explain how the Core Fractal Framework can be used, we show here how the example application can be deployed by directly using the Fractal API.

2.1 Creating the component types

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 composite component, which only provides a Main server interface named "m":

// type of the root component
ComponentType rType = tf.createFcType(new InterfaceType[] {
  tf.createFcItfType("m", "Main", false, false, false)
});

The type of the client and server components are created in a similar way:

// type of the client component
ComponentType cType = tf.createFcType(new InterfaceType[] {
  tf.createFcItfType("m", "Main", false, false, false),
  tf.createFcItfType("s", "Service", true, false, false)
});
// type of the server component
ComponentType sType = tf.createFcType(new InterfaceType[] {
  tf.createFcItfType("s", "Service", false, false, false),
  tf.createFcItfType("attribute-controller", "ServiceAttributes", false, false, false)
});

2.2 Creating the component templates

We could now create the components directly, but we will use intermediate template components here, in order to illustrate how they can be used. We must therefore create template components corresponding to the components of the application. In order to do this, we first get the GenericFactory interface provided by the bootstrap component:
GenericFatory cf = (GenericFatory)boot.getFcInterface("generic-factory");

We then create a composite template component to instantiate the composite component. Here the "compositeTemplate" argument is supposed to describe, in the Fractal implementation that is actually used, a component with the Factory, BindingController and ContentController interfaces. Likewise, the "composite" argument is supposed to describe a component with the LifeCycleController, BindingController and ContentController interfaces.

// template to create the root component
Component rTmpl = cf.newFcInstance(
  rType, "compositeTemplate", new Object[] {"composite", null});

We then create a template to instantiate client component instances. Here the "primitiveTemplate" argument is supposed to describe, in the Fractal implementation that is actually used, a component with the Factory and BindingController interfaces. Likewise, the "primitive" argument is supposed to describe a component with the LifeCycleController and BindingController interfaces. The "ClientImpl" argument is of course the name of the client component class.

// template to create the client component
Component cTmpl = cf.newFcInstance(
  cType, "primitiveTemplate", new Object[] {"primitive", "ClientImpl"});

We then create a template to instantiate server component instances. Here the "parametricPrimitiveTemplate" argument is supposed to describe, in the Fractal implementation that is actually used, a component with the Factory, BindingController and AttributeController interfaces. The "ServerImpl" argument is of course the name of the server component class.

// template to create the server component
Component sTmpl = cf.newFcInstance(
  sType, "parametricPrimitiveTemplate", new Object[] {"parametricPrimitive", "ServerImpl"});

We can then configure the attributes of the server component template, which has the same attribute controller interface as the server component. The template attributes will be automatically copied into all the components created by the template.

ServiceAttributes sa = (ServiceAttributes)sTmpl.getFcInterface("attribute-controller");
sa.setHeader("-> ");
sa.setCount(1);

At this stage we have the following architecture:

We can then either instantiate each template one by one, put the resulting primitive components inside the composite component, connect all these components, and finally start them. But we can also put the primitive templates inside the composite template, connect these templates together, and then instantiate the whole application by just instantiating the composite template component. This is what we do here.

We begin by putting the primitive template components inside the composite one:

ContentController cc = (ContentController)rTmpl.getFcInterface("content-controller");
cc.addFcSubComponent(cTmpl);
cc.addFcSubComponent(sTmpl);

We then bind the internal client interface "m" of the composite template to the server interface "m" of the client template:

((BindingController)rTmpl.getFcInterface(
  "binding-controller")).bindFc("m", cTmpl.getFcInterface("m"));

Finally, we bind the client interface "s" of the client template to the server interface "s" of the server template:

((BindingController)cTmpl.getFcInterface(
  "binding-controller")).bindFc("s", sTmpl.getFcInterface("s"));

The final architecture is the following:

2.3 Instantiating and launching the application

Now that the template components have been created and bound to each other, the application components can be instantiated and bound to each other automatically, by just calling the newFcInstance method on the root composite template component:
Component rComp = ((Factory)rTmpl.getFcInterface(
  "factory")).newFcInstance();

The result is shown in the figure below:

Likewise, all the application components can be started automatically by just calling the startFc method on the root application component:

((LifeCycleController)rComp.getFcInterface(
  "lifecycle-controller")).startFc();

We can finally (!) launch the application:

((Main)rComp.getFcInterface("m")).main(null);

Copyright © 1999-2009, OW2 Consortium | contact | webmaster | Last modified at 2012-12-03 09:57 PM