• Aucun résultat trouvé

d'utilisation donné. Ainsi, la spécialisation permet à des composants logiciels génériques d'être personnalisés en fonction du contexte dans lequel ils sont utilisés.

Dans notre cas, la spécialisation peut être utilisée plus particulièrement an d'opti-miser l'implémentation d'un programme DSL. Parce que les unités de compilation sont invoquées par le code généré par le compilateur, les contextes de personnalisation sont déterminés par le compilateur du DSL. En outre, parce que les composants sont xes, ou évoluent lentement, leur spécialisation peut être déterminée de manière précise. Ainsi, cette transformation de programme peut être prévisible, ce qui n'est pas le cas en gé-néral.

Les deux sections suivantes proposent une illustration de l'application de ces ap-proches de programmation générative au sein de notre méthodologie. Elles montrent comment ces techniques permettent de compléter notre représentation abstraite en termes de génération de code et de combler les détails d'implémentation à travers la compilation d'unités fonctionnelles et non-fonctionnelles.

JAIN SIP complet présenté à la gure 7.1, cette étape consiste notamment à extraire des informations des messages SIP reçus par la plate-forme.

public aspect Environment {

// processRequest

public Request Counter.rq_request;

public SipProvider Counter.rq_sipProvider;

pointcut processRequest():

execution(public void Counter.processRequest (RequestEvent));

before (RequestEvent requestEvent, Counter obj): processRequest() &&

args(requestEvent) && target(obj) { obj.rq_request = requestEvent.getRequest();

obj.rq_sipProvider = (SipProvider) requestEvent.getSource();

}

// processResponse

public Response Counter.rs_response;

public SipProvider Counter.rs_sipProvider;

pointcut processResponse():

execution(public void Counter.processResponse (ResponseEvent));

before (ResponseEvent responseEvent, Counter obj): processResponse() &&

args(responseEvent) && target(obj) { obj.rs_response = responseEvent.getResponse();

obj.rs_sipProvider = (SipProvider) responseEvent.getSource();

} }

Fig. 8.5 Aspect correspondant à la facette environnement d'exécution

La gure 8.5 présente l'aspect Environment responsable de cette facette. Il déclare deux points de coupures et deux greons, qui correspondent respectivement au code à insérer au niveau des méthodes processRequest et processResponse de la représen-tation abstraite. Cet aspect implémente le modèle SPL en termes de l'architecture à évènements de la plate-forme JAIN SIP. Pour cela, les greons présentés ici permettent d'extraire respectivement la requête et la réponse courantes correspondant aux objets requestEvent et responseEvent générés par la plate-forme. La distribution des re-quêtes selon leur type (e.g., REGISTER ou INVITE) peut alors être faite au niveau de la représentation abstraite (lignes 17-18 et 31 de la gure 8.3) an d'invoquer le gestion-naire SPL associé traduit en JAVA. De plus, le code pour extraire l'objet sipProvider est généré an d'instancier l'entité capable de manipuler et d'envoyer les messages SIP.

Grâce à cette facette fonctionnelle, la représentation abstraite du programme n'a pas besoin de s'occuper des détails d'implémentation liés à la plate-forme. Une telle stratégie aide à isoler la génération de programme dépendante de l'environnement d'exécution cible sous la forme modulaire d'un aspect.

8.4.2 Facette langage

La facette fonctionnelle langage est chargée de l'interprétation et l'expansion des mécanismes du langage cible. Dans notre exemple, cette facette est notamment illustrée par la gestion de l'état et l'implémentation d'opérations de routage avec ou sans état selon les contextes. En eet, ces opérations nécessitent la génération de motifs de code

récurrents qui sont liés à l'utilisation de Java et de l'API de programmation JAIN SIP.

Les aspects correspondants sont donnés au niveau des gures 8.6 et 8.7.

Gestion de l'état. Comme l'illustre la gure 8.3, la logique du programme GPL n'inclut pas de code pour attacher un état à une session et gérer cet état tout au long du cycle de vie de la session. Ainsi, par exemple, l'état associé à la session d'enregistrement a besoin d'être créé lors du traitement de la requête REGISTER et détruit si la requête échoue ou si la session se termine (section 7.2). Pour mettre en ÷uvre ce processus, une facette langage est dédiée à la gestion de l'état. Un extrait de cette facette est exposé à la gure 8.6.

