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
· SVN Repository
· ObjectWeb Forge Site

About
· Team
· Users
· Partners
· Mailing List Archive

Overview (AOKell 2.0 Documentation)

AOKell 2.0 Documentation

 AOKell: an Aspect-Oriented Implementation of the Fractal Specifications

Lionel Seinturier - Nicolas Pessemier - Thierry Coupaye
02/08/06

This documentation is based on AOKell version 2.0 available for download on the Fractal Forge site.

Table of Contents

  1. Introduction
  2. Membrane Types
  3. Structure of a Component
  4. Features
  5. Code Structure
  6. Tests and Applications
  7. Performances
  8. Writing new Control Membranes
  9. Conclusion

AOKell is INRIA & France Telecom's implementation of the Fractal Specification [1]. This document describes the design choices made to implement AOKell and the overall organization of the framework. This document is intended for framework developers that want to extend AOKell, but it can also be useful for component developers who want to understand how AOKell works.

1 Introduction

AOKell is a framework for implementing component controllers and membranes. As with Julia, the main design goal of AOKell is to implement a framework to program component controllers: we want to implement an extensible set of control objects, from which the user can freely choose and assemble the controller objects he or she wants, in order to build the membrane of a Fractal component.

Compared to other implementations of the Fractal model, the first originality of AOKell is to provide a component-based approach for implementing controllers. The control membrane of a component is an assembly of control components. The notions of a client interface, a server interface, a binding, a composite component are used at the control level much like they are used at the business level. AOKell is then a reflective model in which the notion of a component is used to engineer both the control level and the application level.

The second originality of AOKell is to use notions from the domain of aspect-oriented programming for gluing the application and the control levels. Each control component is associated with an aspect which monitors the execution of the application component and which delegates to the control component the realization of control functionalities. This approach fosters modularity by decoupling the implementation of the control from the integration of this control in applications. Currently, two alternative implementations of the glue layer are available in the AOKell distribution. The first one relies on Spoon [3] which is a fast open-compiler for transforming Java code. The second one relies on the aspect-oriented programming language AspectJ [2]. By default, the Spoon version is used as this is one which provides the best performances, both in term of compilation time and in term of execution of the compiled code.

2 Membrane Types

AOKell is a level 3.3 implementation of the Fractal Specification (see the specifications for the meaning of compliance levels):

A Fractal component is associated to a membrane. The membrane provides a level of control and supervision on the component. Membranes can be decomposed into smaller units which are called controllers. A controller implements a particular functionality such as controlling the component bindings or managing the lifecycle of the component.

Several types of a membrane can be constructed depending on how controllers are grouped together. Following the Julia membrane type system, we support 13 different types of a membrane. The two most common ones are:

  • primitive: provides the binding, lifecycle, naming controllers and the super controllers.
  • composite: provides the same controllers as the ones provided by primitive, plus the content controller.

With AOKell, each type of a membrane is associated to a marker interface. The membrane interfaces are defined in a hierarchy. Figure 1 sums up the part of the hierarchy which corresponds to the two aforementioned types. The controller interfaces provided by each type are also mentioned. Notice that a root interface, BaseType, is defined.

Membrane types

Fig 1: Membrane types.

The interfaces FlatType, PrimitiveType and CompositeType are used to type components. This is a design choice which is specific to the current version of AOKell. This hierarchy has been set up to factorize definitions, and to reuse as much code as possible. However, the use of this hierarchy for implementing membranes and controllers is not mandatory. The glue layer uses this type hierarchy but nothing prevent the programmer from implementing membranes and controllers without using it.

Notice that, for clarity sake, the marker interfaces for parametric and template components have been omitted in Figure 1.

3 Structure of a Component

A Fractal component is composed of a content and is associated to a membrane. The content is put under the control and the supervision of the membrane. A membrane can be decomposed into smaller units which are called controllers.

The purpose of this section is to present the structure set up by AOKell for a Fractal component. Two structures exist:. the AO-structure and the JL-structure. AO stands for aspect-oriented and JL for Julia-like. They are presented respectively in Sections 3.1 and 3.2.

3.1 AO-Structure

With AOKell, controllers are programmed as aspects (the orange circles on Figure 2) which inject code into the content class and control its behavior. The logic of the controller is not implemented directly into the aspect but in a delegated instance which is associated to the aspect (the "controller implementations" of Figure 2.)

Fig 2: Structure of a component.

A Fractal component owns Fractal interfaces. A Fractal interface acts as an entry point (server interface) or an exit point (client interface) for the component. A Fractal interface is associated to an InterfaceType with a name and a Java interface.

The Fractal interfaces of a component (the green circles on Figure 2) are instances which implement the Interface interface and the Java interface defined in their InterfaceType. The type of a Fractal interface is only known at runtime when newFcInstance is called to instantiate a component. This approach is very dynamic and allows creating interface and component types on the fly. Conversely, this late binding mechanism decreases type safety and prevents compile-time checks.

