• Aucun résultat trouvé

Dans cette section, nous d´ecrivons les d´etails concrets de l’impl´ementation actuelle du langage d´edi´e FScript. Nous donnons tout d’abord un aper¸cu des interfaces de programmation qui permettent de charger et d’invoquer des scripts FScript dans un programme Java, puis nous d´ecrivons le mod`ele d’ex´ecution de l’interpr`ete FScript qui lui permet d’offrir les garanties de consistance que nous avons identifi´ees dans la section 6.2.3.

6.5.1

Interface de programmation

L’interface de programmation de FScript est quasi-identique `a celle de FPath. La seule diff´erence importante est qu’en plus de permettre la d´efinition de variables, un interpr`ete FScript est capable de charger des d´efinitions de nouvelles fonctions ou actions `a partir d’un fichier texte.

Les classes FPath et FPathInterpreter sont remplac´ees par leurs ´equivalents FScript et FScript- Interpreter:

– FScript est une classe utilitaire qui permet d’analyser (parser ) une chaˆıne de caract`eres repr´esentant une expression FPath ou une instruction FScript et d’obtenir en retour un objet de type Expression ou Statement, respectivement.

– FScriptInterpreter repr´esente un interpr`ete FScript, associ´e `a un contexte d’´evaluation qui re- groupe les valeurs des variables et les d´efinitions des proc´edures connues par l’interpr`ete.

– Enfin, l’interface FractalValueFactory est la mˆeme que celle d´ecrite pour FPath, et permet d’en- capsuler des objets Java en leur ´equivalent FScript.

La proc´edure typique pour utiliser FScript dans le contexte d’une application Fractal est la suivante : FScriptInterpreter inter = new FScriptInterpreter();

FractalValueFactory fact = inter.getValueFactory(); // D´efinition de variables

inter.define("c", fact.createComponentNode(aComponent)); // D´efinition de nouvelles fonctions et actions

inter.loadFunction("function1", MyNewFunction.class); inter.loadFunction("action1", MyNewAction.class); inter.loadDefinitions(aReader);

// Ex´ecution d’une instruction

Statement stat = FScript.parseStatement("my-reconfiguration($c)"); Value val = interp.execute(stat);

Les actions primitives d´ecrites dans la section 6.4.4 sont pr´ed´efinies et toujours disponibles dans FScript. Cependant, puisque le mod`ele Fractal est extensible et permet d’ajouter de nouvelles interfaces de contrˆole, l’impl´ementation de FScript est aussi extensible et permet de d´efinir facilement de nouvelles proc´edures primitives afin de rendre ces extensions utilisables par des programmes FScript.

Jusqu’ici, nous n’avons pas indiqu´e comment sont g´er´ees les erreurs, qui peuvent toujours se produire lorsque l’on reconfigure une application en cours d’ex´ecution. Le langage lui-mˆeme ne contient d’ailleurs aucun moyen pour signaler ou g´erer des erreurs explicitement. Nous avons fait le choix de d´el´eguer la d´etection et la gestion des erreurs `a l’impl´ementation du langage. La seule contrainte au niveau de la sp´ecification du langage est que toute impl´ementation du langage FScript doit garantir les crit`eres de consistance d´ecrits dans la section 6.2.3. Par rapport `a une sp´ecification plus formelle des crit`eres de validit´e d’un programme FScript, cette approche a l’avantage d’offrir une plus grande souplesse aux impl´ementations quant aux strat´egies `a employer pour offrir ces garanties, sans compromettre la « sˆuret´e » du langage. Du moment qu’elle garantit les crit`eres de consistance des reconfigurations – la seule chose r´eellement importante – chaque impl´ementation est libre d’utiliser les techniques qui lui semblent les plus appropri´ees : syst`eme de type, analyses statiques plus ou moins complexes ou v´erification dynamique par exemple.

La section suivante d´ecrit justement comment l’impl´ementation actuelle garantit la consistance des reconfigurations.

6.5.2

Mod`ele d’ex´ecution

