Programación Orientada a Aspectos AspectJ y otras plataformas de desarrollo orientadas a aspectos Abait, Esteban S. Agenda Motivación AspectJ Ejemplos en AspectJ Otras plataformas AOP Recursos y Referencias Motivación - Modularización Concerns que dada su naturaleza crosscutting son imposibles de modularizar mediante el uso de la programación orientada a objetos. Ejemplos: Logging (típico), persistencia, sincronización, etc. Motivación - Consecuencias Código difícil de modificar y mantener. Ejemplo: modificar el esquema de sincronización utilizado por eclipse involucró cambiar 2573 métodos e insertar en 1284 métodos una llamada a lock() así como también una llamada a unlock(). Código difícil de entender. On November 10, 2004, Silenio Quarti committed code changes “76595 (new lock)” to the Eclipse CVS repository. They fixed bug #76595 “Hang in gfk pix-buf new” that reported a deadlock and required the implementation of a new locking mechanism for several platforms. The extent of the modification was enormous: He modified 2573 methods and inserted in 1284methods a call to lock, as well as a call to unlock. As it turns out, AOP could have been used to add these. [Breu y Zimmerman 2006] Motivación - Ejemplo Método extraído de Tomcat 6.x public void removeChild (Container child) { synchronized(children) { if (children.get(child.getName()) == null) return; children.remove(child.getName()); } if (started && (child instanceof Lifecycle)) { try { if( child instanceof ContainerBase ) if( ((ContainerBase)child).started ) ((Lifecycle) child).stop(); else ((Lifecycle) child).stop(); } catch (LifecycleException e) { log.error("ContainerBase.removeChild: stop: ", e); } } fireContainerEvent(REMOVE_CHILD_EVENT, child); Concerns Sincronización Ciclo de vida Logging Notificación 10% del código corresponde al concern original. } Tomado de: org/apache/catalina/core/ContainerBase.java Apache Tomcat is the servlet container that is used in the reference implementation for Sun's Java Servlet and JavaServer Pages technologies. Código fuente disponible en: http://jakarta.apache.org/tomcat. Lifecycle es una interfase común para diferentes componentes de Catalina, la cual provee mecanismos para comenzar (start) y detener (stop) al componente. Este concern está implementado en más de 40 clases. La operación start debe ser llamada antes que cualquier método público del componente, mientras que stop termina el uso del objeto. Mecanismo de notificación: Las clases que implementan la interfase Lifecycle deben adoptar el rol de Sujeto (Observable) del patrón Observer/Observabl. Más información sobre crosscutting concerns en Tomcat ver [Marin et al. 2007]. Motivación - AOP Solución: Programación Orientada a Aspectos. Introduce el concepto de aspecto como mecanismo que permite encapsular e implementar un crosscutting concern. Consecuencia: mejor separación de concerns. Motivación - Elementos Un sistema se descompone en clases y aspectos. La relación entre ambos se determina en base a los conceptos de join points y weaving. Un join-point indica un posible punto del sistema donde el código definido por el aspecto puede insertarse (por ejemplo, al inicio o final de un método). Un aspect weaver tiene la responsabilidad de procesar el lenguaje base y el lenguaje de aspectos y componerlos con el objetivo de obtener la implementación del sistema. El aspect weaver utiliza los join-points para componer los aspectos con el código base. Agenda Motivación AspectJ Ejemplos en AspectJ Otras plataformas AOP Recursos y Referencias AspectJ - Introducción AspectJ es una extensión al lenguaje Java mediante la cual es posible modularizar un amplio rango de crosscutting concerns. Al ser una extensión de Java provee distintos niveles de compatibilidad entre los lenguajes. AspectJ - Compatibilidad Compatibilidad con Java: Upward compatibility: todo programa legal en Java es legal en AspectJ. Platform compatibility: todo programa legal en AspectJ puede correr sobre una maquina virtual de Java. Tool compatibility: es posible extender herramientas existentes para soportar AspectJ de manera natural (plugins para Eclipse o Netbeans). Programmer compatibility: La programación en AspectJ surge naturalmente como extensión de Java. Ventaja: Rápida adopción por parte de la comunidad. AspectJ - Weaving Weaving en AspectJ Weaving en tiempo de compilación (estático) Entrada: código java y aspectj. Salida: bytecode ejecutable en cualquier JVM. Futuro: weaving en tiempo de ejecución (dinámico), al momento de cargar las clases en la VM. Weaving is the process of composing the system from individual core modules by following the weaving rules. In essence, the weaving rules determine the final form of the system. The weaving rules are defined in aspects that are separate entities from the individual core modules. This separation makes it possible to change the woven system simply by providing alternative weaving rules in the aspects. AspectJ – Ejemplo (I) Display * FigureElement Figure makePoint(..) makeLine(..) Point getX() getY() setX(int) setY(int) moveBy(int, int) moveBy(int, int) 2 ¿Cómo actualizar el Display cada vez que un elemento gráfico modifica su estado? Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Operaciones para mover elementos Las características del lenguaje serán presentadas usando este simple ejemplo. A Figure consists of a number of FigureElements, which can be either Points or Lines. The Figure class provides factory services. There is also a Display. Para este ejemplo podríamos observar que el crosscutting concern de actualización de la pantalla atraviesa aquellas clases de tipo FigureElement en aquellos métodos que modifican el estado de las mismas (setters y getters). AspectJ – Ejemplo (II) cl ass Lin e { vo id setP1 (Point p1) { this .p1 = p1; Disp lay.u pdate(thi s); } vo id setP2 (Point p2) { this .p2 = p2; Disp lay.u pdate(thi s); } } cl ass Point { voi d setX( int x ) { this .x = x; Disp lay.u pdate(thi s); } vo id setY( int y ) { this .y = y; Disp lay.u pdate(thi s); } } No hay localidad para el concern “actualización del Display” La evolución es costosa, los cambios se propagan por todas las clases. AspectJ – Ejemplo (III) class Line ex tends Obs er vable { voi d setP1( Point p1) { this .p1 = p1; supe r.s etC hange d(); su per.notifyO bser vers( ); } vo id setP2 (Point p2) { this .p2 = p2; supe r.s etC hange d(); su per.notifyO bser vers( ); } Uso patrón observer/observable. Mejor evolución: } class Po int exten ds Obser v able { voi d setX( int x ) { this .x = x; supe r.s etC hange d(); su per.notifyO bser vers( ); } . .. } agregar nuevas vistas no repercute en cambios. Aún existe tangling. AspectJ – Constructores AspectJ añade a Java un nuevo concepto: join-point. un conjunto de constructores: pointcuts. advices. inter-type declarations. aspects. Pointcuts and advice afectan el flujo dinámico del programa, mientras que las inter-type declarations modifican de forma estática la jerarquía de clases del programa. Aspects agrupan los constructores anteriores. AspectJ – Join points Join points: son puntos bien definidos en el flujo de un programa. AspectJ expone sólo un subconjunto de join points posibles. void setX(int x) { this.x = x; } Ejemplo: Del código anterior podemos identificar dos join points bien definidos: - la ejecución del método setX. - el acceso al campo x. Cuando un método o constructor son corridos, existen dos tiempos asociados a ellos: cuando son llamados, y cuando son ejecutados. Por lo tanto son dos join points diferentes. Ejemplos de join points son: llamadas a métodos, ejecución de métodos, instanciación de un objeto, ejecución de constructores, referencias a campos y manejo de excepciones. Los joinpoints no son posiciones en el código fuente sino en la ejecución del programa. Son eventos que ocurren durante la ejecución del programa. AspectJ – Pointcuts (I) Estructura declarativa. Son constructores que agrupan join points y datos del contexto de ejecución de dichos puntos. call(void Point.setX(int)) call (void Point.setX(int)) || call (void Point.setY(int)) Elige join points que son llamadas a un método setX con parámetro int, tipo de retorno void de la clase Point. Es posible componer diferentes pointcuts (uso &&, || y !). AspectJ – Pointcuts (II) Los pointcuts pueden tener nombres Ejemplo: pointcut move(): call(void FigureElement.moveBy(int,int)) || call(void Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)); Name-based pointcuts: enumeración explícita de un conjunto de signaturas de métodos. Buenas prácticas a la hora de escribir un pointcut: “Before we introduce the designators, a few words about good practice in writing pointcuts. Although anonymous pointcut definitions can be used, in all but the simplest cases using a well-named pointcut is preferred because it greatly aids program understanding. Try to keep individual pointcut definitions relatively simple, and build up more complex expressions from smaller, named, constituent parts. A pointcut definition would normally contain at most one type of kind designator (for example, a set of calls, or a set of method executions, but not a mixture of bothand definitely not if they are combined with &&) and optionally some context and scoping designators...” [Colyer et al. 2004]. AspectJ – Pointcuts (III) Property-based pointcuts: son especificado en base a las propiedades de los métodos en vez de su nombre exacto. Ejemplo: call ( void FigureElement+.set*( ) ) Al invocar (call) cualquier método de la clase (FigureElement) o de una subclase (+) cuyo nombre comience con set (set*) que tenga void como retorno y cualquier cantidad de parámetros (...) Cómo escribir un buen pointcut [Colyer et al. 2004] Four steps are involved in writing a good pointcut. First, come up with a meaningful name that describes the characteristic of the join points you want to match, ideally in a way that serves to make your program selfdocumenting. For example, secureOperation: pointcut secureOperation(); Join point names tend to be singular (secureOperation rather than secureOperations), and fit into the sentence "is this a <pointcut_name> join point?" The second step is to decide what kind of join points you want to match, and add the appropriate kind designator to the pointcut definition (call, execution, handler, get, set, adviceexecution, or one of the initialization designators). It is rare to combine kind designators of more than one type in a pointcut; if you do, be sure to combine them using || rather than &&. An example might be get(* x) || set(* x) to match all references to a field named x. If you are matching call or execution join points, do not specify a declaring type in the signature pattern unless you are sure that you want static typing matches. pointcut secureOperation() : execution(public *(..)); The third step is to decide in which context the pointcut should match (using target, this, or args). If the pointcut should match in any context, you can omit this stage. If the users of the pointcut need access to the contextual values at a matched join point, add these to the pointcut parameter list and bind the values in the context designators. pointcut secureOperation(ISecured secured) : execution(public *(..)) && this(secured); Finally, decide on the scope in which the pointcut should match (within a certain component, for example, or a given control flow). pointcut secureOperation(ISecured secured) : execution(public *(..)) && target(secured) && within(com.xyz.someapp..*); Try to keep individual pointcut definitions small and simple, and build more complex pointcuts by combining named, simpler pointcuts. AspectJ – Advices (I) Un Advice define el código que se debe ejecutar cuando un join point que cumple con un pointcut es alcanzado en el flujo de ejecución de un programa. Ejemplo: Tiempo after(): move() { Display. update(); } Pointcut asociado al advice Código crosscutting Pointcuts and advice together form the dynamic crosscutting rules. While the pointcuts identify the required join points, the advice completes the picture by providing the actions that will occur at the join points. [Laadad – AspectJ in Action]. Mediante los pointcuts especificamos las reglas de weaving y con los advices asociamos el comportamiento crosscutting al código base. AspectJ – Advices (II) Un advice puede ejecutarse antes (before), después (after) o en vez (around) de un join point. before(): move() { System.out.println(A punto de moverse); } after(): move() { System.out.println(Se movió); } void around(): move() { System.out.println(A punto de moverse); proceed(); System.out.println(Se movió); } Before Advice: es el más simple de los tres. Around Advice Around Advice: es especial ya que tiene la habilidad de ejecutar en lugar del point join, continuar la ejecución original , o causar la ejecución con un contexto alterado. proceed(): Ejecuta la operación del punto de unión detectado El advice puede retornar un valor, como si fuera un método y por lo tanto debe declarar un tipo de retorno. After Advice There are three basic forms of after advice: after returning advice, which executes after the successful return from a matched join point; after throwing advice, which executes after returning from a matched join point via an exception condition; and after finally advice (also known as just "after" advice), which executes whether returning from a matched join point by either normal or exceptional return. AspectJ – Advices (III) Exponer el contexto de ejecución de los join points. Los valores expuestos por un pointcut pueden ser usados en el cuerpo de los advices. Ejemplo: void around(Point p, int x) : target(p) && args(x) && call(void setX(int)) { System.out.println(El punto + p + se movió a x: + x); } Advice can take zero or more parameters, specified in the advice declaration. Because advice is invoked implicitly, there is no way for the programmer to pass parameter values on a call to the advice. Where do the values for advice parameters come from then? Recall that a pointcut declaration is a statement that the pointcut will provide contextual values of the type specified in the pointcut declaration, these provided values from the pointcut expression associated with a piece of advice are matched to the required values from the advice specification. The matching happens by name. Each parameter in an advice specification is given an identifier (a name)and every parameter name must appear somewhere in the pointcut expression associated with the advice. Failure to "bind" every parameter in this way results in the compile time error "formal unbound in pointcut," AspectJ – Aspects (I) Aspect: empaqueta pointcuts, advices e intertype declarations en una unidad modular. Es similar a una clase, puede contener métodos, variables de instancia, etc. public aspect DisplayUpdate { pointcut move(FigureElement e) : this(e) && (call(void moveBy(int, int)) || call(void set*(..))); after(FigureElement e) : move(e) { Display.update(e); } } Primera solución al ejemplo. AspectJ – Aspects (II) Similitudes respecto a clases: Pueden incluir campos y métodos. Poseen especificaciones de acceso (public, private,...). Pueden ser abstractos (permitir mayor reuso). Un aspecto abstracto puede definir un pointcut o un advice como abstracto. Pueden extender clases, otros aspectos e implementar interfaces. AspectJ – Aspects (III) Pero un aspecto no es una clase, estas son sus diferencias: No pueden ser instanciados. La VM se encarga de ello, sólo existe una única instancia. Un aspecto no puede heredar de otro aspecto concreto. Pueden ser marcados como privilegiados. Lo que implica que estos podrían acceder a los miembros privados de las clases. AspectJ – Inter-type declarations (I) Permiten modificar la estructura estática de un programa. private Vector<Display> Point.observers = new Vector<Display>(); Introduce un nuevo campo 'observers' a la clase Point. El mismo sólo es visible dentro del aspecto en el cual fue declarado. public Point.new(int x, int y) { this.setX(x); this.setY(y); } Declara públicamente un nuevo constructor de dos argumentos para la clase Point. AspectJ – Inter-type declarations (II) Más ejemplos: declare parents : Point implements Comparable; Declara que la clase Point implementa la interfase Comparable. public int Point.compareTo(Object cmp) { ... } Declara que cada Point posee un método compareTo. De existir otro método con el mismo nombre declarado dentro de la clase existirá un conflicto en tiempo de compilación. AspectJ – Solución al ejemplo Segunda solución al ejemplo “display update”. public aspect ObserverInstance { private Vector<Display> Point.observers = new Vector<Display>(); public static void addObserver(Point p, Display d) { p.observers.add(d); } public static void removeObserver(Point p, Display d) { ... } pointcut changes(Point p) : target(p) && ( call (void Point.set*(int)) || call (void Point.moveBy(int, int)) ); after(Point p) : changes(p) { for (Display d : p.observers) { updateObserver(p, d); } } static void updateObserver(Point p, Display d) { d.update(p); } } Solución al ejemplo Ventajas: Todo el código correspondiente al patrón se encuentra en el aspecto. El código de la clase Point corresponde sólo a su funcionalidad básica. Consecuencia: mejor separación de concerns y evolución. Agenda Motivación AspectJ Ejemplos en AspectJ Otras plataformas AOP Recursos y Referencias Ejemplos – Tracing (I) Ejemplo clásico de AOP. Este aspecto imprime las llamadas a los métodos invocados por un programa durante su ejecución. public aspect Tracing { pointcut thePublicMethods(Object t) : target(t) && execution(* org.aspectj..*(..)) && !within(Tracing); before(Object t) : thePublicMethods(t) { System.out.println("Entrando: " + thisJoinPoint.getSignature()); } } Ejemplos – Tracing (II) La salida es Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: Entrando: void org.aspectj.Point.setX(int) void org.aspectj.Point.setY(int) void org.aspectj.Line.moveBy(int, int) void org.aspectj.Point.moveBy(int, int) int org.aspectj.Point.getX() void org.aspectj.Point.setX(int) int org.aspectj.Point.getY() void org.aspectj.Point.setY(int) void org.aspectj.Point.moveBy(int, int) int org.aspectj.Point.getX() void org.aspectj.Point.setX(int) int org.aspectj.Point.getY() void org.aspectj.Point.setY(int) void org.aspectj.Point.setY(int) Ejemplos – Pre/Pos Condiciones (I) Establecer pre/pos condiciones sobre parámetros de entrada/salida. public aspect PointBoundsChecking { pointcut setX(int x) : ((call(void Point.moveBy(int,int)) && args(x, *)) || (call(void Point.setX(int)) && args(x))); pointcut setY(int y) : ((call(void Point.moveBy(int,int)) && args(*, y)) || (call(void Point.setY(int)) && args(y))); before(int x) : setX(x) { if (x < MIN_X || x > MAX_X) throw new IllegalArgumentException("x está fuera de rango."); } before(int y) : setY(y) { if (y < MIN_Y || y > MAX_Y) throw new IllegalArgumentException("y está fuera de rango."); } } Ejemplos – Pre/Pos Condiciones (II) Para un setY(900) la salida es: Ejemplos – Detectar salida estándar Detectar utilización de la salida estándar. public aspect SystemOutMonitor { pointcut syserrAccess() : get(* System.err); pointcut sysoutAccess() : get(* System.out); declare warning : syserrAccess() || sysoutAccess() : "Please don't write messages to System out or err."; } Ejemplos – Más... Implementación de los patrones de diseño en AspectJ [Hannemann and Kiczales 2002]. Pooling de recursos, transaction management, reglas de negocio, etc. [Laadad 2003]. Patrones, persistencia, unit-testing, políticas de seguridad, etc. [Miles 2005]. Agenda Motivación AspectJ Ejemplos en AspectJ Otras plataformas AOP Recursos y Referencias Plataformas AOP - AspectWerkz No utiliza precompilador. Define los pointcuts mediante anotaciones de Java o archivos XML. Soporta weaving tanto en tiempo de compilación como en tiempo de ejecución. Se unió a AspectJ a partir de la versión 1.5. Plataformas AOP – Spring (I) Spring es un framework open source para el desarrollo de aplicaciones Java. Dispone de un módulo AOP para dar soporte a aspectos. Ofrece integración con AspectJ. Plataformas AOP – Spring (II) Spring no provee una completa implementación de AOP. El objetivo es facilitar la implementación de algunos crosscutting concerns comunes a las aplicaciones empresariales. Plataformas AOP – Spring (III) Sólo admite join points de llamadas a métodos. Los pointcuts son especificados mediante anotaciones u archivos XML. Tres tipos de advices: before, after y around. Plataformas AOP – JBoss AOP (I) Framework desarrollado puramente en Java. Puede ser utilizado en cualquier ambiente de desarrollo o integrado al servidor de aplicaciones JBoss. Existe un plug-in para eclipse. Plataformas AOP – JBoss AOP (II) Los advices se implementan mediante “interceptors” Un interceptor puede interceptar invocaciones a métodos o constructores, accesos a campos, etc. Para implementar un inteceptor utilizamos la interfase org.jboss.aop.Interceptor. Pointcuts se especifican mediante XML u anotaciones. Plataformas AOP – Otras Otros frameworks: Nanning Sólo soporta pointcuts en llamadas a métodos. Todos los objetos necesitan implementar una interfaz. JAC La filosofía de JAC es dejar los componentes funcionales de negocio libres de extensiones impuestas por los application servers (realizado a través del container JAC). Agenda Motivación AspectJ Ejemplos en AspectJ Otras plataformas AOP Recursos y Referencias Referencias [Breu y Zimmermann 2006] “Mining Aspects from Version History”. [Colyer et al. 2004] Colyer A., Clement A., Harley G., Webster M. “Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the Eclipse AspectJ Developments Tools”. Addison-Wesley. December 14 2004. ISBN: 0-321-24587-3. [Hannemann and Kiczales 2002] Hannemann, J. and Kiczales, G. (2002) “Design pattern implementation in Java and AspectJ”. In: Proceedings of the 17th ACM conference on Object-oriented programming, systems, languages, and applications, pages 161–173. ACM Press. [Laadad 2003] Ramnivas Laadad. “AspectJ in Action”. ©2003 by Manning Publications Co. All rights reserved. [Marin et al. 2007] Marin, M., van Deursen, A. and Moonen, L. (2007) “Identifying Crosscutting Concerns Using Fan-in Analysis”. ACM Transactions on Software Engineering and Methodology (TOSEM), 17(1). Referencias & Recursos [Miles 2005] Russ Miles. “AspectJ Cookboock”. O'Reilly Media. Recursos en Internet: http://eclipse.org/aspectj - Página oficial de AspectJ IBM AOP@Work – Artículos sobre AspectJ.