Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Seconde partie Un exemple de programmation orientée Aspect avec AspectJ
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC AspectJ est un langage aspects, points de jointure, advices, … sont des extensions syntaxiques de Java une approche par transformation de code compilateur ajc (remplace javac) => nécessite le code source les.class générés par ajc sont compatibles avec toute JVM statique => performant mais peu flexible supporté par des IDE (emacs, JBuilder, Forte 4J, Eclipse) open source et gratuit
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Un éditeur de figure operations that move elements factory methods Display * 2 Point getX() getY() setX(int) setY(int) moveBy(int, int) Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Figure makePoint(..) makeLine(..) FigureElement moveBy(int, int)
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Un éditeur de figure class Line implements FigureElement{ private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) {... } } class Point implements FigureElement { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) {... } }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC a Line a Point Où sont les points de jointure (join points) ? returning or throwing dispatch a method call returning or throwing a method execution returning or throwing a method execution Un appel : l.moveBy(2, 2)
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Les coupes (pointcuts) Un moyen didentifier un ensemble de points de jointure Une coupe est un prédicat sur les points de jointure Primitives de coupes disponibles : call, execution initialization, preinitialization, staticinitialization get, set handler within, withincode cflow, cflowbelow this, target, args...
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Combinaison des primitives de coupe or a void Line.setP2(Point) call a void Line.setP1(Point) call call(void Line.setP1(Point)) || call(void Line.setP2(Point)); Utilisation de &&, || et ! comme pour les prédicats
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Type dadvices Des actions supplémentaires à faire lorsquune coupe est identifiée before pour ajouter des actions avant after pour ajouter des actions après after returning sur retour normal after throwing sur retour avec erreur around pour prendre le control sur lexécution du point de jointure
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Coupe anonyme ou non dans un advice pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)); Nom de coupe Paramètres de coupe after() returning: call(void Line.setP1(Point)) || call(void Line.setP2(Point)) { Display.update(); } after() returning: move() { Display.update(); } Coupe anonyme Utilisation de la coupe définie
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Exemple daspect : Rafraîchissement dimage collection of figure elements that move periodically must refresh the display as needed complex collection asynchronous events we will initially assume just a single display
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Rafraîchissement sans et avec AspectJ class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(); } void setP2(Point p2) { this.p2 = p2; Display.update(); } aspect DisplayUpdating { pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)); after() returning: move() { Display.update(); } DisplayUpdatingV1
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Les coupes peuvent concernées plusieurs classes aspect DisplayUpdating { pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)); after() returning: move() { Display.update(); } class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(); } void setP2(Point p2) { this.p2 = p2; Display.update(); } void moveBy(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; Display.update(); } void setY(int y) { this.y = y; Display.update(); } void moveBy(int dx, int dy) { … } } DisplayUpdatingV2
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Primitive de coupe this/target this/target(VarName | TypeName) permet de : - tester dynamiquement le type de lobjet courant/de la cible (restreint la coupe) this(Point), target(Line) - récupérer lobjet courant/la cible du point de jointure en cours (ne restreint pas la coupe) this(myLine), target(myPoint) Valeur de variable extraite de la droite vers la gauche du : du target vers la coupe définie par lutilisateur de la coupe vers ladvice, et donc dans le corps de ladvice pointcut move(Line l): target(l) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point))); after(Line line) returning: move(line) {... }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Gestion du contexte utilisant target aspect DisplayUpdating { pointcut move(FigureElement figElt): target(figElt) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); after(FigureElement fe) returning: move(fe) { Display.update(fe); } figElt est lié à FigureElement dans la coupe move fe est lié à FigureElement dans ladvice DisplayUpdatingV3
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Primitive de coupe args aspect PointBoundsPreCondition { before(int newX): call(void Point.setX(int)) && args(newX) { assert newX >= MIN_X; assert newX <= MAX_X; } before(int newY): call(void Point.setY(int)) && args(newY) { assert newY >= MIN_Y; assert newY <= MAX_Y; } aspect PointBoundsPostCondition { after(Point p, int newX) returning: call(void Point.setX(int)) && target(p) && args(newX) { assert p.getX() == newX; } after(Point p, int newY) returning: call(void Point.setY(int)) && target(p) && args(newY) { assert p.getY() == newY; } Vérification de contrats Même principe que target et this mais pour les paramètres du point de jointure
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Méthode spéciale proceed Utilisée dans les advices de type around ReturnType around(T1 arg1, T2 arg2, …) La méthode proceed ReturnType proceed(T1, T2, …) Permet dexécuter ce qui aurait été exécuté si ladvice navait pas été défini Même type de paramètres et même type de retour
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Méthode spéciale proceed aspect PointBoundsEnforcement { void around(int newX): call(void Point.setX(int)) && args(newX) { proceed( clip(newX, MIN_X, MAX_X) ); } void around(int newY): call(void Point.setY(int)) && args(newY) { proceed( clip(newY, MIN_Y, MAX_Y) ); } private int clip(int val, int min, int max) { return Math.max(min, Math.min(max, val)); } Gestion des bornes
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Déclaration de méthodes et dattributs aspect PointCaching { private MyLookupTable cache = new MyLookupTable(); Point around(int x, int y): call(Point.new(int, int)) && args(x, y) { Point ret = cache.lookup(x, y); if (ret == null) { ret = proceed(x, y); cache.add(x, y, ret); } return ret; } Gestion dun cache
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC aspect DisplayUpdating { private Display FigureElement.display; static void setDisplay(FigureElement fe, Display d) { fe.display = d; } pointcut move(FigureElement figElt): ; after(FigureElement fe): move(fe) { fe.display.update(); } Un afficheur par element de figure DisplayUpdating v4 display est un attribut dans les objets de type FigureElement, mais : appartient à laspect DisplayUpdating DisplayUpdating doit fournir getter/setter (appelé dans le code de setup) Déclaration de méthodes et dattributs
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Généricité des coupes target(Point) target(graphics.geom.Point) target(graphics.geom.*) tout type dans graphics.geom target(graphics..*) tout type dans un sous-package de graphics call(void Point.setX(int)) call(public * Point.*(..)) toute méthode publique de Point call(public * *(..)) toute méthode publique de nimporte quel type call(void Point.setX(int)) call(void Point.setY(*)) call(void Point.set*(*)) call(void set*(*)) tout mutateur call(Point.new(int, int)) call(new(..)) tout constructeur
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Access réflexif au point de jointure courant Disponible dans les advices thisJoinPoint.getSignature(), thisJoinPoint.getArgs(),... aspect PublicErrorLogging { Logger log = Logger.global; pointcut publicInterface(): call(public * graphics..*.*(..)); after() throwing (Error e): publicInterface() { Method m = thisJoinPoint.getSignature(); log.throwing( m.getDeclaringType().getName(), m.getName(), e); } thisJoinPoint permet de savoir où ladvice sexécute
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Initialization/ preinitialization/ staticinitialization ( Pointcut ) initialization(Foo.new(int)) linitialisation qui débute avec le constructeur Foo.new(int) preinitialization(Foo.new(int)) comme initialisation mais avant que le constructeur de super soit appelé staticinitialization(Foo.new(int)) quand le type Foo est initialisé après le chargement de la classe Dautres primitives de coupes : contrôle des initialisations
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Dautres primitives de coupes within( TypeName ) withincode( MemberSignature ) Tout point de jointure tel que : - le code en cours dexécution est défini dans le type TypeName - le code en cours dexécution est défini dans la méthode ou contructeur dont la signature est MemberSignature get( int Point.x ) set( int Point.x ) Accès à un attribut en lecture/écriture
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Exemple avec withincode : gestion des constructions de figures class Figure { public Line makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... }... } aspect FactoryEnforcement { pointcut illegalNewFigElt(): (call(Point.new(..)) || call(Line.new(..))) && !withincode(* Figure.make*(..)); before(): illegalNewFigElt() { throw new Error("Use factory method instead."); } Assurer que la création dune figure se fait uniquement en utilisant les méthodes de fabrique Erreur dexécution
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC class Figure { public Line makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... }... } aspect FactoryEnforcement { pointcut illegalNewFigElt(): (call(Point.new(..)) || call(Line.new(..))) && !withincode(* Figure.make*(..)); declare error: illegalNewFigElt(): "Use factory method instead."; } Erreur de compilation Assurer que la création dune figure se fait uniquement en utilisant les méthodes de fabrique Exemple avec withincode : gestion des constructions de figures
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC class Figure { public Line makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... }... } aspect FactoryEnforcement { pointcut illegalNewFigElt(): call(FigureElement+.new(..)) && !withincode(* Figure.make*(..)); declare error: illegalNewFigElt(): "Use factory method instead."; } Erreur de compilation Tous les sous-types Assurer que la création dune figure se fait uniquement en utilisant les méthodes de fabrique Exemple avec withincode : gestion des constructions de figures
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC all join points on this slide are within the control flow of this join point a Point a Line a Point Flot de contrôle et point de jointure Un appel : l.moveBy(2, 2)
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC cflow( Pointcut ) tous les points de jointure dans le flot de contrôle du point de jointure Pointcut cflowbelow( Pointcut ) tous les points de jointure au dessous du flot de contrôle du point de jointure Pointcut Dautres primitives de coupes
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Exemple avec cflowbelow : contrôle des mouvements de plus haut niveau (top-level) DisplayUpdating v5 aspect DisplayUpdating { pointcut move(FigureElement fe): target(fe) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); pointcut topLevelMove(FigureElement fe): move(fe) && !cflowbelow(move(FigureElement)); after(FigureElement fe) returning: topLevelMove(fe) { Display.update(fe); }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Composition daspects Quest ce quil se passe si 2 « advices » sont appliqués au même point de jointure ? aspect Security { before(): call(public *(..)) { if (!Policy.isAllowed( thisJoinPoint )) throw new SecurityExn(); } } aspect Logging { before(): logged() { System.err.println("Entering " + thisJoinPoint ); } pointcut logged(): call(void troublesomeMethod()); }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Composition daspects Ordre indéfini sauf dans un même aspect, dans les sous aspects, en déclarent une règle de précédence aspect Security { before(): call(public *(..)) { if (!Policy.isAllowed( thisJoinPoint )) throw new SecurityException(); } declare precedence: Security, *; } aspect Logging { pointcut logged(): call(void troublesomeMethod()); before(): logged() { System.err.println("Entering " + thisJoinPoint ); }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Réutilisation daspects : aspects abstraits et héritage abstract aspect Tracing { abstract pointcut trace(); before(): trace() { TraceSupport.traceEntry(thisJoinPoint); } after(): trace() { TraceSupport.traceExit(thisJoinPoint); } aspect PointTracing { pointcut trace(): within(Point) && execution(* *(..)); before(): trace() { TraceSupport.traceEntry(thisJoinPoint); } after(): trace() { TraceSupport.traceExit(thisJoinPoint); } } aspect PointTracing extends Tracing { pointcut trace(): within(Point) && execution(* *(..)); }
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC plug in: unplug: Plug & debug ajc Point.java Line.java TraceSupport.java Tracing.java Activer/désactiver la trace sans éditer les classes Pas de coût dexécution quand désactivé
Séparation des préoccupations (c) 2004, Audrey Occello, LF8 MOC Conclusion Bonne modularité Code + naturel, + petit, - mélangé Maintenance et évolution + facile application + facile à comprendre, à débugger, à changer Réutilisation facilitée ajout/retrait daspects aspects abstraits