Consortium    Solutions    Middleware    Forge    MyObjectWeb 
ObjectWeb Consortium
Print

Advanced - Powered by Google






THINK

Project Links
· Home
· More about Think
· Related projects
· thinkClipse

Support
· Download Nuptse
· Documentations
· Labs : Think fundamentals

Developement
· ObjectWeb Forge Site
· Report a bug

Contact
· Think team

Think fundamentals in practice

Hands on Think through Fractal concepts.

Foreword

Eight component-based development incremental lessons :

  • Lab 1 : From C to Nuptse components
  • Lab 2 : Attributes and attribute controller
  • Lab 3 : Life cycle controller
  • Lab 4 : Binding controller
  • Lab 5 : Extension and abstact components
  • Lab 6 : Component identity
  • Lab 7 : Composite component
  • Lab 8 : Content controller

Classical errors

Configuration and download.

Before trying the following labs, make sure you've installed Nuptse properly. Then you need to define an environment variable named THINK_PATH pointing on the nuptse directory (for example: $ export THINK_PATH=path/to/nuptse). For those labs we provide some sources, download labs.zip and unzip its content in a directory (unzip labs.zip). You are ready to start.

What is in the zip...

Lab 1: From C to Nuptse components

back to the top

The aim of this first lab is to transform a C program into a Nuptse application. The following notions are introduced in this lab:

  • idl files (Interface Description Language);
  • adl files (Architecture Description Language) and the following adl keywords:
    • component
    • provides
    • requires
    • content
  • nuptC using the following annotations:
    • @@ ServerInterfacePrefix(...) @@
    • @@ ClientInterfacePrefix(...) @@
    • @@ PrivateData(...) @@
    • @@ PrivateMethod(...) @@

To start here, try to compile the program given in lab1/pureC/ by using the Makefile (given in lab1/), run it (./helloWorld), and have a quick look over the source. Basically, a main function is defined in wishToSayHello.c and this function makes several calls to functions defined in multiLanguageHello.h. Moreover, a tokens variable is defined in multiLanguageHello.c and initialized to 7, printing "Hello World" could then be considered as a limited resource. Running this program, you should get the following trace:

[nb of tokens left: 6] Hello World
[nb of tokens left: 5] Bonjour le Monde
[nb of tokens left: 4] Hallo Welt
[nb of tokens left: 3] Hello World
[nb of tokens left: 2] Bonjour le Monde
[nb of tokens left: 1] Hallo Welt
[nb of tokens left: 0] Hello World
				

Based on the sources of this program, we are going to build a Nuptse application. The sought architecture is given in figure 1. To get the equivalent Nuptse application you will have to:

  1. Give an interface description to enable two or more components to collaborate. This will be done in an idl file.
  2. Describe components properties and requirements at an architectural level. At this level, a component is described for example through provided/required interfaces. This will be done through several adl files.
  3. Enable the matching between architecture's description and functional code. To do this, specific annotations needs to be directly added in C files in commented sections.
  4. Finally, you will have to describe a top level component (ie "main" in figure 1) to instantiate and bind low level components (ie "boot", "wtsh" and "mlh").

Multi-language Hello World architecture image.

Figure 1

1. MultiLanguageHello interface description

To describe the MultiLanguageHello interface you need to copy the methods signature defined in lab1/pureC/multiLanguageHello.h into common/api/MultiLanguageHello.idl. This specifies that printEnglishHello(), printFrenchHello() and printGermanHello() methods can be accessed through an interface of type MultiLanguageHello.

2. whishToSayHello and multiLanguageHello component description

In lab1/src edit a new file named wishToSayHello.adl to describe the wishToSayHello component type. As it is shown in figure 1 ("wtsh" stands for wishToSayHello), this component has two interfaces: one provided and one required. This must be described in the adl giving the path to the type definitions of those interfaces. In the adl you must also indicate where to find the functional code, this is done with "content wishToSayHello" which means that the content is in a file named wishToSayHello.c. The wishToSayHello.adl should look like this:

component wishToSayHello {
	provides activity.api.Main as main
	requires api.MultiLanguageHello as hello
	content wishToSayHello
}
			

A similar description needs to be done for multiLanguageHello component type. This time the component only provides one interface named hello of type api.MultiLanguageHello and the content will be found in multiLanguageHello.c. Make this description in a new file lab1/src/multiLanguageHello.adl.

3. Functional code