To sum up, the structure of a Fractal component with AOKell is composed of:

  • One instance for the content.
  • One instance per Fractal business interface.
  • One instance per controller.

In addition to that, one needs to mention that, when AspectJ is used, there is also one instance per aspect for the whole application.

This structure could be optimized by merging the controllers with the content. For that, the aspects implementing controllers could directly introduce the logic of the controllers into the content class, instead of introducing a pattern which delegates this logic to some external instances.

Evaluation of the AO-Structure

With this structure, the control features are directly injected into the content by the controllers. This structure is a bit different from the one defined by other implementations of the Fractal Specifications such as Julia.

With Julia, the content and the component are by default, two separated instances. The content class is then just a java.lang.Object which is not aware of the controllers. Nevertheless, if the content class implements one or several control interfaces, then this implementation is notified of any call issued on the controllers. For example, if the content class implements the BindingController interface, then whenever two components are bound, the bindFc method of the content class will be notified. By implementing the control interfaces, the programmer has then the possibility of reacting to control events. Notice that this is not a redefinition of the controllers behavior, as, in all cases, the behaviors implemented in the membrane are always executed.

The structure implemented by AOKell merges the content and the component, whereas, with Julia, the content and the component are separated.

The advantage of merging the content and the component is that the control interfaces are directly accessible at the content level. For example, with AOKell, the programmer can write the following content class.

public class ClientImpl implements PrimitiveType {
  public ClientImpl() {}
  public void run() {
    Service s = (Service) lookupFc("s");
    s.message();
  }
}

ClientImpl is controlled by a primitive membrane, and the interfaces of all controllers included in this membrane are then visible. The programmer can thus call the lookupFc method which is implemented by the binding controller. The programmer does not have to implement the BindingController interface to manipulate bindings as this is the case with Julia. Instead, the programmer must tag the content class as a primitive component. This tagging takes the form of the implementation of the marker interface PrimitiveType.

3.2 JL-Structure

While the structure of a Fractal component with AOKell relieves the programmer from the pain of implementing control interfaces, this structure can be problematic for reusing applications already written with Julia. In order to facilitate this, AOKell also provides a component structure which is more similar to the one of Julia where:

  • The content is free of any injection of control interfaces.
  • If the content class implements control interfaces, the content is notified whenever a call is issued on the controller implementations.

With the JL-structure, the previous class needs to be written as follows:

public class ClientImpl implements BindingController {

  public ClientImpl() {}
  public void run() {
    Service s = (Service) lookupFc("s");
    s.message();
  }

  public String[] listFc() { return new String[]{"s"}; }
  public void bindFc(String itfName, Object target) {
    if( itfName.equals("s") ) { s = (Service) target; }
  }
  public Object lookupFc(String itfName) {
    if( itfName.equals("s") ) { return s; }
    return null;
  }
  public void unbindFc(String itfName) {
    if( itfName.equals("s") ) { s = null; }
  }
  private Service s;
}

3.3 Conclusion on the Structure of a Component

Two structures of a Fractal component are available with AOKell: AO and JL. With the AO-structure, the control interfaces are directly injected into the content, and the programmer can use them directly. The JL-structure is similar to the one implemented by Julia, and facilitates the use of applications developed initially with Julia.

4 Features

In order to promote flexibility, the AOKell framework has been decomposed in modules called features. The purpose of this decomposition is to introduce some degrees of variability in the framework and to let developers change the implementation of these features. So far, five features exist:

  • fcinterface: is concerned with the way Fractal interfaces are generated,
  • glue: is concerned with the way the control and the content are glued together,
  • loggable: is concerned with the ability of having a logger attached to Fractal components,
  • membrane: is concerned with the way control membranes are implemented,
  • platform: is concerned with the targeted Java platform.

Different implementations of these features are provided with AOKell. While a default implementation is provided for each feature, the developer can still choose an alternative version or provide her/his own version by configuring the properties defined in the build.properties file and recompiling AOKell.

4.1 Feature fcinterface

Fractal components provide and require interfaces. These interfaces are associated with a signature (defined with a Java interface), a cardinality (singleton or collection), a contingency (mandatory or optional) and may be client (required) or server (provided). With AOKell these Fractal interfaces are Java classes which implement the Interface interface and the Java interface defined in their signature.

The fcinterface feature is concerned with the way these classes are generated. Two versions of the feature exist:

  • rt (default): the class is generated at runtime with the ASM bytecode engineering library,
  • ct: the class is pre-compiled with the InterfaceGenerator tool.

The default version of this feature is rt. The rt version is more dynamic: Fractal interface signatures can be determined at very lately, at run-time, just before instantiating the component.

