République Algérienne Démocratique et populaire Ministère de l’Enseignement Supérieur et de la Recherche Scientifique UNIVERSITE DES SCIENCES ET DE LA TECHNOLOGIE D’ORAN MOHAMMED BOUDIAF Faculté de Mathématique et Informatique Département d’Informatique Mémoire Pour l’obtention du diplôme de magistère en Informatique Thème Orthogonalité de persistance dans les applications orientées aspect Présenté par : Bendida Aissam Dirigé par : Prof. Chouarfia Abdallah Devant la commission d’examen composée de : Président Mme H. Belbachir Prof USTO-MB Rapporteur M. A. Chouarfia Prof USTO-MB Examinateur M. B. Yagoubi Prof Université d'Oran Examinatrice Mme M. Noureddine MC-A USTO-MB Examinatrice Mme L. Zaoui MC-A USTO-MB Année universitaire 2014-2015 REMERCIEMENTS Je présente mes sincères remerciements pour mon directeur de mémoire , Monsieur le Professeur Chouarfia Abdallah pour son aide et ses conseils très avisés tout au long de ma formation. Je lui témoigne ma profonde gratitude et ma reconnaissance. Son expérience et ses compétences ont facilité mon travail et m’ont profondément marqué. Je tiens à remercier très sincèrement l’ensemble des membres du jury qui me font le grand honneur d’avoir accepté de juger mon travail. Je remercie madame Belbachir Hafida Professeur à université de science et technologie d'Oran pour l’honneur qu’elle me fait en acceptant la présidence de ce jury. Qu’elle trouve donc ici l’assurance de ma profonde gratitude. Je tiens à exprimer toute ma reconnaissance à Monsieur Belabbas Yagoubi Professeur à l’université d'Oran et madame Noureddine Myriam Maître de conférence à l’université de science et technologie d'Oran, ainsi que madame Zaoui Lynda Maitre de conférence à l’université de science et technologie d'Oran , pour avoir accepté d’être examinateurs de ce travail et pour le temps qu’ils ont investi à l’évaluer malgré leurs nombreuses obligations. Aussi, je remercie tout particulièrement mes parents pour leur encouragement et leur soutien. A ma femme et mon fils. A mes sœurs et mes frères. A toute ma famille. A tous mes amis (es). Avec toute mon affection. Résumé La couche de persistance doit assurer l’optimisation des interactions avec la base de données. A cet effet, plusieurs techniques peuvent être mises en œuvre pour réduire le nombre et la complexité des accès au support de persistance. Le travail présenté dans ce papier, s’intéresse à l’utilisation de mémoire cache pour éviter les requêtes redondantes et l'utilisation optimale des ressources dans un réseau Nous proposons un système de caching de requête qui permet de traiter le problème d’invalidation du cache, en cas de modification d’une table, et donc à éviter que les résultats des requêtes en relation avec cette table deviennent périmés. Nous montrons qu’il est possible de tirer de la programmation orienté aspect en offrant des capacités d’ajouter dynamiquement le code du cache à l’application afin d’éviter le goulot d’étranglement créé par l’utilisation d’un cache. En plus la réutilisation le code de cache dans des contextes applicatifs différents. Mots clés : persistance, cache de requête, la programmation orient aspect, AspectJ. Table des matières Introduction générale ............................................................................................................... 7 Objectifs de mémoire ........................................................................................................... 8 Plan du mémoire ................................................................................................................... 8 Chapitre I : Etude de la persistance ..................................................................................... 10 1. La problématique de la persistance objet .................................................................... 10 2. Différentes approches pour la persistance objet ......................................................... 12 3. Principe de la persistance transparente ....................................................................... 13 4. Rôle et obligations de la couche de persistance ........................................................... 15 4.1 L’outil de projection du modèle objet ................................................................... 15 5. Fonctionnalités attendues dans un Framework de persistance ................................. 19 5.1 La couche de persistance facilement manipulable ................................................ 19 5.2 Mapping objet relationnel ....................................................................................... 20 5.3 Chargement du graphe objet .................................................................................. 20 5.4 Gestion de différentes bases de données ................................................................. 21 5.5 Concurrence .............................................................................................................. 21 5.6 Transactions .............................................................................................................. 22 5.7 Cache objet ................................................................................................................ 22 6. La gestion cycle de vie et accès aux objets.................................................................... 23 6.1 Cache objet transactionnel : .................................................................................... 23 7. L’externalisation de la Persistance des objets dans un aspect ................................... 24 7.1 Distribution et Persistance avec AOP (Distribution and Persistence in AOP) ... 24 7.2 La Persistance comme un Aspect (Persistence as an Aspect) .............................. 26 7.3 Java Aspect Components (JAC) ............................................................................ 27 8. Concepts et techniques de cache ................................................................................... 28 8.1 Notion de cache ......................................................................................................... 28 8.2 La politique de remplacement ................................................................................ 29 8.3 Anomalie de Belady .................................................................................................. 30 8.4 Les travaux de gestion de cache de donnée ........................................................... 30 9. Conclusion ....................................................................................................................... 31 Chapitre II: Présentation de la Programmation orientée aspect ...................................... 32 1. Introduction .................................................................................................................... 32 2. Problèmes résolus par la programmation orientée aspect ......................................... 33 2.1. Fonctionnalités transversales ................................................................................. 33 2.2. Dispersion du code .................................................................................................. 34 3. Découverte du monde des Aspects ................................................................................ 35 3.1. Historique ................................................................................................................. 35 3.2. Principe de POA ...................................................................................................... 35 3.3. Quelques concepts de la programmation orientée aspect .................................... 36 4. Aperçus sur le langage AspectJ ..................................................................................... 40 4.1 Aspects dans AspectJ ............................................................................................... 40 4.2 Points de jonction dans AspectJ .............................................................................. 40 4.3 Coupes dans AspectJ ................................................................................................ 43 4.4 Codes Advices ........................................................................................................... 44 4.5 Introspection de point de jonction .......................................................................... 45 4.6 Mécanisme d’introduction ....................................................................................... 45 4.7 Héritage dans la POA .............................................................................................. 46 7. Conclusion ....................................................................................................................... 46 Chapitre III: approche proposée .......................................................................................... 47 1 .Introduction .................................................................................................................... 47 2. Le cache sémantique ...................................................................................................... 48 3. Stratégie de mise en cache ............................................................................................ 49 4. Intérêt de mettre la fonction de mise en cache comme aspect................................. 53 4 .Conclusion ....................................................................................................................... 55 Chapitre IV: Etude de cas ..................................................................................................... 56 1. Introduction .................................................................................................................... 56 2. coupes relatives à la persistance .................................................................................... 57 3. Résultats du test .............................................................................................................. 60 4. Conclusion et perspectives ............................................................................................. 61 Référence bibliographique ................................................................................................ 63 Table des Figures I .1 Approche hybride de représentation d’objets : gestionnaire d’objets……... 16 I .2 Différentes approches de représentation de l’héritage……………………… 18 I .3 Les classes de Player et Address avec la convention de nommage DPA…... 25 I .4 Illustre comment faire rendre les classes PLAYER et ADDRESS des classe Persistante ………………………………………………………………………. 26 I .5 la persistance des objets Players dans JAC………………………………… 27 I .6 Fonctionnement d’un cache………………………………………………… 29 II .1 Dispersion du code d’une fonctionnalité………………………………….. 34 II .2 Localisation du code d’une fonctionnalité transversale dans un aspect…. 36 II .3 Identification des aspects d’une application……………………………… 37 II .4 Tissage des aspects………………………………………………………… 39 III.1 Fonctionnement d’un cache sémantique………………………………….. 49 III.2 Le cache avant la modification de la Table T1…........................................ 50 III.3 Utilisation d’une table temporaire séparé pour les nouvelles insertions et réutiliser la réponse de la requête précédente (A) avec la réponse(B)....................... 51 III.4 Processus de mise en cache les requête………………………………….. 52 III.5 Le Patron de conception DAO……………………………………………. 55 IV.1 Dispersion du code de mise en cache…………………………………….. 60 IV.2 Impact d’un aspect sur la location d’une mise en cache...……………….. 61 Page |7 Introduction générale Introduction générale Le génie logiciel est un ensemble de méthodes et outils permettant de maîtriser les coûts et le temps de développement des applications logicielles. L'une des principales idées du génie logiciel est de modulariser les applications [1]. Nous utilisons actuellement toujours cette notion de modules à travers la programmation orientée objet et la programmation par composants logiciels. La programmation orientée objet a été largement utilisée dans les milieux industriels. Elle a indéniablement fait avancer l'ingénierie des systèmes logiciels à différents points de vue. Cependant, les nombreuses retombées d'expériences ont révélé les limites sérieuses qu'elle éprouve dans le développement de systèmes logiciels complexes. Ces limites sont, entre autres, dues à la difficulté de prise en compte des préoccupations transverses (qui recoupent plusieurs parties d'un système). Ces préoccupations sont souvent dupliquées (et dispersées) dans le code. Ceci affecte la qualité des systèmes, en particulier au niveau de la modularité, et rend difficile, entre autres, leur compréhension ainsi que leur maintenance et évolution. L' évolution des systèmes industriels complexes actuels est souvent inéluctable et présente des enjeux économiques et qualitatifs considérables. Une des solutions envisagées consiste à utiliser la programmation orientée aspect. Ce nouveau paradigme de programmation cherche, en effet, à améliorer la séparation (et l’ intégration) des préoccupations en « modularisant » les éléments transversaux des systèmes sous forme d’ unités séparées appelées « aspects ». La migration d’un système orienté objet vers un système orienté aspect passe d' abord par l'identification et la localisation de ces préoccupations dans le code objet (aspect mining) pour les représenter, par la suite, par des aspects (aspect refactoring). Dans ce mémoire, nous nous intéressons à la préoccupation transversale ˝ la persistance˝. La couche de persistance doit prendre en charge l’instanciation (création, recherche) des objets requis par l’application depuis le support de persistance. L’interface entre l’application et le mécanisme de persistance doit être réalisée au niveau objet, et non au niveau base de données ou requête SQL. La couche de persistance doit gérer les Introduction générale Page |8 connexions entre les sessions applicatives, la base de données et l’infrastructure du serveur d’application, de manière transparente. Ce mémoire se situe à l'intersection des trois domaines langages de programmation, le génie logiciel et les bases de données pour réaliser un modèle de persistance en se basant sur l’approche orientée aspect. Objectifs de mémoire L’instanciation, la recherche, et la mise à jour des objets ainsi que le transit de ces objets entre application et support de persistance sont des opérations très coûteuses en temps d’accès. L’un des objectifs de ce mémoire est de fournir un mécanisme fondamental d’optimisation des performances par l’utilisation d’un cache objet, qui permet de réduire les accès au support de persistance en conservant en mémoire les objets les plus fréquemment accédés. Grâce à la POA, il est possible de rendre le cache et les applications qui manipulent cet cache plus indépendante, pour cela nous allons développer un aspect de mise en cache reposant sur Framework Cacheonix et Aspectj. L’ensemble de ces objectifs nous amène à proposer une solution combinant la persistance et l’approche POA de façon orthogonal. Plan du mémoire Dans ce mémoire, nous adopterons une organisation comportant quatre différents chapitres. Les deux premiers présentent l’état de l’art sur les techniques de gestion et d’optimisation de persistance, et les concepts de la programmation orientée aspect. Le premier chapitre de cette étude sera consacré à l’explication de la problématique de la persistance objet, de l’intégration des modèles objet et relationnel, et des différents mécanismes mis en œuvre au sein d’un moteur de persistance objet, ainsi qu’à l’optimisation de persistance où on définira la notion de cache, les concepts associés et les caractéristiques d’un cache liées au déploiement afin de mieux comprendre la difficulté de proposer un service de caches. Nous montrons à travers quelques travaux que l’optimisation de persistance peut bénéficié des avantages de la programmation orientée aspect. Le deuxième chapitre sera consacré sommairement à la programmation orientée aspect. Nous commençons cette étude par la présentation des problèmes résolus par la programmation orientée aspect. Puis nous introduisons les concepts de la programmation orientée aspect et du langage AspectJ. Le troisième chapitre nous présentons l’approche globale retenue pour la persistance dans les applications orientées aspect. Introduction générale Page |9 Le quatrième chapitre est une réalisation de l’approche proposée sur un cas concret de gestion de budget d’état. Enfin nous terminons par une conclusion et des perspectives sur des travaux futurs. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 10 Chapitre I Chapitre I : Etude de la persistance 1. La problématique de la persistance objet Le modèle objet offre des capacités de modélisation et d’abstraction avancées, mais il est en revanche handicapé par une grave limitation : il se focalise exclusivement sur les aspects dynamiques de l’exécution des programmes, sans couvrir réellement la problématique de la persistance des objets [20]: Dans une application, les objets représentent des entités, des informations qui peuvent être partagées par plusieurs applications, et qui peuvent avoir une durée de vie très longue, dépassant la durée d’une session applicative. Cependant, les objets n’existent qu’en mémoire ; par conséquent, une fois la session applicative terminée, les objets sont détruits, et les informations qu’ils contiennent disparaissent avec eux. La persistance objet est ainsi le fait d'exister dans le temps. Un objet qui reste en l'état quand il est sauvegardé puis chargé par une autre application, ou à un moment éloigné, possède la propriété de persistance. En conséquence, l'objet n'est plus dépendant de l'application qui l'a créé et sa durée de vie est donc totalement indépendante du cycle de vie des sessions applicatives qui le manipulent. Dans le modèle relationnel, les données sont persistantes : une fois le schéma relationnel défini, les données ajoutées restent accessibles durablement. La durée de vie des données est donc totalement indépendante du cycle de vie des sessions applicatives qui les manipulent. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 11 La problématique de la persistance objet consiste donc à élaborer et à mettre en œuvre les mécanismes permettant de sauvegarder de façon fiable et durable l’état des objets manipulés par une application. La persistance consiste à sauvegarder l’état d’un objet (les différentes valeurs de ses attributs) sur un support de stockage permanant pour être en mesure de le reconstituer ultérieurement. Cependant, plusieurs considérations viennent compliquer la simplicité apparente de cette tâche : Le type de objet : chaque objet appartient à une classe donnée ; il est donc nécessaire de sauvegarder les informations relatives à l’appartenance de l’objet à une classe particulière. Des références vers d’autres objets : au-delà des types scalaires simples, les objets peuvent avoir comme attributs des références vers d’autres objets. Sauvegarder un objet implique la sauvegarde de tous les objets référencés par cet objet. L’identité objet : un identificateur d’objet, ou OID (Object IDentifier), est un moyen de désigner de façon unique un objet dans le système. Il assure à un objet une existence indépendante de sa valeur. L’OID peut être physique ou logique. Dans le premier cas, il contient l’adresse physique de l’objet qu’il désigne, dans le deuxième cas, il est nécessaire de le décoder pour déterminer cette adresse. Techniquement, il existe plusieurs approches pour implémenter la persistance objet: Sérialisation : La sérialisation d'objets prend l'état de l'objet et le convertit en un flux d'octets. Ce flux d'octets pourra être sauvegardé sur disque ou encore être envoyé sur le réseau. On peut définir et utiliser des flux binaires (fichiers binaires), ou textuels (fichiers XML par exemple). Mapping objet/relationnel : est la persistance des objets d'une application orientée objets dans des tables d'une base de données relationnelle. Pour ce faire, un fichier qui décrit le mapping entre les objets et la base de données est employé. Ce fichier est en général écrit en XML (Extensible Markup Language). ORM (object/relational mapping) transforme donc des données d'une représentation dans une autre. Persistance en base de données objet : Les systèmes de gestion de base de données objet (SGBDO) permettent de stocker dans les bases de données objet les valeurs des attributs, les relations d'héritage entre les objets, les références d'un objet sur un autre objet. Certains SGBDO permettent également d'exécuter les méthodes des objets dans le cadre des manipulations du contenu de la base de données. Contrairement aux bases de données relationnelles qui emploient des structures bi-dimensionnelles (les tables), les bases de données objet utilisent les mêmes représentations que les objets : des structures hiérarchiques (graphes). Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 12 Au-delà du mécanisme de persistance utilisé, il est également nécessaire d’implémenter la logique de persistance qui va permettre aux applications d’effectuer la sauvegarde et la restauration de l’état des objets manipulés. C’est le rôle de la couche de persistance (ou moteur de persistance). Le moteur de persistance expose une API aux applications pour la création, la mise à jour, et la recherche d’objets sur le support de persistance. A ce moteur de persistance, il est également nécessaire d’associer un outil de projection des objets sur le support de persistance : le rôle de cet outil est de fournir le moyen de définir. 2. Différentes approches pour la persistance objet Les systèmes de gestion de bases de données (SGBD) [29] relationnels ont pris une place importante sur le marché des SGBD. Ils répondent de façon satisfaisante aux besoins des applications classiques de gestion (administration, comptabilité, banques, etc.). Des domaines d’application moins classiques sont apparus, comme la CAO (Conception Assistée par Ordinateur), la bureautique, le génie logiciel, les applications géographiques, etc. Ces applications gèrent des données beaucoup plus complexes, qu’il est difficile de représenter avec le modèle relationnel. Les limites du modèle sont d’une part un manque de capacité de représentation structurelle, et d’autre part l’impossibilité de représenter pleinement la sémantique des entités de la base. La seule structure de données disponible dans le modèle relationnel est le tableau à deux dimensions (qui matérialise une relation n-aire entre plusieurs domaines de valeurs). Ces structures plates ne permettent pas de représenter directement des objets complexes comprenant plusieurs niveaux d’imbrication. Il faut alors décomposer ces objets en plusieurs relations. Cette décomposition est visible à l’utilisateur qui doit formuler des requêtes permettant de reconstituer de tels objets (jointures). D’autre part les données stockées sont purement descriptives ; l’essentiel de la sémantique associée à ces données se trouve dans les programmes de manipulation. Seules les contraintes d’intégrité permettent de représenter une certaine sémantique associée aux données. Ces lacunes sont justement comblées par certaines approches de l’orientation objet. Les recherches menées dans le domaine des bases de données se sont donc inspirées de l’approche objet. En réalité, plusieurs approches sont envisageables pour mettre en œuvre une solution de persistance d’entités métier [18] : Purement relationnel : L'application entière est programmée autour du modèle relationnel et toutes les opérations sont relationnelles et basées sur SQL. Cette solution peut être très intéressante pour des petites applications comportant peu de code destinées à être réutilisables. Cependant pour les applications d'une certaine importance cette solution n'est pas optimale. La raison en est qu'il devient très compliqué d'étendre l'application car comme le tout est vu sous formes de tables Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 13 et relations entre elles, l'ajout de nouvelles tables signifie également un ajout de nouvelles relations entre elles qui sont souvent difficiles à gérer. De plus il faut par la suite adapter tout le code SQL. Correspondance objet légère (Light Object Mapping) : Les entités sont représentées comme des classes qui sont mappées manuellement vers les tables du modèle relationnel. Le code SQL/JDBC est masqué grâce à l'emploi de design patterns. Cette approche est très répandue et est bonne pour des applications avec un petit nombre d'entités. Correspondance objet moyenne (Medium Object Mapping) : L'application, contrairement au modèle purement relationnel, est modelée autour d'un modèle orienté objets. SQL est généré à un outil de génération de code. L'association entre les objets est supportée par le mécanisme de persistance et les requêtes peuvent être réalisées grâce à un langage d'expression orienté objet comme par exemple HQL (Hibernate Query Language). Ce dernier n'est toutefois pas un langage de manipulation de données comme SQL. Il est utilisé uniquement pour la récupération d'objets et non pour effectuer des opérations d'insertion, de mise à jour ou de suppression. Le Medium object mapping convient bien aux applications de moyenne taille contenant quelques transactions complexes. Correspondance objet complète (Full Object Mapping) : Ce procédé supporte des procédés complexes propres au monde orienté objet : héritage, composition, polymorphisme. La couche persistance implémente de la persistance transparente; les classes persistantes n'héritent d'aucune classe de base et ne doivent pas implémenter d'interface spéciale. 3. Principe de la persistance transparente Un problème difficile de l’utilisation des SGBD relationnels dans le contexte d’un programme objet est la nécessité d’effectuer la conversion entre les types de données du langage objet et les types de données du SGBD. Dans le cas d’un SGBD relationnel, il faut traduire les objets sous forme de lignes dans des tables. Il faut en même temps gérer l’identification des objets par des clés primaires qui servent aussi à réaliser les associations. Un objectif visé par les bases de données objet est la possibilité de supporter la persistance des objets de manière directe et transparente sans avoir à effectuer de conversion entre les types du programme et les types du SGBD. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 14 En particulier l’écriture de code source d’une classe persistante doit s’effectuer sans préoccupations de mécanismes sous-jacents liés à la persistance de son état. Les bases d’une solution de persistance orthogonale ont été établies de manière théorique : Persistance orthogonale : est le découplage total entre l’application et l’infrastructure de persistance. En d’autres termes, garantir le découplage entre applications, moteur de persistance, et base de données. Le principe de persistance orthogonale est apparu à la fin des années 1970, dans le domaine des langages de programmation. Il proclame que toute donnée doit être habilitée à persister pendant un délai aussi long qu’il est utile, et que la méthode d’accès à une donnée doit être indépendante de la nature de sa persistance. Permet de masquer au développeur les mécanismes de transformation entre l’objet en mémoire et sa représentation sur le support de stockage. Afin de démontrer ce caractère de persistance orthogonale, une application doit posséder les caractéristiques suivantes définies par M. Atkinson [2], [4], [5] : Règle 1: l'indépendance de la persistance Le statut de persistance pour un objet doit être indépendant des programmes qui les manipulent: un objet persistant ne l'est pas pour des programmes particulier. Réciproquement, le code des programmes manipulant des objets persistants doit pouvoir être indépendant de leur statut persistant ou volatile. L'intérêt de cette règle se situe essentiellement dans le domaine de la réutilisation des composants. Il est important de ne pas être obligé de considérer, au moment de l'écriture d'un composant, le statut des objets manipulés (persistant ou volatile). Règle 2: l'orthogonalité des types de données persistants En accord avec le principe général de conception des langages de programmation sur la complétude des types de données, tous les objets, quelque soit leur type, doivent pouvoir être rendus persistants et manipulés comme tels sans limitations. Quelque soit son type, un objet a le droit de devenir persistant; on ne veut pas, à cause de la non possibilité de rendre certaines structures persistantes, influencer les choix de conception d'un programme. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 15 Règle 3: l'orthogonalité de la gestion de la persistance La manière d'introduire et de reconnaitre le statut persistant d'un objet est orthogonale au système de type, au modèle d'exécution et aux structures de contrôle du langage. Cette règle garantit l'intégrité des objets quelque soient leur statut: tous les objets référencés par un objet persistant restent accessibles après la terminaison du processus, c'est-à-dire sont eux-mêmes persistants. Réciproquement, un objet ne peut être effacé que lorsqu'il n'y a plus aucun objet qui le référence. A ces considérations théoriques sur le fondement de la persistance transparente, il faut ajouter les exigences pratiques auxquelles doit satisfaire toute couche de persistance. 4. Rôle et obligations de la couche de persistance La partie du code responsable de l’accès aux données dans une application multi niveaux doit être encapsulée dans une couche dédiée aux interactions avec la base de données de l’architecture généralement appelée couche de persistance. Celle-ci permet notamment : D’ajouter un niveau d’abstraction entre la base de données et l’utilisation qui en est faite. De simplifier la couche métier qui utilise les traitements de cette couche. De masquer les traitements réalisés pour mapper les objets dans la base de données et vice et versa. De faciliter le remplacement de la base de données utilisée. Le rôle principal du moteur de persistance est d’implémenter [20], de façon transparente, la logique de persistance des objets des applications. En plus, il doit être associé un outil de projection qui va permettre de définir les structures utilisées pour la persistance des objets, ainsi que les règles de transformation entre la représentation des objets en mémoire et leur représentation sur le support de persistance. 4.1 L’outil de projection du modèle objet En tenant compte des divergences croissantes apparaissant entre les formalismes de modélisation conceptuelle et logique, depuis les années 1990, plusieurs études ont été menées en vue de concilier l’univers objet de l’approche relationnelle. Deux grandes approches ont été proposées [23]: 1. La connexion des applications objets directement aux bases données relationnelles en codant dans les méthodes de classes ("getters", "setters", constructeurs, Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 16 destructeurs, etc.) l’accès aux données. Cet accès se fait généralement par du "Embedded SQL" repartit dans tout le code des applications. 2. La définition d’un gestionnaire d’objets (ou Object Layers) entre les applications objets et les bases de données relationnelles (figure I .1). Le gestionnaire d’objets (1) reçoit les requêtes (OQL par exemple) venant des applications objets, (2) les traduit en SQL et les envoie au SGBDR pour exécution ; (3) récupère les résultats et enfin (4) les traduit de nouveau dans le format adapté (OQL par exemple) pour les applications objets. Les deux approches précédentes partagent le fait que les objets des classes de l’application sont stockés dans des bases de données de type relationnel (ou relationnelobjet). Ces approches nécessitent donc de définir des correspondances entre les concepts objets et les concepts relationnels, puis de gérer de façon la plus simple possible les transformations associées à ces correspondances. APPLICATION (1) requêtes (7) données objets (OQL) (OQL) 2) Traduction GESTIONNAIRE D’OBJETS Requêtes en SQL(3) Envoie de la requête au SGBDR/O (5) récupération de données (6) Traduction des données en objets (OQL) SGBDR/O (4) exécution de la requête SQL Figure I .1 – Approche hybride de représentation d’objets : gestionnaire d’objets Parmi les principaux concepts objets qui exigent des règles de transformations particulières pour être représentés en relationnel, les relations d’héritage, d’associations et d’agrégations. 4.1.1 Représentation de la relation d’héritage Les propositions de représentation de la relation d’héritage peuvent être classées en trois catégories [23] : Chapitre I : Etude sur les techniques de gestion de persistance 1. 2. 3. P a g e | 17 Représentation "à plat", Représentation horizontale, Représentation verticale. Représentation "à plat" : une table par arborescence Représenter toutes les classes d’une arborescence d’héritage par une seule table relationnelle. C'est la solution la plus simple à mettre en place. Pour consulter les différents objets d'un même type, aucune jointure n'est nécessaire, ils sont situés à chaque ligne d'une même table. La table peut être très dense car elle regroupe plusieurs types d’objets. Deux autres attributs sont ajoutés. Le premier représente l’identifiant, ou clé primaire de la table. Le second est généralement un code spécifiant le type effectif de l’objet instancié ((1a) dans la figure I .2). Une variante de cette approche consiste à remplacer ce second attribut par plusieurs attributs de type booléen ((1b) dans la figure I .2) ; chacun d’eux correspondants à un des types possibles. Cette approche est simple et supporte le polymorphisme car tous les objets polymorphes appartiennent à la même table. Ils peuvent donc être référencés par une même clé étrangère. L’accès aux instances d’un niveau quelconque de la hiérarchie demande seulement la projection d’une sélection car toutes les instances sont rangées dans une seule table. C’est la seule méthode qui sera économe en jointure. L’inconvénient, par contre, se situe au niveau de l’espace de stockage car les tables peuvent être très grandes et contenir de nombreuses valeurs nulles. Ceci est en particulier le cas lorsque des motifs de classes abstraites sont utilisés pour représenter des mécanismes génériques, tels, par exemple, le motif Modèle-Vue-Controleur (MVC) [19]. La mise à plat impose alors de regrouper un très grand nombre de classes disparates, qui n’ont pas d’autres points communs que d’utiliser le même mécanisme. Représentation horizontale : une table par classe concrète Dans cette approche ((2) dans la figure I .2), une table est créée pour chaque classe concrète. Tous les attributs de la classe concrète et ceux hérités des super-classes de cette dernière constituent les colonnes de la table. A ces colonnes, s’ajoute une clé primaire. Lorsqu’il n’y a qu’un seul niveau de classes concrètes, l’avantage de cette approche est qu’il est facile d’obtenir et de stocker les informations sur les objets existants car toutes les informations sur un objet se retrouvent dans une seule table. Les inconvénients principaux de cette représentation sont (1) qu’une requête générique sur une classe abstraite nécessite des unions de projections des tables des sous-classes et Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 18 (2) que le polymorphisme n’est pas supporté de façon aisée car un lien vers une classe abstraite se matérialise par une référence vers des tables différentes selon les instances. Représentation verticale : une table par classe Dans cette approche ((3) dans la figure I .2), on crée une table pour chaque classe. Chaque table a pour colonnes les attributs définis au niveau de la classe qu’elle représente. Un même identifiant est utilisé comme clé primaire pour toutes les tables. Au niveau des sous-classes, il représente à la fois une clé primaire et une clé étrangère. L’avantage de cette approche est qu’elle représente de façon très adéquate les concepts orientés objets. Le polymorphisme peut se représenter assez aisément par une clé étrangère unique (avec jointure automatique avec les super-classes) parce que nous avons un enregistrement dans la table correspondant à chaque classe auquel un objet appartient. Il est facile également de modifier les super-classes et d’ajouter de nouvelles sousclasses car cela correspond respectivement à la modification et à l’ajout d’une table. Outre le nombre important de tables dans la base de données (une pour chaque classe), le principal inconvénient de cette approche est que les informations sur un objet d’une classe se retrouvent reparties entre grand nombre de tables. Ainsi, tout accès à un objet nécessite de faire de nombreuses jointures, ce qui peut devenir très coûteux. Figure I .2 – Différentes approches de représentation de l’héritage Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 19 Pour ce qui concerne les associations, agrégations et compositions, on trouve également dans la littérature, plusieurs propositions de représentation [40], [34], [35], [36], [37]. Ces propositions sont définies en fonction des cardinalités des relations (1 : 1, 1 : n, n : m) et des propriétés des relations d’agrégations (dépendance, partage, exclusivité, prédominance). [38] énumère toutes les solutions envisageables pour la représentation de ces relations en relationnel. Ces différentes solutions se résument soit à la création de tables intermédiaires entre les tables des classes participantes à la relation, soit à la déclaration de clés étrangères sur des colonnes de tables. Ces différentes propositions ne s’appliquent que lorsque le choix de représentation de l’héritage est vertical ou à plat. Lorsque seules les classes concrètes sont représentées, il est nécessaire de représenter tout lien par un aiguillage comportant en particulier une colonne qui indique le type effectif de l’instance référencée. - La possibilité de répartir les attributs d’un objet dans plusieurs tables (mapping multi-table). - La possibilité d’utiliser des champs binaires (BLOB) pour sauvegarder l’état d’objets dépendants. - Des mécanismes de conversion de types entre types objet et types de bases de données. 4.1.2 La gestion de l’identité objet : Dans le modèle objet, l’unicité des objets est uniquement fonction de l’unicité des pointeurs, tandis que dans le modèle relationnel, l’unicité des tuples est garantie par l’utilisateur de clés primaires. La notion d’unicité est commune aux mondes objet et relationnel. Dans le monde relationnel, elle se retrouve dans deux contraintes d’intégrité. Pour rappel, une contrainte d’intégrité vise à garantir la cohérence des données. Pour assurer l’unicité d’un enregistrement dans une table, ou tuple, la première contrainte qui vient à l’esprit est la clé primaire. Une clé primaire (primary key) est un ensemble de colonnes dont la combinaison forme une valeur unique. La contrainte d’unicité (unicity constraint) possède exactement la même signification. 5. Fonctionnalités attendues dans un Framework de persistance 5.1 La couche de persistance facilement manipulable Il faut avant tout que la couche d’accès soit très facilement manipulable, et que le programmeur puisse exploiter un système de données de façon très intuitive. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 20 Concrètement, il faut que des méthodes CRUD soient implémentées, si possibles des patterns Factory permettant de charger des objets de différentes manières. Il faut aussi que chaque attribut ou relation du modèle de classe soit interprété en tant qu’accesseurs sur les champs et les entités correspondants, chargeant de manière transparente les entités en question. 5.2 Mapping objet relationnel Une couche Mapping Objet Relationnel(ORM) est une interface logicielle qui permet de représenter et de manipuler sous forme d’objet tous les éléments qui composent une base de données relationnelle. Ainsi, une table ou bien un enregistrement de celle-ci est perçu comme un objet du langage sur lequel il est possible d’appliquer des actions (les méthodes). L’avantage de cette approche est de s’abstraire complètement de la technologie de gestion de la base de données qui fonctionne en arrière plan, et de ne travailler qu’avec des objets ayant des liaisons entre eux. Il doit être possible de modéliser une couche de données avec un modèle entièrement objet, gérant les concepts d’héritage, de relations complexes, et autres. De manière générale, on pourra par exemple utiliser un éditeur UML du marché, ou créer un fichier XML représentant ce modèle. Cette méthodologie permet une plus grande souplesse de modification du modèle et de la base de données, les modifications du modèle étant répercutées automatiquement sur la base de données de façon transparente pour le programmeur 5.3 Chargement du graphe objet L’accès aux données et donc la récupération des objets est une partie sensible de l’application, sur laquelle l’optimisation peut jouer un grand rôle. Java Persistance fournit une solution complète pour maîtriser l’accès aux données, et ce selon les trois axes principaux suivants : Le chargement à la demande, ou lazy loading. pour éviter le chargement d’instances inutiles ; Chargement des instances associées à l’exécution afin de permettre le chargement d’un réseau d’objets plus large que celui défini par défaut et de limiter ainsi le nombre de requêtes générées ; Optimisation du SQL généré, afin de tirer parti d’un certain nombre de spécificités du dialecte de la base de données cible. On appelle « Lazy Loading » la technique qui consiste à ne charger que les éléments correspondants à la demande spécifique du programmeur. A l’inverse, on parlera d’ « Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 21 aggressive loading » si la couche d’accès aux données charge aussi tous les enregistrements qui ont une relation avec celui demandé, provenant de tables différentes. On comprendra facilement que le lazy loading permet d’optimiser le code, et ne surcharge pas le serveur de bases de données inutilement. Mais il faut bien comprendre qu’il peut arriver que cette technique crée par moment plus de requêtes au serveur que nécessaire. Elle reste tout de même la technique la plus utilisée, car en général plus rapide, et moins gourmande en ressources que l’aggressive loading. 5.4 Gestion de différentes bases de données L’abstraction vis-à-vis de la base de données est très importante pour le programmeur. Mais l’abstraction vis-à-vis du serveur de bases de données est peut être encore plus importante. En théorie, une couche d’accès aux données peut être construite pour être indépendante du serveur de bases de données. En pratique il existe deux méthodologies possibles : utiliser un langage de communication standard (SQL), ou exploiter une API telle qu’ADO.Net et les différents fournisseurs de données. On instancie alors les fournisseurs adéquats au moment voulu, en utilisant par exemple le pattern abstract factory. 5.5 Concurrence La stratégie de concurrence d’accès influence directement les performances et les capacités pour une application client serveur, à partir de laquelle plusieurs utilisateurs peuvent modifier les mêmes objets simultanément. Dans les systèmes objets il y a généralement deux approches pour aborder le problème de la concurrence d’accès : l’approche pessimiste. l’approche optimiste. Dans l’approche pessimiste les objets sont systématiquement verrouillés lorsqu’ils entrent dans une transaction. Les verrous sont gérés de manière à garantir qu’à un instant « t » un seul utilisateur puisse modifier un objet donné. Cette méthode radicale a le désavantage d'être coûteuse en termes de performances, car elle verrouille l’accès à l’objet tant qu’il n’a pas été complètement libéré, même si aucune modification n’est effective. Dans l’approche optimiste les objets ne sont pas verrouillés lorsqu’ils entrent dans une transaction. Lors de la mise à jour la couche d’accès vérifiera qu’ils n’ont pas été modifiés par un autre utilisateur entre le moment où ils ont été lus et le moment où ils sont mis à jour. Au cas où un objet a été modifié l’ensemble de la transaction est annulée. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 22 La méthode optimiste ne modifie pas le niveau de concurrence d’accès que peut supporter une application, simplement en général elle est plus efficace car elle économise le coût de la gestion des verrous. Une bonne couche d’accès aux données doit rendre ce fonctionnement transparent pour le programmeur, et lever automatiquement une exception typée lui permettant d’effectuer les traitements adéquats. 5.6 Transactions Une transaction permet de ne valider un ensemble de traitements sur le base de données que si ils se sont tous effectués correctement, la couche d’accès doit implémenter des services permettant de définir le début et la fin d’une transaction, et proposer un mécanisme d’interception des erreurs pour annuler cette dernière. 5.7 Cache objet Lorsque l’on cherche l’utilité d’un cache objet, on pense tout d’abord à la notion de performance. Dans le cas d’une couche d’accès aux données, cette partie est surtout nécessaire pour conserver la cohérence des instances générées. Les caches peuvent ainsi différer en plusieurs aspects, telles que les informations manipulées, ou encore les politiques de remplacement ou de résolution employées. L’amélioration des performances étant fortement liée à la configuration du cache en fonction de l’environnement d’exécution, les caches sont très souvent construits de bout en bout, rendant le développement coûteux (en temps et en argent). Le cache a une taille maximale qu’il ne peut pas dépasser. Cette taille est gérée par le Framework de persistance, plus le cache est grand, plus le SGBD sera rapide. Si le cache est plein, il garde en mémoire les objets récemment utilisés et supprime les autres de remplacement. Le cache stocke les dernières données récupérées de la base. Par exemple, si on fait une requête qui ramène la liste de tous les objets d’un certain type, le temps d’exécution sera différente que si l’on exécute la requête pour la première fois. A la première exécution, le cache ne contient pas le résultat de la requête donc cette dernière va jusqu’au serveur, interroge la base puis le résultat est retourné au client et est mémorisé dans le cache. A la deuxième exécution, la base de données n’est pas interrogée étant donné que le cache possède déjà la réponse. Le résultat est ainsi récupéré beaucoup plus rapidement sans avoir besoin d’interroger la base. Le rapatriement d'objets dans le cache se fait par pages, ce qui signifie que les objets demandés ainsi que les objets présents sur les mêmes pages sont remontés dans le cache. On peut donc trouver dans le cache des objets auxquels on n'a jamais accédés mais qui le seront peut-être, d'où le gain de performance. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 23 Un Framework de persistance a beau implémenter un cache [31], il faut surtout voir ce dont il se charge, car entre une simple pile d’objets et un cache qui distingue les accès en lecture/écriture, qui automatise le recyclage des instances à l’aide d’un ramasse miette (garbage collector) et qui gère les accès concurrentiels, la différence peut vite être énorme. 6. La gestion cycle de vie et accès aux objets La couche persistance nommée aussi couche d’accès aux données. Elle prend en charge l'accès à la source de données (SGBDR, fichiers XML, LDAP, …). La couche Persistance offre les fonctionnalités de base qui permettent de créer, rechercher, modifier et supprimer des composants objets métiers dans le respect des propriétés transactionnelles classiques. D’utiliser le mécanisme de projection objet vers relationnel (mapping Objet / Relationnel) qui consiste en la transformation de la représentation des données en une représentation objet. Elle est structurée en routines ou méthodes qui contiennent les accès aux tables des bases de données et réalisent les fonctions élémentaires de gestion : création, modification, lecture. La couche de persistance doit prendre en charge l’instanciation (création, recherche) des objets requis par l’application depuis le support de persistance. L’instanciation, la recherche, et la mise à jour des objets, ainsi que le transit de ces objets entre application et support de persistance, sont des opérations très coûteuses en temps d’accès. Le mécanisme fondamental d’optimisation des performances est l’utilisation d’un cache objet, qui va permettre de réduire les accès au support de persistance en conservant en mémoire les objets les plus fréquemment accédés et donc d’accroître les performances du système. 6.1 Cache objet transactionnel : L’utilisation d’un cache a toujours été importante dans les systèmes informatiques, afin de fournir une haute qualité de service et notamment une amélioration des temps de réponse perçus par les utilisateurs. En effet, un cache fournit un accès rapide aux données et permet de conserver en mémoire une partie des objets stockés sur le support de persistance. Lorsque l’application demande le chargement d’un objet, cet objet est chargé depuis le support de persistance, et stocké dans la mémoire cache. Si l’application accède ultérieurement à cet objet, la couche de persistance peut utiliser la copie de l’objet en cache pour satisfaire la demande de l’application, ce qui évite un accès à la base de données. Au fur et à mesure de l’exécution, les objets chargés par l’application remplissent le cache. De plus, employer un cache réduit la charge sur les serveurs et éventuellement sur les liens de communication, la récupération des éléments étant alors plus rapide. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 24 La politique de remplacement est utilisée pour déterminer les entrées du cache à remplacer, appelées victimes. Il n’existe pas de politique de remplacement meilleure que les autres pour toutes les séquences d’accès. C’est pourquoi, de nombreuses solutions sont proposées dans la littérature. Le choix d’une politique est fortement lié à l’application et aux objectifs fixés par le développeur de la solution de cache. Les politiques de remplacement peuvent, en particulier, s’appuyer sur le principe de localité pour le choix des éléments à supprimer, notamment sur la localité spatiale ou la localité temporelle. A travers la couche de persistance, l’accès aux objets doit être réalisé de façon transactionnelle, afin de permettre la concurrence d’accès aux objets et d’assurer les propriétés transactionnelles de base lors des opérations de mise à jour: atomicité, cohérence, isolation, et durabilité. Par conséquent, le cache objet doit lui-même être transactionnel, et assurer la gestion des accès concurrents aux objets à travers l’utilisation de stratégies de verrouillage paramétrables [20]. 7. L’externalisation de la Persistance des objets dans un aspect Dans la littérature de la programmation orientée aspect, la persistance a été identifiée comme un aspect [25] [26]. Le problème de la persistance est de savoir où ajouter son code. La motivation pour les systèmes d’aspects conçus pour fournir une meilleure séparation des préoccupations de persistance sont basées sur les caractéristiques suivantes de ces systèmes. Tout d'abord, point de jonction, il s’agit d’un point dans l’exécution d’un programme autour duquel l’aspect de persistance peut être ajouté. Ensuite, le code advice définissant le comportement d’un aspect et chaque advice est associé à une coupe, le coupe fournit l’ensemble des points de jonction autour desquels sera greffé le bloc de code advice. Enfin, le mécanisme de tissage ajoute le comportement aspect de persistance définie dans code advice aux points de jonction sélectionné. Dans la suite, nous présentons trois exemples d’approches dans chacune pour résoudre le problème de la persistance. 7.1 Distribution et Persistance avec AOP (Distribution and Persistence in AOP) Soares et Laureano [09] mis en place une solution orientée aspect pour les deux préoccupations, la persistance et la distribution. Ils distinguent l'aspect de distribution sur le côté serveur et le celui du côté client suivant le patron de conception façade [10]. Chacun de ces aspects propose un service d'accès distant en utilisant le RMI (Java Remote Method Invocation) [11]. Aspect de persistance fournir des fonctionnalités de base parmi lesquelles : la connexion à la base de données, la gestion des transactions, la gestion cache et la Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 25 synchronisation l'état de l'objet avec base de données. Les auteurs ont considéré le problème de synchronisation de l'état objet comme une préoccupation transversale entre la distribution et l’aspect de persistance, ils ont choisi de résoudre cette dépendance comme une partie de l’aspect de persistance. Enfin, les auteurs soulignent certaines faiblesses dans le langage AspectJ pour soutenir une bonne modularisation et suggérer certaines propositions. Dans [09], les auteurs ont émis un certain nombre de conventions de nommage qui doit être suivi. Par exemple, les classes de la couche métier doivent avoir le suffixe «Record», chaque type persistant déclaré doit implémenter l’interface PersistentObject dans l'aspect persistance et les méthodes setter et getter aussi doivent commencer par "set" et "get", respectivement. De plus, les développeurs doivent également déclarer que les méthodes de la façade ont des paramètres sérialisables et les types de retour à l'intérieur de l'aspect persistance côté serveur en étendant leurs types par l'implémentation de l'interface Serializable. Pour utiliser ce Framework, les développeurs doivent étendre deux aspects abstraits: le AbstractPersistenceControl et AbstractTransactionControl. public class Player { String plyerID; Address address; // … public void setPlayerID(String playerID) { … } public Address getAdddres() { … } // … } public class Address { // … public void setStreet(String street) { … } public String getStreet() { … } // … } Figure I .3 – Les classes de Player et Address avec la convention de nommage DPA Pour des raisons de simplicité, cet exemple n'est pas concerné par la distribution et les aspects de transaction, il considérait comme une simple partie d'une application qui peut se conformer à la structure du système de plaintes de la santé. En particulier, il montre comment les types simples de logique métier sont déclarés comme persistants. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 26 7.2 La Persistance comme un Aspect (Persistence as an Aspect) Rashid et Chitchyan [12] ont proposé la première tentative d'un Framework de persistance basé sur les concepts de la programmation orientée aspect (POA). Ce Framework utilise une base de données relationnelle, il se compose de plusieurs aspects qui offrent des différentes fonctionnalités de persistance. Ces fonctionnalités incluent une connexion à la base de données, les opérations de persistance, mécanisme de traduction SQL et les démarcations des transactions. Ce Framework est annoncé pour soutenir la persistance par accessibilité. Comme dans DPA, le Framework PAA s'appuie sur les annotations, les types persistants devraient être déclarés à étendre la classe PersistentRoot. PAA permet également l'utilisation des conventions de nommage des méthodes setter et getter qui doivent être utilisées à l'intérieur du code de base chaque fois qu'on veuille rendre un objet persistant. public aspect ApplicationDatabaseAccess { declare parents: (Player || Address) extends PersistentRoot; pointcut traplnstantiations: call(PersistentRoot+.new(..)); pointcut trapupdates(PersistentRoot obj): … && this(obj) && execution(public void PersistentRoot+.set*(..); // … } public aspect EstablishMapping dominates DatabaseAccess { pointcut setupMapping(): ApplicationDatabaseAccess.establishconnection(); before(): secupMapping() { LookupTable mappingTable = LookupTable.getLookupTable(); mappingTable.createClassToTableMapplng(“Player", “PLAYER"); mappingTable.createClassToTableMapping(“Address",“ADDRESS"); } Figure I .4 – illustre comment faire rendre les classes PLAYER et ADDRESS des classe persistante Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 27 7.3 Java Aspect Components (JAC) Composants Java Aspect (JAC) [13] est un Framework basé sur l’aspect qui est utilisé pour construire des applications distribuées en java. Les objets distribués sont également possible de se définir comme des objets persistants dans JAC. Les développeurs utilisent deux niveaux du Framework JAC afin de définir la distribution et la persistance de leurs objets POJO. Un programme orienté aspect avec JAC est un ensemble d'objets d'aspects qui peuvent être dynamiquement déployés et retirés sur des objets applicatifs en cours d'exécution. Les objets d'aspects peuvent définir trois types de méthodes d'aspects: des méthodes encapsulantes (qui encapsulent des méthodes applicatives et permettent d'exécuter du code avant et après la méthode encapsulée), des méthodes de rôle (qui ajoutent de nouvelles fonctionnalités aux objets applicatifs) et des gestionnaires d'exceptions. Le problème de la composition d'aspects est traité à l'aide d'un contrôleur d'encapsulation bien défini qui spécifie pour chaque objet encapsulé, au moment de l'encapsulation, à l'exécution ou dans les deux cas, l'ordre d'exécution des objets d'aspects. Deux aspects de persistance sont disponibles avec JAC : PersistanceAC et HibernateAC. PersistanceAC permet de sauvegarder des données soit dans des fichiers, soit dans une base de données via JDBC. HibernateAC s’appuie sur le Framework de persistance Hibernate pour réaliser cette sauvegarde. // déclarer la classe Player comme une classe persistante 1. MakePersistent roster. Player; // define the aspect 2. public class RosterAC extends AspectComponent { 3. public RosterAC() { 4. pointcut(“.*", // all objects 5. “Player", // classe Player 6 . “set*(*):void", // setter methods 7. “ObjChanged", // the name of wrapper (advice) 8. null, // no Exception handler 9. false); // wrapper cardinality, here is singleton 10. } 11. class ObjChanged extends Wrapper { // advice code 12. public Object invoke(MethodInvocation mi) throws Throwable 13. { // …} 14. } 15.} Figure I .5 – la persistance des objets Players dans JAC Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 28 Dans la figure I .5, le fichier de configuration JAC précise que la classe Player déclarer comme une classe persistante. Dans l’aspect RosterAC, la méthode Pointcut (ligne 4) spécifie que la méthode sélectionne toutes les invocations de méthodes (ligne 12) qui commencent par Set (ligne 6) de n'importe quel objet (ligne 4) du type Player (ligne 5). 8. Concepts et techniques de cache 8.1 Notion de cache [24] Cache et un Espace physique d’accès rapide, généralement de taille limitée, utilise pour stocker des copies des éléments les plus susceptibles d’être référencés. L’utilisation d’un cache a toujours été importante dans les systèmes informatiques, afin de fournir une haute qualité de service et notamment une amélioration des temps de réponse perçus par les utilisateurs. En effet, un cache fournit un accès rapide aux données, pouvant tirer bénéfice de l’utilisation d’un support physique efficace, d’un ensemble d’éléments gérés plus petit autorisant des recherches plus rapides, ou encore d’un placement évitant des communications réseaux. De plus, employer un cache réduit la charge sur les serveurs et éventuellement sur les liens de communication, la récupération des éléments étant alors plus rapide. Ainsi un cache améliore généralement les performances, en dépit des couts de déploiement, d’administration, de gestion et éventuellement des problèmes de cohérence qu’il peut engendrer. Le fonctionnement classique d’un cache est illustré par la figure I.6 . A la suite d’une demande d’un client (1), un cache vérifie la présence de l’élément dans son espace de stockage. Si celui-ci est contenu en cache (succès de cache ou cache hit), il est fourni directement au client (2). Dans le cas contraire (défaut de cache ou cache miss), il est nécessaire de contacter le serveur (2’) afin de le récupérer (3’). Le cache renvoie l’élément (4’) au client et le stocke pour une utilisation future. A noter qu’en général, l’utilisation du cache est transparente, le client n’ayant pas conscience qu’il accède aux données au travers d’un cache. Cependant, la transparence est parfois levée, permettant notamment à un client de contacter directement le serveur s’il veut s’assurer d’obtenir la dernière version d’un élément. Chapitre I : Etude sur les techniques de gestion de persistance (1) P a g e | 29 (2’) CLIENT CACHE SERVEUR (4’) (2) (3’) Figure I .6 – Fonctionnement d’un cache Définition 2.2. Succès de cache Un succès de cache (cache hit) se produit, lorsque l’élément demande est présent en cache. Définition 2.3. Défaut de cache Un défaut de cache (cache miss) se produit, lorsque l’élément demandé n’est pas présent en cache. 8.2 La politique de remplacement La politique de remplacement qui a pour charge de déterminer les éléments à supprimer lorsqu’il n’y a pas suffisamment d’espace libre pour stoker un nouvel élément. Cette fonctionnalité peut être réalisée par un grand nombre d’algorithmes, parmi lesquels nous retiendrons : L'algorithme optimal : La règle de remplacement est «remplacer la page qui ne sera pas utilisée pendant la durée la plus longue». Malheureusement, cet algorithme nécessiterait de connaître l'avenir. FIFO (First In First Out) : La page la plus ancienne est la victime (celle qui a été placé en mémoire depuis le plus long temps). Cet algorithme est très simple à implémenter. LRU (Least Recently Used) : Il consiste à choisir comme victime le cadre qui n'a pas été référencé depuis le plus longtemps. LFU (Least frequently used) «la moins souvent utilisée» : on garde un compteur qui est incrémenté à chaque fois que le cadre est référencé, et la victime sera le cadre dont le compteur est le plus bas. ZIZE : le plus gros élément en cache est expulsé. Le choix d’une politique est fortement lié à l’application et aux objectifs fixés par le développeur de la solution de cache. Les politiques de remplacement peuvent, en Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 30 particulier, s’appuyer sur le principe de localité pour le choix des éléments à supprimer, notamment sur la localité spatiale ou la localité temporelle. 8.3 Anomalie de Belady L’anomalie de Belady caractérise un cache dont l’augmentation de l’espace physique ne produit pas une réduction du nombre de défauts. 8.4 Les travaux de gestion de cache de donnée De nombreux travaux ont été mènes à l’optimisation des ressources en cache afin d’augmenter la charge locale et réduire le nombre de requêtes évaluées sur les serveurs et la consommation de bande passante. Par exemple : Un site web à usage intensif de données est un site Web qui gère un grand nombre de pages dont le contenu est construit dynamiquement, à partir de grandes bases de données. Dans ce contexte, la demande d'une page par un client peut nécessiter une interaction couteuse avec le système de gestion de bases de données (pour la connexion au système et l'exécution des requêtes nécessaires à la récupération des données), risquant ainsi d'augmenter considérablement le temps de réponse. Les auteurs [14] adressaient ce problème de performance en s'appuyant sur la spécification déclarative de sites Web. Ils ont proposé une architecture configurable de caches à plusieurs niveaux et sa mise en œuvre dans le cadre de Weave, un système de gestion de sites web développé à l’INRIA. Des fragments XML et/ou des fichiers HTML. Ils illustraient leur approche à l’aide d’un site Web construit à partir de la base de données du benchmark TPC/D. Puis ils évaluaient expérimentalement, les performances de différentes stratégies de gestion de cache en utilisant leur plate-forme de test weaveBenche. Enfin, ils donnaient un ensemble de directives permettant de configurer la gestion du cache en se basant sur les spécifications du site Web. Les travaux de chercheurs [15], [16] tels qu’ARUN IYENGAR et JIM CHALLENGER présentent une nouvelle approche pour la mise en cache des pages Web dynamiques afin d'améliorer les performances. Selon les chercheurs l’algorithme appelé propagation des mises à jour des données (Data Update Propagation -DUP-) déterminent la façon dont les pages Web mises en cache sont affectés par les modifications. Par exemple, un ensemble de plusieurs pages Web en cache peuvent être construit à partir de tables appartenant à une base de données. Dans cette situation, une méthode est nécessaire pour déterminer quelles sont les pages Web sont touchées par les mises à jour de la base de données. De cette façon, les caches peuvent être synchronisées avec les bases de données de sorte qu'ils ne contiennent pas de données périmées. Chapitre I : Etude sur les techniques de gestion de persistance P a g e | 31 9. Conclusion Le problème de la gestion de la persistance dans les langages orientés objet est un problème capital pour tous les développements logiciels. D’autant plus qu’une partie de la performance d’une application se joue sur le lien entre celle-ci et la base de données. Ce problème peut devenir rapidement complexe et coûteux. Ce chapitre introduit de nombreux concepts qui sont liés au domaine de problème de persistance. Nous avons présenté les solutions actuelles pour résoudre la persistance des objets en utilisant l'approche orient aspect. Dans la suite de ce document, nous présenterons la programmation orientée aspect. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 32 Chapitre II Chapitre II: Présentation de la Programmation orientée aspect 1. Introduction La réutilisation est l'une des principales promesses de la programmation par objets. Malheureusement, elle n'est pas toujours possible du fait du mélange, lors de l'implémentation des applications, des définitions des services réalisés (fonctionnalités) avec des propriétés non-fonctionnelles représentant des mécanismes d'exécution spécifiques (distribution, tolérance aux fautes, persistance, …). Afin de pallier ce défaut, le paradigme de la programmation par aspects considère que les services d'une application et ses propriétés non-fonctionnelles correspondent à des aspects qui doivent être découplés les uns des autres [08]. La POA se propose de pallier aux faiblesses des paradigmes classiques (programmation modulaire, POO, programmation par composant) en introduisant un autre type d’organisation au sein des programmes. Il existe des fonctionnalités transverses à plusieurs entités logicielles comme par exemple la persistance, la tolérance aux fautes. De même, il existe, au sein du même application, des préoccupations complètement réparties dans le code. L’exemple le plus cité est la gestion des traces au sein d’une application. En tant que paradigme de programmation, la programmation orientée aspect se positionne comme le successeur de la programmation orientée objet. Néanmoins, elle ne vise pas à remplacer entièrement la POO mais a plutôt comme objectif de la compléter afin d’obtenir des programme mieux structurés et plus clairs. Il est à noter que la programmation par aspects est indépendante de la programmation par objets. En effet, il est possible d’utiliser la programmation par aspects conjointement à d’autres paradigmes de programmation (fonctionnelle, impérative, …) [08]. Dans ce chapitre nous rappellerons d’abord les problèmes résolus pas la programmation orientée aspect. Puis nous introduisons les concepts de la programmation orientée aspect, nous présentons aussi le langage AspectJ ainsi que ses concepts. Nous Chapitre II : Présentation de la Programmation orientée aspect P a g e | 33 introduirons alors quelques exemples choisis de programmation par aspects écrits principalement avec le langage AspectJ vu comme une extension du langage Java. Ces exemples seront l’occasion de caractériser le modèle d’aspects sous jacent en précisant les notions de points de jonction, de coupes, d’introductions. 2. Problèmes résolus par la programmation orientée aspect Le développeur d’application ont généralement plusieurs préoccupations à apprendre en compte dans leurs développement .ces préoccupations peuvent être divisées en deux catégories : les préoccupations fonctionnelles, qui correspondent au cœur de métier de l’application, et les préoccupations techniques, liées à l’environnement d’exécution. Le principe de séparation des préoccupations cherche à rendre indépendantes ces préoccupations afin d’améliorer la modularité des applications. La programmation orientée objet a permis d’atteindre un certain degré d’indépendance sans pour autant casser totalement les liens entre les préoccupations fonctionnelles restent encore dépendantes des préoccupations techniques. La programmation orientée aspect est un nouveau paradigme de programmation qui cherche à améliorer la séparation des préoccupations en modularisant les éléments transversaux des applications. bon nombre de préoccupations, elles sont modularisées sous forme d’aspects. L’objectif de la POA est la séparation des préoccupations. La contrainte principale est la dispersion des préoccupations transversales à travers le code métier. [06] Dans cette section nous expliquons les problèmes résolus par la programmation orientée aspect. 2.1. Fonctionnalités transversales La POO s’efforce de découper une application en classes cohérentes et indépendantes. Cependant, ces entités peuvent parfois être liées. Pawlak, Retaillé et Seinturier [17] nous donnent comme exemple un cas de contrainte d’intégrité référentielle : un objet client ne peut être supprimé s’il n’a pas honoré toutes ses commandes. Comme la classe client n’est pas censée connaitre les contraintes imposées par les autres classes et que la classe commande n’a aucune raison de permettre la suppression d’un client, nous nous retrouvons face un problème quant à la séparation indépendante des tâches. C’est ce que l’on appelle une fonctionnalité transversale (crosscutting concern). Chapitre II : Présentation de la Programmation orientée aspect P a g e | 34 2.2. Dispersion du code La programmation procédurale induit un découpage en fonction des traitements à implémenter, tandis que la programmation objet induit un découpage en fonction des données qui seront encapsulées dans les classes. Certaines fonctionnalités s’accommodent mal de ce découpage, et les instructions correspondant à leur utilisation se retrouvent dispersées dans l’ensemble de l’application. Tout changement dans l’utilisation de ces fonctionnalités implique dés lors de devoir consulter et modifier un grand nombre de fichiers. On dit en ce cas que leur code est éparpillé, ou dispersé. La figure II .1 schématise une application composée de trois classes. Les traits représentent les lignes de code correspondant à des fonctionnalités (par exemple, la gestion de persistance). Ces fonctionnalités sont transversales à l’application car elles affectent toutes ses classes. Figure II .1 – Dispersion du code d’une fonctionnalité Outre le problème de mélange de code, la réutilisation d’une propriété nonfonctionnelle est d’autant plus difficile que sa définition se trouve dispersée. De ce fait, elle ne peut être isolée pour être réutilisée. En effet, une même propriété nonfonctionnelle peut toucher plusieurs objets [08]. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 35 3. Découverte du monde des Aspects Dans la présente section nous rappellerons les grandes lignes des paradigmes de programmation sur lesquels s’appuiera la programmation par aspects. 3.1. Historique La programmation orientée aspect (POA) est un nouveau paradigme de programmation ont été définies au centre de recherche de Xerox à Palo Alto[07] au milieu des années 1990 . La POA a émergé à la suite de différents travaux de recherche, dont l’objectif était d’améliorer la modularité des logiciels de faciliter la réutilisation et la maintenance. Elle ne remet pas en cause les autres paradigmes de programmation, comme l’approche procédurale ou l’approche objet, mais les étend en offrant des mécanismes complémentaires pour mieux modulariser les différentes préoccupations d’une application et améliorer ainsi leur séparation. 3.2. Principe de POA Une solution aux problèmes de mélange et de dispersion du code des propriétés nonfonctionnelles rencontrés avec la programmation par objets, consiste à séparer et découpler leurs définitions comme le veut le principe de la separation of concerns. La programmation orientée aspect complémente la POO en apportant des solutions aux deux challenges que sont l’implémentation de fonctionnalités transversales et le phénomène de dispersion du code. Une application orientée aspect fondée sur la POO est composée de deux parties, les classes et les aspects : Les classes constituent le socle de l’application. Il s’agit des données et des traitements qui sont au cœur de la problématique de l’application et qui répondent aux besoins de celle-ci. Les aspects intègrent aux classes des éléments (classes, méthodes, données) supplémentaire, qui correspondent à des fonctionnalités transversales ou à des fonctionnalités dont l’utilisation est dispersée. Il est important de souligner que la programmation par aspects utilise la technologie objet dans un cadre qui en conserve les bénéfices tout en palliant les limitations suscitées. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 36 3.3. Quelques concepts de la programmation orientée aspect Dans cette section, nous introduisons les concepts de ce nouveau paradigme. Nous expliquons quelques les constituants de l’aspect, comment les points de jonction, les coupes, le ‘codes advice’, ‘Tissage d’aspects’, ‘Mécanisme d’introduction’ . a) Notion d’Aspect La programmation orientée aspect propose l’utilisation d’aspects qui permettent d’implémenter des fonctionnalités transversales, ou dont l’utilisation s’avère dispersée dans le code, en intégrant aux classes des éléments (comme des méthodes ou des données) supplémentaires. Un aspect est une unité modulaire qui recouvre et concerne la structure des autres unités modulaires globalement. Des aspects existent au niveau de la conception et de l’implémentation. Un aspect de niveau conception est une unité de conception qui s'applique à l’ensemble de la structure de conception. Un aspect de niveau programmation ou code est une unité de programme qui, là encore, concerne la structure de la plupart, voire de tous les autres modules du programme. L’intégration de ces éléments supplémentaires dans les classes se fait au moyen de codes advices et de coupes qui permettent de spécifier le code de la fonctionnalité et où celui-ci va s’appliquer. Ainsi, les aspects résolvent le problème d’indépendance des classes puisqu’ils se chargent des fonctionnalités transversales et permettent de réduire la dispersion de code dont nous avons parlé grâce à une localisation de l’utilisation des services (voir Figure II .2) [07]. Figure II .2 – Localisation du code d’une fonctionnalité transversale dans un aspect Chapitre II : Présentation de la Programmation orientée aspect P a g e | 37 L’apport essentiel de la POA est de fournir un moyen de rassembler dans un aspect du code qui autrement, serait dispersé au sien d’une application. Figure II .3 – Identification des aspects d’une application Le développement de logiciels en utilisant l'approche orientée aspect est similaire au développement de logiciels avec d'autres méthodologies (Figure II .3) : identification des préoccupations, leur implémentation, et leur combinaison pour former le système final. La communauté des chercheurs de l'AOP définit ces trois étapes comme suit : 1. Décomposition aspectuelle : Les besoins sont ici décomposés pour identifier les préoccupations fonctionnelles et transversales. Par exemple, un développeur pourra identifier les préoccupations suivantes : cache, gestion transactionnelle de la persistance, authentification, etc. Ensuite, il pourra décider que seul le logique métier est une préoccupation fonctionnelle. Toutes les autres préoccupations sont des préoccupations transversales au système et qui vont être utilisées par plusieurs modules. 2. Implémentation des préoccupations : Chaque préoccupation est implémentée indépendamment des autres. Le développeur aura à implémenter le logique métier, le logging, le cache, la gestion transactionnelle de la persistance, l'authentification, etc. 3. Recomposition aspectuelle : Des règles de recompositions sont spécifiées en créant des unités appelées aspects. Le processus de recomposition, aussi connu sous le nom de tissage ou d'intégration, et le programme qui la réalise est un tisseur d’aspect. L’application obtenue à l’issue du tissage est dite tissée. b) Notion de Point de jonction Un aspect était une entité logicielle implémentant une fonctionnalité transversale à une application. La définition de cette structure transversale passe par la notion de point de jonction [17]. Un point de jonction est un point dans le flot de contrôle d'un programme dans lequel un ou plusieurs aspects peuvent être appliqués. Méthodes : Les différents scénarios d'exécution d'une application peuvent être exprimés en termes de séquences de messages qui déclenchent l'exécution de Chapitre II : Présentation de la Programmation orientée aspect P a g e | 38 méthodes. Les appels et les exécutions de méthodes sont donc deux types de points de jonction couramment utilisés. Les trois principaux événements qui constituent des points de jonction de méthodes sont l’appel, le début et la fin d’une méthode. Constructeur : Les constructeurs sont des méthodes qui revêtent un caractère particulier puisqu’ils sont exécutés lorsqu’un objet est instancié. Comme pour les méthodes, la POA permet d’intercepter cet événement. Attributs : Les opérations de lecture et d'écriture sur les attributs comme des types de points de jonction. Exception : La levée et la récupération d’une exception constituent donc des points de jonction. Le code à exécuter lors de la levée de cette exception sera défini dans un aspect. c) Notion de Coupes Une coupe désigne un ensemble de points de jonction [07]. Elles permettent au programmeur de spécifier les points de jonction comme l'appel à une méthode, l'instanciation d'objet ou l'accès à une variable). Tout "pointcut" est une expression vérifiant la correspondance d'un point de jonction. Les points de coupure représentent des coupes qui sont de deux types : les coupes simples et les coupes composées. Une coupe est définie à l’intérieur d’un aspect .dans les cas simples, une seule coupe suffit pour définir la structure transversale d’un aspect. Dans les cas plus complexes, un aspect est associé à plusieurs coupes. d) Codes advice Un aspect définit une fonctionnalité transversale. Comme nous l’avons vu, il spécifie le caractère transversal grâce aux coupes. La fonctionnalité est, quant à elle, spécifiée par des codes advices. [07] Une fois la coupe définie, il faut écrire le code à exécuter au moment où l'événement décrit par la coupe est levé. Avant d'écrire ce code advice, il faut décider à quel moment exécuter le code : avant l'événement, après ou autour de l'événement. Advice before (avant) : Le mot-clé before permet d'exécuter du code avant d'entrer dans le code lié à l'événement (exemple appel de méthode, lecture d'attribut, etc...). Advice after (après) : Le mot-clé after permet quant à lui permet d'exécuter du code après l'événement. Advice around (autour): Le mot-clé around permet soit de remplacer l'événement en lui même, soit d'exécuter du code avant et après le point de jonction. Pour exécuter le code de l'événement il faut utiliser le mot-clé proceed à l'intérieur du code advice. Si la méthode a un type de retour, alors il faut faire Chapitre II : Présentation de la Programmation orientée aspect P a g e | 39 précéder le mot-clé around de Object. L'appel à proceed renvoie alors un type Object qu'il est possible de récupérer pour le renvoyer [33]. Un code advice définit quant à lui ce que l’aspect greffe dans l’application, autrement dit les instructions ajoutées par l’aspect. e) Tissage d’aspects Une application orientée aspect contient des classes et des aspects. L'opération qui prends en entrée les classes et les aspects et produit une application qui intègre les fonctionnalités des classes et des aspects est connu sous le nom de tissage d'aspect (aspect weaving) [07]. Le programme qui réalise cette opération est appelé tisseur d'aspects (aspect weaver) ou bien tisseur (Weaver) tout court (Figure II .4). Figure II .4 – Tissage des aspects L’opération de tissage peut être effectuée à la compilation ou à l’exécution [22]. Dans le cas du tissage à la compilation, le tisseur est un programme qui, avant l’exécution, prend en entrée un ensemble de classes et un ensemble d’aspects et fournit en sortie une application augmentée des aspects. Dans le cas du tissage à l’exécution, le tisseur est un programme qui permet d’exécuter à la fois l’application et les aspects qui lui ont été ajoutés. Les aspects ont une existence propre lors de l’exécution. f) Mécanisme d’introduction L'objectif du mécanisme d'introduction est d'être capable d'étendre le comportement d’une classe à l'aide de méthodes ou d'attributs définis dans un aspect. Le terme introduction renvoie au fait que ces éléments sont introduits, c’est-à-dire ajoutés à l’application. L’introduction est donc un mécanisme purement additif, qui ajoute de nouveaux éléments à une classe. A la différence de l’héritage en POO, l’introduction ne peut Chapitre II : Présentation de la Programmation orientée aspect P a g e | 40 étendre les classes qu’en rajoutant de nouveaux éléments, il n’est donc pas possible de redéfinir une méthode. 4. Aperçus sur le langage AspectJ AspectJ est aujourd'hui une référence pour la programmation par aspects telle que définie par Kiczales. C’est un langage qui étend Java et permet la définition des aspects. Ce langage représente l’application des aspects dans le paradigme de l’Orienté Objet. AspectJ est en fait une extension à Java [08]. Les composants sont toujours écrits en Java pur. Grâce aux nouvelles possibilités offertes par la syntaxe d’AspectJ, nous pouvons définir des aspects accompagnés de leurs règles d'intégration. Un aspect est donc un mélange de règles AspectJ et d'instructions Java qui modularise une préoccupation. 4.1 Aspects dans AspectJ Un aspect est une entité logicielle qui capture une fonctionnalité transversale à une application [07]. Un aspect est une entité logicielle très similaire à une classe. Il définit du code, lequel Abstrait et modularise une préoccupation. AspectJ introduit le mot-clé aspect qui permet de spécifier que l’entité logicielle définie est un aspect, à l’instar du mot-clé class qui spécifie la définition d’une classe. L’exemple suivant illustre la définition d’un aspect [21]: public aspect PremierAspect { /* code de l’aspect */ } Un aspect est défini dans un fichier qui porte le même nom que le nom de l’aspect. Ce fichier porte l’extension .java, mais il est fortement conseillé d’utiliser les extensions .aj ou .ajava pour des aspects. 4.2 Points de jonction dans AspectJ Un ensemble de points de jonction est identifié dans une coupe à l’aide du mot-clé pointcut. Une expression est également fournie pour filtrer l’ensemble obtenu. Un point de jonction représente un événement dans l’exécution du programme qui peut être intercepté pour exécuter un code advice correspondant. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 41 Bien que n'importe quel point d'exécution dans du programme puisse être défini comme un point de jonction, AspectJ limite les points de jonction à ceux qui peuvent être utilisés de manière systématique [39]. a) Types de point de jonction Les types de points de jonction fournis par AspectJ peuvent concerner des méthodes, des attributs, des exceptions, des constructeurs ou des blocs de code statique (static). Un dernier type concerne les exécutions de code advice. [32]. Les points de jonction, les plus utilisés, dans la POA sont : les méthodes, les attributs, les constructeurs et enfin les exceptions. Méthodes : C’est autour des méthodes que les aspects se greffent le plus souvent. Ce n’est pas étonnant puisque les méthodes forment l’outil principal de la POO et structurent l’exécution du programme. Les événements liés aux méthodes qui constituent des points de jonction sont l’appel d’une méthode et l’exécution de celle-ci. La spécification de ces points se fait dans l’aspect par call (methexpr) ou execution (methexpr) qui identifient tous les appels vers des méthodes dont le profil correspond à une description donnée par methexpr. Appel de méthode : call (methexpr) : Ce mot-clé identifie tous les appels vers des méthodes dont le profil correspond à la description donnée par ‘methexpr’. Exécution de méthode : execution (methexpr) : Ce mot-clé identifie toutes les exécutions de méthodes dont le profil correspond à l’expression ‘methexpr’. La différence fondamentale entre les types call et execution concerne le contexte dans lequel le se trouve l’application lors de l’exécution du code advice associé. Un point de jonction donnée par call (methexpr) correspond à l’appel d’une méthode compatible à methexpr. Il se trouve donc dans le code de la méthode appelante. Alors qu’un point de jonction donnée par execution (methexpr) se trouve dans la méthode appelée. L’ordre d’exécution des différents codes advices serait le suivant : 1. Dans la méthode appelante : Exécution de la première partie du code advice associé au point de jonction call. 2. Dans la méthode appelée : Exécution de la première partie du code advice associé au point de jonction execution. 3. Dans la méthode appelée : Exécution de la méthode appelée. 4. Dans la méthode appelée : Exécution de la deuxième partie du code advice associé au point de jonction execution. 5. Dans la méthode appelante : Exécution de la deuxième partie du code advice associé au point de jonction call. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 42 Attributs : La lecture et la modification d’attributs constituent également des points de jonction. On peut penser notamment à une utilisation à des fins de persistance. La spécification de ces points se fait dans l’aspect par get (attrexpr) qui identifie tous les points de jonction représentant la lecture d’un attribut dont le profil vérifie attrexpr et set (attrexpr) qui identifie toutes les modifications d’un attribut dont le profil est compatible à attrexpr. Ces mots clés sont intéressants pour des aspects qui souhaitent intercepter la modification de l’état d’un objet [13]. Constructeurs : Peuvent être considérés comme des méthodes particulières. Il s’agit de méthodes invoquées lorsqu’un objet est instancié. Comme pour les méthodes, la POA permet d’intercepter cet événement. Il est intéressant de remarquer que l’on peut intercepter les appels des constructeurs avec le mot-clé call que nous avons explicité précédemment. Il suffit de fournir une methexpr identifiant les méthodes de nom new. Aspectj définie des coupes prenant en compte les constructeurs de classe. Pour cela deux types de point de jonction sont disponibles : initialization et preinitization. La façon la plus simple d’appréhender la différance ces deux types consiste à étudier deux situations, selon que le fais ou non appel à un constructeur hérité. Exceptions : Les événements de levée et de récupération d’exceptions peuvent aussi constituer des points de jonction. Le code à exécuter lors de la levée de cette exception sera défini une seule fois dans un aspect. La spécification de ces points se fait dans l’aspect par handler (exceptexpr) qui identifie toutes les récupérations d’exception dont le profil vérifie exceptexpr. Il s’agit donc, en Java de l’exécution des blocs catch. AspectJ ne permet pour le moment que l’utilisation de codes advice de type before sur les points de jonction de récupération d’exception. AspectJ introduit d’autres de point de jonction, comme par exemple : Les points de jonction de type staticinitialization correspondent aux exécutions des blocs de code static. Le point de jonction, adviceexecution permet de déclencher un aspect avant ou après l’exécution d’un code advice. Il est ainsi possible de définir un aspect qui modifie le comportement d’un autre aspect. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 43 b) Définition des profils Tous les mots-clés que nous avons vus requièrent un paramètre. Ce paramètre est une expression qui permet, en spécifiant un profil, de filtrer l’ensemble de points de jonction donné par le mot-clé. L’expression précisant le profil des éléments qui nous intéresse peut faire usage de quantificateurs (appelés wildcards) afin d’introduire de la généricité dans les profils sélectionnés. Ces wildcards sont *, .. et +. [32] 4.3 Coupes dans AspectJ Elles permettent au programmeur de spécifier les points de jonction comme l'appel à une méthode, l'instanciation d'objet ou l'accès à une variable). Tout "pointcut" est une expression vérifiant la correspondance d'un point de jonction [32]. Les points de coupure représentent des coupes qui sont de deux types : les coupes simples et les coupes composées. Une coupe est introduite grâce au mot clé pointcut. La syntaxe est : pointcut nomDeLaCoupe (paramètres) Définition de la coupe Une coupe permet de regrouper un ou plusieurs points de jonction. Pour savoir quelle syntaxe de définition de coupe utiliser, il faut savoir sur quelle sorte de point de jonction on souhaite faire une coupe pour exécuter du code. Un point de jonction peut être une méthode. Le mot clé alors utilisé pour définir la coupe peut être call ou execution. Les coupes d’un aspect sont identifiées par un nom et des paramètres. Les paramètres sont des informations qui sont transmises de la coupe vers les codes advice qui l’utilisent. Selon le type de point de jonction, le mot clé à utiliser pour définir la coupe est différent. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 44 La syntaxe est résumée dans le tableau suivant [13]. Syntaxe call (methodeExpression) Execution (methodeExpression) Get (attributExpression) Get (attributExpression) Handler (exceptionExpression) Initialization (constanteExpression) Preinitialization (constanteExpression) Staticinitialization (classeExpression) Adviceexecution () Description du point de jonction Appel d'une méthode dont le nom vérifie methodeExpression. Exécution d'une méthode dont le nom vérifie methodeExpression. Lecture d'un attribut dont le nom vérifie attributExpression. Exemple : get(int Point.x) Ecriture d'un attribut dont le nom vérifie attributExpression. Exécution d'un bloc de récupération d'une exception (catch) dont le nom vérifie exceptionExpression. Exemple : Handler (IOException+) Exécution d'un constructeur de classe dont le nom vérifie constanteExpression. Exemple : Initialization (Customer.new (..)) Exécution d'un constructeur hérité dont le nom vérifie constanteExpression Exécution d'un bloc de code static dans une classe dont le nom vérifie classeExpression. Exemple : staticinitialization(Point) Exécution d'un code advice. Tableau II.1 - Tableau récapitulatif des points de jonction possibles et la syntaxe à utiliser pour définir la coupe 4.4 Codes Advices Le code advice définit le code greffé par l’aspect dans l’application d’origine. Dans le cas de code advice de type before (resp. after, around), celui-ci sera intégré avant (resp. après, autour) le pointcut de l’aspect. AspectJ introduit deux nouveaux types : after returning et after throwing. Ces deux types supplémentaires représentent respectivement le retour normal et anormal (avec levée d’exception) d’un point de jonction. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 45 Il existe trois modes d’exécution des advices : - before : le compilateur insère un appel à l’advice avant le code correspondant au pointcut. - after : le compilateur insère un appel à l’advice après le code correspondant au pointcut. - around : le compilateur insère un appel à l’advice à la place du code correspondant au pointcut et fournit au programmeur un moyen d’appeler le code original avec les arguments qu’il souhaite et de récupérer l’éventuelle valeur de retour. 4.5 Introspection de point de jonction Le terme introspection peut être interprété comme l’action d’inspecter à l’intérieur. Il s’agit d’obtenir des informations sur le point de jonction courant. Ce point de jonction peut correspondre à l’exécution d’une méthode ou d’un constructeur. A cet effet, certains outils de POA tels que AspectJ propose un mécanisme d’introspection de point de jonction. AspectJ permet d’utiliser le mot-clé thisJoinPoint pour référencer à un objet décrivant le point de jonction en cours. Ce mot-clé appartient à la classe prédéfinie org.aspectj.lang.JoinPoint. 4.6 Mécanisme d’introduction Il s’agit d’un mécanisme qui permettre d’étendre le comportement en ajoutant des éléments. Les catégories d’éléments peuvent êtres ajoutés concernent: l’attribut, la méthode, le constructeur, la classe héritée, l’interface implémentée et exception non conditionnelle à l’exécution d’un point de jonction [32]. AspectJ permet l’ajout de cinq types d’éléments : attribut, méthode, constructeur, classe héritée et interface implémentée L’introduction de ces éléments se déclare à l’intérieur d’un aspect. En réalité, l’aspect déclare des éléments pour le compte des classes. C’est ce que l’on dénomme une déclaration intertype. Le programmeur doit toutefois faire attention à ne pas introduire un élément déjà existant dans la classe visée sous peine de voir la compilation échouer. Bien évidemment, les éléments introduits ne peuvent être utilisés que par des aspects. L’application n’est pas supposée en connaitre l’existence. Cependant, il est quand même possible d’utiliser certains de ces éléments grâce au package java.lang.reflect. En effet, il est par exemple possible de récupérer toutes les méthodes d’une classe au runtime. Les méthodes introduites se retrouvent alors dans l’ensemble obtenu. Chapitre II : Présentation de la Programmation orientée aspect P a g e | 46 4.7 Héritage dans la POA AspectJ permet également de modifier la hiérarchie d’héritage des classes. A l’aide du motclé declare parents. AspectJ offre la possibilité de rendre une classe héritière d’une autre. Mais cette introduction n’est pas sans condition, il se peut en effet que la classe visée hérite déjà d’une surclasse. Dans ce cas, l’introduction ne sera possible que si la nouvelle surclasse est une sousclasse de l’ancienne surclasse. Le mécanisme d’introduction d’interface implémentée est similaire à l’introduction de classe héritée. Elle se réalise à l’aide du même mot-clé mais l’introduction d’interface implémentée est sans condition. Le code suivant montre un exemple de cinq types d’éléments du mécanisme d’introduction. 7. Conclusion Le développement applicatif et les méthodologies liées ont fortement évoluées tout au long de l’histoire de l’informatique. Le but recherché étant de pouvoir produire des logiciels répondant le mieux possible à la demande du client en moins de temps possible. La programmation orientée objet a su introduire de nouveaux concepts intéressants avec ce même objectif de produire des produits de qualité. Toutefois, nous pouvons encore trouver certaines limites à ces concepts que l’on aimerait pouvoir surpasser. Ce sont ces limites que la POA se propose de résoudre en introduisant les notions de séparation des préoccupations. [39] Nous avons présenté dans ce chapitre les principes concepts de la programmation orientée aspect, les points de jonction, les coupes, les codes advice, et le mécanisme d’introduction. Nous avons aussi présenté les concepts de langage AspectJ, support de notre travail de recherche. Comme notre travail consiste à propose une approche intégrant des techniques de persistance des données dans un seul aspect, nous avons choisi d’optimiser les performances des systèmes d’interrogation, avec l'utilisation d’un cache objet, qui va permettre de réduire les accès au support de persistance en conservant en mémoire les objets les plus fréquemment accédés. Chapitre III: La proposition P a g e | 47 Chapitre III Chapitre III: approche proposée 1 .Introduction Notre objectif principal est de définir une approche qui intègre de la persistance dans le domaine de la programmation orientée aspect afin de bénéficier des avantages de localisation, de réutilisation et de réduire le temps et le coût dans le développement et la maintenance des systèmes orientés aspects. Ces avantages sont considérés comme le but majeur de notre travail. Nous commençons tout d’abord par la présentation de notre stratégie sur la gestion du cache objet. Ce dernier constitue un espace mémoire pour le stockage d’objets, le mécanisme de gestion de cet espace permet de conserver en mémoire une partie des objets stockés sur le support de persistance. Lorsque l’application demande le chargement d’un objet, ce dernier est chargé depuis le support de persistance et stocké dans la mémoire cache. Si l’application accède ultérieurement à cet objet, la couche de persistance utilise la copie de l’objet en cache pour satisfaire la demande de l’application et ainsi évite un accès à la base de données. Au fur et à mesure de l’exécution, les objets chargés par l’application remplissent le cache. Plusieurs techniques existent pour gérer efficacement le cache : on peut choisir de ne conserver que les objets les plus récemment utilisés, ou bien les objets tels qu’ils sont référencés par l’application. L’utilisation d’un cache est toujours importante dans les systèmes informatiques, afin de fournir une haute qualité de service et notamment une amélioration des temps de réponse. En effet, un cache fournit un accès rapide aux données, pouvant tirer bénéfice de l’utilisation d’un support physique efficace, d’un ensemble d’éléments gérés plus petit autorisant des recherches plus rapides, ou encore d’un placement évitant des communications réseaux. De plus, employer un cache réduit la charge sur les serveurs et éventuellement sur les liens de communication, la récupération des éléments étant alors plus rapide. Ainsi un cache améliore généralement les performances, en dépit des coûts Chapitre III: La proposition de déploiement, d’administration, de gestion et cohérence qu’il peut engendrer. P a g e | 48 éventuellement des problèmes de La mise en cache d'objets offre plusieurs avantages avec un accès rapide aux données, mais il souffre également de certains inconvénients tels que la complexité de la synchronisation et la surcharge de la mémoire. Notre objectif est de proposer et étudier le problème de la synchronisation et de définir un modèle qui permet la gestion de la cohérence du contenu de la mémoire cache avec le serveur. Cette solution est valable pour les systèmes d’interrogation de données, notamment dans des contextes à grande. Pour réaliser ce travail on se base sur le cache sémantique et les déclencheurs (triggers). Un cache sémantique est utilisé pour conserver des résultats de requêtes, en maintenant une connaissance des données présentes, permettant des accès intelligents aux entrées stockées. Les applications de grande taille sont souvent difficiles à maintenir et à faire évoluer car elles contiennent un trop grand nombre de préoccupations emmêlées les unes aux autres. Les modèles de composants à base de conteneurs tels que les Enterprise Java Beans (EJB) ou le modèle de composant de CORBA (CCM) proposent une séparation des préoccupations. Les conteneurs sont chargés de réaliser des préoccupations telles que la gestion de persistance ou la sécurité, de façon transparente pour les composants. Ces préoccupations sont appelées des services non fonctionnels. Cependant, les conteneurs actuels ne gèrent qu'un nombre limité de services non fonctionnels, avec des sémantiques fixes. Avec l’approche Orientée aspect il est possible de rendre le cache indépendant de l’application. Dans notre approche, nous définirons un modèle qui facilite l’ajout de services non fonctionnels. Et nous exposons les avantages et les inconvénients de cette approche. On se base sur la programmation aspect et le patron de conception (Data Access Object DAO), ce dernier permet de séparer la couche d’accès aux données de la couche logique applicative. Son utilisation permet d’abstraire la façon dont les données sont stockées au niveau des objets métier. L’ensemble de ces objectifs nous amène à garantir l'orthogonalité de la persistance. Dans ce qui suit, nous décrivons la notion de cache sémantique. Nous terminons ce chapitre par la présentation d’un modèle de conception par aspects, en s’appuyant sur le patron de conception DAO. 2. Le cache sémantique Dans les systèmes de gestion de données repartis, deux types d’architectures existent: les architectures par rapatriement de données (data shipping) et les architectures par rapatriement de requêtes (query shipping). Avec la première approche, les données sont récupérées en terme d’objets ou de pages à l’aide de leur identifiant. On parle alors Chapitre III: La proposition P a g e | 49 d’accès navigationnel, les opérations étant exécutées du côté des clients. La seconde approche repose sur un accès associatif, les requêtes étant transférées et calculées sur les serveurs. Les caches sémantiques visent à fusionner ces deux architectures en proposant un travail d’évaluation local dans un système d’accès associatif. L’accès associatif évite de récupérer des données inutiles et le cache autorise un raisonnement fin sur son contenu, autorisant l’évaluation de requêtes sur les entrées stockées. Figure III.1 – Fonctionnement d’un cache sémantique Les caches sémantiques gèrent leur contenu (recherche, remplacement et résolution) en termes de résultats de requêtes. Dans la littérature, ces résultats sont appelés régions sémantiques ou segments Sémantiques. Le fonctionnement d’un cache sémantique est illustre par la Figure III.1 Quand une requête est posée, elle est décomposée en deux parties disjointes : la requête de consultation (probe query), qui récupère la portion de résultat disponible dans le cache, la requête restante (remainder query) utilisée pour récupérer tous les objets absents. Si la requête restante n’est pas nulle, autrement dit si celle-ci couvre une partie de l’espace sémantique qui n’est pas présent en cache, elle est envoyée au serveur pour être évaluée. 3. Stratégie de mise en cache Le cache de requêtes sauvegarde la requête Select conjointement avec le résultat qui a été envoyé au client. Les requêtes sont comparées avant l’analyse. Si une requête identique est reçue plus tard, le serveur récupère les résultats stockés dans le cache de requête plutôt que de l'analyser et l'exécuter à nouveau. Le cache de requête est partagé entre les sessions, un jeu de résultats généré par un client peut être envoyé en réponse à la même requête émise par un autre client. Le cache de requêtes est utile dans un environnement où les tables ne changent pas très souvent, et pour lesquelles le serveur reçoit plusieurs requêtes identiques. C'est une situation typique pour de nombreux serveurs Web qui génèrent beaucoup de pages dynamiques basées sur le contenu de base de données. Chapitre III: La proposition P a g e | 50 Chaque fois qu'une table est modifiée, le cache doit être vérifié, Si une table change (INSERT, UPDATE, DELETE, TRUNCATE, ALTER ou DROP TABLE|DATABASE), alors toutes les requêtes mises en cache qui utilisaient cette table deviennent obsolètes. Afin d’éviter l'invalidité du résultat de premier SELECT, on peut calculer le résultat comme suit : nous gardons les lignes récemment insérées dans des tables distincts appelées des tables temporaires, pour chaque table temporaire correspond une table de base de données. La valeur de chaque champ dans les lignes de la table temporaire est la même que si les lignes étaient présentes dans la principale table. A la réception d'une requête de sélection, le résultat de la requête finale est obtenu par la fusion de résultat de l’exécution de la requête sur la table temporaire avec les résultats de la requête en cache avant la modification de la base de données. Cette optimisation a des avantages potentiels d’interroger des résultats, qui autrement seraient invalides par des changements dans la base de données. La Figure III.2 montre un SELECT qui récupère un grand nombre de lignes (A) serait invalidé par une suite INSERT (∆T1). Figure III.2 – Le cache avant la modification de la table T1 En place les nouvelles lignes insérées (∆T1) dans une table temporaire, pour que nous évitions d'invalider le résultat de premier SELECT, on peut calculer le résultat rapidement par la fusion de la réponse mise en cache (A) avec la réponse (B) calculée à partir de la table temporaire (Figure III.3). Dans le cas d'une requête de mise à jour (update ou delete) l’exécution d'une requête nommée suit le m êm e processus comme INSERT. Chapitre III: La proposition P a g e | 51 Figure III.3 – Utilisation d’une table temporaire séparé pour les nouvelles insertions et réutiliser la réponse de la requête précédente (A) avec la réponse (B) Dans le cas d’une requête contenant des jointures de deux ou plusieurs tables, l’exécution de requête se déroule selon la formule suivante : Notre stratégie présente plusieurs avantages, elle réduit le coût d'une jointure, parce que les tables temporaires sont moins volumineuses, elle fournit des requêtes concurrentes permettant aux applications de sauvegarder les d'objets persistants et les résultats en mémoire pour les partager entre les unités d'exécution, ainsi que l'optimisation du temps de réponse. On ce qui concerne les tables temporaires on se base sur la notion de trigger (déclencheurs) pour historiser les modifications d'une table. Les déclencheurs sont donc des objets de la base qui nous permet d’automatiser des actions. La particularité des déclencheurs du LMD (Langage de Manipulation des données), ils vont nous permettre P a g e | 52 Chapitre III: La proposition d’effectuer une instruction, à chaque fois qu’une instruction du LMD est effectuée pour une table donnée. Pour mieux comprendre, prenons un exemple : nous avons deux tables T1 et sa table temporaire tA1. Nous définissons un déclencheur du LMD sur T1, qui fait après tout INSERT sur T1, le même insert est appliqué sur la table temporaire tA1 . C’est de cette manière que sont automatisées les actions sur les objets de la base. Pour une instruction donnée sur un objet, une action liée s’exécutera à la suite, ce qui perm et d’éviter de lire la table modifiée ligne par ligne pour chercher les m odifications faites sur la table. REQUETE SQL SELECT OUI NON Le Résultat de la requête Existe-t-il dans cache ? NON OUI Le serveur traite la requête Modification d’une table (insert, update...) Ecriture du résultat de la requête dans le cache Exécute la requête de sélection sur la table temporaire. Le résultat final de la requête est obtenu par la fusion de la réponse mise en cache avec la réponse calculée à partir de la table temporaire Envoi du résultat de la requête Figure III.4 – Processus de mise en cache les requête P a g e | 53 Chapitre III: La proposition 3.2 Intérêt de mettre la fonction de mise en cache comme aspect La mise en cache d'objets offre plusieurs avantages tels qu’un accès rapide aux données. Mais elle possède aussi des inconvénients comme surcharge de la mémoire et la complexité de synchronisation. La mise en œuvre en tant qu'aspect fournissant la possibilité d'ajouter dynamiquement le code du cache à l’application. Nous pouvons également désactiver la mise en cache chaque fois qu'il devient un goulot d'étranglement en termes d'utilisation de la mémoire. La mise en cache d'objet possède des caractéristiques qui en font un candidat de choix pour la mise en œuvre en tant qu’un aspect. Certaines de ces caractéristiques sont: Code de la mise en cache est souvent dupliqué dans une application. Il n'est pas facile de tourner ou de désactiver la mise en cache dynamique lorsqu’elle est une partie du logique métier. La mise en cache n'est pas liée au domaine métier. Le tableau suivant résume les étapes à suivre pour faire une demande d'accès aux données avec et sans mise en œuvre d'un cache en tant qu’aspect : Accès aux données Type de mise en cache Première demande Scénario 1 Scénario 2 Scénario 3 Pas de mise en œuvre mise en œuvre d'un mise en œuvre d'un cache en d'un cache cache traditionnel en tant qu’aspect Récupérer les données 1. Vérifier si les 1. Intercepter l'accès aux de la base et remettre les données disponibles données à l'aide d'un résultats au client. dans le cache Pointcut. 2. Aucune 2. Utilisation des codes advice correspondance n'est de type around. trouvée 3. Vérifier si les données 3. Récupérer des disponibles dans le cache données à partir de 4. Aucune correspondance n'est la base trouvée alors appeler la 4. Stocker le résultat méthode PROCEED () qui à dans le cache et son tour exécute la méthode de renvoyer les chargement de données. résultats au client. 5. Stocker le résultat dans le cache, remettre le résultat au client. Les demandes Récupérer les données 1. vérifier si elles sont 1. Intercepter l'accès aux ultérieures de la base à nouveau et disponibles dans le données à l'aide d'un P a g e | 54 Chapitre III: La proposition renvoyer le résultat au client. logique de Aucune. mise en cache Non Modifier le code source cache. 2. Retourner les données trouvées dans le cache au client. 3. Si les données dans le cache sont obsolètes, récupérer les données de nouveau depuis la base de données. 4. Stocker le résultat dans le cache et le remettre au client. Pointcut. 2. Vérifier si les données sont dans le cache en utilisant les codes advice de type around. 3. Une correspondance est trouvée, renvoyer le contenue du cache au client. Intégré dans le code Encapsulée dans un aspect. d'application. Oui (changements dans Non (complètement séparé du le code d'application code de l'application. Mise en sont nécessaires pour cache est dynamiquement tissée. activer ou désactiver la dans l'application) mise en cache) Sur la base des exigences de la mise en cache comme un aspect, on fixe des objectifs suivant : 1. Éviter de dupliquer la logique de mise en cache à chaque point d'exécution dans l’application. 2. Écrire le code lié au cache d'une manière non intrusive, afin de ne pas affecter le code de l’application. La logique de la mise en cache n'a rien à voir avec les fonctionnalités de base de l'application. Elle est utilisée principalement pour des raisons de performances. 3. La possibilité de commuter entre les scénarios de fonctionnement de l'application avec et sans mise en cache, afin d'évaluer l'efficacité de la mise en cache dans l'accès aux données. Si la mise en cache ne s'avère pas vraiment efficace, nous nous sommes pas obligés de modifier le code source pour supprimer la logique de mise en cache. Cette approche est très utile lors de la phase de test unitaire où nous voulons tester la fonctionnalité de mise en cache avec des paramètres différents pour arriver au meilleur scénario. 4. La flexibilité de basculer facilement ou remplacer la mise en œuvre d'un cache avec une autre sans affecter le code d'application. Il y a beaucoup de différents algorithmes et paramètres de configuration pour obtenir la configuration optimale et les meilleurs résultats de la mise en œuvre d'un cache. P a g e | 55 Chapitre III: La proposition 5. La capacité de retracer et de suivre les statistiques de mise en cache pour une meilleure gestion des objets stockés dans le cache. Nous devons constamment surveiller l'utilisation du cache pour déterminer son efficacité et faire les ajustements nécessaires. En outre, comme les exigences opérationnelles et les changements au fil du temps des fonctionnalités de l'application sont primordiaux, nous devons nous assurer de stocker que les données fréquemment demandées dans le cache. Pour consolider l’application et arriver à une bonne modulation, nous avons mis en œuvre le Patron de conception (ou pattern) Data Access Object qui permet de séparer la couche d’accès aux données de la couche logique applicative. Son utilisation permet de s’abstraire de la façon dont les données sont stockées au niveau des objets métier. Utilisate ur Couche interface utilisateur Couche métier Couche d'accès aux Données Données (DAO) Application Figure III.5 – le Patron de conception DAO Le Data Access Object (DAO) vise à faciliter le travail avec les technologies d'accès aux données, comme JDBC, Hibernate ou JDO d'une manière cohérente. Cela permet de basculer entre les techniques de persistance aisément et aussi à coder sans se soucier de la capture des exceptions qui sont spécifiques à chaque technologie. Les opérations de base pour la persistance des données sont toutes identiques quel que soit l’objet à sauvegarder, les méthodes en question sont celles d’un CRUD : enregistrement (Create), lecture (Read), mise à jour (Update) et suppression (Delete). La plupart du temps ces méthodes sont répétées et redéfinies dans chacun des DAO de l’application, le travail repose sur les méthodes de la classe DAO pour localiser les points de tissages (pointcut) afin d’introduire le système cache. 4 .Conclusion Le but de ce chapitre est la présentation de la proposition. Dont nous décrivons de façon détaillée notre approche qui consiste à utiliser les résultats d'une requête placés dans le cache pour calculer le résultat de la requête. Cette méthode permet d'améliorer la performance. Notre méthode de conception repose sur l'approche orientée aspect pour développer une architecture faiblement couplée et distribuée et d'assurer l'indépendance entre le code lié au cache et le code métier. Chapitre I IV: Implémentation P a g e | 56 Chapitre IV Chapitre IV: Etude de cas 1. Introduction La représentation théorique de notre approche consistait à résoudre le problème de la gestion cohérente du contenu du cache avec le serveur, ainsi que le problème de dispersion et d’enchevêtrer du code. Dans cette section, nous essayons de développer un aspect de mise en cache reposant sur Aspectj et Framework cacheonix. Ce dernier est un cache Open Source distribué. Il permet aux organisations de développement de logiciel de gérer leurs applications métier et de répondre à leurs besoins sur le plan de performance. Avant de décrire notre aspect de mise en cache, nous devons écrire une classe DAO qui encapsule les accès aux bases de données telles que la connexion JDBS, cette classe est nommé aspect_SIO. L’intérêt de cette classe est d’abstraire au maximum des appels techniques, qui représentent principalement la gestion des exceptions et la séquence de connexion et de fermeture d’une session normale qui suit une exception. Afin de valider notre solution. Nous essayons d’implémenter un logiciel de gestion de budget d’état. Ce système permet de gérer les opérations inscrites sur les programmes au titre des différents budgets (programme communal développement PCD, programme sectoriel développement PSD). La figure IV.1 présente un diagramme de classe simplifié représentant l’application. P a g e | 57 Chapitre I IV: Implémentation NUMERO DE L’OPERATION REALISATEURS Crédit paiements par chapitres PLAN 1 CODE DE CHAPITRE PROGRAMME INTITULE DE CHAPITRE FINANCEMENT EXERCICE CHAPITRE PROGRAMME ARTICLE CP INITIAL CODE DE GESTION CP FINAL INDICATIF CONSOMMATION Etat d’avancement du projet MAITRE DE L’OUVRAGE NATURE JURIDIQUE 1 1 ANNEE N°SEQUENTIEL N IDENTIFICATION FISCAL MAITRE DE L’ŒUVRE BUDGET DE L’ETAT OBSERVATIONS N NON ET PRENOM DU GERANT DEGRE DE QUALIFICATION N° REGISTRE DE COMMERCE MODE DE PASSATION INTITULE DE L’OPERATION COMMUNE/DIRECTION IMPACT DU PROJET MONTANT AP INITIAL LIBELLE DE LOTS MONTANT AP FINAL DELAI CONSOMMATION CUMULEES UNITE TAUX REALISATION PHYSIQUE NOMBRE DE FOYERS BENEFICIAIRES EMPLOIS CREES TEMPORAIRES EMPLOIS CREES PERMANENTS SITUATION D’OPERATION Figure IV.1 –Diagramme de classe simplifié de l’application gestion de budget d’état 2. coupes relatives à la persistance Le composant d’aspect de cache permet de définir les coupes liées à la persistance ainsi que les codes advice qui y sont attachés. Nous définissons les coupes suivantes : Un point de jon Get_Cache désigne les points où la méthode Find_Requet de classe ASPECT_SIO est invoquée .le code advice de type around associé à cette coupe exécute son code avant l’exécution de la méthode Find_Requet pour interroger le cache et voir si le résultat de la requête est disponible dans le cache et détermine si la table a été changé. S'il ya un défaut de cache le advice utilise proceed() pour déclencher l’exécution du point jonction. Chapitre I IV: Implémentation P a g e | 58 Un coup pour sélectionner toutes les exécutions de méthode Find_joint_Requet, qui permet de gérer une requête avec jointure, et la sauvegardé ou la suppression d’un objet persistant. L’extrait de code présente l’implémentation, en langage AspectJ, de l’aspect de mise en cache package SIO; Import java.sql.Connection; // Réalise la connexion et l’authentification à la BD Import java.sql.DriverManager; // Charge et configure le pilote de la BD Import java.sql.PreparedStatement; // Contient la requête SQL et la transmet à la BD Import java.sql.ResultSet; // Permet de parcourir les informations retournées par la BD Import java.sql.SQLException; // Gestion des erreurs SQL Import java.sql.Timestamp; Import java.text.SimpleDateFormat; Import java.util.ArrayList; Import java.util.Date; Import java.util.Iterator; Import java.util.List; Import cacheonix.Cacheonix; /** Cacheonix Framework fournit Import cacheonix.cache.Cache; des fonctionnalités de mise en cache **/ public aspect aspect_Cache_modifie { pointcut Get_Cache( String dat , String query): args ( dat , query)&&(execution(* aspect_SIO.Find_table(..))); pointcut Get_Cache( String dat , String query ): args (dat , query)&&(execution(* aspect_SIO. Find_Requet (..))); /** Code advice pour mise en cache. */ Resuqute_resulta around (String dat, String query): Get_Cache (dat , query ) { if(Activer_cahe) /** verifier si le cache est activé */ { /** Déclaration de variables */ /**Obtenir le résultat du cache */ final Cache<key_requete, resuqute_resulta> queryCache = cacheonix.getInstance().getCache("ASPECT_PERSISTANCE"); final key_requete queryKey = new key_requete(query, queryParameters); queryResult =(resuqute_resulta) queryCache.get(queryKey); if (queryResult!= null ) /** verifier si le resultat en cache */ { /** Le résultat de la requête est en cache */ /** Chercher le temps de dernière modification de la table * et comparer avec le temps de conserver le résultat d'une requête en cache. */ if (résultat du cache est modifié) /** vérifie si un cache de résultat valide*/ { P a g e | 59 Chapitre I IV: Implémentation /**le résultat dans le cache mais avec une modification sur la table */ /** calculer le résultat de la requête par la fusion de la réponse mise en cache /* * avec la réponse calculée à partir de la table temporaire */ long stop = System.currentTimeMillis(); \** le délai de traitement System.out.print ("\n délai de traitement : "+ (stop – start )+"" ); de la requite*/ } e ls e { /**Le résultat dans le cache mais sans modification faite sur une table */ } return queryResult; } /** Le résultat de la requête n'est pas mis en cache * proceed permet l'exécution du point jonction (méthode ASPECT_SIO) */ queryResult = proceed ( dat,query); /* Calculer le résultat par le point de jonction */ queryCache.put (queryKey, queryResult); /** mettre le résultat en cache */ long stop = System.currentTimeMillis(); System.out.print ("\n time final : "+ (stop - start )+"" ); return queryResult; } } \** le délai de traitement de la requite*/ Cet extrait de code, est une partie de l’aspect (aspect_cache_modifié), qui déclare qu’un advice est invoqué avant et après chaque exécution de la méthode Find_Requet (* aspect_SIO. Find_Requet (..)) De la classe aspect_SIO, l’aspect (aspect_cache_modifié) déclare un greffon de type around. L’instruction proceed permet de revenir à l’exécution du programme, autrement dit l’exécution du point jonction. En premier lieu, le code advice vérifie si un cache de résultat valide est disponible en mémoire. S’il est, le résultat est récupéré du cache directement. Par contre, si le résultat de la requête est disponible dans le cache mais non valide, le résultat est calculé suivant notre proposition. Si le résultat n'est pas disponible, la requête est exécutée normalement. Puis la réponse est adressée au client. Après l’exécution du point de jonction, le résultat est sauvegardé en cache, et finalement l’exécution du programme reprend juste après le point de jonction. A cet effet, nous pouvons constater que cette application est complètement indépendante du code de mise en cache. P a g e | 60 Chapitre I IV: Implémentation 3. Résultats du test Le résultat produit par l’exécution de cette application avec cache et sans cache, ainsi après la modification dans les tables de la base de données. Première demande Les demandes ultérieures Les demandes ultérieures (après modification dans les tables) Sans mise en cache (ms) Avec la mise en cache (ms 587 571 530 657 31 35 Nous avons observé la rapidité d'accès aux données dû à l’utilisation d’un cache pour stocker les requêtes fréquemment utilisées. Il a fallu peu de temps pour obtenir des données mises en cache, par rapport au temps qu’il dispose pour l'obtenir à partir d’une base de données (31 millisecondes avec mise en cache par rapport à 571 millisecondes sans mise en cache). Suite à notre proposition, nous avons constaté dans le cas où une table d'une base de données a été modifiée par une requête d’insertion, le délai entre une requête d'un client et la réponse du serveur est court. Un avantage d'utiliser un aspect par rapport à la programmation traditionnelle orientée objet, c’est que l'ajout ou le retrait de l'aspect peut être parfaitement fait sans aucun impact sur le code d'application. Un outil tel qu’Aspect browser permet d'analyser le phénomène de dispersion du code. En fournissant une vue graphique la Figure IV.2 montre ce phénomène avant et après la mise en cache comme un aspect, les rectangles verticaux symbolisent les classes de notre application, et les traits les lignes de code utilisant L'API de gestion de cache, comme nous le constatons, les traces sont utilisées à nombreux endroits. La fonctionnalité de gestion de cache est entièrement dispersée. Figure IV.2 – Dispersion du code de mise en cache Chapitre I IV: Implémentation P a g e | 61 Par contre, la Figure IV.3 montre que l'appel à l'API de cache est regroupé dans un aspect, est donc bien assurer la maintenance et à l'évolutivité d'application. Nous avons atteint nos objectifs suivant : Une architecture faiblement couplée. La séparation de la logique. L’optimisation des performances d'accès aux données. Figure IV.3 – Impact d’un aspect sur la localisation du code de cache 4. Conclusion et perspectives Dans ce mémoire, nous nous sommes intéressés à l'introduction de la persistance dans un langage orientés aspect. En particulier, l’objectif et d’aborder les problèmes de gestion de la cohérence du contenu de cache avec le serveur, à l’aide des technologies orientés aspect. Aussi de trouver une meilleure structuration des programmes, notamment au travers de l’encapsulation et de la séparation des préoccupations transversales au découpage des applications en termes de classes La programmation orientée aspect est un nouveau paradigme qui permet la production d ‘un code métier découplé du code non-fonctionnel. C’est une solution aux problèmes de mélange et de dispersion du code des propriétés non fonctionnelles rencontrés avec la programmation par objets, elle consiste à séparer et découpler leurs définitions comme le veut le principe de la séparation des préoccupations dans le but de permettre une meilleure réutilisation. Deux sortes de perspectives sont envisageables après un travail comme celui-ci. Les premières concernent la gestion du cache. Les solutions habituellement utilisées sur la base d’une gestion statique par tuples ou pages, c'est-à-dire on stocke des données qui sont plus proches de la réponse d’une requête en termes de localité dans la base de données. On peut réaliser une gestion pareille suivant des solutions qui reposent sur les Chapitre I IV: Implémentation P a g e | 62 sémantiques. L’autre classe de perspectives est de créer un système de mise en cache pour pouvoir fonctionner et réagir de manière autonome aux changements de son environnement. Ainsi, il convient d’étendre les travaux sur la gestion de caches à la gestion de l’environnement d’exécution, Afin d’assurer une bonne qualité de service. Référence bibliographique P a g e | 63 Référence bibliographique [1]:D. L. Parnas. “On the Criteria for Decomposing Systems into modules". Communication of the ACM, 15(12) pp: 1053-1058, Decembre 1972. [2]: M.P. Atkinson, P. Bailey, K.J. Chisholm, W.P. Cockshott, R. Morrison, “An approach to persistent programming”, the computer journal, Vol. 26 N°4, November 1983, pp. 360-365, also in [3] pp. 141-146. [3]: S.B. Zdonik, D. Maier, “readings in object-oriented database systems”, eds. S.B. Zdonik and D. Maier, 1990. [4]: M.P. Atkinson,”Persistent architectures”, proc. third international workshop on persistent object systems, eds. J. Rosenberg and D. Koch, Newcastle, Australia, January 1989. [5]: M. Atkinson, “Questioning persistent types”, proc. of the 2nd workshop on database programming languages, eds R. Hull, R. Morrison, D. Stemple, Morgan Kaufmann publishers Inc., 1989, pp. 2-24. [06] : Guillaume Dufrêne, Simon Morvan, ”La programmation Orientée Aspects AspectJ et JAC”. [07] : Antoine MAROT,” L’animation d’algorithmes en programmation orientée aspect” 2006. [08] : Ferut Térence, Leroy Sébastien,” La programmation Orientée Aspects”, Juin 2004. [09] : S. Soares, E. Laureano, and P. Borba. “Implementing distribution and persistence aspects with aspect”. In OOPSLA '02: Proceedings of the annual ACM SIG-PLAN conference on Object oriented programming, systems, languages, and applications, pages 174-190, Seattle, Washington, USA, November 2002. ACM Press. [10]: E. Gamma, R. Helm, R. Johnson, and J. Vlissides. “Design Patterns: Elements of Reusable Object-Oriented Software”. Addison-Wesley, 1994. ISBN 0-20163- 361-2. [11]: SUN. Java Object Serialization Specification. Sun Microsystems, Inc., 2006b. URL http://java.sun.com./javase/6/docs/technotes/guides/serializeation /index.html. [12]: A. Rashid and R. Chitchyan.” Persistence as an aspect”. In AOSD '03: Proceedings of the International Conference on Aspect-Oriented Software Development, pages 120-129, Boston, MA, USA, March 2003. ACM Press. [13]: R. Pawlak, L. Seinturier, L. Duchien, G. Florin, F. Legond-Aubry, and L. Martelli. Jac: “an aspect-based distributed dynamic framework”. Software - Practice and Experience (SPE), 34(12):1119-1148, 2004. [14]: K. Yagoub, D. Florescu, V. Issarny, and P. Valduriez. “Caching strategies for data-intensive web sites”. In Proceedings of the 26th International Conference on Very Large Databases, pages 188–199, Sept. 2000. Référence bibliographique P a g e | 64 [15]: J. Challenger, A. Iyengar, and P. Dantzig. “A scalable system for consistently caching dynamic web data”. In Proceedings of IEEE INFOCOM’99, pages 294–303, Mar. 1999. [16]: A. Iyengar and J. Challenger. “Improving web server performance by caching dynamic data”. Dec. 1997. [17] : Renaud pawlak, jean-philippe Retaillé, lionel seinturier, Programmation orientée aspect pour Java/J2EE , 2004. [18] : Thibaut Schorderet, thèse :”Compiere Installation et adaptation avec Hibernate ”, Septembre 2005. [19]: E. Krasner, P. Glenn, and T. Stephen. “A cookbook for using the model-view controller user interface paradigm in smalltalk-80”. Journal of Object-Oriented Programming, 3(1): 26–49, 1988. [20] Denanot, Laurent. «Maîtriser la persistance objet métierau sein d’une architecture J2EE.» avril 2003. [21] : Allouch Bachir, Trantoul Gilles, “Les nouvelles formes de programmation“, juin 2003 [22] : Thomas GIL, “ la Conception Orientée Aspects 2.1 ”, 21 janvier 2006. [23] : Hondjack DEHAINSALA, thèse :” Explicitation de la sémantique dans les bases de données : Base de données à base ontologique et le modèle OntoDB ”, Avril 2002. [24] : Laurent D’ORAZIO, thèse :” Caches adaptables et applications aux systèmes de gestion de données répartis à grande échelle”, décembre 2007. [25]: K. Mens, C. Lopes, B. Tekinerdogan, and G. Kiczales. “Aspect-oriented programming workshop report. In ECOOP Workshops”, volume 1357 of LNCS, pages 483{496, Jyvaskyla, Finland, June 1997. Springer-Verlag. [26]: A. Rashid. On to aspect persistence. In GCSE " International Symposium on Generative and Component-Based Software Engineering ” , volume 2177 of Lecture Notes in Computer Science, pages 26-36, Erfurt, Germany, October 2000. Springer. [27] : Assia AIT ALI SLIMANE, Muhammad Usman BHATTI, ” Utilisation des services et des aspects pour la réutilisabilité du logiciel d’un automate pour l’analyse de plasma”. [28] : NGUYEN Manh Tien, Programmation Orientée Aspect, Juillet 2005. [29] : François EXERTIER, thèse :” Extension orientée objet d’un SGBD relationnel ”, décembre 1991. [30] : Ouafa Hachani, thèse :”Patrons de conception a base d’aspects pour l’ingénierie des systèmes d’information par réutilisation ”,2006. [31] : Sébastien Ros : ”Livre blanc sur le Mapping objet-relationnel, la couche d’accès aux données et les Frameworks de persistance ”, Février 2003. Référence bibliographique P a g e | 65 [32]: G. Kiczales, E. Hilsdale, J. Hugunin, M. Kersten, J. Palm et W.G. Griswold ’’An Overview of AspectJ”. In Proceedings of ECOOP 2001, European Conference on Object- Oriented Programming, Budapest, Hungary, LNCS, vol. 2072, Springer, pp. 327-353, 2001. [33] : Sophie LEVY,” Programmation on Orientée Aspect avec Aspectj ” , Février 2006. [34] : W. Rahayu, E. Chang, and T. Dillon. “A methodology for the design of relational databases from object-oriented conceptual models incorporating collection types “. In 18th Internat. Conf. on Technology of Object-oriented Languages and Systems. Prentice-Hall, 1995. [35]: W. Rahayu, E. Chang, T. Chang, and D. Taniar. “Aggregation versus association in object modelling and databases ”. In Australian Conf. on Information Systems, 1996. [36]:W. Rahayu, E. Chang, and T. Dillon. “Implementation of object-oriented association relationships in relational databases ”. In IDEAS, pages 254–263, 1998. [37]: W. Rahayu, E. Chang, and T. Dillon.” Representation of multi-level composite objects in relational databases”. In Internat. Conf. on Object-oriented Information Systems OOIS’98. Springer, 1998. [38]: C. Soutou. “Modeling relationships in objectrelational databases ”. Data and Knowledge Engineering, 36(1):79–107, 2001. [39]: http://en.wikipedia.org/wiki/Aspect-oriented_software_development [40]: P. Eric, J. Rahayu, and D. Taniar. “Mapping methods and query for aggregation and association in object-relational database using collection”. In ITCC ’04: Proceedings of the International Conference on Information Technology: Coding and Computing (ITCC’04) Volume 2, pages 539–543. IEEE Computer Society, 2004.