First of all, copy all .c files from lab1/pureC/ into lab1/src/. To transform c code into nuptC code a few modification are needed mainly in section of comments.

  1. In multiLanguageHello.c you need to specify which method comes from wich interface. This can be done by adding the following comment at the beginning of the file:
    /*
     * @@ ServerInterfacePrefix(hello, HELLO_) @@
     */
    					
    You then need to modify the method names corresponding to this interface by prefixing them with HELLO_ (eg. HELLO_printEnglishHello(), HELLO_printFrenchHello() and HELLO_printGermanHello()). Using the annotation this way is a bit heavy in this case but it facilitates the general comprehension. The last modification to be done in this file is to add the following annotation telling that the tokens variable corresponds to a private data. Private data are attached to each instance of a component.
    /*
     * @@ PrivateData(tokens) @@
     */
    					
  2. In the file wishToSayHello.c, you must firstly add the following annotation:
    /*
     * @@ ClientInterfacePrefix(hello, HELLO_) @@
     * @@ ServerInterfacePrefix(main, MAIN_) @@
     */
    					
    You then need to add the HELLO_ prefix to each calls to a method of the client interface hello (eg. HELLO_printEnglishHello(), HELLO_printFrenchHello() and HELLO_printGermanHello()), the "#include <multiLanguageHello.h>" is not needed anymore and can be removed. You also need to add the MAIN_ prefix to the only method main() of the interface main. Finally, you must specify that the method allLanguagePrint() is a private method. To do this, add the following annotation before its declaration:
    /*
     * @@ PrivateMethod(allLanguagesPrint) @@
     */
    					

4. Application description

It is now needed to describe the general application architecture in a top level component (see the main component in figure 1). In lab1/src/ edit a new file named main.adl. In this file make the main component declaration component main {...}. This top level component contains instances of previously defined components types, that is to say wishToSayHello and multiLanguageHello. Those instances can be renamed as wtsh (Wish To Say Hello) and mlh (Multi Language Hello). Another component is also added mainly to tell where to start from. This component named boot is defined in common/kortex/src/generic/boot/lib/ and will just be reused. Declarations of contained components are done as follow giving a name to the component instance and the type of this component:

  contains boot = boot.lib.boot
  contains wtsh = wishToSayHello
  contains mlh = multiLanguageHello
				

Finally, the top level component must describe the bindings between contained components instances. For example, it describes the binding from the hello client interface of the component instance wtsh to the hello server interface of the mlh component instance. Bindings are always defined from client to server interfaces.

  binds boot.entry to wtsh.main
  binds wtsh.hello to mlh.hello
				

5. Building and running the example

It is now time to test your application. Firstly try to build the application with ant by running the default target (you might need to have a look in the build.properties file to tell which os you are using). Building the application will create a set of files in a folder named build, the generated executable can be found in lab1/build/obj_YOUROS/main. Run it, you should obtain the same trace as previously obtained with the pureC example.

In this first lab, we have introduced interface description in idl files, as you can notice these files are very close to .h files. We have also introduced how to attach functional code to a component. This is done in .c files where annotations are added. To help understanding the correspondence between interface description and methods defined in the nuptC code, we used annotations to rename those methods. Nevertheless other annotations which enable not to rename interface methods are available and will be introduce later.

Looking closely the functional code of the wishToSayHello code, you will notice that calls to methods from the multiLanguageHello component (eg. printEnglishHello()) are made even if there aren't any tokens left. In this case, nothing happens but invocations are still made. To avoid this, we would like to be able to test if there are some tokens left before the method invocation. In the next lab, we introduce a way to deal with this by defining tokens as an component attribute and using an attribute controller defined in the Fractal model.

Lab 2: Attributes and attribute controller

back to the top

The aim of this lab is to use an attribute controller to get and set component's attributes. The following notions are introduce in this lab:

  • Fractal attribute controller
  • adl files (Architecture Description Language) and the following adl notions:
    • attribute
    • Multi content
  • nuptC using the following annotations:
    • @@ DefaultAttributes @@

The Fractal model, and thus Nuptse offers the possibility to define and initialize attributes at an architecture level, that is to say in adl files. As you will probably notice, all what can be done with architectural attributes, can be done with private data as it has previously been done. However, the roles of attributes and private data are quite differents. Actually, private data are to be used locally and can be seen as architecturally independent, whereas attributes are involved in the architecture definition. Those attributes can become very useful to define generic components that can be tuned at an architectural level for some specific purposes. Moreover, the Fractal model specifies a set of controllers. The first one we introduce here is the attribute controller, which enables to access to architectural attributes.

Subfolder lab2/ corresponds to what you should obtained if you have followed the lab1 (classical errors). From this situation, we are firstly going to redefine the tokens private data from lab2/src/multiLanguageHello.c as an architectural attribute. We are then going to enable the access to this attribute from another component, that is to say wishToSayHello, using an attribute controller.

Step 1: defining the tokens attribute.

Architectural attributes are defined in adl files. So you need to add "attribute int tokens = 7" in lab2/src/multiLanguageHello.adl to define and initialize this attribute. Then remove both the tokens declaration and the PrivateData annotation from file lab2/src/multiLanguageHello.c. Finally, add @@ DefaultAttributes @@ annotation in a commented region of this same file.

