• Aucun résultat trouvé

I. Intégrer de nouvelles préoccupations dans les applications objets 17

2.2. Notre approche pour un service de persistance

2.2.3. Illustration des mécanismes de requêtes

do session.start ; if p.age > 3 then p.set_age(p.age+1) ; end ; session.validate rescue session.abort end -- routine

Fig. 2.7.: Mise en œuvre explicite des mécanismes transactionnels.

matiquement6. Dans un univers où plusieurs applications se partagent les objets persistants, chaque application doit prendre le relais de l’exécutif pour déclencher, annuler ou valider une transaction (voir la figure 2.7) ; elles utiliseront respectivement les primitives de la classe

SESSION_MANAGER : start, abort et validate, disponibles via la primitive session de la

classe HERE.

On notera que dans le cas où l’annulation est le fait de l’application par le déclenchement d’une exception, c’est à l’application de demander l’annulation de la transaction (par exemple dans la clause rescue de la routine). Le mécanisme transactionnel a une influence sur la gestion des objets persistants ; en particulier, la synchronisation éventuelle (validation de la transac-tion) d’un objet persistant avec son image dans la mémoire volatile se fait à la terminaison de la transaction.

Lors de l’accès à un objet, le système doit en particulier pouvoir réagir aux situations suivantes : i) l’objet est occupé (utilisé) par une autre application, ii) l’objet est obsolète, c’est-à-dire qu’il a été marqué obsolète par une application disposant des droits suffisants sur l’objet7, iii) l’application n’a pas le droit d’accès à l’objet.

Dans le premier cas, l’exécutif réagira en fonction d’un indicateur géré par l’application et accessible à partir de la classe HERE. Si un objet est occupé, l’application peut demander à attendre qu’il ne soit plus utilisé ou abandonner sa requête. Dans le second cas, l’exécutif déclenchera une exception signifiant à l’application qu’elle ne peut utiliser un objet déclaré obsolète. Dans le dernier cas, l’exécutif déclenchera aussi une exception.

2.2.3. Illustration des mécanismes de requêtes

Nous proposons deux exemples afin de montrer i) l’expressivité du mécanisme, ii) l’intérêt de pouvoir voir une méthode comme un objet, c’est-à-dire considérer une méthode comme un objet de première classe et, iii) de présenter l’intégration par bibliothèque d’une partie des fonctionnalités liées à la gestion d’objets persistants.

Une requête est décrite dans une classe qui contient à la fois la fonction booléenne de requête (R-fonction) à appliquer à chaque objet de la collection persistante8 ainsi que des attributs éventuels correspondant aux paramètres de la R-fonction. Le code source de la figure 2.8 est un exemple de classe qui contient une fonction de requête (triviale) qui doit s’appliquer sur

6Une transaction est déclenchée dés qu’une routine est activée par un objet volatile sur un objet persistant, et elle se termine avec la fin de cette routine.

7Pour plus d’informations au sujet de la gestion des objets obsolètes, voir [183].

class BASIC_QUERY1

-- Query to execute on each person (R-function) feature

query_is_named(p : PERSON ) : BOOLEAN is do

Result := p.is_named(last_name,first_name) ; end -- query_is_named

-- Parameters of the R-function last_name : STRING is "Dupond" ; first_name : STRING is "Claire" end -- BASIC_QUERY1

Fig. 2.8.: Définition d’une requête.

... local context : BASIC_QUERY1 do context.create ; x := p.pcollection.select("query_is_named", one_argument(context)) ; ...

Fig. 2.9.: Utilisation d’une requête.

chaque objet de la pcollection des personnes. La mise en œuvre de la requête (figure 2.9) pourra être réalisée dans une autre classe comme par exemple la classe racine de l’application. Le premier argument de la primitive select est le nom de la R-fonction à appliquer à chaque objet de la collection persistante pour construire le résultat9. Comme ces traitements sont à la charge du serveur d’objets, celui-ci doit donc disposer à la fois de l’image O2 de la classe représentant la requête et d’un objet persistant de cette classe pour retrouver dynamiquement le nom de la R-fonction à exécuter ; cette information est appelée le contexte. Le deuxième argument est un tableau d’objets (ARRAY[ANY]) qui permet la transmission du contexte (toujours) et des paramètres de la R-fonction (éventuellement)10. Une variante permet une transmission plus directe de paramètres à la R-fonction (code source 2.10). Les primitives de sélection acceptent ces paramètres et les transmettent (code source 2.11). Tous les autres services de requête disponibles dans FLOO (cardinal, exists, all, do_all, do_if, count_if ) re-posent sur les mêmes principes d’utilisation.

Une requête emboîtée met en jeu plusieurs collections persistantes et permet ainsi de réaliser des traitements complexes. À titre d’exemple, nous proposons une requête (figure 2.12) qui permet de constituer la collection persistante des villes qui ont au moins 500000 habitants et qui ont au moins 200 habitants qui sont âgés de plus de 90 ans. Sa mise en œuvre pourra être décrite par le code source de la figure 2.13.

9La version 5 d’Eiffel permet sous certaines conditions de passer une méthode en paramètre et donc notre approche utilisant le nom de la routine pourrait être avantageusement remplacée par ce mécanisme.

10Les routines xxx _argument sont des routines utilitaires qui construisent un tableau avec xxx argument(s) puisque Eiffel2.3 (à la différence d’Eiffel3 et suivants) ne dispose pas de routines à liste d’arguments en nombre variable.

2.2. Notre approche pour un service de persistance class BASIC_QUERY2

feature

-- Query to execute on each person (R-function)

query_is_named(p : PERSON , last_name :STRING , first_name : STRING ) : BOOLEAN is do

Result := p.is_named(last_name, first_name) ; end ; -- query_is_named

end -- BASIC_QUERY2

Fig. 2.10.: Définition d’une requête paramétrée.

... local

context : BASIC_QUERY2 do

context.create ;

x := p.pcollection.select("query_is_named", three_arguments(context, "Dupond", Claire") ; ...

Fig. 2.11.: Utilisation d’une requête paramétrée.

class COMPLEX_QUERY feature

inhabitants_min : INTEGER is 200 ; town_size : INTEGER is 500000 age_max : INTEGER is 90

query_town(v : TOWN ) : BOOLEAN is do Result := type_pcollection("person") .select("person", two_arguments(current, v)) .cardinal >= inhabitants_min and v.nb_inhabitants >= town_size end ; -- query_town

query_person(p : PERSON , v : TOWN ) : BOOLEAN is do

Result := p.town.town_name.equal(v.nomville) and p.age > age_max end ; -- query_person

end -- COMPLEX_QUERY

Fig. 2.12.: Définition de requêtes emboîtées.

... local

context : COMPLEX_QUERY ; one_town : TOWN ;

towns : PCOLLECTION [TOWN ] do

towns ?= one_town.pcollection ; context.create ;

sel := towns.select("query_town", one_argument(context)) ; ...

2.3. Intégration par couplage fort et définition d’une