• Aucun résultat trouvé

6.2 Modèle de composants natif

6.2.2 Présentation de LLCM j

Une instance de composant primitif est caractérisée par trois aspects principaux :

1. son type qui défini son comportement et ses interactions possible ;

2. ses éventuels paramètres génériques et leurs valeur ;

3. les interactions auxquelles l’instance participe.

Ces trois aspects correspondent à trois étapes du cycle de vie d’un composant : sa version

sur l’étagère, sa configuration au sein d’un assemblage, son instanciation. Dans la mesure

où LLCMj est un modèle statique, il ne faut rajouter qu’une dernière étape pour constituer

le cycle de vie complet des composants LLCMj : l’exécution.

En ce qui concerne le type, l’approche la plus simple consiste à s’appuyer sur les types

de Java, c’est à dire les classes. Un composant LLCMj est donc constitué d’un objet Java

marqué par l’annotation@LlcmjComponent pour le distinguer des objets qui ne constituent

pas un composant.

Paramètres génériques Les paramètres génériques sont caractérisés par le fait qu’il s’agit

de valeurs qui peuvent être utilisées pour configurer les composants qui sont connues au

moment de son instanciation. Il faut distinguer trois catégories de paramètres génériques :

ceux qui représentent un type de HLCM qui ne correspond pas à un type du modèle primitif,

ceux qui représentent un type du modèle primitif et ceux qui représentent une valeur de

donnée.

Dans la première catégorie, il s’agit de paramétrer la classe qui représente le composant

par un type spécifique à HLCM (composant, connecteur ou type de port) qui ne correspond à

rien dans le modèle primitif. Dans la mesure où ces types et leurs instances ne peuvent pas

6.2. Modèle de composants natif 83

être manipulés au niveau du modèle primitif, les utiliser comme paramètres d’un composant

primitif n’a pas de signification et ils ne sont donc pas supportés par LLCMj.

Dans la seconde catégorie, il s’agit de paramétrer la classe qui représente le composant

par un type de donnée du modèle primitif, c’est à dire une classe Java. Deux approches

peuvent être adoptées pour utiliser une classe pour en paramétrer une autre en Java

sui-vant l’utilisation qui en est faite. Une classe peut être spécifiée soit en s’appuyant sur les

paramètres génériques de Java soit à l’aide du mécanisme d’introspection en passant un

objet de la classeClass<C>.

La première approche qui s’appuie sur les paramètres génériques de Java permet une

prise en compte à la compilation. Il est par exemple possible d’utiliser un tel paramètre pour

spécifier le type d’instances internes à la mise en œuvre. Il faut cependant noter que comme

cela a déjà été étudié dans le chapitre 4, l’approche par effacement de type (type erasure)

ne permet pas d’utiliser le paramètre à l’exécution.

La seconde approche qui consiste à utiliser le mécanisme d’introspection de Java possède

les caractéristiques inverses. Dans la mesure où il s’agit d’un objet, sa valeur n’est pas

connue et ne peut donc pas être prise en compte lors de la compilation. Lors de l’exécution,

un tel objet peut toutefois être utilisé pour vérifier qu’un objet est bien du type spécifier ou

pour en créer de nouvelles instances.

Ces deux approches sont donc complémentaires et suivant les cas, il conviendra

d’adop-ter l’une, l’autre ou la combinaison des deux. Pour suppord’adop-ter la première approche, chaque

paramètre générique de la classe Java qui constitue le composant LLCMj constitue aussi

un paramètre générique du composant. Pour la seconde approche, il faut noter que le

pa-ramètre est alors un objet, c’est à dire une valeur de donnée et on utilisera alors la même

approche que pour cette dernière catégorie de paramètres.

Finalement, la dernière catégorie de paramètres génériques est constituée de ceux qui

représentent une valeur de donnée. Dans la mesure où leur valeur est connue lors de la

construction de l’instance, ils se prêtent bien à un passage sous la forme de paramètres du

constructeur. C’est cette approche qui est adoptée et chaque paramètre du constructeur de

la classe Java qui constitue le composant LLCMj constitue aussi un paramètre générique

du composant.

Interactions Quelles que soient les interactions, elle se traduisent au niveau des

compo-sants primitifs en Java par des échanges de référence sur des objets entre les compocompo-sants.

Les ports de LLCMj sont donc constitués par des accesseurs à des références de la classe

qui met en œuvre le composant. Pour les distinguer des méthodes qui ne constituent pas

un port, ces accesseurs sont marqués par des annotations.

