Mostrando entradas con la etiqueta Crosscutting. Mostrar todas las entradas
Mostrando entradas con la etiqueta Crosscutting. Mostrar todas las entradas

sábado, junio 19, 2010

AOP: crosscutting (II)

Durante la última entrada de la serie relacionada con la programación orientada a aspectos analizamos el modo en el que se puede alterar el comportamiento de un sistema, analizando las categorías de advices y realizando una comparativa con los métodos de una clase. A lo largo de esta entrada realizaremos un análisis en profundidad de los advices.

Before advice
Este tipo de advices se ejecutan antes de la ejecución del joint point sobre el que actúan. En el siguiente ejemplo:

              before():execution(@Secured * * (..)){
            // asegurarse de que el usuario puede realizar la operación
              }                   

el advice realiza una comprobación de seguridad antes de que se produzca la ejecución de cualquier método anotado con Secured.

En caso de que el advice dispare un excepción, el joint point no se ejecutará. Este tipo de advices son comúnmente utilizados en aspectos tales como seguridad o trazabilidad.
After advice

Se ejecutan después de la ejecución del joint point sobre el que actúan.Dentro de esta categoría, AspectJ ofrece tres tipos de advices:
  • Ejecución del advice independientemente del resultado de la ejecución del joint point.
  • Ejecución del advice única y exclusivamente si el joint point ha finalizado correctamente.
  • Ejecución del advice después que el joint point haya disparado una excepción.
Veamos en detalle cada uno de los tres tipos anteriores:

1.- Advice After
Este tipo de advices se ejecutan independientemente del resultado de la ejecución del joint point sobre el que actúan.Habitualmente se conoce a este tipo de advices como after finally puesto que su semántica es similar a la de un bloque finally.

El siguiente advice:
                after(): call(@Logging * ServiceManager.*(..)){
                   // registrar el resultado de la operación
               }

registra el resultado de todas las operaciones de la clase ServiceManager que estén marcadas con la anotación Logging, independientemente si retornan correctamente o terminan su ejecución de forma inesperada mediante el disparo de una excepción.

2.-Advice After Returning
En muchas ocasiones,será necesario ejecutar el código de nuestro advice única y exclusivamente cuando la ejecución del joint point haya terminado de forma correcta. Continuando con el ejemplo anterior:
               
            after () returning: call(@Logging * ServiceManager.*(..)){
              // registrar el resultado de la operación
            }

se seguirá registrando el resultado de las operaciones, siempre y cuando, la ejecución haya terminado correctamente, sin el disparo de ninguna excepción.

AspectJ ofrece una pequeña variante para este tipo de advices:

after() returning (ReturnType returnObject)

gracias a la cual se permite recuperar el objeto retornado por la ejecución del joint point dentro del advice. Veamos un pequeño ejemplo ilustrativo:
                           
        after() returning (java.sql.Connection connection):
            call(java.sql.Connection DriverManager.getConnection(..)){
                    System.out.println("Se ha recuperado la conexión "
                                   + connection);
       }

Es importante tener claro que no se puede retornar un objeto nuevo (sí puede ser modificado pero no retornar uno nuevo).

3.- Advice After Exception
Este tipo de advices son similares a los descritos en el apartado anterior. En este caso, el advice se ejecutará única y exclusivamente cuando el joint point dispare una excepción. Presentan la siguiente estructura:
                            
after() throwing:execution (* ServiceManager+.*(..))
                       
El advice del ejemplo anterior se ejecutará siempre y cuando algún método de la clase ServiceManager (o alguna de sus subclases) dispare una excepción. En el supuesto de que la ejecución del joint point termine correctamente, este tipo de advices no serán ejecutados.

Al igual que los advices del apartado anterior, AspectJ ofrece un modo de recuperar la excepción que ha sido disparada por el joint point de manera que esté disponible en el cuerpo del advice. Siguiendo una sintaxis similar a la anterior, tendríamos:
                          
