Fractal - Fraclet

About this document

This document is a tutorial of the Fraclet Annotation implementation of the Fraclet Framework.

  • Title: Fraclet Tutorial
  • Author: Nicolas Pessemier (INRIA)
  • Version: 2.0 (Fraclet-annotation)
  • Released: January 17th, 2007

Table of Contents

  1. Fraclet Annotation: A Java 5 annotation support for Fractal
  2. Fraclet Availability
  3. Dissemination

1. Fraclet Annotation: A Java 5 annotation support for Fractal

Fraclet is the annotation framework of the Fractal component model. Fraclet provides an annotation-oriented programming (@OP) style for developing Fractal applications. Fraclet leverages the process a writting a Fractal application by providing a set of annotations which reduces the amount of code which must be written by developers.

Fraclet-annotation is an implementation of Fraclet using Java5 annotations and Spoon, an Java 5 annotation-driven open compiler and program transformation tool.

About Spoon

Spoon is a Java 5 open compiler built on javac. It uses compile-time reflection to specify program analysis and transformations. Spoon provides the user with a representation of the Java AST called a meta-model, which allows both for reading and writing. Each interface of the meta-model is a compile-time program element (CtElement), which represents an AST node. Using this meta-model and a specific API, Spoon allows the programmer to process Java 5 programs. This processing is implemented with a visitor pattern that scans each visited program element and can apply some user-defined processing jobs called processors. In particular, the processing can be annotation-driven, in a similar way to XDoclet, but using Java 5 annotations. In that case, programmers define so-called annotation processors. Once the program's model has been processed, a processor pretty prints the Java program, which is usually compiled again to generate the processed program's class files.