With the ct version, all Fractal interfaces are defined at compile-time and the developer can not use at run-time an unforeseen interface. This version is then less flexible. However, with this version, ASM is not needed. In many cases, this simplifies the merging of AOKell with other existing framework, especially those which themselves, perform bytecode engineering or class loading. For example, this version has been used to port FROGi to AOKell.

4.2 Feature glue

Two dimensions exist when programming Fractal component: the business part which deals with the core functionalities provided by the component, and the control part which deals with the way components are managed.

The glue feature deals with the way these two parts are glued together. Two versions of this feature exists:

  • spoon (default): the glue is implemented with Spoon processors,
  • aspectj: the glue is implemented with AspectJ aspects.

4.3 Feature loggable

As with Julia, Fractal components may be associated with a Monolog logger. When this feature is enabled, each primitive component is bound to a logger instance and a monolog-factory instance. These instances can be retrieved by calling the lookupFc method provided by the BindingController interface.

The loggable feature is concerned with whether this feature is enabled or not. Two versions of the feature exist:

  • off (default): the feature is disabled,
  • on: the feature is enabled.

4.4 Feature membrane

Control membranes defines the way components are managed.

The membrane feature is concerned with the way membranes are implemented. Two versions of the feature exist:

  • oo (default): controllers are implemented with Java objects and control membranes are lists of controllers,
  • comp: controllers are implemented with component, called control components, and control membranes are composites which contain control components and which export control interfaces.

The following sub-sections provide more details about the componentization of membranes. Notice that, in both cases the implementation code of controllers is similar. The difference lies in the way the membrane is constructed: a simple list of objects in the first case, and a composite component with sub-components, interfaces and bindings in the second case.

4.4.1 Componentized Membranes

Each membrane is decomposed into a set of controllers. The purpose of this decomposition is to separate the various functionalities involved (lifecycle, binding, naming, etc.) and to make them more reusable in different types of a membrane. However, dependencies exist between controllers which prevent their reuse. For example, when considering a flat membrane which provides a component, a naming, a binding and a lifecycle controllers, the following dependencies can be found:

  • the lifecycle controller depends on the binding controller: indeed, mandatory interfaces must be bound before starting a component. Hence, before performing the start operation, the lifecycle controller must query the binding controller to check if this condition evaluates to true.
  • the lifecycle controller depends on the component controller: for the same reason as before, the lifecycle controller has to retrieve the list of mandatory interfaces which is a piece of information managed by the component controller.
  • the binding controller depends on the component controller: the listFc method defined in the binding control interface returns the names of the client interfaces defined by the current component. This piece of information is stored by the component controller.
  • the component controller depends on the binding controller: indeed, when returning the list of component interfaces, the getFcInterfaces method must return the singleton interfaces and the collection interfaces currently bound. This last piece of information is stored by the binding controller.

Several other dependencies can be defined when considering the other membrane types such as primitive and composite. Notice that these dependencies depends on the semantics chosen for each controller. A different control logic may lead to different dependencies.

In our opinion, a pure object-oriented approach is not adequate for capturing these dependencies. The best solution that could be designed would be to declare references and to provide methods for setting and unsetting these references. Yet, this solution is not satisfactory as the dependencies are tangled with the rest of the code. A better solution is to adopt a component-oriented approach and to isolate these dependencies into a software architecture. The solution implemented in AOKell is then to provide a component-oriented approach for implementing membranes:

  • each control membrane is associated to a composite component which exports the control interfaces provided by this membrane,
  • each controller is programmed as a component and is inserted into the previously defined composite component,
  • controllers are bound together depending on the relations deduced from their semantics.

Figure 3 illustrates this approach.

Fig 3: Componentized membranes.

When designing control membranes as components, the expected benefits are the same as the ones expected when designing an application with components: to foster the reuse of components (controllers) and to facilitate the evolution of the application (the control logic) by defining explicitly existing relations between components.

4.4.2 Fractalization of the Membrane

Once the principle of defining the control membranes with components and bindings has been set up, all the notions and tools defined by Fractal can be reused. Among others, the Fractal API and the Fractal ADL are the two best candidates. Hence the architectures of the 13 existing membrane types in AOKell have defined with Fractal ADL. Figure 4 illustrates the architecture of the flat membrane. The Fractal ADL definition follows.

Fig 4: Architecture of the flat membrane type.