public aspect Langage {

// Drapeaux indiquant le contexte private boolean handler_REGISTER = true;

private boolean handler_REREGISTER = false;

private boolean handler_unregister = true

public State Counter.state;

[...]

pointcut register():

execution(private void Counter.handler_REGISTER (Request, String));

before (Request rq, String method, Counter obj): register() &&

args(rq, method) && target(obj) { State state = new State();

int ident = obj.lib.env.getId (obj.rq_request);

obj.lib.env.setEnv (ident, state);

obj.stat = state;

}

pointcut processRequest():

execution(public void Counter.processRequest (RequestEvent));

before (Counter obj): processRequest() && target(obj) { if (!handler_REGISTER) {

if (obj.method.equals(Request.REGISTER)) {

if (!obj.lib.registrar.hasExpirationZero(obj.rq_request)) { if (!obj.lib.registrar.hasRegistration(obj.rq_request)) {

State state = new State();

int ident = obj.lib.env.getId (obj.rq_request);

obj.lib.env.setEnv (ident, state);

obj.state = state;

} } [...]

} [...]

}

Fig. 8.6 Aspect correspondant à la facette langage (1)

Le premier greon spécie le code à exécuter pour la création d'un état d'enregistre-ment lié au traited'enregistre-ment de la requête REGISTER. Cependant, parce que le gestionnaire de la requête REGISTER n'est pas obligatoire en SPL, la méthode correspondante peut ne pas être présente dans la représentation abstraite dans le cas où le programmeur ne spé-cie pas de comportement particulier pour cette requête. Dans un tel cas, le code doit tout de même être inséré dans le programme JAIN SIP nal pour capturer la requête REGISTER et créer l'état d'enregistrement. Toutefois, la capacité de ltrage du langage

public aspect Langage {

[...]

public Request Counter.rs_request;

public ClientTransaction Counter.rs_ct;

pointcut rq_sendRequest():

call(public void Counter.sendRequest (boolean, Request)) &&

(withincode(public void processRequest (RequestEvent))

|| withincode(* Counter.handler_*(..)));

void around(boolean b, Request rq_request, Counter obj): rq_sendRequest() &&

args(b,rq_request) && target(obj) { if (b) {

ClientTransaction ct = obj.rq_sipProvider.getNewClientTransaction(rq_request);

ct.sendRequest (rq_request);

return;

} else {

obj.rq_sipProvider.sendRequest (rq_request);

return;

} }

pointcut processResponse():

execution(public void Counter.processResponse (ResponseEvent));

void around(ResponseEvent responseEvent, Counter obj): processResponse() &&

args(rs_Event) && target(obj) { obj.rs_ct = responseEvent.getClientTransaction();

if (obj.rs_ct != null) {

obj.rs_request = obj.rs_ct.getRequest();

proceed(rs_Event, obj);

} else {

obj.rs_sipProvider.sendResponse (obj.rs_response);

return;

} } [...]

} 1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

Fig. 8.7 Aspect correspondant à la facette langage (2)

de points de jonction ne nous permet pas de tester la non-existence de la dénition d'une méthode. Pour résoudre ce problème, cette information est encodée sous la forme de drapeaux introduits dans l'aspect (partie haute de la gure 8.6). Ces drapeaux per-mettent de savoir si le greon doit être introduit on non à partir du contexte, comme illustré par l'utilisation du drapeau handler REGISTER. Dans notre exemple, le second greon n'est pas inséré dans la mesure où le drapeau indique que le gestionnaire est déjà présent dans la représentation abstraite. Ce mécanisme permet de rendre le compilateur contextuel et d'insérer les mécanismes du langage seulement si cela est nécessaire.

Opérations de routage. Un point important qui complexie l'utilisation de l'API JAIN SIP concerne la gestion du ot de contrôle au sein du programme (section 7.2.2).

En eet, dans le cas de transactions avec état, lors de la réception d'une réponse, le code du service doit explicitement associer cette réponse à sa requête initiale. Il est donc important de diérencier les envois de requêtes qui nécessitent une restauration de contexte, car leur compilation est diérente. Cette propriété est totalement abstraite dans SPL grâce à l'utilisation de l'expression forward. Pour permettre une génération automatique, cette information est rendue explicite dans la représentation abstraite,

grâce à une analyse du programme SPL. Ainsi, dans la représentation abstraite, le premier argument de la méthode sendRequest indique si les opérations de routage nécessitent une gestion d'état. L'aspect de la gure 8.7 (partie haute) décrit le fragment de code récurrent qui doit être exécuté lors du traitement d'une telle opération. Dans notre exemple, les appels à la méthode sendRequest (ligne 7) , qu'ils soient à l'intérieur de la méthode processRequest ou au niveau des diérents gestionnaires, sont compilés en opérations de routage, avec ou sans état, selon le contexte et la valeur de ce premier argument (utilisation du booléen b ligne 13).

De la même manière, le second greon permet d'insérer le code nécessaire pour associer une requête à une réponse au niveau de la méthode processResponse (ligne 23). En eet, ce code restaure l'entité responsable de la transaction (ClientTransaction rs ct ligne 5) à partir de l'évènement ResponseEvent déclenché par la plate-forme (ligne 27). Cette entité permet alors de récupérer la requête qui a généré cette réponse (ligne 29).

L'utilisation de cette facette fonctionnelle langage permet à la représentation abs-traite du programme d'être plus près de la logique. Par exemple, la gestion de l'état nécessite une compréhension précise du protocole SIP an de déterminer le moment où les sessions doivent être créées et détruites. Une telle complexité est factorisée dans cette facette, permettant au développeur du compilateur de s'occuper de ce type de problème d'implémentation une fois pour toutes et de manière localisée. De plus, cette facette gère d'autres mécanismes d'expansion du langage, non-présentés ici (e.g., gestion de réponse avec état ou encore sauvegarde et restauration de l'état). Toutefois, le mécanisme est identique et les aspects similaires.

8.4.3 Facette programme

La dernière facette fonctionnelle concerne le code spécique au programme. Cette facette permet de manipuler les informations qui changent d'un service SPL à un autre.

Par exemple, la création et la modication de l'état attaché à une session dépendent des variables de session et des gestionnaires qui les manipulent. La manipulation de telles variables est intimement liée au service.

Une telle situation est illustrée par le service Compteur (gure 8.2), où la variable count est utilisée par le gestionnaire REGISTER (ligne 9). La manipulation de cette variable ne dépend ni de l'environnement ni du langage. Ses occurrences conduisent à la déclaration d'un aspect, illustré à la gure 8.8. Cet aspect insère la dénition d'une classe State et des méthodes pour accéder à la variable d'instance. De plus, il spécie que chaque occurrence de la variable count dans la représentation abstraite doit être remplacée pour un accès à l'objet State.

La facette fonctionnelle programme permet donc de dénir une compilation du DSL au niveau de la granularité du programme, en utilisant des informations implicites et explicites du programme source. Elle permet de garder la représentation abstraite plus proche du programme source SPL.

La compilation des unités fonctionnelles a donc permis à travers la

programma-public aspect Program {

public int Counter.count;

class State implements Lib.State { private int count;

public State() {}

void setCounter (int x) { count = x; } int getCounter() { return count; } }

// Modification de la variable pointcut set_count():

set(int Counter.count);

void around (int count, Counter obj): set_count() && args(count) && target(obj) { obj.state.setCounter(count)

}

// Accès à la variable pointcut get_count():

get(int Counter.count);

int around (Counter obj): get_count() && target(obj) { return obj.state.getCounter();

} }

Fig. 8.8 Aspect correspondant à la facette programme

tion orientée aspect de générer la totalité du code nécessaire à l'implémentation du service. Elle a comblé au niveau de la représentation abstraite les détails manquants liés à l'environnement, au langage et au programme. Pour être applicable correctement, l'ordre d'insertion des aspects est toutefois important. Par exemple, certains greons dé-nissent des entités qui sont manipulées par des parties de code d'un autre aspect. Pour résoudre ce problème, l'AOP permet de dénir des relations de précédence entre aspects an de déterminer l'ordre d'exécution des greons. Notre approche par décomposition en facettes s'insère complètement dans cette démarche puisque l'ordre d'insertion re-ète le niveau de spécicité du code à introduire (environnement, puis langage et enn programme).

La modularité introduite par la programmation orientée aspect permet entre autres de limiter les eets d'une évolution de l'API de programmation sur la compilation. En eet, en localisant le code à introduire dans des aspects bien dénis, il sut de propager la modication au niveau du greon qui se répercutera automatiquement à de multiples endroits dans le code (e.g., l'envoi de requêtes).