Build this example using the ant default target, and run it. Once again, this shouldn't change anything at runtime, the trace you should obtain is the same as in lab1.

[nb of tokens left: 6] Hello World
[nb of tokens left: 5] Bonjour le Monde
[nb of tokens left: 4] Hallo Welt
[nb of tokens left: 3] Hello World
[nb of tokens left: 2] Bonjour le Monde
[nb of tokens left: 1] Hallo Welt
[nb of tokens left: 0] Hello World
				

Step 2: adding the attribute controller.

As shown in figure 2, we are going to slightly change the architecture by adding an interface, named mlhAc and ac, of type fractal.api.AttributeController to wtsh and mlh. The AttributeController.idl defines two methods: get and set. Binding those interfaces together enables wtsh to get and set all attributes associated to mlh.

Multi-language Hello World architecture with attribute controller image.

Figure 2

  1. Nuptse provides a set of controllers that can directly be used, nevertheless you can still implement your own controller if you wish for some particular purpose. To enable multiLanguageHello to provide an AttributeController interface and to use the implementation provided by Nuptse, you must add the following in lab2/src/multiLanguageHello.adl. The multiLanguageHello component will have two "content", ie two .c files will be associated to multiLanguageHello, one for the MultiLanguageHello interface and another one for the functional code of the AttributeController interface. You will also need to add a "requires fractal.api.stdc.String as std-string" in the lab2/src/multiLanguageHello.adl. This is due to the fractal.lib.acstring implementation of the attribute controller which has dependencies to fractal.api.stdc.String methods. This particular interface will be automatically resolved by the compiler, ie the component and the binding needed by this interface are not necessarily described explicitly.
    Note: the "provides ...", "content ...", or other adl concept orders doesn't matter in adl file, so a relevant way to organize adl is to gather "provides ..." together, all "content ..." together, and so on...
      provides fractal.api.AttributeController as ac
      requires fractal.api.stdc.String as std-string
      content fractal.lib.acstring
    			
  2. Then, in lab2/src/wishToSayHello.adl you must add a required interface of type fractal.api.AttributeController:
      requires fractal.api.AttributeController as mlhAc
    			
  3. In the top level component (ie. main.adl) you must define the binding between the AttributeController interface of wtsh and mlh (see figure 2). As we said previously the std-string client interface of component multiLanguageHello do not need to be bound (cf point 1).
  4. Finally, in file lab2/src/wishToSayHello.c, add the annotation @@ ClientInterfacePrefix(mlhAc, MLHAC_) @@ and modify the "MAIN_main(...) function to use the attribute controller methods to get and set the mlh tokens attribute. Note that as we don't know the type of the element at the adress returned by the get method this value need to be casted and dereferenced. For example:
      MLHAC_set("tokens", * ((int *)MLHAC_get("tokens"))-2);
      while(* ((int *) MLHAC_get("tokens"))>0){
      	allLanguagesPrint();
      }
    			

Build the application using the default ant target and run it. You should obtain something like:

[nb of tokens left: 4] Hello World
[nb of tokens left: 3] Bonjour le Monde
[nb of tokens left: 2] Hallo Welt
[nb of tokens left: 1] Hello World
[nb of tokens left: 0] Bonjour le Monde
			

Here the attribute controller enables to manipulate from a component an attribute defined in another component. In this example inefficient call to methods from the MultiLanguageHello interface might still be made, but this is because calls to these methods are made three by three.

Lab 3: Life cycle controller

back to the top

The aim of this lab is to use an life cycle controller to define components behavior when starting and stopping. The following notions are introduced in this lab:

  • Fractal life cycle controller, implicit and explicit binding
  • @@ DefaultServerMethods(...) @@

Now, we would like to define and update a flag (namely started) describing the current state of component multiLanguageHello. If this flag equals 1, the component is able to say "Hello World", otherwise the component don't do anything. To manage this flag we will use another Fractal controller, the Life Cycle Controller (lcc). The interface of this controller offers two methods ie start() and stop(). When invocation to method start() (respectively stop()) is made then the started flag will be set to 1 (respectively to 0). Unlike other controllers, lcc bindings do not necessarily need to be explicitly defined. These bindings are managed at compilation time, and the start() and stop() methods are called once, respectively when the application starts and ends. Nevertheless, you can also explicitly bind and use this interface whenever it's needed.

Step 1: implicit call to the life cycle controller interface.