<!-- o.o.f is a shortcut for org.objectweb.fractal -->
<definition name="o.o.f.aokell.lib.membrane.flat.Flat"
 extends="o.o.f.aokell.lib.control.component.ComponentControllerType,
          o.o.f.aokell.lib.control.name.NameControllerType,
          o.o.f.aokell.lib.control.lifecycle.LifeCycleControllerType,
          o.o.f.aokell.lib.control.binding.BindingControllerType" > 

  <component name="Comp"
   definition="o.o.f.aokell.lib.control.component.ComponentController" />

  <component name="NC"
   definition="o.o.f.aokell.lib.control.name.NameController" />

  <component name="LC"
   definition="o.o.f.aokell.lib.control.lifecycle.NonCompositeLifeCycleController" />

  <component name="BC"
   definition="o.o.f.aokell.lib.control.binding.FlatBindingController" />

  <binding client="this.//component" server="Comp.//component" />
  <binding client="this.//name-controller" server="NC.//name-controller" />
  <binding client="this.//lifecycle-controller" server="LC.//lifecycle-controller" />
  <binding client="this.//binding-controller" server="BC.//binding-controller" />

  <binding client="BC.//component" server="Comp.//component" />
  <binding client="LC.//binding-controller" server="BC.//binding-controller" />
  <binding client="LC.//component" server="//Comp.component" />

  <controller desc="mComposite" />
</definition>

The previous ADL piece of code defines 4 components (controllers) and 8 bindings. 4 of them are export bindings and the next 4 are normal bindings between components. In addition, the Flat component is of type mComposite. mComposite (resp. mPrimitive) is a new controller description which is defined by AOKell for designating composite (resp. primitive) components which implement a control function.

We won't go into the details of the other 12 membranes. Their definitions can be find in the AOKell source code (see package org.objectweb.fractal.aokell.lib.membrane). The philosophy of control membranes with AOKell is summarized in Figure 5.

Fig 5: Componentized membranes revisited.

4.4.3 Membrane Factory

When instantiating a component, the component factory needs to create a membrane (composite component), create the content, and then, link the content to the membrane. A factory is provided by AOKell to create membranes. This factory is implemented as an interface (membrane-factory) provided by the bootstrap component. The Java definition of this interface follows.

Fig 6: AOKell bootstrap component.

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.factory.InstantiationException;

public interface MembraneFactory {

 /**
  * Return the membrane associated with the given controller description.
  */
 public Component newFcMembrane( Object controllerDesc )
 throws InstantiationException;

}

The implementation of the membrane factory relies on the generic component factory. Indeed, few differences exist between business component and control components. They both provide and require interfaces, they are bound with other components, and their architecture is defined with Fractal ADL.

In order to save execution time, the ADL definition of the membranes is compiled into Java code with the static backend provided by Fractal ADL. Indeed, parsing the ADL definition of the membrane for each newly created business component is costly whenever numerous business components, and then componentized membranes for these components, need to be created. By parsing and compiling membrane definitions once, we save a significant amount of execution time.

4.4.4 Controlling the Controllers

The rationale for defining control membranes and controllers with components and bindings is to provide a powerful mean to engineer the control dimension of the component model. The idea is that different applications will have different needs in terms of control. The functionalities needed to control embedded real-time components is certainly not the same as the one required by components for information systems. Yet, few component models tackle the issue of engineering the control dimension. AOKell addresses this issue by providing the same notions (components and bindings) for engineering the control dimension and the business dimension. The glue between these two dimensions is provided by Spoon processors or AspectJ aspects. By offering developers powerful means to program control membranes, we hope to promote adaptability and to ensure that applications will be better integrated within various different execution contexts.

However, as controllers are programmed as components, the issue of the control functions applied to controllers can be raised. This leads to a three-level model where M0 is the business level, M1 is the level controlling M0 and M2 is the level controlling M1 (see Figure 7).

Fig 7: Control layers.

As the layering of control functions can not be infinite, we have to provide a way to stop the recursion. Basically, two solutions can be designed:

  • either M2 is implemented with M1: in such a case, a control membrane (at the M1 level) controls other M1 membranes and provides also the control function for itself,
  • or M2 is implemented as an ad-hoc policy in M1 (hence M2 does not exists per se).

The second solution has been implemented with AOKell. The rationale for this choice is that, although we need to adapt the control policy to several application domains and execution contexts, the control of controllers is an application domain per se, and is unlikely to be subject to tremendous chances over time. Hence, although less flexible, the ad-hoc solution chosen for level M2 is also certainly more efficient than a fully open and reflective solution.

4.5 Feature platform

AOKell primarily targets the development of Fractal applications for computers equipped with a standard Java virtual machine (the so-call J2SE). However, AOKell can also run on much more constrained environments such as the virtual machines for PDA and smart phones. 

The platform feature allows generating versions of AOKell for different types of Java platforms.

Three versions of this feature exists:

  • j2se (default): with this version, AOKell runs on any standard J2SE virtual machine.
  • j2me-cdc: the target platform is J2ME CDC. With this version, AOKell can be used on virtual machines for PDA such as IBM J9 VM.
  • j2me-cldc: the target platform is J2ME CLDC. With this version, AOKell can then be used on virtual machines for smart phones and embedded devices such as Sun KVM.

4.6 Conclusion on Features