after() throwing (ExceptionType exceptionObject):
                       
Un after throwing advice nunca podrá tragarse la excepción, por lo que seguirá subiendo por la pila de llamadas hasta llegar al objeto que realizó la invocación del joint point.

Around advice
Este clase de advices engloban al joint point, pudiendo ejecutar la lógica del mismo un número indefinido de veces. Incluso pueden omitir la ejecución del propio joint point. Algunos de los usos principales de este tipo de advices son los siguientes:
  • Ejecución de lógica adicional antes y después de la ejecución de un joint point, como por ejemplo, acciones de profiling.
  • Omitir la ejecución original, y realizar otra en su lugar, como por ejemplo, operaciones con cachés.
  • Envolver la operación con el objetivo de aplicar una política de gestión de excepciones. Un ejemplo de este uso sería la gestión de transacciones.
Este advice ofrece una potencia superior a todos los advices vistos hasta el momento, puesto que podrían sustituir a los anteriores. De todos modos, se considera una buena práctica utilizar el advice más sencillo que cumpla las necesidades de la tarea que necesita ser llevada a cabo.

Si desde el around advice se desea llevar a cabo la ejecución del joint point, será necesario hacer uso de la palabra reservada proceed() dentro del cuerpo del advice. Recuérdese que, puesto que la invocación de proceed() ejecuta el joint point, deberán pasarse el mismo número de argumentos que han sido recolectados por el advice.
Asimismo, puesto que la invocación de proceed() supone la ejecución del joint point, el valor de retorno será el retornado por éste último.