We are going to build the architecture shown in figure 3. The multiLanguageHello component will have to provide an interface of type fractal.api.LifeCycleController. At compilation time an lcc-init component is automatically created and bound to the boot component one side and to every component providing an fractal.api.LifeCycleController interface on the other side. This is represented in green in the figure 3. In this lab, the given sources corresponds to what you should have ended with if you have followed Lab2. For this first step, you will only have to modify the multiLanguageHello adl and .c, as lcc-init creation and bindings are managed automatically.

Multi-language Hello World architecture with lifecycle controller image.

Figure 3

  1. You firstly need to add a provided LifeCycleController interface in the multiLanguageHello definition (ie lab3/src/multiLanguageHello.adl) as described here:
      provides fractal.api.LifeCycleController as lcc
    				
  2. Then edit the lab3/src/multiLanguageHello.c file corresponding to the functional code associate to the multiLanguageHello component:
    • Add the private data started initialized to 0:
        /*
         * @@ PrivateData(started) @@
         */
        char started = 0;
      						
    • Another annotation need to be added to establish the correspondence between the LifeCycleController interface and the start() and stop() methods you will have to implement. To do this, we introduce a new annotation "@@ DefaultServerMethods(...) @@". This annotation enables to use the default methods names defined in the idl. We could have used the previously introduced "@@ ServerInterfacePrefix(...) @@" which enable for example to rename methods considering their related interface. The use of one annotation or another depends on your preferences, but also if there are ambiguous situation such as a component providing two interfaces both having a method sharing the same name, in this case they will need to be renamed.
        /*
         * @@ DefaultServerMethods(lcc) @@
         */
      						
    • Now, you need to define the implementation corresponding to the start() and stop() methods of the LifeCycleController interface:
        void start (){
          printf("Starting the multiLanguageHello component...\n");
          started = 1;
        }
        
        void stop (){
          printf("Stopping the multiLanguageHello component...\n");
          started = 0;
        }
      						
    • Finally, use the started flag in the "if" condition of each "printLANGUAGEHello()" methods:
        if (started && tokens > 0){...}
      						

You can now build and run the application. You should obtain something like:

Starting the multiLanguageHello component...
[nb of tokens left: 4] Hello World
[nb of tokens left: 3] Bonjour le Monde
[nb of tokens left: 2] Hallo Welt
[nb of tokens left: 1] Hello World
[nb of tokens left: 0] Bonjour le Monde
Stopping the multiLanguageHello component...
			

Step 2: explicit call to the life cycle controller interface.

In the previous step, the started is not really used, as the component is stopped only when the application ends. We would like here to start and stop the component several times to see what happens. To do this we are going to invoke the multiLanguageHello LifeCycleController interface method from the wishToSayHello component. As shown in figure 4, the wishToSayHello component need to be bound to the multiLanguageHello component through the LifeCycleController.

Multi-language Hello World architecture with lifecycle controller explicite call image.

Figure 4

  1. Firstly, add a fractal.api.LifeCycleController interface to the wishToSayHello component (in the wishToSayHello.adl file):
      requires fractal.api.LifeCycleController as mlhLcc
    					
  2. Then, use the LifeCycleController methods (start() and stop()) in the wishToSayHello functional code (wishToSayHello.c) by:
    • adding the annotation @@ ClientInterfacePrefix(mlhLcc, MLHLCC_) @@
    • making several calls to MLHLCC_start() and MLHLCC_stop(), for example stop the multiLanguageHello component before the HELLO_printFrenchHello() and re-start it just after. The result should be that the HELLO_printFrenchHello() call will never be effective.
        MLHLCC_stop();                                                                                     
        HELLO_printFrenchHello();                                                                          
        MLHLCC_start();
      							
  3. Finally, you need to explicitly bind the multiLanguageHello lcc interface. This is done from the main component. Edit main.adl and bind the mlhLcc interface of component wtsh to lcc interface of component mlh as shown in figure 4.

You can now build and run the application. You should obtain the following trace without any "Hello World" in French:

Starting the multiLanguageHello component...
[nb of tokens left: 4] Hello World
Stopping the multiLanguageHello component...
Starting the multiLanguageHello component...
[nb of tokens left: 3] Hallo Welt
[nb of tokens left: 2] Hello World
Stopping the multiLanguageHello component...
Starting the multiLanguageHello component...
[nb of tokens left: 1] Hallo Welt
[nb of tokens left: 0] Hello World
Stopping the multiLanguageHello component...
Starting the multiLanguageHello component...
Stopping the multiLanguageHello component...
			

Lab 4: Binding controller

back to the top

The aim of this lab is to use a binding controller to dynamically bind component at runtime. The following notions are introduced in this lab:

  • Fractal binding controller
  • adl files (Architecture Description Language) and the following adl notions:
    • selfbinds
    • assigns
  • nuptC using the following annotations:
    • from given c files????

In this lab we are going to intanciate two multiLanguageHello components, each of them bound to the wtsh component through distinct interfaces. Bindings are then dynamically swapped several times at run time. The general architecture of this exercise is shown in figure 5.