The feature mechanism provide some degrees of variability and allow the developers to customize the behavior of AOKell. The default feature configuration is: fcinterface rt, glue aspectj, loggable off, membrane component. By configuring the properties defined in the build.properties file and recompiling AOKell, the developer can choose his/her own version.

5 Code Structure

The source code of AOKell is organized in four modules:

  • aokell-glue: this module contains the glue (Spoon processors or AspectJ aspects) for integrating control membranes with the application level.
  • aokell-component: this module contains the Bootstrap component.
  • aokell-lib: this module contains controllers and membranes implementations.
  • aokell-tools: this module contains some utility tools. These are compile-time tools are not needed at run-time.

All the glue code is packaged in aokell-glue.jar. Two verfsions of this file exist depending on whether the glue feature is set to spoon or to aspectj. The file are named respectively aokell-glue-spoon.jar and aokell-glue-aspectj.jar. The other modules only contain classes and interfaces. These modules can not be used independently: aokell-glue, aokell-component and aokell-tools use classes and interfaces defined in aokell-lib. The components in aokell-component are modified by the glue code contained in aokell-glue.

Package dependencies

Fig 8: Package dependencies.

AOKell jar size occupation is 100KB (compared to 180KB for Julia 2.2): 16KB for aokell-glue-spoon.jar, 2KB for aokell-component.jar and 82KB for aokell-lib.jar.

6 Tests and applications

This section reports on the tests and the applications which are available with AOKell.

6.1 Compliance Tests

Julia is distributed with a set of 142 JUnit compliance tests. On the 142 tests, 138 run successfully with AOKell. This section comments on the 6 tests where the behavior of AOKell differs from that of Julia.

The test testParametricPrimitiveTemplate in TestComponent reports an error, because the setter method for the attribute X11 is named setWriteOnlyX11. As specified in the Fractal Specifications, AOKell expects a setter method named setX11. In our opinion, the problem lies with the test, not with AOKell.

The test testStarted in TestLifeCycleController assumes that a method call on a stopped interface hangs. This is not an issue related with the Fractal Specifications but with the semantics chosen by Julia for lifecycle controllers. AOKell chooses instead to throw an exception. This test is then not a Fractal test, but a Julia specific one.

The test testWouldCreateNonLocalImportBinding in TestContentController fails. It seems that the test tries to bind a mandatory interface to an optional one.  This is not permitted with AOKell.

The test testRemoveNotStopped in TestLifeCycleController fails because AOKell does not automatically starts components when they are added in a composite, which is what is assume by the test. As far as we understand the Fractal Specifications, this is a Julia specific behavior.

6.2 Sample Applications

The following applications are known to run with AOKell:

  • comanche: a Web server.
  • Fractal RMI: the extension of Fractal for dealing with remote components.
  • Fractal Explorer: a run-time graphical console for Fractal applications.
  • GoTM: a framework for building transaction services.
  • FROGi: a framework for packaging Fractal components in OSGi bundles.
  • various "Hello world" examples to illustrate the use of Fractal ADL, templates, collection interfaces, etc.

Some controllers, originally developed with Julia, have also been successfully implemented with AOKell:

  • Dream: a framework for the construction of communication middleware containing a set of dedicated controllers.
  • Fractal pool: a controller for managing caches.

This initial list of applications and controllers running with AOKell is permanently under construction and we welcome any proposal for extending it.

7 Performances

In this section, we compare the performances of AOKell with those of Julia on a test suite known as JACBenchmark, and the performance tests of Julia.

The purpose of our benchmark is to evaluate the overhead of running a "Fractalized" application with AOKell. The purpose is not to evaluate the time needed to compile an AOKell application, nor to evaluate the time taken by control operations (such as starting or stopping a component). The times we are measuring are the ones experienced by an end user who is running an "AOKellized" application.

JACBenchmark is a benchmark which we have developed for AOP applications. The benchmark has been adapted to support Fractal components. The benchmark contains a composite and two primitive components: a client and a server. The server component implements an interface with 8 different methods. Each method implements a different signature. The methods are not empty, but do not perform any interesting stuff (empty methods could distort the benchmark). A warm-up procedure is executed before taking any measures to avoid side-effects introduced by the JVM.

The client component issues one million calls on each method of the server interface (total: 8 million calls). The procedure is repeated 4 times. The result of the benchmark is the mean time of the 4 measures. The measures are taken on a 1.5GHz PC running Windows XP Pro and Sun JDK 1.5.0.

Table 1 compares the results of the current version of AOKell (2.0) to those of Julia 2.1.1. The performances of AOKell with two different configurations, glue feature implemented with AspectJ or Spoon, are measured. The performances of two Julia optimization levels, NONE and MERGEALL, are also measured. The optimization level of Julia known as MERGEALL consists in merging the content, the controller and the interceptors. We also measure the cost of the interception performed by the frameworks.