Nous d´ecrivons maintenant le mod`ele d’ex´ecution utilis´e par l’impl´ementation actuelle de FScript pour garantir les crit`eres de consistances que nous avions identifi´es concernant les reconfigurations, `a savoir :

1. Terminaison des reconfigurations. 2. Atomicit´e des reconfigurations.

3. Isolation entre plusieurs reconfigurations.

4. Consistance des ´etats, en particulier de l’´etat final de l’application, et de la transition.

En dehors de la terminaison, qui est garantie par la conception mˆeme du langage par l’interm´ediaire des structures de contrˆole disponibles, nous avons choisi de d´el´eguer la gestion de ces crit`eres `a l’im- pl´ementation. Ce choix nous donne une plus grande libert´e quant aux strat´egies d’impl´ementation sans compromettre la sˆuret´e des reconfigurations.

Notons que les crit`eres de consistance ´enonc´es plus haut n’ont de sens qu’en ce qui concerne les reconfigurations de plus haut niveau (top-level ), c’est-`a-dire initi´ees par du code Java par l’interm´ediaire de la m´ethode FScriptInterpreter.execute(). L’int´egrit´e structurelle en particulier peut ne pas ˆetre toujours v´erifi´ee en cours de reconfiguration, tant que le syst`eme garantit qu’elle l’est avant et apr`es la reconfiguration. Cette similitude avec les transactions des bases de donn´ees – que nous avions d´ej`a ´evoqu´e plus haut – nous a conduit `a concevoir le mod`ele d’ex´ecution de haut niveau des reconfigurations FScript comme des transactions. Ainsi, l’algorithme principal qui contrˆole l’ex´ecution des reconfigurations et qui constitue le sch´ema de fonctionnement de la m´ethode FScriptInterpreter.execute() est le suivant : T1. D´emarrage de la reconfiguration.

T2. Ex´ecution de l’instruction FScript. Si une erreur fatale se produit (en pratique une exception est lev´ee), annulation de la reconfiguration et retour `a l’´etat initial.

T3. Test de validit´e du nouvel ´etat du syst`eme.