Multi-language Hello World architecture with binding controller image.

Figure 5

Given sources corresponds to the sources obtained at the end of lab 1 with small changes in functional codes. In multiLanguageHello, we aren't using anymore tokens, the multiLanguegaHello component is able to say "Hello World" each time he is asked to. Moreover, a parameter is given to each printLANGUAGEHello(char* moreInfo), for example to tell through which interface the method is invoked. Having a look to the given wishToSayHello.c, you will see that they aren't any more private method, all call to printLANGUAGEHello(...) are made directly in the main(...) function. In this main(...) method, we firstly print "Hello World" through interface hello1 followed by hello2. Then we swap the bindings and print "Bonjour le Monde" through interface hello1 followed by hello2. Finally, this operation is repeated once again printing "Hallo Welt" through interface hello1 followed by hello2. In this exercise, only adl files will have to be modified.

1. First of all, in multiLanguageHello.adl:

  • use the api.MultiLanguageHelloParam interface which defines printLANGUAGEHello methods with a parameter, instead of api.MultiLanguageHello;
  • add a "name" attribute of type jstring (corresponding to a char*) to multiLanguageHello. This attribute will enable to differentiate the two multiLanguageHello component instances.
      attribute jstring name
    					

2. Then, in wishToSayHello.adl:

  • replace the single api.MultiLanguageHello interface with two interfaces hello1 et hello2 of api.MultiLanguageHelloParam type:
      requires api.MultiLanguageHelloParam as hello1
      requires api.MultiLanguageHelloParam as hello2
    					
  • add a provided and a required interface of type fractal.api.BindingController (for the self binding), and also the associated implementation definition. As for the attribute controller in lab 2, the implementation of the binding controller requires fractal.api.stdc.String methods. The wishToSayHello component will then be able to control its own bindings:
      provides fractal.api.BindingController as bc in bindingController
      requires fractal.api.BindingController as selfBc
      requires fractal.api.stdc.String as std-string in bindingController
      content fractal.lib.bcstring for bindingController
    					
  • Nuptse propose a facility when self-bindings are needed by enabling to bind two interfaces provided and required by the same component directly in the component adl. The binding will not have to be defined in the super-component. To use this facility here, we introduce a new adl keyword selfbinds:
      selfbinds selfBc to bc
    					

3. Finally, in main.adl:

  • to add two instances of the multiLanguageHello component, replace the the single "contains mlh = multiLanguageHello" with:
      contains mlh1 = multiLanguageHello
      contains mlh2 = multiLanguageHello
    					
  • initialize each "name" attributes of multiLanguageHello instances with different names:
      assigns mlh1.name = "mlh 1"
      assigns mlh2.name = "mlh 2"
    					
  • replace the "binds wtsh.hello to mlh.hello" to bind MultiLanguageHello interfaces of wtsh to those of mlh 1 and 2:
      binds wtsh.hello1 to mlh1.hello
      binds wtsh.hello2 to mlh2.hello
    					

It is now time for you to build and run the application. You should obtain the following trace. Looking closer to this trace you will notice that the bindings are effectively swapped twice.

[Through interface hello1] Component mlh 1 says:  Hello World
[Through interface hello2] Component mlh 2 says:  Hello World
[Through interface hello1] Component mlh 2 says:  Bonjour le Monde
[Through interface hello2] Component mlh 1 says:  Bonjour le Monde
[Through interface hello1] Component mlh 1 says:  Hallo Welt
[Through interface hello2] Component mlh 2 says:  Hallo Welt
			

Lab 5: Extension and abstract components

back to the top

The aim of this lab is to introduce the notion of abstract component and how to extends components. The following notions are introduce in this lab:

  • Abstract component, and extensions;
  • adl files (Architecture Description Language) and the following adl notions:
    • abstract
    • extends
  • nuptC using the following annotations:

Now, we make the architectural decision to define one component for each language to use. This could be motivated by the fact that in one use case an application only wish to say hello in a specific language, and in another use case the same application only wish to say "hello" in another language. The wishToSayHello component could then be the same in both use case, and the only difference could come from the instantiation of one or another "language hello" component (cf figure 6). New components to say "hello" in even more languages could then easily be defined and used without having to re-implement the entire application. In such architecture, each component saying "hello world" are mostly identical. They provides the same interface however only the implementation differs. We can consider this property defining an abstract component that will be extended, by each component saying "hello world" in a particular language, to associate a specific content (ie. associating a specific functional code).

Spliting the multiLanguageHello component in specific language for 1rst use case. Spliting the multiLanguageHello component in specific language for 1rst use case.

Figure 6