Implementation

Time (in ms)

lifecycleno lifecycle
AOKell 2.0AspectJ443 212
Spoon221212
Julia 2.1.1NONE484 234
MERGEALL396234
 
Implementation

Time (in ms)

interceptionno interception
Pure Java 1.5.0172 122
AspectJ 1.2.1206  
JBoss AOP 1.1.11046  

Tab 1: Cost of invoking and executing an operation (x 8,000,000).

The left part of Table 1 compares the figures obtained with AOKell with those obtained with Julia. Two series of figures are given: with the lifecycle activated or not. Let remind that this controller performs some interception on incoming calls to implement the lifecycle policy.

A difference between AOKell and Julia can be witnessed when the lifecycle is activated. The main reason for this difference lies in the semantics implemented by the lifecycle controller. With AOKell, this controller rejects calls on stopped components. With Julia, the lifecycle controller blocks calls on stopped components and counts the number of executing methods (to avoid removing a component which is still executing some requests.) This semantics implies some additional treatments, among others the management of shared data which needs to be synchronized. The cost of these treatments leads to the difference witnessed in Table 1.

Another difference which is worth mentioning concerns the figures for AOKell with Spoon and AOKell with AspectJ. The results show that Spoon performs better than AspectJ when the lifecycle control comes into play. The reason is that Spoon, being a general purpose transformation tool, allows optimizing more aggressively than AspectJ, the transformed code. For example, the interception code can be inserted directly into the intercepted methods. By contrast, AspectJ inserts a call to the interception code. This indirection leads to a additional method call for every component operation invocation, and thus, multiply by 2 the cost of invoking an operation.

Notice that the performance of AOKell are the same whatever the chosen values are for the features loggable, membrane and fcinterface. Indeed, these features only impact the execution time of control functions, not the execution time of business methods. Hence, it is worth noticing that there is no cost, in terms of business method execution, in using componentized membranes compared to using membranes with plain old Java objects.

As a matter of comparison, the right part Table 1 gives the execution time of the same benchmark in pure Java (no Fractal components), in AspectJ 1.2.1 and in JBoss AOP 1.1.1. For the first case, a proxy instance is inserted in front of the server objects to evaluate the interception cost. For the last two cases, AspectJ and JBoss AOP, an empty around advice is woven on the server object. Compared to the Java version, the AOKellization with Spoon introduces an overhead of 28% (221ms vs. 172ms), which is in our opinion bearable given the benefits brought by the component orientation of Fractal and AOKell.

8 Writing new Control Membranes

This section provides, step by step, a guide for writing new controllers and new control membranes with AOKell. As explained in section Features, two styles of control membranes are supported: object-oriented or componentized. The choice of one of these two modes is a compile-time issue which depends on the value of the fcinterface feature.

Section 8.1 details the writing of plain old Java controllers. This guide is illustrated with an example, a Logger controller, taken from the porting of the Dream framework to AOKell. Section 8.2 revisits this example in the context of componentized membranes.

The whole source code of this tutorial, both for Section 8.1 and Section 8.2, can be downloaded in a single zip file. It is recommended to download the binary distribution of AOKell and to unzip the tutorial archive in the installation directory of AOKell. A tutorial subdirectory will be created.

8.1 POJO controllers

This section presents the writing of a POJO controller with AOKell.

There is two ways of writing a POJO controller, depending on whether the controller is weakly or tightly coupled with the component. In the former case, the controller is registered with the membrane. This process is presented in Section 8.2.1. When a controller needs to be more tightly coupled, some glue code must be provided. As explained in the previous sections, this glue code may perform injections and interceptions. Section 8.2.2 extends the process of Section 8.2.1 with the details of programming the glue code for controllers tightly coupled with the components.

8.2.1 Simple POJO controllers

The process of writing a POJO controller goes through the following three steps:

  1. Determining the control interface
  2. Implementing the controller
  3. Registering the controller

Step 1: Determining the control interface

The control interface is the externally visible entry point for the controller. For the purpose of our example, we define the LoggerControllerItf interface which is the aggregation of two existing Dream interfaces: LoggerController and LoggerControllerRegister. Of course, it is not mandatory to write a new interface, and the programmer can reuse any existing Java interface (e.g. java.lang.Runnable), even if its source code is not available (of course, the bytecode must be available).

Step 2: Implementing the controller

A controller is a class which implements a given interface, such as the one previously defined, and the Controller interface defined by AOKell. This interface defines three methods:

interface Controller {
  public void setFcCompCtrl( Component compctrl );
  public void initFcCtrl();
  public void cloneFcCtrl( Component dst, Object hints ) throws CloneCtrlException;
}