T4. Si le nouvel ´etat est consistant, validation de la transaction / reconfiguration. T5. Sinon, annulation et retour `a l’´etat initial.

Ce sch´ema de fonctionnement garantit l’int´egrit´e structurelle de l’application Fractal reconfigur´ee (v´erifi´ee `a l’´etape T3) et l’atomicit´e de la reconfiguration (par le retour `a l’´etat initial en cas d’erreur). Cependant, il ne s’agit que d’un sch´ema, qui peut donner lieu `a plusieurs impl´ementations. Afin de permettre d’exp´erimenter plusieurs approches, l’impl´ementation de l’interpr`ete FScript est con¸cue selon le sch´ema Pont (Bridge) [Gamma et al., 1994]. L’interpr`ete est s´epar´e en deux parties : la premi`ere, fixe, est charg´ee de l’interpr´etation des constructions du langage (flot de contrˆole, gestion de variables, etc.) ; la seconde est repr´esent´ee par l’interface FractalModel et est une abstraction de l’application sur laquelle les reconfigurations s’appliquent. Ainsi, l’interpr`ete FScript ne manipule jamais directement les interfaces de programmation Fractal, mais passe toujours par l’interm´ediaire d’une impl´ementation de FractalModel. Cette interface Java a la signature suivante :

public interface FractalModel {

FractalValueFactory getValueFactory();

void loadContributedFunctions(Environment env);

boolean startReconfiguration(FScriptInterpreter inter, Statement stat); boolean isValidReconfiguration();

void commitReconfiguration(); void cancelReconfiguration(); }

La premi`ere m´ethode, getValueFactory() renvoie une fabrique de valeurs FScript qui permet une impl´ementation sp´ecifique des types de donn´ees FScript, et en particulier des diff´erents types de nœuds. La deuxi`eme m´ethode est utilis´ee par l’interpr`ete au d´emarrage pour charger toutes les fonctions et actions primitives support´ees par ce mod`ele. Cet ensemble doit contenir au moins toutes les proc´edures

standards d´efinies dans ce chapitre et dans l’annexe A. Enfin, les quatre autres m´ethodes sont utilis´ees par l’interpr`ete pendant la reconfiguration et correspondent respectivement aux ´etapes T1, T3, T4 et T5 du sch´ema d’ex´ecution d´ecrit plus haut.

Cette conception permet de d´el´eguer `a l’abstraction FractalModel tous les aspects de la reconfigu- ration qui interagissent directement avec Fractal. Il existe de nombreuses strat´egies d’impl´ementation possibles, qui se distinguent par le moment o`u elles d´etectent les erreurs et la fa¸con dont elles les traitent. Ainsi, si l’on dispose d’analyses statiques pouss´ees permettant de d´etecter `a l’avance certaines erreurs, il est possible de les effectuer dans la m´ethode startReconfiguration(), `a laquelle toutes les informations n´ecessaires sur la reconfiguration sont pass´ees en param`etre. Si ces analyses d´etectent que la reconfigura- tion est invalide, la m´ethode renvoie faux et l’interpr`ete annule la reconfiguration et consid`ere qu’elle a ´echou´ee.

Cependant, nous ne disposons pas actuellement de telles analyses statiques, et nous avons donc choisi une impl´ementation de FractalModel bas´ee sur une strat´egie diff´erente que l’on pourrait appeler « try / repair » : la reconfiguration est appliqu´ee « en direct » `a l’application, mais le mod`ele garde un journal des actions effectu´ees contenant toutes les informations n´ecessaires pour pouvoir les annuler. Si `a la fin de la reconfiguration le nouvel ´etat atteint n’est pas valide, ce journal est utilis´e pour d´efaire (undo) la reconfiguration et revenir `a l’´etat initial. Plus concr`etement :

T1. Aucune analyse statique n’est effectu´ee, et la m´ethode startReconfiguration() se contente donc d’initialiser quelques structures de donn´ees.

T2. Toutes les proc´edures primitives demand´ees par l’interpr`ete sont appliqu´ees imm´ediatement et direc- tement sur les composants Fractal constituant l’application, mais lorsqu’une action est appliqu´ee et modifie l’application, une action primitive inverse, qui annule la premi`ere (undo) est ajout´ee dans un journal correspondant `a cette transaction de reconfiguration.

De plus, `a chaque fois qu’une action primitive stoppe implicitement un composant afin de pouvoir ˆetre appliqu´ee sans erreur, ce composant est ajout´ee dans un ensemble S. Si jamais une op´eration stop() est explicitement invoqu´ee sur l’un de ces composants pendant la reconfiguration, il est retir´e de l’ensemble S.

Si jamais une erreur irr´ecup´erable se produit, c’est-`a-dire si une exception est lev´ee lors de l’ex´e- cution d’une action primitive, cette exception est propag´ee vers l’interpr`ete pour qu’il d´eclenche l’annulation de la reconfiguration.

T3. La m´ethode isValidReconfiguration() effectue les tests suivants :

1. Pour tous les composants de l’ensemble S et pour tous leurs sous-composants directs et indirects (qui ont aussi ´et´e stopp´es puisque cette op´eration est r´ecursive dans l’impl´ementation que nous utilisons), on v´erifie que toutes les interfaces clientes obligatoires sont bien connect´ees. Cela se fait tr`es simplement en ´evaluant l’expression FPath suivante :

descendant-or-self::*[interface::*[client()][mandatory()][not(bound())]] qui doit renvoyer un ensemble vide de nœuds. Si ce n’est pas le cas, cela signifie que ces composants ne peuvent pas ˆetre red´emarr´es sans erreur.

2. Pour chaque composant ayant ´et´e impliqu´e dans la reconfiguration, toutes ces contraintes m´etiers d´efinies dans son constraints-controller sont test´ees (cf. 4.3.2).

Si l’un de ces deux pr´edicats n’est pas v´erifi´e, alors le nouvel ´etat final est consid´er´e comme incon- sistant.

Ces tests sont suffisants pour garantir l’int´egrit´e structurelle de l’´etat final puisque, arriv´e `a cette ´etape de la reconfiguration, toutes les primitives ´etant appliqu´ees au fur et `a mesure, les autres crit`eres ont ´et´e v´erifi´es pendant l’ex´ecution.

T4. La validation de la reconfiguration (commitReconfiguration()) consiste simplement `a red´emarrer tous les composants de l’ensemble S. Les tests effectu´es `a l’´etape pr´ec´edente nous garantissent qu’aucune erreur ne peut se produire ici.

T5. Enfin, l’annulation de la reconfiguration se fait en rejouant `a l’envers le journal des actions effectu´ees, ou plus pr´ecis´ement de leurs inverses (undo). ´Etant donn´e l’impl´ementation de Fractal que nous utilisons, toutes les actions primitives d´ecrites dans ce chapitre ont une action inverse bien d´efinie qui est garantie de s’ex´ecuter sans erreur si l’´etat de d´epart du syst`eme avant l’annulation correspond `

a l’´etat final apr`es l’ex´ecution initiale. Cette impl´ementation nous garantit aussi que les op´erations primitives elles-mˆemes sont atomiques, c’est-`a-dire que si une exception est lev´ee pendant l’ex´ecution d’une action primitive, l’´etat du syst`eme n’a pas ´et´e modifi´e.

Ainsi, si l’on consid`ere que la reconfiguration globale est une fonction R constitu´ee d’une s´erie de fonctions primitives R = f1◦ · · · ◦ fn, alors l’annulation de la reconfiguration consiste `a appliquer la

fonction inverse R−1= f−1

n ◦ · · · ◦ f −1

1 constitu´ee des inverses des fonctions primitives, appliqu´ees

`

a l’envers.

Cette strat´egie d’impl´ementation « optimiste » nous garantit dont bien les propri´et´es d’int´egrit´e struc- turelle et d’atomicit´e.

L’int´egrit´e comportementale, qui indique qu’aucun message n’est perdu pendant l’ex´ecution de la reconfiguration nous est garantie automatiquement par l’impl´ementation de Fractal que nous utilisons. En effet, dans cette impl´ementation lorsqu’un composant est dans un ´etat STOPPED, tous les messages envoy´es vers l’une de ses interfaces de services sont bloqu´es pour ˆetre rejou´es – dans le mˆeme ordre – lorsque le composant est red´emarr´e. Puisque toutes les op´erations primitives qui modifient les canaux de communications par lesquels passent les messages stoppent le composant impliqu´e avant de le modifier, aucune message ne peut donc ˆetre perdu.

Le dernier crit`ere `a v´erifier est celui de l’isolation entre plusieurs reconfigurations : les ´etapes interm´e- diaires d’une reconfiguration R1 ne doivent pas ˆetre visibles de la reconfiguration R2car mˆeme si R1 est

consistante prise dans sa globalit´e, certains de ces ´etats interm´ediaires peuvent ne pas l’ˆetre7. Ce crit`ere,

qui est li´e `a la s´erialisabilit´e des reconfigurations, est tr`es complexe `a impl´ementer de fa¸con efficace, sur- tout ´etant donn´e notre strat´egie d’impl´ementation actuelle qui modifie r´eellement les composants au fur et `a mesure. Notre impl´ementation actuelle, potentiellement tr`es inefficace mais correcte, consiste `a ne permettre qu’`a une seule reconfiguration `a la fois de s’ex´ecuter. Si une reconfiguration est initi´ee pendant qu’une autre est encore en cours d’ex´ecution, elle sera mise en attente jusqu’`a la fin de la premi`ere. Nous avons ´etudi´e quelques approches qui permettraient une prise en compte plus efficace de ce crit`ere mais ces ´etudes n’ont pas encore abouties faute de temps.

Pour conclure cette section, nos disposons donc d’une impl´ementation concr`ete du langage FScript, bas´ee sur une strat´egie optimiste de type « try / repair », qui garantit tous les crit`eres de consistance que nous avions identifi´es. De plus, la conception de l’interpr`ete permet d’exp´erimenter facilement d’autres strat´egies d’impl´ementation.