The targeted architecture we want to build here is shown in figure 7. Here, even if the multiLanguageHello component is split in three language specific components, the wishToSayHello component will still wish to say "hello" in several languages. Thus wishToSayHello requires one interface of the same type for each language to use.

Multi Hello World component architecture image.

Figure 7

In lab5 provided sources mostly corresponds to what you should have ended with at the end of lab 1 except that multiLanguageHello.adl has been removed, and multiLanguageHello.c has been replace by three LANGUAGEHello.c files (englishHello.c, frenchHello.c and germanHello.c). So we are firstly going to split the multiLanguageHello adl, defining one abstract component that shall be extended by each component able to say Hello World (note that a component can either extend an abstract component or a classic one). Then we are going to adapt the wishToSayHello component so it provides one interface for each LANGUAGEHello component. The implementation of this component will also need to be adapted. Finally, we are going to redefined the top level component to match with this new architecture.

1. Spliting the multiLanguageHello component.

We are firstly going to define an abstract component providing one interface to say "hello". Then LANGUAGEHello components will be defined extending the previously defined abstract component to add a content to say "hello" in a particular language.

  1. Edit a new file hello.adl to define the abstract component. Abstract component are defined adding abstract keyword before the component definition (abstract component hello {...}). This component only provides one interface of type api.SayHello. Having a look to common/api/SayHello.idl you will find out that this interface provides only one method: void printHello(). Note that no content are defined in hello.adl which should look like this:
      abstract component hello {
        provides api.SayHello as hello
      }
    				
  2. We are now going to build the three LANGUAGEHello components. As LANGUAGEHello functional codes are provided in this exercise, only adl description need to be defined. This is done extending the hello abstract component to add a specific content. To extend a component we introduce a new keyword extends. As an example, a definition of englishHello component is given below, so now you need to edit englishHello.adl, frenchHello.adl and germanHello.adl.
      component englishHello extends hello {
        content englishHello
      }
    				

2. Building a new wishToSayHello component.

  1. We start here with the wishToSayHello adl. As shown in figure 7, this component now requires three interfaces to say "hello" in different languages. Those interfaces are all of type api.SayHello. So you need to remove the "requires api.MultiLanguageHello as hello", replacing it with three required interfaces:
      requires api.SayHello as englishHello
      requires api.SayHello as frenchHello
      requires api.SayHello as germanHello
    				
  2. We now need to update the wishToSayHello functional code to use the methods provided by the three LANGUAGEHello interfaces. Firstly, you need add some annotations to rename the method provided by the LANGUAGEHello interfaces to define which methods come from which interface. To do this you can use the ClientInterfacePrefix annotation:
      @@ ClientInterfacePrefix(englishHello, ENG_) @@
      @@ ClientInterfacePrefix(frenchHello, FR_) @@
      @@ ClientInterfacePrefix(germanHello, GER_) @@
    				
    You can then remove the "@@ ClientInterfacePrefix(hello, HELLO_) @@", and rename the "HELLO_printLANGUAGEHello()" methods properly as "PREFIXprintHello()".

3. Top level component modification

Finally, you must describe this new architecture in the top level component (main.adl). Modify the existing main.adl to correspond to the architecture described in figure 7.

Build using ant and run the example, I hope you get something like:

Hello World
Bonjour le Monde
Hallo Welt
Hello World
Bonjour le Monde
Hallo Welt
Hello World
Bonjour le Monde
Hallo Welt

