Programación Orientada a Aspectos AspectJ y otras plataformas de

Anuncio
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.
Descargar