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

miércoles, junio 29, 2011

AspectJ y Scala (y III)

Última entrada (en esta ocasión mucho menos ladrillo) de esta mini-comparativa que estamos llevando a cabo entre AspectJ y Scala (podéis consultar las anteriores entradas en los siguientes enlaces: AspectJ y Scala (I), AspectJ y Scala (II).

Hemos estado revisando cómo llevar a cabo la aplicación de varias funcionalidades como el uso de cachés o la gestión de transacciones mediante el uso de AspectJ y Scala, haciendo hincapié en las ventajas y desventajas de las dos aproximaciones. Nótese que hemos estado hablando de funcionalidad transversal como las transacciones y la caché pero no hemos hablado de otro tipo de funcionalidades las cuales abarcan un espectro mucho más amplio como por ejemplo FFDC (First Faiulure Data Capture), Profiling, gestión de la concurrencia, etc. En este tipo de situaciones el uso de Scala y HFOs resultaría mucho más tedioso que los ejemplos vistos en las entradas anteriores; derivando en los síntomas clásicos de funcionalidad "difuminada" por toda nuestra apliación.

¿Conclusión? El uso de cualquiera de las dos aproximaciones que hemos estado analizando dependerá, como en el 90 por ciento de todas las situaciones, del problema que estemos resolviendo, por lo que tendremos que escoger la "herramienta" más adecuada para el problema que se nos plantea.

¿Y por qué no un mixin de las anteriores? Esta es una de las alternativas que más me convence:
  • Compilamos nuestros programas Scala de la manera habitual.
  • Definimos nuestros aspectos para llevar a cabo la funcionalidad transversal deseada.
  • Realizamos un proceso de binary weaving (ya sea build time o load time)
Mediante el mecanismo anterior estaremos aplicando nuestros aspectos a nuestro código Scala compilado.

En mi cuenta de GitHub podréis encontrar dos proyectos Eclipse (scala-simple-service y aspectj-advice-scala) que deben ser utilizados de manera conjunta. Mediante estos dos pequeños proyectos de ejemplo se ilustra cómo podemos utilizar conjuntamente aspectos escritos en AspectJ que hagan advice sobre código Scala compilado.

Hasta pronto!


martes, junio 28, 2011

AspectJ y Scala (II)

En la entrada anterior analizamos algunas de las similitudes y diferencias existentes entre Scala y AspectJ, llevando a cabo una implementación de una caché extremadamente simple en ambos lenguajes, describiendo las ventajas y desventajas presentes en cada una de las aproximaciones. Durante esta entrada analizaremos un ejemplo de aplicación de una funcionalidad transversal como la gestión de transacciones. 

El ejemplo que aquí nos ocupa no persigue realizar una implementación detallada de control de transacciones sino simplemente ilustrar como podríamos aplicar una funcionalidad transversal mediante el uso de AspectJ o Scala.

Del mismo modo que en el post anterior, comenzamos por la aproximación basada en AspectJ. Para ello en primer lugar definimos una simple anotación que nos sirva como elemento de marcado de aquellas clases y/o métodos que se deben ejecutar bajo un contexto transaccional.

Una vez definida la anotación anterior definiremos un aspecto que sea capaz de detectar aquellos joint point en los que deseamos establecer un contexto transaccional. 

pointcut transactionalOperation() : execution(@Transactional * * (..) );

Con el pointcut anterior estaremos capturando las llamadas realizadas sobre cualquier método anotado con nuestra anotación @Transactional. Ahora necesitaremos definir un advice en el que llevamos a cabo la gestión de la transacción (nótese que este gestor de transacciones es completamente absurdo y simplemente imprime por pantalla la funcionalidad real que debería ser llevada a cabo por un gestor de transacciones operativo)


En muchas ocasiones todos los métodos de una clase necesitarán ser transaccionales, y no querremos anotar todos los métodos de dicha clase. En este supuesto podríamos utilizar anotaciones a nivel de clase que nos permitan seleccionar aquellos métodos que deseemos (por ejemplo, todos los métodos públicos).

Adicionalmente, podríamos hacer uso de los ITD y establecer la transaccionalidad a todas aquellas clases  que nosotros deseemos. Por ejemplo, supongamos que todos los nombres de nuestros servicios, los cuales deseamos que sean transaccionales, terminan con el sufijo Service. Podríamos, mediante el uso de un aspecto, añadir la anotación @Transactional a todos nuestros servicios.

declare @type : *Service : @Transactional ;

Dicho aspecto, en conjunción con el anterior, provocaría que todos nuestros servicios fueran transaccionales

En Scala, de nuevo, haremos uso de funciones de alto nivel (high order functions) de manera muy similar a la solución de la caché descrita en la entrada anterior:

Si deseamos que un método de uno de nuestros servicios sea transaccional no tendremos más que extender de la clase abstracta y utilizar la HOF anterior:

Si comparamos las soluciones aportadas por cada una de las dos alternativas tendremos que
  • En el caso de AspectJ no necesitamos anotar cada método de manera independiente puesto que podríamos hacer uso de anotaciones a nivel de clase. El nivel de granularidad que podemos alcanzar está definido a nivel de método (aunque tampoco es un problema excesivamente grande puesto que podríamos utilizar el patrón Extract Method para extraer la funcionalidad transaccional a un método sobre el cual podríamos aplicar nuestro aspecto).
  • En el caso de Scala necesitaremos recubrir nuestra lógica con la HOF definida en nuestro ejemplo anterior. En esta situación, el nivel de granularidad es notablemente superior puesto que podemos recubrir una simple parte de nuestro método
Durante esta segunda entrada hemos vuelto a confrontar Scala y AspectJ a la hora de aplicar una funcionalidad transversal, intentando plasmar las ventajas y desventajas de cada uno de ellos.

En la siguiente y última entrada de esta serie analizaremos cómo aunar lo mejor de ambos mundos en un ejemplo práctico que nos sirva como base para futuros ejemplos.

Como siempre podéis encontrar el código fuente de todos los ejemplos del blog (o casi todos :) ) en mi cuenta de GitHub, bajo el proyecto blog-examples.