What about specialization... (cf Olivier's slides)

Lab 6: Component identity

back to the top

The aim of this lab is to introduce the component identity controller. The following notions are introduce in this lab:

  • Component identity controller;
  • adl files (Architecture Description Language) and the following adl notions:
    • optional
  • nuptC using the following annotations:

In the previous lab (lab5) the wishToSayHello component had one api.SayHello interface for each LANGUAGEHello components. The objective is now to enable the wishToSayHello component to alternatively share one client interface with several components. The idea is that the wishToSayHello component is only bound to the component corresponding to the language it wishes to use. The architecture we are aiming to should increase flexibility and facilitate future evolutions such as adding new languages to say hello. This architecture is shown in figure 8.

The wishToSayHello component can indifferently be bound to one or another LANGUAGEHello component. The wishToSayHello component doesn't have to worry about the language used, it just has to ask to say "Hello". Actually, here the wishToSayHello asks to say hello but also to change the current language. However the language change could be manage by other components, the use of one or another language would then become totally transparent to the wishToSayHello component. Moreover, adding a new language would not change anything to the wishToSayHello component.

Here, the wishToSayHello component needs to retrieve api.SayHello server interfaces provided by a LANGUAGEHello component so it could bind to them. This can be done using the component identity interface specified in the Fractal model. For this new architecture we introduce a new component: languageChoice. This component is in charge to manage the current language choice (cf figure 8).

Multi Hello World component architecture image.

Figure 8

In this exercise we provide the following:

  • the LANGUAGEHello.c and .adl, the hello.adl and the top level main.adl comes from the previous exercise;
  • the wishToSayHello.adl and .c, the "optional" property can be notice in the adl;
  • the languageChoice.adl and an languageChoice.c skeletton to be filled.

It then remains to update and fill the hello.adl and languageChoice.c files and to adapt the top level component.

1. Adding a component identity interface to LanguageHello components

In hello.adl add a provided interface of type ComponentIdentity and define its implementation (you also need to a String interface required by the ci implementation):

  provides fractal.api.ComponentIdentity as ci
  requires fractal.api.stdc.String as std-string
  content fractal.lib.ci

2. languageChoice functional code

The languageChoice component is central to this architecture. Its role is to manage the bindings between the wtsh and LANGUAGEHello components (BindingController interfaces). To do this, languageChoice needs to be aware of the hello interfaces provided by LANGUAGEHello components (using the ComponentIdentity). The languageChoice component also has to know when wtsh requires to change the current language (api.LanguageChoice interface), and finally it needs to invoke the binding methods of the wtsh component to dynamically swap from one LANGUAGEHello to another (using the fractal.api.BindingController).

Here a life cycle controller (lcc) server interface is added. The start method is used to get the hello interfaces' ids when the application starts. In the start method, invocation to the getInterface methods provided by the comp_identity interfaces are made. As nothing has to be done while the application is stopping, the stop method is an empty method.

Then, the changeLanguageTo implementation is quite simple. Considering the wtsh language choice, the languageChoice component binds the wtsh hello interface to the corresponding component.

  • in the start(){} method add:
    				
      english_helloItfId = ENG_getInterface("hello");
      french_helloItfId = FR_getInterface("hello");
      german_helloItfId = GER_getInterface("hello");
    
  • finally, in method changeLanguageTo(...) add the bind("hello", LANGUAGE_helloItfId);

3. Top level component modification

Adapt the main component to match with the architecture shown in figure 8. Note that bindings represented with doted lines should not be explicitly bound here as they will be bound to one or another hello interface provides by LANGUAGEHello components at runtime.

Even if this approach seems to be more flexible, the languageChoice component still needs to have one comp_identity interface for each LANGUAGEHello component. The problem has only been shifted from the wtsh to the languageChoice component. In lab 8 we will see how to avoid this in order to become even more flexible.

Lab 7: Composite component

back to the top

We introduce here a fundamental Fractal concept which is composite component. The Fractal model specifies two types of components, namely primitive and composite components. So far, we have mainly used primitive components, nevertheless the top level component can be see as a specific composite. The possibility of using composite components drastically increases the structuring capabilities of the Fractal model, and thus the Think framework.

Components are formed out of two parts: a membrane and a content. The content can either corresponds to the servers interfaces implementation or to a set of other components called sub-components. Sub-components are under control of the enclosing component. Whereas the membrane is in charge of controllers implementation.

In the Nuptse version of Think, there aren't any distinction between functional and control interfaces. Thus, composite can simultaneously be composed of sub-components and implementations. Here we are firstly going to build a application where a composite component contains one primitive component. In this example we will see how interface methods invocation goes through the composite membrane. Then we are going to build a second application which is mostly based on the same architecture, but showing how functional code can be directly attached to a composite.

Step 1. Composite component

The targeted architecture is shown in figure 9. Provided sources corresponds to the result of lab 2, with a hello.adl and a englishHello.c which will be used in step 2.

Multi Hello World component architecture image.

Figure 9

  1. Create a new component helloComp which contains the multiLanguageHello component and provides the same interfaces as multiLanguageHello. As helloComp and multilanguageHello provides the same interfaces the only differences are the attributes and the implementation. So the simplest way to build the helloComp component is to make a copy of multilanguageHello.adl renaming it as helloComp.adl. Then you need to:
    • remove the attribute;
    • remove every "content ... ";
    • remove the "requires fractal.api.stdc.String as std-string " as it is not needed anymore;
    • add a "contains mlh = multiLanguageHello" to define that helloComp is made of one multilanguageHello component;
    • and finally, add the following bindings, the "this.itfName" is used to define the internal view of an interface itfName provided or required by the composite component.
        binds this.hello to mlh.hello
        binds this.ac to mlh.ac
      						
  2. In main.adl make the following changes:
    • the main component doesn't need to know anything about the helloComp composite content's. Change the "contains mlh = multiLanguageHello" in "contains helloComp = helloComp";
    • update the bindings so that wtsh is bound to the helloComp component and not anymore to the hello component.

Now you can build and run the example, you should obtain the following trace:

  [nb of tokens left: 4] Hello World
  [nb of tokens left: 3] Bonjour le Monde
  [nb of tokens left: 2] Hallo Welt
  [nb of tokens left: 1] Hello World
  [nb of tokens left: 0] Bonjour le Monde
			

Step 2. Adding functional code attached to composite

To attach some code directly to a composite, the only thing to do is to add some content using the content keyword in the composite's adl. Here we are going to add a provided interface of type api.SayHello to the composite (cf figure 10) and directly give it implementation.

Multi Hello World component architecture image.

Figure 10

  1. In helloComp.adl, add a provided interface "provides api.SayHello as eng_hello" and the corresponding implementation "content englishHello".
  2. In wishToSayHello.adl, add a required interface "requires api.SayHello as eng_hello".
  3. In wishToSayHello.c add a "printHello();" somewhere (don't forget to add the annotation corresponding to the defined interface).
  4. Finally in main.adl add the following binding:
      binds wtsh.eng_hello to helloComp.eng_hello
    					

You can now build an run the example, you should obtain the same trace as before with a "Hello World" printed somewhere as show below:

  [nb of tokens left: 4] Hello World
  [nb of tokens left: 3] Bonjour le Monde
  [nb of tokens left: 2] Hallo Welt
  [nb of tokens left: 1] Hello World
  [nb of tokens left: 0] Bonjour le Monde
  Hello World
			

Lab 8: Content controller

back to the top

In the previous lab we've presented the composite component concept, we introduce here the content controller which makes it possible to manage sub-components.

We are now focusing on the component content introspection problem. In lab 6, while aiming to increase flexibility we had only shifted the problem from functional interfaces to component identity interfaces. Using the content controller will enable to dynamically retreive contained sub-components. This approach has several advantages primarily based on the flexibility it provides. Actually the flexibility comes from the fact that in this architecture very little prior knowledge is needed on the number and type of sub-components. This results in the possibility of easily add and use more sub-components. Going further, we could think of adding more sub-components at runtime.

The targeted architecture is shown in figure 11. This architecture has several similitudes with the architecture of lab 6. Components wtsh, languageChoice and LANGUAGEHello can be retrieved here, but this time they are all defined as sub-components of a composite component called helloWorld. In this application, the languageChoice component will have to retrieve the right LANGUAGEHello component using the content controller. Then languageChoice will have to bind and use de Component Identity interface (ci) of the chosen LANGUAGEHello. The Component Identity enable to retreive the hello interface, thus languageChoice will become able to manage the binding between wtsh and LANGUAGEHello.

The major part of the job is located in the languageChoice component. Instead of having one fractal.api.ComponentIdentity interface for each LANGUAGEHello component, languageChoice has only one of this type of interface. Moreover, an interface of fractal.api.ContentController has been added for the introspection.

Multi Hello World component architecture image.

Figure 11

In this lab, provided sources mainly correspond to what you should have obtained at the end of lab 6. However, new versions of languageChoice.adl and languageChoice.c are given.

  1. helloWorld composite. The simplest way to build the helloWorld composite is to make a copy of the main.adl, renaming it as helloWorld.adl. Modify helloWorld.adl to match with the architecture description presented in figure 11.
    • remove the boot component;
    • add the provided and required interfaces:
        provides activity.api.Main as main
        provides fractal.api.ContentController as content_controller in ContentController
        requires fractal.api.ContentController as selfCc
      				
    • add the content for the Content Controller interface and the associated StdC required interface:
        requires fractal.api.StdC as stdc in ContentController
        content fractal.lib.cc for ContentController
      				
    • check the bindings so they corresponds to the bindings defined in figure 11. Note that you can either use the selfbinds keyword directly in helloWorld.adl (resp. languageChoice.adl) to define the selfCc bindings (resp. selfBc), up to you. Just make sure that the binding is not defined twice.
  2. hello abstract component. In hello.adl specify the implementation to use:
      provides fractal.api.ComponentIdentity as ci in componentIdentity
      requires fractal.api.stdc.String as std-string in componentIdentity
      content fractal.lib.ci for componentIdentity
    		
  3. Top level. Update main.adl to match with the architecture of figure 11. The top level only contains a boot and a helloWorld component, remove all the others. Finally update the bindings considering the selfbindings definition.

Classical errors

back to the top

Lab 1

  • The lab 1 doesn't build.
    Make sure that you have follow all the description. Have a look to the IDL file (labs/common/api/MultiLanguageHello.idl).

Lab 2

  • Lab 2 doesn't build. I'm getting the following error: "Cannot access private data 'tokens' here".
    Make sure the labs/common/api/MultiLanguageHello.idl has been correctly completed. This should have been done during lab 1.

Copyright © 1999-2005, ObjectWeb Consortium | contact | webmaster | Last modified at 2011-06-24 04:44 AM