The programmer implements these methods but never calls them directly. They are called by the AOKell framework. In addition a controller class must provided a no-argument constructor. The definition of the methods from the Controller interface follows:

  • setFcCompCtrl: the given parameter is the reference of the Component interface associated to the current component. By this way, every controller in a membrane knows the Component identity and can retrieve any other control interface by invoking getFcInterface().
  • initFcCtrl: this method is available for initializing the controller. This method is invoked by the framework after setFcCompCtrl. The reference to the Component interface is then known and some initialization treatments, depending on this reference, can be performed.
  • cloneFcCtrl: this method is invoked by the Factory controller to clone the controller state. The given parameters are the reference of the component which is a clone of the current one, and some controller-dependant hints which carry additional information to perform this cloning.

To summarize, the AOKell component factory guarantees that, whatever the implementation of the controller is, the invocation order for a newly created controller is:

  1. no-argument constructor
  2. setFcCompCtrl method
  3. initFcCtrl method

As a matter of example, the BasicLoggerControllerImpl class is the implementation of our chosen Logger controller.

Step 3: Registering the controller

The last step consists in adding our Logger controller into an existing membrane or to create a new membrane. In both cases the principle is the same. We illustrate the first case.

The o.o.f.aokell.AOKellMembranes class defines the getMembranes() method which returns a description of existing membranes. This method is called by the component factory when a membrane must be created for a new component. Membranes are described by an array of MembraneDef instances.

Each MembraneDef instance contains:

  • the name of the membrane as a string (e.g. "primitive", "composite", etc.),
  • an array of ControllerDef instances,
  • the marker type for the membrane (e.g. PrimitiveType, CompositeType, etc.).

Each ControllerDef instance describes a controller associated to the membrane and contains:

As a matter of example, we can add our Logger controller to the primitive membrane by appending the following lines at the end of the ControllerDef array associated to the primitive membrane:

new ControllerDef(
  new InterfaceTypeImpl(
    "logger-controller",
    LoggerControllerItf.class.getName(),
    false,    // server interface
    false,    // mandatory
    false     // singleton
  ),
  BasicLoggerControllerImpl.class
);

Once this has been done, all created primitive components hold a new control interface named logger-controller which can be retrieve, as any other control interface, by calling getFcInterface() on the Component reference.

8.2.2 POJO controllers with glue code

The previous process illustrates the writing of a POJO controller with is registered with a membrane and which can be retrieved via the Component controller. This section extends this process for controller which needs to be more tightly coupled with component implementations and which need to perform code injection or interception. In this case, some glue code needs to be provided.

The process of writing a POJO controller with glue code goes through the five following steps:

  1. Determining the control interface
  2. Implementing the controller
  3. Defining a marker interface
  4. Writing the glue
  5. Registering the controller

Steps 1, 2 and 5 are identical to the ones presented in Section 8.2.1. In the remainder of this section, we only details steps 3 and 4.

Step 3: Defining a marker interface

This step consists in writing a marker type for our new controller. A marker type is an interface, such as java.io.Serializable, whose role is to tag a class with a particular property. In our case, we want to let know that our component will be put under the control of our Logger controller. As we will see it in the next step, marker types are used by the glue code to introduce the elements needed by each controller.

Marker interfaces must extends the o.o.f.aokell.lib.membrane.BaseType interface defined by AOKell. The DreamLogType interface defines our marker type.

Note that marker types are most of the time grouped together to form aggregate marker types for membranes. For example, o.o.f.aokell.lib.membrane.primitive.PrimitiveType extends the marker types of all controllers contained in a primitive membrane. Although this is not mandatory, this interface can then be extended to include our new marker type.

Step 4: Writing the glue

The glue code for a given controller selects the classes implementing the associated marker type and performs any transformation needed by this controller. These transformations usually take two forms:

  • methods and interface injection: a component class is extended with some methods and implements a new interface. This is usually the control interface (in our case LoggerControllerItf) and its associated methods.
  • methods interception: some before and/or after treatments may be associated to each method of a component. For example, the lifecycle controller blocks method executions on a stopped component.

Of course, the transformations can take any other forms which is required by the controller.

Two versions of the glue feature of AOKell are available: Spoon or AspectJ. When Spoon is used (which is the default case) a processor must be written. In the case of AspectJ, an aspect must be written. As a matter of example, the classes LoggerControllerProcessor and LoggerControllerTemplate implement the Spoon glue code, and the ALoggerController aspect implements the AspectJ version.

This document does not go into the details of programming with Spoon or AspectJ. The reader is either supposed to be familiar with these tools, or to refer to their respective web sites for documentation.

8.2 Component controllers

When componentized membranes are used, e.g. when the membrane feature is set to comp, the process of writing a controller goes through the following six steps:

  1. Determining the control interface
  2. Implementing the controller
  3. Defining a marker interface
  4. Writing the glue
  5. Membrane architecture description
  6. Registering the membrane