In addition, Spoon provides templates in pure Java, which are Java class that contains template parameters, which are defined as fields annotated with @Parameter. Template parameters can represent primitive values (such as literal values, program element's names, types), or actual CtElements (ASTs). In the template code, all the references to template parameters can be substituted by their actual values using the substitution engine API provided by Spoon. The templates are usefull to express patterns that can be used for generating code, as exampled by Generative Programming.

To implement Fraclet-annotation with have used a set of Spoon processors and templates to process our defined annotations. This tutorial doesn't detail the implementation of the processors and templates but explain how to use Fraclet-annotation annotations and how features are generated or transformed.

1.1 Available Annotations

Some annotations have been defined in order to describe the component meta-information. The list below describes these annotations.

AnnotationLocationDescription
@FractalComponentClassAnnotation to describe a Fractal component.
@InterfaceInterfaceAnnotation to describe a Fractal business interface.
@ProvidesClassAnnotation to specify that the component provides a server interface which is not annotated with a @Interface (such as java.lang.Runnable).
@AttributeFieldAnnotation to describe an attribute of a Fractal component.
@RequiresFieldAnnotation to describe a binding of a Fractal component.
@LifeCycleMethodAnnotation to specify that the annotated method should be run at component starting or stopping.
@ServiceFieldAnnotation to describe a component service: for example "component" gives the component part reference.

Note that the @FractalComponent, @Interface, @Provides, and @Requires annotations are used together to generate .fractal files for Fractal-ADL. See the revisiting of the Hello World example for more details

In addition to these generic component annotations, two more annotations are used to generate monolog configuration file and getter/setter for the logger and the logger factory.

AnnotationLocationDescription
@MonologHandlerClassAnnotation to generate the monolog.properties file.
@MonologFieldAnnotation to inject the Logger and LoggerFactory references.

@FractalComponent

Details:

This interface annotation is used to generate the .fractal file associated to a Fractal component. The processor associated to this annotation works with all the other annotations to retrieve the informations required to generate a concrete definition of the component: its name, its required interfaces, its provided interfaces, and its attributes.

Parameters:

ParameterDescriptionContingency
controllerDescthe membrane description of the Fractal component. For instance "primitive".Optional

Examples:

@FractalComponent(controllerDesc="MyPrimitiveType")
public class MyComponent { ... }
				

will generate the following .fractal definition:

<component name="MyComponent">
(...)				
  <controller desc="MyPrimitiveType"/>	
</component>
                

@Interface

Details:

This interface annotation has to be used on Java interfaces that need to be used as Fractal interfaces. This annotation is also used by the @FractalImportedInterface annotation as a nested annotation.

IMPORTANT NOTE: you don't need to put again this annotation on content classes which provide this interface. Thanks to Spoon meta model, this information is automatically computed. So you just need to annotate your interface with @Interface, your component with @FractalComponent, and the processor of the @FractalComponent annotation will automatically see that you implement an interface that is annotated with @Interface

Parameters:

ParameterDescriptionContingency
namethe name of the Fractal interface.Required
signaturethe signature of the Fractal interface.Optional
(default value is the Class signature)
cardinalitythe cardinality of the Fractal interface.Optional
(default value is Cardinality.SINGLETON)
contingencythe contingency of the Fractal interface.Optional
(default value is Contingency.MANDATORY)

Examples:

@Interface(name="service")
public interface Service { ... }
                

...will generate the following Fractal ADL abstract definition:

<definition name="Service">
<interface name="service" signature="Service" role="server" 
                          cardinality="singleton" contingency="mandatory"/>
</definition>
			

... and the combination of the @FractalComponent and @FractalItf annotations of the following example:

@Interface(name="service")
public interface Service {
	(...)
}
@FractalComponent
public class ServerA implements Service {
	(...)
}						
		

... will provide the following Fractal-ADL definitions:

<definition name="Service">
<interface name="service" signature="Service" role="server" 
                          cardinality="singleton" contingency="mandatory"/>
</definition>			
			
<definition name="ServerA" extends="Service">
  <content class="ServerA"/>
</definition>		
		

@Provides

Details:

This class annotation handles the particular case of imported Fractal interface, i.e., Java interfaces that are not annotated with @Interface, but directly used as Fractal interfaces. The best example of this is the java.lang.Runnable interface which is regularly used to start a Fractal application.

This annotation use nested annotations which are @Interface annotations. This allows the definition of multiple imported interfaces.

Parameters:

ParameterDescriptionContingency
@Interface[]An array of @Interface annotations.Required

Examples:

@FractalComponent
@Provides(interfaces=@Interface(name="r",signature=java.lang.Runnable.class))
  public class Client implements Runnable {
  (...)
  }					
                

...will generate the following Fractal ADL abstract definition:

<definition name="Client">
  <interface name="r" signature="java.lang.Runnable" role="server" 
                            cardinality="singleton" contingency="mandatory"/>
  <content class="Client"/>
</definition>
			

@Attribute

Details:

This field annotation describes a Fractal attribute. A Fractal attribute is a Java attribute whose value can be configured and introspected from the Fractal component. Fractal attributes are managed by the attribute control feature of the Fractal component model.

The @Attribute will transform the original class to introduce getter/setter, and generate a new interface for these getter/setter. See the examples below.

Used with the @FractalComponent annotation, the Fractal-ADL definition of the Fractal attribute are also generated.

Parameters:

ParameterDescriptionContingency
argumentthe name of the component argument used to configure the Fractal attribute.Optional
(default value is the Field name is no value is defined)
valuethe default value of the Fractal attribute.Optional

Examples:

The following example:

public class ServerA implements Service {

    @Attribute(value=">>")
    private String header ;

    @Attribute(argument="msg")
    private String message ;
	
	(...)
}		
            

... will introduce the following getter/setter methods and generate a ServerAAttributeController interface just as follows:

 public class ServerA implements ServerAAttributeController {
	(...)		
	/** 
     * This method has been automatically injected by Spoon
     * 
     * @return the reference of the field
     */
    public String getHeader() {
        return header;
    }
    
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @return the reference of the field
     */
    public String getMessage() {
        return message;
    }
    
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @param value
     *            the value to set to the field
     */
    public void setHeader(String value) {
        header = value;
    }
    
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @param value
     *            the value to set to the field
     */
    public void setMessage(String value) {
        message = value;
    }
    
}			
			
			
public interface ServerAAttributeController extends AttributeController {
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @return the reference of the field
     */
    public String getHeader();
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @return the reference of the field
     */
    public String getMessage();
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @param value
     *            the value to set to the field
     */
    public void setHeader(String value);
    /** 
     * This method has been automatically injected by Spoon
     * 
     * @param value
     *            the value to set to the field
     */
    public void setMessage(String value);
}			
		

The combination of the @FractalComponent and @Attribute annotations of the following example:

@FractalComponent
public class ServerA implements Service {

    @Attribute(value=">>")
    private String header ;

    @Attribute(argument="msg")
    private String message ;
	
	(...)
}								
		

... will provide the following Fractal-ADL definitions:

<definition name="ServerA" arguments="msg">
  <content class="ServerA"/>
  <attributes signature="ServerAAttributeController">
    <attribute name="message" value="${msg}"/>
    <attribute name="header" value=">>"/>
  </attributes>
</definition>
		

@Requires

Details:

This field annotation describes a Fractal binding. A Fractal binding is a Java attribute representing a client interface. Fractal bindings are managed by the binding control feature of the Fractal component model.

Used with the @FractalComponent annotation, the Fractal-ADL definition of the Fractal attribute are also generated. See the following examples.

Parameters:

ParameterDescriptionContingency
namethe name of the Fractal binding.Required
signaturethe signature of the Fractal binding. Use this parameter only to generate Java 1.4 compatible code for collection interfaces. Don't use this parameter for singleton interfaces.Optional
(default value is None.class)
contingencythe contingency of the Fractal binding (mandatory|optional).Optional
(default value is Contingency.MANDATORY)
cardinalitythe cardinality of the Fractal binding (singleton|collection).Optional
(default value is Cardinality.SINGLETON)

Examples:

public class Client implements Runnable {

    @Requires(name = "default")
    private Service defaut;

    // signature of the Fractal binding is computed because the Map is well typed
    @Requires(name = "service", cardinality = Cardinality.COLLECTION)
    private Map<String,Service> services = new HashMap<String,Service>();

}
            

... will transform the Client class into the following:

public class Client implements Runnable {

    private Service defaut;

    // signature of the Fractal binding is computed because the Map is well typed
    private Map<String,Service> services = new HashMap<String,Service>();				
    /** 
     * (non-Javadoc)
     * 
     * @see org.objectweb.fractal.api.control.BindingController#bindFc(java.lang.String,
     *      java.lang.Object) Method automatically generated with Spoon
     *      <http://spoon.gforge.inria.fr>
     */
    public void bindFc(String clientItfName, Object serverItf) throws NoSuchInterfaceException, 
                                                        IllegalBindingException {
        if (clientItfName.equals("default")) {
            if (!Service.class.isAssignableFrom(serverItf.getClass())) {
                throw new IllegalBindingException((("server interfaces connected to " + clientItfName) 
                                          + " must be instances of ") + (Service.class.getName()));
            }
            defaut = ((Service)(serverItf));
            return ;
        }
        if (clientItfName.startsWith("service")) {
            if (!Service.class.isAssignableFrom(serverItf.getClass())) {
                throw new IllegalBindingException((("server interfaces connected to " 
                                          + clientItfName) + " must be instances of ")
                                          + (Service.class.getName()));
            }
            services.put(clientItfName ,((Service)(serverItf)));
            return ;
        }
        throw new NoSuchInterfaceException(("Client interface \'" + clientItfName) 
                                          + "\' is undefined.");
    }
    
    /** 
     * (non-Javadoc)
     * 
     * @see org.objectweb.fractal.api.control.BindingController#listFc() Method
     *      <http://spoon.gforge.inria.fr>
     */
    public String[] listFc() {
        List<String>  __interfaces__ = new ArrayList<String> ();
        __interfaces__.add("default");
        __interfaces__.addAll(services.keySet());
        return __interfaces__.toArray(new String[]{  });
    }
    
    /** 
     * (non-Javadoc)
     * 
     * @see org.objectweb.fractal.api.control.BindingController#lookupFc(java.lang.String)
     *      Method automatically generated with Spoon
     *      <http://spoon.gforge.inria.fr>
     */
    public Object lookupFc(String clientItfName) throws NoSuchInterfaceException {
        if (clientItfName.equals("default")) {
            return defaut;
        }
        if (clientItfName.startsWith(clientItfName)) {
            return services.get(clientItfName);
        }
        throw new NoSuchInterfaceException(("Client interface \'" + clientItfName) 
                                                + "\' is undefined.");
    }
    
    /** 
     * (non-Javadoc)
     * 
     * @see org.objectweb.fractal.api.control.BindingController#unbindFc(java.lang.String)
     *      Method automatically generated with Spoon
     *      <http://spoon.gforge.inria.fr>
     */
    public void unbindFc(String clientItfName) throws NoSuchInterfaceException {
        if (clientItfName.equals("default")) {
            defaut = null;
            return ;
        }
        if (services.containsKey(clientItfName)) {
            services.remove(clientItfName);
            return ;
        }
        throw new NoSuchInterfaceException(("Client interface \'" 
                                                   + clientItfName) + "\' is undefined.");
    }
}							
			

The combination of the @FractalComponent and @Requires annotations of the following example:

@FractalComponent
public class Client implements Runnable {

    @Requires(name = "default")
    private Service defaut;

    @Requires(name = "service", cardinality = Cardinality.COLLECTION)
    // signature of the Fractal binding is computed because the Map is well typed
    private Map<String,Service> services = new HashMap<String,Service>();

}								
		

... will provide the following Fractal-ADL definitions:

<definition name="Client">
  <interface name="default" signature="Service" role="client" 
                            cardinality="singleton" contingency="mandatory"/>
  <interface name="service" signature="Service" role="client" 
                            cardinality="collection" contingency="mandatory"/>
  <content class="Client"/>
</definition>
		

@LifeCycle

Details:

This method annotation provides a way to define a lifecycle handler supported by the controller part of the associated Fractal component.

Parameters:

ParameterDescriptionContingency
onthe lifecyle transition to handle (LifeCycleType.START | LifeCycleType.STOP).Required

Example:

@FractalComponent
public class Client implements Runnable {
  @LifeCycle(on=LifeCycleType.START)
  private void init() {
    System.out.println("Starting the component Client...");
  }
  (...)
}
                

@Service

Details:

This field annotation provides a way to get a component service. So far, only one service is implemented: "component". This service gives the reference of the Component part.

Parameters:

ParameterDescriptionContingency
nameThe name of the service.Optional (default value is "component")

Example:

public class Client implements Runnable {
  @Service("component")
  private Component c; 
}
                

@MonologHandler

Details:

This class annotation allows the developer to describe a Monolog handler specific to the Fractal component. The available annotation attributes support the various properties that can be defined in a Monolog configuration file.

Parameters:

ParameterDescriptionContingency
namethe identifier of the handler.Required
typethe type of the handler.Optional
(default value is Console)
outputthe output flow of the handler.Optional
(default value is System.out)
patternthe output pattern of the handler.Optional
(default value is %l: %m%n)
max-sizethe maximal size of the output file.Optional
file-numberthe number of files used by the handler.Optional
append-modetag to use the handler in append mode (true|false).Optional
(default value is false)

Example:

@MonologHandler(name="clientHandler", output="System.err", pattern="%-5l [%h] <%t> %m%n")
public class Client implements Runnable { ... }
                

@Monolog

Details:

This field annotation provides a way to define a Monolog logger to log the execution of the Fractal component.

Parameters:

ParameterDescriptionContingency
namethe name of the Monolog logger.Mandatory
(default value is the Class name)
levelthe level of the Monolog logger.Optional
(default value is INFO)
additivitythe additivity tag for the Monolog logger (true|false).Optional
(default value is true)
clean-handlersthe clean-handlers tag for the Monolog logger (true|false).Optional
(default value is true)

Example:

public class Client implements Runnable {
  @Monolog (name="c", handler="clientHandler,fileHandler")
  private Logger log; 

  @Monolog
  private Logger log2;

  ...
}
                

Julia Configuration:

To enable the Monolog support in Julia, the julia.cfg configuration file should be modified to override the lifecycle-controller-impl definition as follows:

...                  
                  
# LifeCycleController implementation (for primitive or composite components)
(lifecycle-controller-impl
  ((org.objectweb.fractal.julia.asm.MixinClassGenerator
        LifeCycleControllerImpl
        org.objectweb.fractal.julia.BasicControllerMixin
        org.objectweb.fractal.julia.UseComponentMixin
        org.objectweb.fractal.julia.control.lifecycle.BasicLifeCycleCoordinatorMixin
        org.objectweb.fractal.julia.control.lifecycle.BasicLifeCycleControllerMixin
        # to check that mandatory client interfaces are bound in startFc:
        org.objectweb.fractal.julia.control.lifecycle.TypeLifeCycleMixin
        # to automatically assign the logger and logger factory:
        org.objectweb.fractal.julia.BasicInitializableMixin
        org.objectweb.fractal.julia.logger.LoggerLifeCycleMixin        
        # to notify the encapsulated component (if present) when its state changes:
        org.objectweb.fractal.julia.control.lifecycle.ContainerLifeCycleMixin
  ) 
        # optional initialization parameter (monolog configuration file name):
        (monolog-conf-file monolog.properties)
  )
)
 
...                                   
                  

AOKell Configuration:

To enable the Monolog support in AOKell, the build.properties configuration file should be modified to activate the feature.loggable.on property as follows:

...
                  
# The loggable feature determines whether primitive components are equipped
# with a monolog logger or not (which is the default case).
# Uncomment the following property for using primitive components equipped
# with a monolog logger.
feature.loggable.on 	true                  

...
                

1.2 Revisiting the HelloWorld Example

This section provides a quick overview of the benefits of Fraclet. It shows that using annotations, Fractal source code becomes more concise and easier to maintain. It shows that using Fractlet, more than 60% of the source code (Java, FractalADL, properties) can be saved.

HelloWorld Architecture Overview

The figure below introduces the well-known HelloWorld example implemented with Fractal components.

HelloWorld Implementation

The source code below represents the Java code and the Fraclet annotations written to implement the Client and the Server Fractal components:

@FractalComponent
@Provides(interfaces=@Interface(name="r",signature=java.lang.Runnable.class))
public class Client implements Runnable {

  @Requires(name = "s")
  private Service defaut;

  @Attribute(value="Hello !")
  protected String message;
            
  public void run() {
    this.defaut.print(this.message);
  }
}
                          

The Client class defines the Runnable interface as a Fractal interface using the @Provides annotation. The field default is annoted as a client interface (using @Requires), and will be named "s". The attribute message is annoted as a Fractal attribute (using @Attribute), which will be automatically initialized to "Hello !".

  
@Interface(name="s")
public interface Service {
  void print(String message);
}
                          

The Service interface is annoted with @fractalItf to define the name of the Fractal interface as s.

  
@FractalComponent							  
public class Server implements Service {
  @Attribute
  protected String header;
                
  @Attribute
  protected int counter;
                
  public void print(final String message) {
    for (int i = 0; i < this.counter; i++)
      System.out.println(this.header + message);
  }
}
                        

The Server class inherits automatically from the Service annotations. The header and the counter attributes are defined as Fractal attributes (using @FractalAC), whose initial value will be defined when the component will be created.

HelloWorld Architecture Definition

The assembly definition below represents the FractalADL assembly defined to describe the HelloWorld Fractal component:

<definition name="HelloWorld">
  <component name="client" definition="Client";
  <component name="server" definition="Server('>>',2)"/>
  <binding client="this.r" server="client.r"/>
  <binding client="client.s" server="server.s"/>	
</definition>
                

This definition creates a composite component HelloWorld. Then it specifies that the definition of the component named server is Server and that the values of the header and the counter attributes are '>>' and 2, respectively.

Directory Structure

The directory structure below presents the list of compiled, generated and written files when using Fraclet to implement the HelloWorld example:

%example%
  * build/
    - Client.class
    - Client.fractal
    - ClientAttributeController.class
    - Server.class
    - Server.fractal
    - ServerAttributeController.class
    - Service.class
    - Service.fractal
    - HelloWorld.fractal
  * generated/
    - Client.java
    - Client.fractal
    - ClientAttributeController.java
    - Server.java
    - Server.fractal
    - ServerAttributeController.java
    - Service.java
    - Service.fractal
  * src/
    - Client.java
    - HelloWorld.fractal
    - Server.java
    - Service.java
                

2. Fraclet Availability

Fraclet is freely available under an LGPL licence at the following URLs:

3. Dissemination

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