A continuación se adjunta un pequeño ejemplo de utilización de advices de este tipo:
                          
        void around(User user,int credits)
                   throws InsufficientCreditsException:
                call(* User.pay*(int)) && target(user) & &  args(credits){
                    try
                    {
                        proceed(user,credits);
                    }catch(InsufficientCreditsException ex){
                        if(!processException()){
                            throw ex;
                        }
                    }

Analicemos en detalle la construcción anterior:
  1. El pointcut selecciona cualquier llamada a los métodos de la clase User cuyo nombre comience por pay y disparen una excepción de tipo InsufficientCreditsException.
  2. La segunda parte del pointcut recolecta el contexto del joint point: el usuario sobre el que se está realizando la llamada y el número de créditosque se están pasando como argumento del método que se está ejecutando.
  3. En el cuerpo del advice, se engloba la ejecución del método con un bloque de gestión de excepciones, para realizar una protección adicional en caso de que se produzca una excepción. En el caso de que la protección adicional no sea correcta, la excepción será disparada de nuevo.
Todos los around advices deben declarar un valor de retorno (pudiendo ser void). Habitualmente el tipo de retorno de éstos se corresponde con el tipo de retorno de los joint points sobre los que está actuando.
En algunas ocasiones, todos los joint points sobre los que actúa el advice no presentan el mismo tipo de retorno, como puede ocurrir cuando estamos añadiendo soporte transaccional a diferentes  operaciones. En estas situaciones el tipo de retorno que debe declarar el advice será Object. AspectJ acomodará el valor de retorno de acuerdo a las  siguientes reglas:
  • Si se está retornando un tipo primitivo, AspectJ realizará el boxing/unboxing correspondiente. Esta característica es similar a la incluida a partir de Java 5, pero AspectJ no precisa de dicha versión de Java para realizar la operación.
  • En el caso en el que el tipo de retorno no sea primitivo, AspectJ realizará los casts oportunos antes de retornar el valor.
Muchas ocasiones es necesario acceder a los objetos que conforman la ejecución del joint point para que el advice pueda llevar a cabo la lógica correspondiente. Por tanto, los pointcuts, necesitan exponer el contexto disponible en la ejecución del joint point de modo que pueda estar disponible en el cuerpo del advice. Dicho contexto puede definirse de dos modos diferentes:
  • Objetos (incluyendo los tipos primitivos) que conforman el joint point.
  • Anotaciones asociadas al joint point.
La siguiente tabla describe el conjunto de pointcuts que AspectJ ofrece para recuperar el contexto en los joint points.

Table 1.14. Pointcuts para recuperar el contexto en un joint point
Pointcut Contexto recuperado
this(obj) Objeto this en el joint point que se está
ejecutando
target(obj) Objetivo de la llamada en el joint
point
que se está ejecutando. En el caso de un
joint point de una
llamada a un método, el target será el objeto que realiza la llamada.
Para la ejecución de un método, el target será el objeto this. En los accesos a campos, el target será el objeto que se está
accediendo. En el resto de joint
points
no existe un target disponible
args(obj1,obj2,...) Objetos que representa los argumentos en el joint point. Para las
llamadas/ejecuciones de métodos/constructores, recupera los
argumentos de los mismos. En el caso de los manejadores de
excepciones, recupera la excepción producida. Para los accesos
en modo escritura a un campo, recupera el nuevo valor del
campo.
@this(annot) Anotación asociada con el tipo del objeto this del joint point
@target(annot) Anotación asociada con el tipo del objeto target del joint point
@args(annot1, annot2,
...)
Anotación asociada con el tipo de los argumentos del joint point
@within(annot) Anotación asociada con el tipo "enclosing" del joint point
@withincode(annot) Anotación asociada con el método "enclosing" del joint point
annotation(annot) Anotación asociada con el asunto actual del joint point.

Durante la próxima entrada analizaremos el concepto de Aspecto.

Hasta pronto!

domingo, mayo 30, 2010

AOP: crosscutting (I)

A lo largo de las entradas anteriores hemos analizado el modelo de joint point de AspectJ y la manera de definir las reglas que permitan seleccionar aquellos joint points de nuestro interés. 
Durante esta entrada analizaremos el modo en el que se puede alterar el comportamiento de un sistema en los joint points seleccionados mediante la definición de los pointcuts.

Descripción general


Las reglas de tejido están compuestas de dos partes:

  • advice: qué deseamos hacer.
  • pointcuts: dónde aplicamos el advice anterior.

AspectJ soporta el crosscutting dinámico mediante el concepto de advices, construcciones similares a los métodos gracias a los cuales se permiten definir las acciones a ejecutar en los joint points seleccionados por un pointcut.

Categorías de advices


Dependiendo de las funcionalidades que se estén implementando será necesario llevar a cabo la lógica en un determinado lugar del flujo de ejecución original; así por ejemplo, si se está construyendo la seguridad de un sistema, el código tendrá que verificar dicha seguridad antes de la ejecución del joint point. En un sistema de cachés, la nueva funcionalidad tendría que ejecutarse alrededor del joint point original, intentando recuperar el valor de la caché, y en caso de que no exista, ejecutar el código real y añadirlo a la misma para futuras invocaciones. AspectJ ofrece tres categorías de advices que satisfacen los escenarios anteriores (y alguno más):

  • Before Advice: se ejecutan anteriormente a la ejecución del joint point
  • After Advice: se ejecutan posteriormente a la ejecución del joint point. Existen tres variantes diferentes
    • After finally: se ejecuta tras la ejecución del join point independientemente del resultado de la misma.
    • After returning: se ejecuta tras la ejecución del joint point siempre y cuando ésta última haya finalizado correctamente, es decir, sin lanzar ninguna excepción.
    • After throwing: se ejecuta tras la ejecución fallida de un joint point, es decir, después de que dicho joint point dispare una excepción.
  • Around Advice: rodean la ejecución del joint point.


Sintaxis de los advices


Aunque la sintaxis varía ligeramente dependiendo del tipo de advice que se esté escribiendo, se podría dividir su estructura general en tres partes claramente diferenciadas:

  • Declaración del advice. En esta parte de la declaración se especifica el momento de ejecución del advice, es decir, si se ejecutará antes, después o alrededor de los joint points.
  • Definición de los pointcuts. Se especifican los pointcuts sobre los que se desea actuar.
  • Cuerpo del advice. Definición del código a ejecutar una vez se haya alcanzado el joint point indicado.

Veamos, por partes, un ejemplo sencillo de definición de un advice:


  •  En primer lugar se define un sencillo pointcut :

pointcut secureOperation(User user): call( * User.*(..)) && target(user)


  • En el pointcut anterior estamos capturando todas las llamadas a cualquier método de la clase User, y, adicionalmente estamos recogiendo el objeto que actúa como target de la llamada. A continuación veremos un around advice para ilustrar la sintaxis:

            Object around(User user):secureOperation(user){
System.out.println("Securing operation on user " 
+ user.toString());
                Object retValue = proceed(user);
System.out.println("Finished secured operation on user " 
+ user.toString());
return retValue;
           }

  • En la definición anterior se puede ver la estructura de la declaración de un advice descrita anteriormente:
    • La parte que precede a los dos puntos indica el momento de ejecución del advice (after,before,around). En este caso, se ejecutará alrededor del joint point seleccionado.
    • La parte que sigue a los dos puntos representa el pointcut, es decir, la definición de los criterios que determinan cuándo se ejecutará el advice.
    • La última parte representa el cuerpo del advice, es decir, el código que se ejecutará cuando alguno de los joint point definidos por el pointcut sea alcanzado.

Advices y métodos

Al igual que los métodos de una clase, los advices se utilizan para definir comportamiento. La sintaxis de éstos últimos es similar a la de los métodos aunque existen algunas diferencias dado que los advices son aplicados de manera automática, sin la necesidad de realizar explícitamente la invocación del mismo.

A continuación se analizan las similitudes entre ambos en tres categorías diferentes: declaración, cuerpo y comportamiento. La declaración de un advice es similar a la signatura de un método tradicional en que:
  • Opcionalmente puede asignarse un nombre al advice mediante el uso de la anotación @AdviceName.
  • Recibe argumentos a través del contexto del joint point, que posteriormente podrán ser utilizados en el cuerpo para implementar la lógica necesaria.
  • Puede declarar el lanzamiento de una excepción.
  • El cuerpo de los advices también es muy parecido al de los métodos puesto que:
  • El código del cuerpo del advice sigue las mismas reglas de acceso a miembros de otros tipos y/o aspectos.
  • Se puede referenciar a la propia instancia del aspecto mediante el uso de this.
  • Los advices de tipo around pueden retornar un valor.
  • Los advices deben declarar las excepciones que sean checked que la implementación podría disparar.
  • En la categoría relativa al comportamiento, los advices :
  • No pueden declarar el disparo de una excepción que no está declarada en TODOS los joint points sobre los que actúa.
  • Pueden omitir algunas de las excepciones de tipo checked que han sido declaradas por alguno de los joint point sobre los que actúa.
  • Pueden declarar el disparo de excepciones más específicas (de tipo checked) que las definidas por los joint point sobre los que está actuando.
  • Pueden lanzar cualquier tipo de excepción de tipo runtime. 

En comparación con los métodos, los advices presentan las siguientes diferencias:

  • La declaración de un nombre es opcional.
  • No pueden ser invocados directamente.
  • No presentan especificadores de acceso (relacionado con la característica de que no pueden ser invocados directamente).
  • No presentan un tipo de retorno en los advices de tipo before y after.
  • Tienen acceso a unas cuantas variables dentro del propio aspecto: thisJointPoint, thisJointPointStaticPart, thisEnclosingJointPointStaticPart.
  • Se puede utilizar la palabra reservada proceed en los advices de tipo around para ejecutar el joint point sobre el cual se está realizando el advice.

La siguiente entrada realizará un análisis más detallado de los advices y cómo se puede acceder a los contextos del joint point.


Hasta pronto!!