Steps 1 to are identical to the ones presented in the previous section. As with POJO controller, it is not mandatory for component controllers to be associated with glue code. Hence, steps 3 and 4 can be skipped. For clarity sake, the following description assumes that this is not the case.

Although step 2, Implementing the controller, does not differ from the POJO version, two points can be mentioned:

  • The compctrl reference transmitted to setFcCompCtrl is the reference of the M2-level component controller (see Section Controlling the Controllers) and not the reference of the M1-level component controller as this is the case with POJO controllers.
  • The controller implementation is the content of a so-called control component, which is a regular Fractal component. Hence:
    • the Fractal API can be used to manipulate these components,
    • these components provide a BindingController, a NameController and a SuperController interface,
    • composite control components can be used and, in this case, a ContentController interface is also available,
    • collection interfaces are supported,
    • note however that:
      • these control components are unstoppable and do not provide a lifecycle control interface,
      • template control components are not supported.

Step 5: Membrane architecture description

As described in Section Feature membrane, the componentization of control membranes reify the communication paths and the dependencies between controllers. The purpose is to apply to the development of the membrane the concepts which have been applied to the development of applications when components were introduced.

A control membrane is then an assembly of control components described with Fractal-ADL. Two new controller descriptions have been introduced: mComposite and mPrimitive. They correspond respectively, to a composite control component and to a primitive control component. A control membrane is then a mComposite component containing other mComposite or mPrimitive sub-components. The interfaces exported by the root mComposite component are the control interfaces provided by the application-level component controlled by this membrane.

When a new membrane is requested to the membrane factory, the corresponding Fractal-ADL assembly is instantiated. However, this is not the XML definition of the assembly, but its Java description generated by a static Fractal-ADL Java backend which is used. This solution is slightly less flexible (indeed, Java descriptions must be regenerated whenever the XML descriptions change), but is more performant (executing a block of Java code is far less costly than parsing and interpreting an XML document.)

In the case of our example, the Logger controller can be inserted in an existing membrane or in a new one. We illustrate the first case by extending the primitive membrane with this controller. The Primitive, LoggerController and LoggerControllerType definitions implement this extension.

Step 6: Registering the membrane

As with the POJO version, componentized membranes need to be registered in the AOKellMembrane class. However, the definition of MembraneDef changes for componentized membranes. It contains:

  • the name of the membrane as a string (e.g. "primitive", "composite", etc.),
  • the name of the control membrane Fractal-ADL definition as a string (in our case "org.objectweb.fractal.aokell.lib.membrane.primitive.Primitive"),
  • the marker type for the membrane (e.g. PrimitiveType, CompositeType, etc.).

Once this has been done, all created primitive control membranes contain a LogC control component which export a logger-controller interface. This interface is available for the application component controlled by this membrane.

9 Conclusion

This document presents AOKell which is INRIA and France Telecom's implementation of the Fractal component model. This is a full Java implementation (level 3.3) of the Fractal Specifications.

The difference between AOKell and other existing implementations is that AOKell uses Aspect-Oriented Programming and the AspectJ language for implementing controllers and membranes. In our opinion, compared to other techniques such as mixins, this leads to a controller development style which is easier to understand, debug and maintain. We have shown in this document that, while providing a high level of abstraction for programming controllers as aspects, Fractal applications developed with AOKell do not suffer from runtime penalties: performances of AOKell applications are similar to those developed with Julia.

We believe that this experiment shows that AOP and AspectJ are mature tools for engineering complex applications such as the ones developed with Fractal components.

Acknowledgment

This work is partially funded by France Telecom under the external research contract #46131097.

References

[1] E. Bruneton, T. Coupaye, M. Leclercq, V. Quema, J.-B. Stefani. An Open Component Model and Its Support in Java. 7th International Symposium on Component-Based Software Engineering (CBSE-7), LNCS 3054, pp. 7-22, May 2004, Springer.
[2] G. Kiczales, J. Lamping, A. Mendhekar, C. Maeda, C. Lopes, J.-M. Loingtier, J. Irwin. Aspect-Oriented Programming. 11th European Conference on Object-Oriented Programming (ECOOP'97), LNCS 1241, pp. 220-242, June 1997, Springer.
[3] R. Pawlak. Spoon: Annotation-Driven Program Transformation the AOP Case. 1st International Workshop on Aspect-Oriented Middleware Development (AOMD'05) @ ACM/IFIP/Usenix Middleware'05, November 2005.

Document history

  • 3/8/06 : Section Writing new Control Membranes added
  • 2/8/06 : Spoon-related information added (v2.0)
     
  • 1/10/06 : Section Features added
  • 12/8/05 : Sections on membrane types and component structure updated
  • 4/24/05 : Initial release of this document (v1.0)

Lionel Seinturier, Nicolas Pessemier, Thierry Coupaye.
Last modified: 04/24/06.


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