Deux types de ports sont au moins nécessaires, un premier pour obtenir une référence

sur une valeur interne au composant et un second pour spécifier la valeur que doit prendre

un élément interne. Ces deux types de ports correspondent en LLCMj à une méthode

pu-blique exposée par la classe qui met en œuvre le composant. Pour le premier cas, cette

méthode n’accepte pas de paramètre et sa valeur de retour correspond à la référence

expo-sée par le composant. Une telle méthode doit toujours renvoyer une référence sur le même

objet et être marquée par l’annotation @LlcmjProvide. Pour le second cas, la méthode ne

revoie aucune valeur mais accepte un unique paramètre qui corresponde à la référence

im-portée par le composant. Une telle méthode est assurée de n’être appelée qu’une fois et doit

être marquée par l’annotation@LlcmjUse.

Ce mécanisme de bas niveau permet de supporter une interaction par appel de méthodes

entre composants. Le composant qui souhaite faire appel à des méthodes importe une

ré-férence typée par l’interface objet qui spécifie les méthodes devant être mises en œuvre. Le

composant qui met en œuvre ces méthodes expose une référence sur l’objet Java qui les

met effectivement en œuvre ; il s’agit souvent du même objet que celui qui met en œuvre

le composant et qui expose les ports comme c’est le cas pour l’exemple présenté sur la

figure 6.5

Une seconde utilisation de ce mécanisme consiste à supporter des attributs de

84 Chapitre 6. HLCMi : une plate-forme de mise en œuvre de HLCM

@LlcmjComponent public class WorldGreeter implements Greeter

{

@LlcmjProvide public Greeter greetWorld() {

return this;

}

@Override public void greet() {

System.out.println("Hello, world!");

}

}

Figure 6.5 – Exemple de composant LLCMj WorldGreeter qui expose un port greetWorld

typé par l’interface objetGreeterqui comporte une unique méthode :greet.

ration. Pour cela, le composant spécifie dans sont interface qu’il importe une référence sur

un objet typée par un type concret (comme par exempleStringouInteger) plutôt qu’une

in-terface. La valeur peut alors être fournie par un autre composant ou bien plus probablement

lors de la phase d’assemblage pour configurer le composant.

Ces mécanismes permettent de mettre deux composants en relation par des appels de

méthodes et de configurer les composants avec des valeurs spécifiées dans l’assemblage ou

bien exportées par d’autres composants. Rien n’empêche la valeur exportée par un

compo-sant d’être utilisée par plusieurs autres compocompo-sants. À l’inverse, la question de l’approche

à adopter pour permettre à un composant d’importer un ensemble de valeurs à la manière

duusesmultiple dans CCM se pose.

De multiples approches peuvent être envisagées pour définir la valeur d’une collection

de références comme par exemple :

1. appeler à de multiples reprises une méthode donnée avec en paramètre l’une des

ré-férences ;

2. appeler une seule fois une méthode avec en paramètre un tableau ou une collection

Java regroupant les différentes références ; ou

3. remplir une collection exposée par le composant avec les différentes références.

D’un point de vue technique, la solution 2 implique que le choix de la mise en œuvre de

la collection n’est pas du ressort du composant. Différent composants peuvent cependant

avoir des utilisations particulières de cette collection auxquelles sont adaptées des mises

en œuvres spécifiques. Il est donc intéressant de permettre aux choisir la mise en œuvre de

la collection par le composant lui-même, ce qui conduit à repousser cette solution.

Les solutions 1 et 3 sont équivalentes de ce point de vue. On peut toutefois noter que

dans l’éventualité d’une évolution vers un modèle dynamique, l’approche 3 offre l’avantage

de supporter l’ensemble des méthodes nécessaires à la modification de la collection alors

que l’approche 1 nécessiterait l’ajout de méthodes supplémentaires au composant. C’est

donc l’approche qui consiste à exposer une référence sur une collection depuis le composant

et à la remplir pendant la phase de configuration qui est adoptée.

Il faut cependant noter que l’utilisation de l’annotation @LlcmjProvide pour exposer la

collection ne correspondrait alors plus à la sémantique décrite. Il y a en effet une différence

au niveau du contrat des ports LlcmjProvide et de ceux utilisés pour l’utilisation d’une

collection de références. Dans le cas des premiers, la référence n’est accédée que pour être

passée à d’autre composants lors de la phase de configuration alors que pour les second,

l’objet référencé (la collection) est modifié. Une nouvelle annotation est donc ajoutée pour

marquer ces ports :@LlcmjMultipleUse.