Hasta pronto!

PD: perdón por el ladrillo de entrada :(.

martes, junio 21, 2011

AspectJ y Scala (I)

¿Podemos establecer alguna conexión entre estos dos mundos? Si nos detenemos por un instante veremos que comparten muchas más cosas de las que a simple vista podría parecer:
  • Son lenguajes de tipado estático.
  • Ambos producen código compatible con la máquina virtual de Java (JVM)
  • Las funciones de alto nivel de Scala comparten algunas características con los advices de AspectJ
  • Los traits de Scala comparten algunas características con el static crosscuting de AspectJ.

Durante el resto de la entrada que nos ocupa (y la siguiente) realizaremos una pequeña comparativa en la que pondremos de manifiesto el modo en que cada uno de estos lenguajes resuelve determinado tipo de problemas. Pongámonos manos a la obra.

Cachés

A través de este ejemplo se pretende presentar el modo en que ambos lenguajes solucionan el problema de aplicar una funcionalidad transversal en un punto determinado del código. Implementaremos un mecanismos de caché extremadamente sencillo de manera que podamos centrar nuestra atención en los aspectos relevantes de las alternativas que estamos planteando

En el caso de AspectJ se está tendiendo a utilizar las anotaciones como un elemento de marcado (y parece que está siendo recibido con una aceptación más que notable). Nuestro primer paso será definir la anotación con la que realizaremos el "marcado" de aquellos métodos que deseamos establecer como cacheados:

@Retention(RetentionPolicy.RUNTIME)
public @interface Cachable {
   String scriptKey () default "";
}


El atributo keyScript de la anotación anterior actuaría (en una caché real), como un pequeño lenguaje de scripting de manera que pudiera ser evaluado por la caché para generar la clave bajo la que se almacenará una determinada llamada (obviaremos esta parte para intentar no distraernos de nuestro principal objetivo).

Una vez definido el elemento de marcado, definiremos nuestro aspecto, el cual será capaz de capturar las llamadas de aquellos métodos anotados con @Cacheable y realizar la lógica necesaria de nuestra sencilla caché



En el caso de Scala llevaremos a cabo nuestra funcionalidad transversal mediante el uso de funciones de primer nivel (high order functions)

Gracias a la funcionalidad anterior, podemos pasar al método cache una función que será ejecutada en caso de ser necesario, devolviendo los valores de la caché en el supuesto de que haya sido calculado en un paso anterior.

Si comparamos las dos alternativas que hemos planteado hasta este momento:
  • En el caso de AspectJ, cada uno de los elementos que deseamos cachear tendremos que anotarlos con, valga la redundancia, la anotación que hemos definido anteriormente. Mientras tanto, en el caso de Scala, tendremos que recubrir cada uno de los métodos que deseamos cachear con la función de alto nivel (high order function).
  • En el caso de AspectJ estamos utilizando un "lenguaje" externo que actua como elementos de las claves de nuestra caché mientras que en el caso de Scala estamos utilizando el propio lenguaje de manera nativa, con el consiguiente beneficio que ello conlleva.
Esta ha sido nuestra primera aproximación a Scala y AspectJ. Durante las siguientes entradas analizaremos algunos ejemplos adicionales y veremos como podemos integrar lo mejor de ambos mundos.

Podéis encontrar el código fuente de los ejemplos anteriores en el siguiente repositorio de GitHub, concretamente en los proyectos AspectJCacheExample y ScalaCacheExample.

Hasta pronto!

    viernes, junio 03, 2011

    Mecanismo de Load Time Weaving (LTW)

    Durante la última entrada analizábamos de manera introductoria los diferentes mecanismos de tejido ofrecidos por AspectJ así como las principales características de los mismos. Durante esta entrada nos acercaremos un poquito más al mecanismo de tejido en tiempo de carga, por normal general más desconocido, desde mi punto de vista, que el mecanismo de tejido en tiempo de construcción (durante todos los ejemplos que hemos visto en anteriores entradas siempre hemos utilizado el tejido en tiempo de construcción).

    Los pasos que tenemos que seguir cuando utilizamos el tejido en tiempo de carga son los siguientes:

    • Iniciar nuestra aplicación con el archivo aspectjweaver.jar el actuando como un agente (hablaremos de agentes en otra entrada). Para ello podríamos utilizar una línea como la siguiente
    java -javaagent:/aspectjweaver.jar
    • Durante el proceso de inicialización del agente (llevado a cabo por la máquina virtual) el propio agente recupera aquellos archivos existentes en el classpath que coincidan con META-INF/aop.xml (en el caso de encontrar múltiples llevará a cabo la combinación de los mismos).
    • Carga de los aspectos indicados.
    • El agente se registra como un listener del evento de carga de clases de la máquina virtual. Mediante este mecanismo se tiene acceso a la definición de la clase, permitiendo incluso la modificación de la misma.
    • Continua el proceso normal de carga de nuestro aplicación.
    • Cada vez que una nueva clase es cargada la máquina virtual notifica al agente dicha situación. En ese momento es posible examinar la clase en cuestión y determinar si algunos de los aspectos cargados con anterioridad necesita ser tejido. En caso afirmativo, la clase será tejida con el aspecto en cuestión, retornando el bytecode resultante a la máquina virtual.
    • La máquina virtual utiliza el bytecode resultante como elemento de definición de la clase.
    Mediante el conjunto de pasos anteriores, aquellas clases que hayan hecho matching con las definiciones de nuestros aspectos tendrán incorporada la funcionalidad definida en éstos últimos.

    El agente anterior utiliza un interfaz de la máquina virtual conocido como JVMTI (Java Virtual Machine Tools Interface) que ha sido introducido a partir de Java 5. En el caso de que utilicéis una versión anterior podréis hacer uso de este mecanismo mediante una versión basada en classloaders específicos.

    viernes, mayo 27, 2011

    Weaving en AspectJ (I)

    Hace un tiempo escribí una serie de entradas relacionadas con la orientación a aspectos en general y AspectJ en particular (podéis encontrar dichas entradas aquí) que posteriormente agrupé en un documento.

    Hasta este momento nos hemos centrado en la teoría de la orientación a aspectos, cómo escribir los mismos y algunas de sus posibles aplicaciones. Sin embargo, no hemos prestado especial atención al modo en el que los aspectos se combinan con las clases para componer nuestro sistema final. Durante esta entrada y las siguientes profundizaremos en diferentes aspectos del proceso de tejido (weaving).

    El mecanismo de tejido es aquel que nos permite combinar la definición de nuestras clases y de nuestros aspectos en un ente ejecutable que conforma nuestro sistema final.

    Podemos clasificar los diferentes modelos en base al momento en el que llevan a cabo el tejido. En este caso tendríamos:

    • Build time. Las clases y los aspectos son combinados durante el proceso de construcción de nuestra aplicación.
    • Load time (LTW). En este caso el tejido se realiza en el momento en el que una clase se carga en la máquina virtual, por lo que en este caso no será necesario llevar a cabo ningún proceso adicional en nuestro proceso de construcción. 
    Otra posible clasificación podría establecerse en función del los tipos de entrada del proceso de weaving
    • Source code weaving. La entrada al proceso son archivos de código fuente (clases y aspectos)
    • Weaving binario (bytecode). La entrada al proceso es el bytecode obtenido como resultado del proceso de compilación de nuestras clases
    De manera esquemática, a continuación se reflejan las diferente combinaciones ofrecidas por el weaver de AspectJ:

    • Tiempo de construcción: las entradas admitidas son tanto código fuente como clases compiladas (bytecode)
    • Tiempo de carga: las entradas pueden ser bytecode o un archivo xml (lo veremos más adelante)

    Con esta pequeña entrada nos hemos aproximado de manera sencilla a los procesos de tejido presentes en AspectJ de una manera teórica. En futuras entradas analizaremos con un poco más de detalle los contenidos aquí presentados, haciendo especial hincapié en los mecanismos de tejido en tiempo de carga.

    Hasta pronto!

    lunes, febrero 21, 2011

    DDD, Spring y AspectJ: inyección de depencias (II)

    En la entrada anterior analizabamos el uso de la anotación Configurable y cómo esta nos ayudaba a realizar la inyección de dependencias en nuestro objetos de dominio, ayudándonos de esta manera a realizar nuestro diseño siguiendo una filosofía orientada al dominio (podréis encontrar mucha información, artículos, referencias, ponencias, . . ., relativas a este tema en http://domaindrivendesign.org/).

    Como ya comentamos anteriormente el uso de la anotación Configurable era beneficioso aunque si la clase anotada se instancia un gran número de veces podríamos incurrir en una notable pérdida de rendimiento puesto que la anotación anterior hace uso de la reflectividad. Adicionalmente, indicábamos que  otro de los "problemas" era que nuestro código estaba acoplado a la plataforma, en este caso, Spring. Durante esta entrada plantearemos otro mecanismo de inyección de dependencias en nuestros objetos de dominio: inyección basada en interfaces de dominio.

    Partiendo del ejemplo analizado en el ejemplo anterior, definiremos un nuevo interface PricingStrategyClient que presentará el método setPricingStrategy(PricingyStrategy pricingStrategy) . Todas aquellas entidades de nuestro dominio que necesiten esta funcionalidad deberán implementar el interfaz anterior.

    Hasta el momento no hemos anotado nuestra clase con ningún artefacto adicional (@Configurable) por lo que ahora tendremos que escribir nuestro propio aspecto para inyectar las dependencias en nuestra clase de dominio. Para llevar a cabo este trabajo extenderemos un aspecto abstracto disponible en spring-aspects-3.0.5.jar (los ejemplos de estas entradas están desarrollados con la versión 3.0.5 de Spring) de tipo GenericInterfaceDrivenDependencyInjectionAspect. De manera resumida, este aspecto base determina cuando se crea una nueva instancia (o serializa) de nuestra clase e invoca al método configureBean. Nosotros únicamente deberemos implementar el método configure tal y como se muestra en el siguiente fragmento de código:


    public aspect PricingStrategyDIAspect extends
      GenericInterfaceDrivenDependencyInjectionAspect {
    protected PricingStrategy pricingStrategy;
    public PricingStrategy getPricingStrategy() {
    return pricingStrategy;
    }
    public void setPricingStrategy(PricingStrategy pricingStrategy) {
    this.pricingStrategy = pricingStrategy;
    }
    @Override
    protected void configure(PricingStrategyClient bean) {
    bean.setPricingStrategyClient(pricingStrategy);
    }
    }

    Como último paso de nuestra nueva solución sólo tendremos que definir nuestro nuevo aspecto en el contexto de aplicación:
    <bean id="pricingStrategy" class="com.blogspot.miguelinlas3.springexamples.ddd.domain.strategy.SimplePricingStrategy"/>

    <bean class="com.blogspot.miguelinlas3.springexamples.ddd.domain.interfacedibased.PricingStrategyDIAspect"
    factory-method="aspectOf">
    <property name="pricingStrategy" ref="pricingStrategy"/>
    </bean>
    Una posible mejora sería incluir el aspecto anterior como un aspecto estático dentro del interfaz PricingStrategyClient de modo que estaríamos enfatizando la relación entre ambos aunque esto os lo dejo como ejercicio ;)

    lunes, febrero 07, 2011

    DDD, Spring y AspectJ: inyección de depencias (I)

    El paradigma de orentación a objetos promueve que los objetos deberían tener datos y comportamiento. Sin embargo, en la realidad esto no se cumple en muchas ocasiones; especialmente en los objetos de dominio, donde habitualmente las clases se convierten en meros contenedores de información que se van pasando de unas capas a otras, delegando la funcionalidad en capas de servicio externas, creando un modelo anémico (para más información podéis acudir a http://www.martinfowler.com/bliki/AnemicDomainModel.html).

    A lo largo de las siguientes entradas promoveremos el paradigma de diseño dirigido por dominio y analizaremos como podremos enriquecer nuestros objetos de dominio mediante la inyección de dependencias. Para aquellos que estéis familiarizados con las últimas versiones de Spring, estas entradas no serán más que un paseo por los internals de @Configurable (anotación que podemos encontrar las versiones más recientes de este popular framework) unidos a un poco de teoría.

    Imaginemos por un momento que estamos desorrollando una tienda online para la venta de libros electrónicos y que la siguiente clase modela nuestra entidad de dominio libro. Como podemos observar estamos delegando el cálculo del precio del libro en una clase independiente la cual será la encargada de realizar los cálculos necesarios (de este modo podremos configurar el precio del libro en función de nuestras necesidades). Y ahora: ¿cómo llevamos a cabo la inicialización del atributo pricingStrategy ?

    Desde aquí vamos a promover el uso de inyección de dependencias, de manera que añadiendo la infraestructura necesaria (en ese caso un simple setter para el atributo en cuestión) podremos delegar en un contenedor la responsabilidad de inyectar las dependencias necesarias en el momento en el que se instancie nuestro objeto. Siguiendo esta filosofía podríamos intercambiar las estrategias de cálculo de precios sin más que realizar una ligera modificación en la configuración, sin la necesidad de modificar nuestro código fuente. Este enfoque también nos permite llevar a cabo nuestras pruebas unitarias de manera extremadamente sencilla.

    Antes de comenzar a escribir nuestros propios aspectos (lo dejaremos para la próxima entrada) veamos cómo podríamos inyectar las dependencias en nuestro objeto de dominio mediante el uso de spring y un poco de configuración.

    En primer lugar tendremos que indicarle a spring que nuestra clase de dominio Book necesita que sus dependencias sean inyectadas: para ello anotamos la definición de nuestra clase con @Configurable y añadimos un setter para nuestro atributo de clase pricingStrategy. Adicionalmente, anotaremos  el setter anterior (o la propiedad) con @Autowired. Finalmente, definiremos en el contexto de aplicación de spring una implementación concreta del interfaz PricingStrategy e indicamos que estamos haciendo uso @Configurable mediante la instrucción .

    Con la sencilla configuración anterior, cada vez que instanciemos un objeto de la clase Book mediante el operador new: Book book = new Book() el atributo pricingStrategy será inyectado con la referencia adecuada.

    El uso de @Configurable hace uso de la reflectividad por lo que en aquellos objetos que se instancian muy a menudo podríamos incurrir en un serio problema de rendimiento. Adicionalmente, el uso de esta anotación acopla nuestro código a Spring, creando una dependencia hacia el framework.

    En la siguiente entrada analizaremos el uso de interfaces de dominio y escribiremos nuestros propios aspectos como alternativa a la solución presentada a lo largo de esta entrada.


    martes, junio 22, 2010

    Aspect Oriented Programming Intro Guide

    Hace tiempo que habíamos comenzado una serie de post relativos a la programación orientada a aspectos y los tenemos dispersos a lo largo de diferentes entradas. He decido recolectar todos los post publicados junto a los que estaban por venir y hacer una sola entrega.

    Espero que os guste:


    Está escrito en docbook por lo que si alguien está interesado en los fuentes no tiene más que pedírmelos.

    Hasta pronto!

    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!!

    lunes, mayo 03, 2010

    AOP: non kinded pointcuts

    En la entrada anterior analizábamos aquellos tipos de pointcuts en los que los joint point seleccionados encajaban en una determinada categoría (de ahí su nombre).

    El mecanismo implementado mediante non-kinded pointcuts permite la selección de joint points basados en criterios adicionales a las signaturas vistas anteriormente. Por ejemplo, podremos seleccionar todos los joint point donde el objeto this es de un determinado tipo. Dicho joint point incluiría las llamadas a métodos, ejecuciones, manejadores de excepciones, etc.

    A continuación, a través de ejemplos, veremos los diferentes tipos de non-kinded pointcuts ofrecidos por AspectJ.

    Non-kinded pointcuts basados en control de flujo

    PointcutDescripción
    cflow( execution(* TransactionManager. commit() ))Selecciona todos los joint points en el flujo de la ejecución de cualquier operación commit de la clase TransactionManager, incluyendo la ejecución del propio método.
    cflowbellow( execution(* TransactionManager. commit() ))Selecciona todos los joint points en el flujo de la ejecución de cualquier operación commit de la clase TransactionManager, excluyendo la ejecución del método.
    cflow(execution(@Secured * * (..)))Todos los joint points en el flujo de la ejecución de cualquier método anotado con la anotación Secured
    cflow(transacted())Cualquier joint point en el flujo de ejecución de los joint points seleccionados por el pointcut transacted()
    Non-kinded pointcuts basados en la estructura léxica
    Dentro de este tipo de pointcuts tenemos dos categorías:
    • within(TypeSingnature): selecciona cualquier joint point que aparece en el cuerpo de las clases y aspectos que concuerden con el tipo especificado.
    • withincode(ConstructorSignature),withincode(MethodSignature): selecciona cualquier joint point que aparezca dentro de un método o un constructor, incluyendo la definición de cualquier clase local que pudiera aparecer en los mismos.

    PointcutDescripción
    within(User)Selecciona todos los joint points que aparecen dentro de la clase User
    within(User+)Selecciona todos los joint points que aparecen dentro de la clase user y cualquiera de sus clases derivadas
    within(@Transactional *)Selecciona todos los joint points que aparecen dentro de cualquier clase que se encuentre marcada con la notación Transactional
    withincode(* TransactionManager. retrieve*(..))Selecciona todos los joint points que parecen dentro de cualquier método de la clase TransactionManager cuyo nombre comience por retrieve
    Non-kinded pointcuts de ejecución
    Este tipo de pointcuts permite seleccionar joint points en base al tipo de los objetos en tiempo de ejecución. De este modo se dispone de:
    • this(). Acepta dos formas diferentes: this(ObjectIdentifier) o this(Type). Seleccionará aquellos joint points cuyo objeto this sea del tipo (o el objeto) indicado.
    • target(). Similar al concepto anterior, aunque en este caso, se utilizar el target del joint point en lugar del this.

    PointcutDescripción
    this(User)Selecciona cualquier joint point en que la expresión this instanceof User sea cierta. Así por ejemplo, seleccionará las llamadas a métodos o accesos a campos donde el objeto actual sea de tipo User o cualquier de sus subclases.
    target(User)Selecciona cualquier joint point en el que el objeto sobre el que se realiza la llamada al método es instanceof User
    Non-kinded pointcuts sobre argumentos
    Este tipo de pointcuts permite seleccionar joint points en base al tipo de los argumentos en tiempo de ejecución. Veamos los distintos tipos de argumentos, en función del tipo del joint point :
    • En el caso de los joint points manejadores de excepciones el argumento será la excepción manejada.
    • En los métodos y constructores, los argumentos serán los argumentos del método y constructor.
    • En los accesos de modificación de un campo, el argumento será el nuevo valor que va a tomar dicho campo.

    PointcutDescripción
    args(User, . . , String)Selecciona cualquier joint point de tipo método o constructor en el que el primer argumento es de tipo User (o cualquiera de sus subclases), y el último argumento es de tipo String.
    args (SqlException)Selecciona cualquier joint point con un único argumento de tipo SqlException. Seleccionaría cualquier método o constructor que esperase un único argumento de tipo SqlException, un acceso de escritura a un campo estableciendo un nuevo valor de tipo SqlException, y también seleccionaría un manejador de excepciones de tipo SqlException
    Non-kinded pointcuts condicionales
    Este tipo de pointcuts permiten seleccionar joint points basados en una expresión condicional.

    PointcutDescripción
    if(debug)Selecciona cualquier joint point donde el campo estático debug (en la definición del aspecto) toma el valor cierto

    Poco a poco vamos completando, de manera sencilla, una idea general de los mecanismos que nos ofrece la orientación a aspectos en general, y AspectJ en particular. En la siguiente entrada analizaremos los conceptos de crosscutting dinámico y los mecanismos disponibles para la modificación del comportamiento de un programa en un determinado joint point.

    domingo, mayo 02, 2010

    AOP: kinded pointcuts

    Continuando la serie de posts relativos a la programación orientada a aspectos y, concretamente, el lenguaje AspectJ, durante esta entrada analizaremos la implementación que este último hace de los pointcuts.

    AspectJ ofrece dos mecanismos diferentes que permiten a los pointcuts realizar la selección de los joint points:
    • Kinded pointcuts. En este caso, los pointcuts seleccionan categorías de joint points (de ahí su nombre). Así, por ejemplo, se ofrece la posibilidad de realizar matching sobre la ejecución de un método.
    • Non-kinded pointcuts. Se seleccionan los joint point en base a la información de la que disponen, como los tipos en tiempo de ejecución o su contexto. En esta situación se seleccionan joint points de cualquier clase, siempre y cuando satisfagan la condición descrita.
    A continuación analizaremos los kinded pointcuts y dejaremos para la siguiente entrada la segunda categoría.
    • Kinded Pointcuts
    Joint Points expuestos y tipos de pointcuts
    Categoría de Joint Point Sintaxis del pointcut
    Ejecución de un método execution(MethodSignature)
    Llamada a un método call(MethodSignature)
    Ejecución de un constructor execution(ConstructorSignature)
    Llamada a un constructor call(ConstructorSignature)
    Inicialización de una clase staticinitializaction(TypeSignature)
    Acceso de lectura a un campo get(FieldSignature)
    Acceso de escritura a un campo set(FieldSignature)
    Ejecución de un manejador de excepciones handler(TypeSignature)
    Inicialización de un objeto initialization(ConstructorSignature)
    Pre-inicialización de un objeto preinitialization(ConstructorSignature)
    Ejecución de un advice adviceexecution()


    Por ejemplo, si deseamos seleccionar todas las llamadas a los métodos públicos commit() de la clase TransactionManager, escribiríamos un pointcut similar al siguiente:
    call(public void TransactionManager.commit(..))

    En la siguiente entrada analizaremos en detalle la segunda categoría de pointcuts ofrecida por AspectJ

    domingo, abril 18, 2010

    Some signatures examples

    En la última entrada dedicada a AOP (podeis verla aquí ) analizamos de manera teórica las características básicas del lenguaje de definición de pointcuts. A continuación realizaremos una serie de ejemplos que intentarán clarificar los conceptos teóricos vistos en anteriores entradas.

    • Signaturas de tipos
    Table 1.2. Ejemplos de signaturas de tipos
    Patrón de la signaturaDescripciónEjemplos de tipos concordantes
    AstVisitorEl tipo AstVisitorSólo el tipo AstVisitor concuerda (ni tipos base ni derivados)
    *AstVisitorCualquier tipo cuyo nombre termine en AstVisitorPor ejemplo, HighlightingAstVisitor o SemanticAstVisitor concuerdan con el patrón
    java.*.DateEl tipo Date en cualquier subpaquete directo del paquete javajava.util.Date o java.sql.Date son ejemplos de tipos concordantes
    javax..*Cualquier tipo en el paquete javax y en sus subpaquetes (tanto directos como indirectos)Cualquier tipo en el paquete javax.security así como cualquiera de sus subpaquetes indirectos como javax.security.auth.login
    javax..*ModelTodos los tipos en el paquete javax (subpaquetes directos e indirectos) cuyo nombre termine en Model, y todos los subtipos de estos.TableModelTreeModel y sus subtipos como DefaultTreeModel oDefaultTableModel.



    • Signaturas de tipos: anotaciones
    Table 1.3. Ejemplos de signaturas de tipos: anotaciones
    Patrón de la signaturaDescripciónEjemplos de tipos concordantes
    @Secured UserEl tipo User anotado con la anotación Secured@Secured class User{. . .}
    @Entity *Cualquier tipo anotado con la anotación Entity@Entity class Section {. . .},@Entity class Report {. . .}
    @Transactional* Manager+El tipo Manager y cualquiera de sus clases derivadas que estén anotados con una anotación cuyo nombre comience por Transactional@TransactionalDefault class Manager{},@TransactionalOracle class OracleManager extends Manager{}




    • Signaturas de tipos: generics
    Table 1.4. Ejemplos de signaturas de tipos: generics
    Patrón de la signaturaDescripciónEjemplos de tipos concordantes
    Map El tipo Map cuyo primer argumento genérico está fijado a Integer y el segundo aStringEn este caso únicamente concordará el tipo Map
    *Cualquier tipo genérico cuyo único argumento genérico sea de tipo UserCollection,List, . . .
    CollectionEl tipo Collection con un parámetro de tipo User o derivadoCollectionCollection, . . .
    CollectionEl tipo Collection, cuyo parámetro será uno de los tipos base de UserCollectionCollection, asumiendo que, User extiende o implementa, directa o indirectamente, Serializable y BaseUser


    • Combinando pointcuts: operadores
    Table 1.5. Signaturas de tipo: operadores
    Patrón de la signaturaDescripciónEjemplos de tipos concordantes
    !CollectionCualquier tipo excepto CollectionUser,ModelList (aunque sea subclase de Collection)
    Set || MapLos tipos Set o MapLos tipos Set y Map únicamente
    !@TransactionalCualquier tipo que se encuentre anotado por la anotación Transactionalclass NonTransactionalManager{}
    @Serializable @Encrypted *Cualquier tipo que se encuentre anotado por las dos anotaciones@Serializable @Encrypted class UserCredentials{}
    (@Serializable || @Encrypted) *Cualquier tipo que esté anotado por alguna de las dos anotaciones@Serializable class User {}


    • Signaturas de métodos y constructores
    Table 1.6. Signaturas de métodos
    Patrón de la signaturaDescripciónEjemplos de métodos concordantes
    public void User.set*(*)Cualquier método público de la clase User cuyo nombre comience por set, cuyo tipo de retorno sea void, y que espere un único argumentoclass User{ public void setName(String name){} }
    public void User.*()Cualquier método público de la clase User cuyo tipo de retorno sea void y que no espere argumentosclass User { public void updateInfo(){}}
    public * User.*()Cualquier método público de la clase User que no espera argumentos y retorna cualquier tipoclass User{ public UserInfo getUserInfo(){}}
    public * User.*(..)Cualquier método público de la clase User que retorna cualquier tipo, y espera cualquier número y tipo de argumentos (incluido cero)class User{ public UserInfo getUserInfo(){},public Date updatesBetween(Date first,Date last){}}
    * *.*(..) o * * (..)Cualquier método independientemente de su tipo, del tipo de retorno, de su nombre y de los argumentos que espereCualquier método del sistema
    !public * User.*(..)Cualquier método que no sea público (privado,protegido o paquete)class User {protected getUserInfo(){}}
    * * (..) throws Recognition ExceptionCualquier método que declare que puede lanzar una excepción de tipoRecognitionExceptionclass AntlRBasedParser { public void parser(String file) throws RecognitionException}
    * User+.*(..)Cualquier método en la clase User y sus subclasesclass UserDetails extends User {}
    User UserService.*(..)Cualquier método del tipo UserService cuyo tipo de retorno sea User. Si alguna de las subclases sobrescribe el tipo de retorno con un subtipo de User (mediante el tipo de retorno covariante introducido en Java 5) también será seleccionado.class UserService{public User retrieveUser(String name){}} class DetailUserService extends UserService{public DetailedUser retrieveUser(String name){}}

    La signatura en el caso de los constructores difiere de la signatura de métodos en los siguientes aspectos:
    • Los constructores no tienen tipo de retorno por lo que no se permite utilizar el valor de retorno en la especificación de un pointcut de este tipo.
    • Dado que los nombres de los constructores no pueden ser libres (tienen que utilizar el mismo nombre de la clase), la parte de la signatura destinada al nombre será sustituida con la palabra new.
    • Por último, puesto que los constructores no pueden ser estáticos, no se podrá utilizar la palabra reservada static
    • Signaturas de campos
    Table 1.7. Signaturas de campos de clase
    Patrón de la signaturaDescripciónEjemplos de campos concordantes
    private String User.usernameCampo privado (tanto campo de una instancia como estático) de la clase Userclass User { private String username;}
    * User.*Cualquier campo de la clase User independiente de sus calificadores, tipo y nombre.class User{private String username;protected int credits;private UserDetails details;}
    * User+.*Cualquier campo de la clase User y todas sus subclases, independientemente de sus calificadores, tipo y nombre.El ejemplo de la fila anterior o class SpecificUser extends User {private String address;}


    jueves, marzo 25, 2010

    AOP Pointcuts Definition

    Durante entregas anteriores hemos analizado los beneficios de la orientación a aspectos, su Hype Cicle, o el modelo de joint point entre otras características. El conjunto formado por el post actual y el siguiente analizarán los conceptos básicos de los pointcuts junto una serie de sencillos ejemplos.

    Inicialmente, deberíamos tener claros los siguientes conceptos:
    • Pointcuts anónimos o con nombre: Se permite la declaración de pointcuts de cualquiera de los dos tipos. Los primeros son similares a las clases anónimas, y por tanto se definen en el lugar en el que se van a utilizar. En el caso de los segundos, podremos referenciarlos desde múltiples lugares, permitiendo de este modo su reutilización.
    • OperadoresAspectJ proporciona el operador unario de negación (!) y dos operadores binarios: && y ||, gracias a los cuales se permite construir reglas de matching complejas mediante la combinación de pointcuts más sencillos. Tanto la semántica como la precedencia es la misma que en el lenguaje Java. Así por ejemplo, en el caso del operador binario &&, se seleccionarán aquellos joint points que concuerden con los dos pointcuts que actúan como operando.
    Las signaturas son la base de la definición de los pointcuts. El lenguaje debe facilitar una manera sencilla que permita definir criterios de selección sobre los diferentes aspectos transversales que estamos implementando. En el caso de AspectJ, se utilizan expresiones regulares (wildcards) en combinación con las signaturas. Los siguientes wildcards son soportados:
    • * especifica cualquier número de caracteres, exceptuando el punto (.). En la signatura de un tipo denota una parte de un tipo o de un paquete. En otros patrones denota una parte del nombre (por ejemplo en métodos o campos).
    • .. determina cualquier número de caracteres, incluyendo en este caso cualquier número de puntos (.). En la signatura de un tipo representa cualquier paquete o subpaquete. En la signatura de un método representa cualquier número de argumentos.
    • + denota cualquier subtipo de un tipo determinado.
    Hasta este momento hemos estudiado, de manera resumida, los elementos básicos para la definición de los pointcuts; en la siguiente entrada describiremos, a través de una serie de ejemplos, los diferentes patrones de signaturas ofrecidos por AspectJ que pueden ser utilizados para seleccionar diferentes joint points.

    Hasta pronto!

    miércoles, noviembre 18, 2009

    JDT Weaving Service

    Tal y como comentabamos hace uno o dos posts, algunos equipos como AJDT o Scala IDE construyen el entorno de desarrollo para Eclipse basándose en el servicio de tejido de JDT (JDT Weaving Service).

    Vamos a realizar un pequeño ejemplo para nuestro lenguaje (ficticio) de programación Demo, que corre sobre la máquina virtual de Java, integrándolo en el modelo JDT (voy a asumir que se disponen de una serie de conocimientos básicos de Eclipse).

    En primer lugar necesitaremos tener disponibles las siguientes dependencias en nuestro entorno de desarrollo:
    • Eclipse 3.5 o superior.
    • AJDT 1.6.4 o superior (http://download.eclipse.org/tools/ajdt/35/update).
    • Servicio JDT de weaving debe estar activado.

    Con el objetivo de facilitar el seguimiento de este pequeño ejemplo creo que será mejor disponer del proyecto desde el principio, por lo que, desde vuestro cliente subversion preferido obteneis una copia de trabajo desde la siguiente dirección:

    http://reclipse.googlecode.com/svn/com.blogspot.miguelinlas3.ajdt.demo/trunk/

    (y haceis un Import del proyecto en el Eclipse si habeis obtenido la copia de trabajo desde un cliente externo).

    Una vez disponemos del proyecto accederemos al archivo plugin.xml y veremos los siguientes puntos de extensión:

    Los puntos de extensión org.eclipse.contribution.weaving.* nos permiten extender el modelo JDT, integrando en él los componentes de nuestro lenguaje.

    La clase principal es DemoSourceProvider, la cual contribuye al modelo mediante el punto de extensión org.eclipse.contribution.weaving.jdt.sourcetransformer.

    El objetivo principal de la clase anterior consiste en traducir nuestro lenguaje (completamente absurdo :) ). Si la definición de un archivo de código fuente de nuestro lenguaje fuese algo como lo siguiente:

    name: MyDemo {
    x: 10
    y: 5
    z: 12
    }

    el traductor intentará convertirlo en algo como lo que sigue:

    class MyDemo {
    int x= 10;
    int y= 5;
    int z = 12;
    }

    Intentad darle un vistazo al código (son cuatro clases con muy poco código) y mañana os dejaré un proyecto de ejemplo que haga uso de esta construcción para que veais que realmente se integra nuestro absurdo lenguaje!

    Hasta pronto!