Chapter 1. Aspect Oriented
Programming

              Table of Contents
              Motivaciones ..........................
Aspect Oriented Programming


Figure 1.1. Herramientas para nuevos lenguajes




Algunos de los problemas que se encuentra...
Aspect Oriented Programming


    Figure 1.2. Proceso de acoplamiento al JDT




    A lo largo de las siguentes secciones...
Aspect Oriented Programming


     fenómeno es conocido como scattering , puesto que el código relacionado con un mismo as...
Aspect Oriented Programming


       Figure 1.3. Diagrama de componentes de R-Eclipse




       La interpretación de la c...
Aspect Oriented Programming


       interesante destacar que muchos de estos nuevos competidores están en la fase de "exp...
Aspect Oriented Programming


               Aquellas aplicaciones que usen Spring como base de su arquitectura ya estarán...
Aspect Oriented Programming


         sería el encargado de realizar la implementación del módulo de lógica de negocio en...
Aspect Oriented Programming


       independientes del núcleo del sistema por lo que con unas nuevas reglas de "tejido" p...
Aspect Oriented Programming


       XP (siglas en inglés de Programación Extrema) siguiendo la práctica del principio YAG...
Aspect Oriented Programming


       encapsulan todo el comportamiento, mientras que AOP elimina estos niveles de control ...
Aspect Oriented Programming


Joint Point
        Como ya se definió anteriormente, un join point es un punto de ejecución...
Aspect Oriented Programming


Conceptos básicos
        A continuación se analizarán los elementos básicos que se deben co...
Aspect Oriented Programming


        Patrón de       la Descripción                               Ejemplos de tipos conco...
Aspect Oriented Programming


        Patrón de              la Descripción                               Ejemplos de tipo...
Aspect Oriented Programming


Signaturas de campos

       Table 1.7. Signaturas de campos de clase
        Patrón de     ...
Aspect Oriented Programming


Non-kinded pointcuts
         Este tipo de pointctus permiten la selección de joint points b...
Aspect Oriented Programming


              • target(). Similar al concepto anterior, aunque en este caso, se utilizar el ...
Aspect Oriented Programming


        • advice: qué deseamos hacer

        • pointcuts: donde aplicamos el advice anterio...
Aspect Oriented Programming


         Object retValue = proceed(user);
         System.out.println("Finished secured oper...
Aspect Oriented Programming


Diferencias
         En comparación con los métodos, los advices:

         • La declaración...
Aspect Oriented Programming


         }

         registra el resultado de todas las operaciones de la clase ServiceManag...
Aspect Oriented Programming


          • Ejecución de lógica adicional antes y después de la ejecución de un joint point,...
Aspect Oriented Programming


               • En el caso en el que el tipo de retorno no sea primitivo, AspectJ realizará...
Aspect Oriented Programming


             La sintaxis general de un aspecto es la siguiente:


             [access speci...
Aspect Oriented Programming


               Evidentemente, esto pretende ser un primer acercamiento al mundo de AOP, y co...
Aspect Oriented Programming


Solución tradicional
            Sin la utilización de una tecnología como AOP disponemos de...
Aspect Oriented Programming



 public aspect WormholeAspect {
  pointcut callerSpace(caller context) : <caller pointcut> ...
Aspect Oriented Programming


Hilos seguros
        La amplia difusión de los procesadores con múltiples núcleos, el uso d...
Aspect Oriented Programming


            // realizamos la operación, en este caso,
            // aumentar el crédito del...
Aspect Oriented Programming introduction
Aspect Oriented Programming introduction
Aspect Oriented Programming introduction
Aspect Oriented Programming introduction
Upcoming SlideShare
Loading in...5
×

Aspect Oriented Programming introduction

2,036

Published on

Intro to aspect oriented programming and AspectJ

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
2,036
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
82
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Aspect Oriented Programming introduction

  1. 1. Chapter 1. Aspect Oriented Programming Table of Contents Motivaciones ................................................................................................................ 1 Evolución de las metodologías de programación ................................................................ 3 AOP en el mundo real ................................................................................................... 4 Hype Cycle .......................................................................................................... 4 ¿Dónde se utiliza AOP? ......................................................................................... 6 Introducción a AOP ...................................................................................................... 7 Metodología AOP ................................................................................................. 7 Anatomía de un lenguaje orientado a aspectos ........................................................... 8 Beneficios de la metodología AOP ........................................................................... 9 Verdades y mentiras sobre AOP ............................................................................ 10 Visión general de AspectJ ............................................................................................. 11 Introducción a AspectJ ......................................................................................... 11 Modelo de Joint Point ......................................................................................... 11 Crosscutting dinámico .......................................................................................... 18 Aspectos ............................................................................................................ 24 Resumen ............................................................................................................ 25 Ejemplos reales ................................................................................................... 26 Bibliografía ................................................................................................................ 33 Motivaciones Antes de profundizar en la temática propia de la orientación a aspectos y sus aplicaciones en el mundo real, sería conveniente describir las motivaciones principales que han ocasionado la escritura de este capítulo de la memoria. La primera de ellas ha sido la intención de introducir al lector en algunas de las técnicas de desarrollo avanzadas utilizadas en la actualidad por los equipos de desarrollo de software más vanguardistas. La segunda está relacionada con el desarrollo de nuevas herramientas de programación y, por ende, con la herramienta desarrollada como ejemplo de este proyecto: R-Eclipse. Si el lector desconoce muchos de los términos definidos en esta sección no se preocupe, a lo largo del capítulo comprenderá la terminología aquí utilizada. En los últimos años han surgido muchos lenguajes similares a Java que se ejecutan sobre la propia máquina virtual de este último (JVM), lo cual es un indicador bastante fiable del futuro del desarrollo de aplicaciones sobre la plataforma Java.1Sin embargo, Eclipse y su solución JDT, no ofrecen soporte para la integración de estos nuevos lenguajes en su modelo Java subyacente. Por ejemplo, no existe un modo de incluir jerarquías de tipos,jerarquías de llamadas, etc. 1 R Eclipse no ha utilizado el enfoque descrito en esta sección por dos motivos principalmente; el primero de ellos es que R no es un lenguaje similar a Java ni corre bajo su máquina virtual. El segundo de ellos consiste en que el framework base de desarrollo, DLTK, no utiliza la arquitectura descrita en estas líneas. 1
  2. 2. Aspect Oriented Programming Figure 1.1. Herramientas para nuevos lenguajes Algunos de los problemas que se encuentran los ingenieros de software cuando intentan construir herramientas para algunos de los lenguajes descritos anteriormente son: • Necesidad de acoplarse al modelo ofrecido por JDT. No se puede. • Necesidad de acoplarse al motor de indexación de código fuente. No se puede. • Necesidad de acoplarse al parser de JDT. No se puede. Una aproximación utilizada por muchos entornos de desarrollo de lenguajes actuales como pueden ser Scala o Groovy es la utilización del servicio de tejido de JDT. Las características principales son: • Utiliza AspectJ añadiendo puntos de unión al modelo JDT • Se encapsula en puntos de extensión de Eclipse, haciéndolo extensible a terceras partes. • Utiliza un proyecto de Eclipse conocido como Equinox Aspects que soporta el proceso de tejido en tiempo de carga en un entorno OSGI. El siguiente gráfico ilustra como funciona el proceso anterior: 2
  3. 3. Aspect Oriented Programming Figure 1.2. Proceso de acoplamiento al JDT A lo largo de las siguentes secciones se introducirá la terminología AOP así como la definición de sus elementos más relevantes. Evolución de las metodologías de programación La ingeniería del software ha recorrido un largo camino desde sus inicios: desde el código máquina hasta la orientación a objetos pasando por los lenguajes procedimentales. Analizando de manera somera la evolución de las metodologías de programación observamos cómo el modelo procedimental introdujo la abstracción funcional y la orientación a objetos los conceptos de composición, reutilización y encapsulación. Este último paradigma ha sido, posiblemente, una de las mayores contribuciones al mundo del software durante toda su historia y,en la actualidad, es el paradigma de programación utilizado en la mayor parte de los grandes desarrollos de software. Entre todas las ventajas del paradigma de orientación a objetos, en el párrafo anterior se destacaba, no al azar, la reutilización y la encapsulación pero . . . ¿siempre es posible ubicar en el mismo lugar todas las tareas relacionadas con una misma función?, ¿en alguna ocasión has visto un fragmento de código que no encaja en ninguna clase en particular?. Imaginemos un sistema en el que determinadas operaciones necesitan llevar a cabo una gestión de transacciones. Resultaría sumamente complicado,por no decir imposible, modularizar todos los conceptos necesarios en un sistema transaccional: inicialización, comunicación con el manager de transacciones, rollbacks, . . . Esto es debido a que todos los métodos de un objeto que requieren un control de transacciones necesitan ser conscientes que la operación a realizar se ejecuta bajo un control transaccional, para comportarse de manera acorde al funcionamiento requerido. Resumiendo, el código encargado de la gestión de transacciones estaría ubicado en cada uno de los objetos que lo necesite. La solución comunmente adoptada a este problema es copiar y pegar el código en todas aquellas partes en las que es necesario, resultando evidente, que dicha solución no permite disponer de una buena modularización y dificulta las labores de mantenimiento y evolución del código. Este 3
  4. 4. Aspect Oriented Programming fenómeno es conocido como scattering , puesto que el código relacionado con un mismo asunto se encuentra repartido a lo largo de todo el sistema. Sumemos, además, que los módulos que contienen código relativo a muchos asuntos diferentes tienden a ser, en general, menos reutilizables. El fenómeno en el que diferentes asuntos son entremezclados en el código se conoce como tangling . La problemática introducida en los párrafos anteriores, tangling y scattering , afecta al diseño y desarrollo de software de muchas y diferentes maneras. Veamos alguna de las principales: • Trazabilidad reducida. La implementación de diferentes conceptos en un mismo lugar dificulta el proceso de establecimiento de una relación entre los requisitos y su implementación y viceversa. Así por ejemplo, para completar la trazabilidad del requerimiento de autenticación podríamos llegar a examinar todos los módulos del sistema. • Baja producitividad. Varios conceptos no relacionados que han sido implementados en un mismo lugar ocasiona que la atención se disperse a lo largo de todos ellos. La falta de un objetivo concreto provoca que los desarrolladores se vean obligados a manejar diferentes conceptos no relacionados con el objetivo principal. En muchas ocasiones,la construcción de un módulo implicará la participación de diversos desarrolladores, con diferentes perfiles, o que el desarrollador tenga conocimiento en todos los ámbitos que se están incluyendo en el nuevo módulo. • Escasa reutilización del código. Los conceptos implementados en un módulo podrían ser requeridos por otros módulos con funcionalidades similares • Baja calidad. El code tangling provoca que el código sea más difícil de leer, de ubicar los potenciales problemas y de hacer revisiones del mismo. Por ejemplo, la revisión de código de un módulo en el que se implementan diversos conceptos requerirá la presencia de expertos en cada uno de ellos. A menudo muchos de estos expertos no estarán disponibles de manera simultánea, y el resto no prestará demasiada atención en los temas que se encuentren fuera de su área de conocimiento. • Dificultad para llevar a cabo evoluciones. La falta de recursos o una visión incompleta derivan en un diseño que maneja únicamente los conceptos actuales. Cuando aparezcan nuevos requerimientos,a menudo, nos veremos obligados a reimplementar. Dado que la implementación no se encuentra modularizada,la inclusión de un nuevo requerimiento podría obligar a realizar cambios en todos los módulos. Los problemas descritos en los puntos anteriores han servido como impulso en la búsqueda de nuevos y mejores enfoques en la arquitectura, diseño e implementación. AOP ofrece una solución a todos los problemas anteriores. AOP en el mundo real ¿Qué lugar ocupa la orientación a aspectos en el mundo real? ¿Deberíamos prestarle atención o ignorarlo por completo? ¿Cuáles son los beneficios aportados? ¿y los posibles riesgos de adopción de esta tecnología?. Estas y otras preguntas serán respondidas, desde un punto de vista práctico, en los siguientes subapartados de esta sección. Hype Cycle Hype Cycle es una representación gráfica del grado de madurez, adopción y aplicación en el mundo real de una tecnología determinada. Es importante comprender bien el gráfico anterior, y la posición de la tecnología que se está considerando; en este caso, AOP, puesto que se dispondrá de una visión mucho más ajustada de los riesgos y beneficios a los que nos estamos exponiendo. 4
  5. 5. Aspect Oriented Programming Figure 1.3. Diagrama de componentes de R-Eclipse La interpretación de la curva Hype Cicle implica cinco fases diferentes las cuales analizaremos a continuación, relacionándolas, evidentemente con AOP. Activación de la tecnología Este es el momento en el que la tecnología aparece con la intención/promesa de solucionar un conjunto determinado de problemas. Podría ser el anuncio de un nuevo producto o la liberación de una nueva versión de un producto ya existente. En el caso de AOP, este proceso de activación se produjo en 2002 con el lanzamiento de AspectJ 1.0, seguido de una liberación mucho más importante (AspectJ 1.1) en el año 2003. Mucha gente se dio cuenta del potencial de AspectJ, especialmente su utilización en aplicaciones empresariales. Expectativas irreales ("peak") Durante esta fase la tecnlogía se hace muy popular. Todo el mundo quiere conocerla y tiene una opinión sobre ella aunque muy poca gente la utiliza en aplicaciones reales. Durante el año 2004 la mayoría de desarrolladores que trabajaban en Aspectj y AspectJ Development Tools (AJDT) eran integrantes de IBM. Esta inversión por parte de IBM proporcionó una base sólida a AspectJ. La ausencia de una adopción masiva hizo que el uso de la tecnología se conviertiese en una auténtica aventura. Afortunadamente para AspectJ el pico en esta fase no fue demasiado eleveado, sobre todo debido al manejo de las expectativas que realizaron sus principales evangelistas. Esta gestión provocó un pico más bajo en el hype cycle, lo que supuso una caída mucho menor en la siguiente fase. Desilusión Esta es la fase de ciclo Hype en la que la tecnología comienza a perder toda la atención que se le había prestado hasta el momento. Mientras que los equipos que la adoptaron desde sus inicios continuan utilizándola con el objetivo de obtener una ventaja competitiva, muchos otros comienzan a observarla con cierto escepticismo. Nuevas tecnologías aparecen en escena aportando soluciones diferentes al mismo problema. Resulta 5
  6. 6. Aspect Oriented Programming interesante destacar que muchos de estos nuevos competidores están en la fase de "expectativas irreales". AOP atravesó esta fase durante 2006 aproximadamente. Enterprise Java Beans ofrecía una seria competencia, permitiendo implementar funcionalidades transversales como seguridad o transaccionalidad de una manera modular. Pero EJB no era el único competidor, y tampoco el más importante. La aparición de lenguajes dinámicos como Groovy o Ruby y sus frameworks asociados, Grails y Rails, supuso una dura competencia para AOP. El modelo de metaprogramación del que disponen estas tecnologías facilitaba una nueva solución para realizar la modularización de funciones transversales. Viendo la luz Numerosos factores intervienen en el desarrollo de esta fase: maduración de la tecnología, cambios en la misma para acercarse a la realidad, búsqueda de un contexto de uso en el que realmente se cause impacto o la "desilusión" con otras alternativas, que en su momento fueron competidores, son algunas de ellas. En el caso de AOP, el comienzo de esta fase se produjo cuando algunos de los líderes del proyecto abandonaron IBM para fichar por SpringSource:Adrian Colyer,Andy Clement, Andrew Eisinberg, . . ., conviertiendo a AspectJ en un proyecto más del portfolio de Spring. Analicemos los factores enumerados anteriormente: • Acercamiento a la realidad:La introducción de anotaciones en Java 5, los cambios en la sintáxis o la supresión del proceso de weaving si se utiliza conjuntamente con Spring fueron algunos de los cambios realizados. La mejora en las herramientas de desarrollo fue otra de las respuestas a las necesidades reales de los usuarios. • Contexto de utilización: la influencia de Spring: En sus inicios AspectJ carecía de un contexto de utilización definido (C nació en el ámbito de los sistemas operativos, C++ en el contexto de interfaz de usuario, etc). Ha sido Spring la que le ha proporcionado a AspectJ un contexto de uso bien definido: la aplicaciones empresariales. • Desilusión con los competidores: Anteriormente indicamos que EJB permitía modularizar los conceptos transversales de un sistema. Los desarrolladores se dieron cuenta de que la solución ofrecida era demasiado pesada en la mayoría de las ocasiones. La versión 3 de EJB introdujo interceptores, un concepto similar a los advices de AspectJ, aunque carecían de un modelo de unión. Los lenguajes dinámicos ofrecen una gran alternativa a AspectJ aunque son relativamente nuevos. Tendremos que esperar el paso del tiempo y el uso en grandes aplicaciones empresariales para determinar la evolución de los mismos, y ver cómo continúa evolucionando AspectJ. Plena productividad Es la última fase definida en el ciclo Hype. En esta situación la tecnología está ampliamente difundida y se utiliza en las solución de problemas para los que ofrece una gran respuesta. Será en esta fase cuando se produzca una aceptación mavisa de la tecnología. En la actualidad,Java o C , se encuentran en esta fase. AOP y AspectJ deberán alcanzar este nivel con el paso del tiempo. ¿Dónde se utiliza AOP? A continuación se analizarán algunos de los principales ámbitos de aplicación de AOP Aplicaciones empresariales Gestión de transacciones, seguridad, auditoría, monitorización, gestión de la concurrencia, manejo de errores y un largo ecétera son funcionalidades transversales en la mayoría de aplicaciones empresariales. 6
  7. 7. Aspect Oriented Programming Aquellas aplicaciones que usen Spring como base de su arquitectura ya estarán utilizando algunos de los aspectos que vienen facilitados por el framework. Gracias a las anotaciones, concretamente @Aspect, la construcción de aspectos se ha convertido en una tarea habitual. Web y servidores de aplicaciones Aplicación de políticas, FFDC, recolección de contextos,trazabilidad o monitorización son algunas de las funcionalidades basadas en AOP que podemos encontrar en los servidores de aplicaciones. Spring Source DM Server y TC Server son un claro ejemplo de estas funcionalidades. Frameworks Gestión de transacciones y seguridad son habitualmente implementadas mediante aspectos. Asimismo, otras utilizaciones de los aspectos podría ser la inyección de dependencias en objetos de dominio. Para obtener un amplio abanico de posibilidades de uso de los aspectos, el lector podría visitar los proyectos de Spring Roo o Apache Magma. Herramientas de monitorización El uso de aspectos facilita también la construcción de herramientas de monitorización. Muchas herramientas utilizan AspectJ como tecnología subyacente: Glassbox,Perf4J,Contract4J,JXInsight o MaintainJ son algunos de los ejemplos. Compiladores e integración de IDE's El mismo equipo de AspectJ utiliza la propia tecnología para extender el compilador de JDT de manera que sea capaz de sopotar las nuevas construcciones. AJDT utilizan un proceso de weaving a través de una implementación basada en OSGI ofrecida por el proyecto Equinox. Scala IDE en Eclipse utiliza un enfoque similar para la construcción del entorno de desarrollo.2 Introducción a AOP AOP se construye sobre metodologías existentes como OOP o programación funcional, mejorándolas con las construcciones necesarias que permiten modularizar correctamente los conceptos transversales. Por ejemplo, si se trabaja con OOP, el núcleo de nuestro sistema será implementado mediante la utilización de clases. Los aspectos serán los encargados de representar las funcionalidades transversales y determinar cómo todos los diferentes módulos deberán ser tejidos para construir el sistema final. Metodología AOP Desarrollar un sistema utilizando AOP es similar a un desarrollo basado en otras metodologías: determinar los requerimientos, implementarlos y construir el sistema final mediante la combinación de los mismos. La comunidad AOP define tres pasos: 1. Descomposición en aspectos En este paso se indentificarán los diferentes concerns , tanto los transversales como los que componen el núcleo. Por ejemplo, en una capa de negocio encargada de calcular el IVA total de los productos los aspectos pertenecientes al núcleo sería el cálculo del IVA en sí, mientras que elementos como la transaccionalidad, registro de actividad,seguridad en hilos, manejo de cachés, . . . serían conceptos de un espectro mucho más amplio, que serán necesitados en otros muchos módulos del sistema, y por tanto, se categorizan como conceptos transversales 2. Implementación de "concerns" En este punto tendrá que realizarse la implementación de los diferentes requerimientos de manera independiente . Siguiendo con el ejemplo anterior, un grupo 2 Ver la sección inicial de este capítulo 7
  8. 8. Aspect Oriented Programming sería el encargado de realizar la implementación del módulo de lógica de negocio encargado de calcular el IVA, otro grupo llevaría a cabo la gestión del logging, un tercer grupo sería el encargado de asegurarse que las operaciones fuesen thread safe , y así sucesivamente. 3. Recomposición de aspectos . En este paso se deben definir las reglas de composición mediante la creación de módulos o aspectos . Este proceso, conocido como weaving , utiliza esta información para construir el sistema final. La principal aportación de AOP es la independencia de los "concerns" en el momento de su implementación. El modo en que se realiza la implementación permite una completa trazabilidad entre los requerimientos y su implementación, por lo que el sistema final será más fácil de comprender, implementar o adaptar ante futuros cambios Anatomía de un lenguaje orientado a aspectos La orientación a aspectos es simplemente una metodología por lo que será necesario definir el lenguaje y ofrecer un conjunto de herramientas para trabajar con la misma. Al igual que otra metodología de programación, una implementación concreta de AOP constará de dos partes: • Especificación del lenguaje encargado de describir las construcciones y sintaxis que serán utilizadas en la construcción y el tejido de todos los "concerns", tanto los pertenecientes al núcleo como los transversales. • La implementación del lenguaje es la encargada de verificar la adherencia del código a la especificación del lenguaje y de trasladar el código a un ejecutable capaz de ser comprendido por una máquina. Esta tarea,habitualmente,es realizada por un compilador o un intérprete. Especificación del lenguaje AOP Cualquier implementación de la metodología AOP requiere la definición de dos lenguajes: el primero de ellos permitirá realizar la implementación de los requisitos individuales mientras que gracias al segundo podremos definir las reglas que "tejen" el conjunto de módulos independientes. • Implementación de concerns .Al igual que en otras metodologías, los requisitos se implementan en módulos que contienen los datos y el comportamiento necesario para ofrecer sus servicios. Imaginemos por ejemplo que estamos construyendo el núcleo del concern de seguridad; dicho núcleo mantendrá un manager de control de accesos y una colección de elementos ( voters ). Habitualmente,tanto los concerns transversales como los del núcleo se implementan mediante la utilización de lenguajes estándar como pueden ser C++ o Java. • Especificación de reglas de "tejido". Las reglas de tejido especifican cómo debemos integrar los diferentes concerns que hemos implementado para componer el sistema final. El lenguaje utilizado para definir estas reglas puede ser una evolución del lenguaje natural o algo completamente diferente. Así por ejemplo, podríamos ofrecer una implementación de la metodología AOP en la que Java es el lenguaje base y la definición de reglas de tejido se realiza mediante un DSL implementado en Groovy . Implementación de AOP La implementación del lenguaje debe llevar a cabo dos acciones:combinar los aspectos individuales mediante las reglas de tejido,para, posteriormente, convertir la información resultante en código ejecutable. El primero de los dos pasos anteriores es conocido como weaving, y el procesador encargado de realizar dicha tarea es el weaver. • Proceso de weaving . Tal y como argumentaba la introducción de este punto, el proceso de weaving es el encargado de componer el sistema final mediante la composición de los diferentes concerns siguiendo las pautas establecidas en las reglas de tejido . Estas reglas son definidas en aspectos 8
  9. 9. Aspect Oriented Programming independientes del núcleo del sistema por lo que con unas nuevas reglas de "tejido" podríamos construir un sistema final completamente diferente. • Procesador (Weaver) El procesador encargado de llevar a cabo el proceso de weaving descrito en el punto anterior es comunmente conocido como weaver Una posible implementación sería utilizar una transformación código a código (un compilador). De ese modo, el compilador de aop "tejería" los aspectos junto al resto de clases generando un nuevo código ya "tejido". Una vez finalizado este proceso, el código generado anteriormente serviría como entrada para el compilador de lenguaje base el cual sería el encargado de generar el ejecutable. Utilizando el enfoque anterior, una aproximación basada en C++ convertiría todos los archivos individuales (aspectos y clases) en código C++ tejido(weaving). El siguiente diagrama ilustra la aproximación descrita en el párrafo anterior: Figure 1.4. Proceso de generación del sistema final Beneficios de la metodología AOP La mayoría de las críticas sobre AOP destacan que en muchas ocasiones es demasiado complejo. Evidentemente, es necesario un poco de tiempo y paciencia para conseguir ser un experto. Sin embargo, el motivo principal de su complejidad es que se trata de una tecnología relativamente nueva. Algunos de los beneficios más detacables de esta metodología son: • Responsabilidades claramente diferenciadas . Cada módulo es el responsable de su funcionalidad principal; dejando de lado los conceptos transversales. Así por ejemplo, un módulo cuyo principal cometido es implementar la lógica de acceso a datos de un sistema de ventas por internet, no tendrá que preocuparse de realizar pooling sobre la base de datos o de la transaccionalidad. Gracias a esta clara asignación de responsabilidades se consigue una alta trazabilidad entre los requisitos y su correspondiente implementación. • Incremento de la modularidad . Utilizando AOP se consigue manejar cada uno de los conceptos de manera independiente con un acoplamiento mínimo. Incluso aunque estén presentes conceptos transversales que afecten al sistema completo, la implementación es modular. • Retraso en las decisiones de diseño .Cuando se arquitecta un nuevo sistema siempre aparece el siguiente dilema: ¿se debe realizar un diseño sumamente complejo y detallado que intente abarcar todas las funcionalidades,incluso las futuras? o, por el contrario, ¿debe arquitectarse una solución que se corresponda con la situación actual?. Gracias a AOP, el arquitecto de la solución, puede retrasar la toma de determinadas decisiones de diseño dado que los futuros requerimientos se implementarán en aspectos independientes. Dando un paso más adelante, AOP conforma un gran equipo junto con una metodología ágil como puede ser 9
  10. 10. Aspect Oriented Programming XP (siglas en inglés de Programación Extrema) siguiendo la práctica del principio YAGNI (siglas en inglés de No lo vas a necesitar ) . De este modo, podemos seguir el principio anterior, dando vida únicamente a las funcionalidades requeridas e implementando futuros requerimientos sin la necesidad de grandes modificaciones en el sistema. • Sencillez en las mejoras . AOP permite añadir una nueva funcionalidad sin más que desarrollar un nuevo aspecto (el cual no afecta al núcleo del sistema). Gracias a ello, el tiempo de respuesta ante nuevos requerimientos disminuye notablemente. • Incremento en la reutilización del código .Puesto que AOP implementa cada aspecto en un módulo independiente, cada uno de ellos es independiente del resto. En general, cada uno de ellos no suelen tener conocimiento del resto de elementos que conforman el sistema final. Realmente, el único elemento consciente del acoplamiento entre los diferentes módulos son las reglas de tejido , de manera que, si cambiamos éstas, podemos componer un sistema final completamente diferente. • Reducción de costes y accesos al mercado más rápidos .Las características descritas en los puntos anteriores generan sistemas más rápidos, tanto en su desarrollo como en su implantación . Eliminando la necesidad de modificar múltiples módulos para la implementación de un nuevo concepto transversal, AOP provoca que dicha implementación sea más barata. Asimismo, permitiendo que los desarrolladores estén centrados en su especialidad logramos que el coste del desarrollo disminuya. Verdades y mentiras sobre AOP En los últimos años AOP ha sufrido un gran crecimiento aunque,en algunas ocasiones, todavía se percibe demasiado complicado, tanto en su implementación como en su proceso de aprendizaje. A continuación veremos algunas hipótesis y los motivos por los que las mismas son consideradas ciertas o falsas: • AOP no soluciona nuevos problemas . Esta afirmación es completamente cierta. La orientación a aspectos no ofrece solución a problemas irresolubles sino que aporta soluciones más sencillas y elegantes a los problemas actuales. Al fin y al cabo, no hay nada que no podamos implementar con código máquina. • Flujos complicados de seguir . De nuevo esta afirmación es cierta. En muchas ocasiones, en función de la complejidad del sistema que estemos construyendo, el orden en el que se ejecutan las instrucciones puede resultar complicado de seguir. La afirmación anterior también es cierta para otras metodologías ajenas a AOP. En OOP, el uso del polimorfismo hace que analizar el flujo de ejecución de un programa no sea una tarea sencilla. Incluso en lenguajes procedimientales, como podría ser C, el uso de punteros a funciones dificulta las tareas de seguimiento del programa. • AOP promueve malos diseños . En esta ocasión la afirmación es falsa. Una aproximación orientada a aspectos no es la solución para un mal diseño sino que simplemente facilita nuevos modos de resolución de problemas en aquellas áreas en las que lenguajes procedimientales u orientados a objetos presentan carencias. • Los interfaces de las metodologías OOP son suficientes . De nuevo en esta ocasión, la afirmación anterior es falsa. La técnica descrita propone utilizar un interfaz e ir intercambiando las implementaciones subyancentes (nótese que esto no implica cambios en el API). Puede parecer a simple vista que esta solución es satisfactoria, pero dista mucho de ser así, puesto que necesitamos invocar al código en todos aquellos lugares en los que corresponda. Esta técnica sirve de gran ayuda (tanto en OOP como AOP), pero en ningún momento los interfaces son una alternativa a la orientación a aspectos. • AOP supone una ruptura de la encapsulación . Esto afirmación es cierta aunque con ciertos matices puesto que dicha ruptura se realiza de una manera controlada y sistemática. Las clases en OOP 10
  11. 11. Aspect Oriented Programming encapsulan todo el comportamiento, mientras que AOP elimina estos niveles de control de las mismas. • AOP reemplazará a OOP . Falso. Los conceptos principales seguirán siendo implementados en OOP ( o puede que en un lenguaje procedimantal como puede ser C). AOP añade un nuevo conjunto de conceptos adicionales a la metodología orientada a objetos. Efectivamente, AOP cambiará el modo en el que se utilizan las metodologías actuales para la implementación de conceptos transversales. Visión general de AspectJ AspectJ es una extensión orientada a aspectos de propósito general al lenguaje Java. Puesto que es una extensión del lenguaje Java, cualquier programa escrito en dicho lenguaje es un programa AspectJ válido. Un compilador AspectJ genera archivos .class de acuerdo a la especificación del bytecode de Java de manera que cualquier implementación de la máquina virtual sea capaz de ejecutar dichos archivos. El lenguaje AspectJ , de acuerdo a los conceptos descritos en el apartado anterior, consta de dos partes: la especificación del lenguaje que define los conceptos principales implementados mediante clases Java, y las extensiones AOP que permiten realizar el tejido de los conceptos transversales. La implementación del lenguaje facilita herramientas como compiladores, depuradores e integración con la mayoría de IDE´s modernos. Los siguientes apartados servirán de introducción a AspectJ y algunos conceptos avanzados gracias a los cuales se podrá comenzar a escribir programas simples. Introducción a AspectJ El compilador de AspectJ es el encargado de utilizar los módulos que contienen las definiciones de las reglas de tejido para añadir nuevo comportamiento a los módulos que implementan las funcionalidades principales. El proceso anterior no realiza ninguna modificación sobre el código fuente del núcleo dado que el proceso de tejido se realiza sobre el bytecode generado por el compilador. Modelo de Joint Point El modelo de joint point (no vamos a traducir el término) está compuesto de dos partes claramente diferenciadas: los "join point", que no son más que puntos en la ejecución de un programa, y los "pointcuts", un mecanismo de selección de los puntos anteriores. Imaginemos por un momento que nos encontramos desarrollando un sistema que necesita gestión de la seguridad, algo muy común en el mundo en el que nos movemos, y, que, debido a la naturaleza transversal del mismo, elegimos AOP como enfoque principal de nuestra solución. ¿Cuáles son los pasos que deberíamos seguir? 1. Identificar los puntos del sistema que necesitan ser protegidos, comprobando, antes de realizar el acceso, que el usuario está autenticado y tiene los privilegios necesarios para hacerlo. En resumen, estamos identificando los "joint point" que requieren ser securizados. 2. Construiremos un pointcut (o varios, todos los que sean necesarios), que permita la selección de los "joint point" descritos en el punto anterior. 3. Construiremos un aspecto que encapsule toda la lógica de seguridad requerida. Los conceptos anteriores son sumamente importantes dado que componen la base de AOP. Los siguientes subapartados profundizarán en cada uno de ellos. 11
  12. 12. Aspect Oriented Programming Joint Point Como ya se definió anteriormente, un join point es un punto de ejecución en un sistema. Así por ejemplo, el acceso al campo de una clase, la ejecución de una función o una sentencia for son ejemplos de join points. AspectJ solamente expone un subconjunto de todos los posibles joint points, limitando de este modo, el acceso a las construcciones más estables. Pointcuts Es un artefacto que nos permite seleccionar joint points y recuperar su contexto.Veamos algunas de las características principales: Selección de joint points Los pointcut especifican un criterio de selección. Utilizaremos tipos, campos, métodos, anotaciones, etc para generar dichas definiciones. También podremos establecer condiciones en tiempo de ejecución que tendrán que cumplirse en el joint point seleccionado. Determinación del contexto Los joint point disponen de información en tiempo de ejecución. Determinados pointcuts pueden recolectar dicha información y pasársela al advice. Por ejemplo, la llamada a un método de un objeto tendrá disponible el propio objeto que realiza la llamada y los argumentos que se están pasando. Utilización de signaturas En Java, todos los elementos que componente un programa tienen una signatura. La utilización de patrones para dichas signaturas permiten a los pointcuts especificar las reglas que permiten seleccionar los joint point deseados. Categorías de joint points A continuación se verá un conjunto de categorías de joints points expuestas por AspectJ. A pesar de ser un número considerable de categorías, no se han incluido todas, sino aquellas que se utilizan con mayor frecuencia. Si el lector desea realizar una lectura más profunda aquí podrá encontrar todo lo que necesita: AspectJ Programming Guide [http://www.eclipse.org/aspectj/docs.php] Table 1.1. Categorías de joint points expuestas por AspectJ (resumen) Categoría Joint Point Expuesto Código que representa Método Execution Cuerpo del método Método Call Invocación del método Constructor Execution Ejecución de la lógica de creación de un objeto Constructor Call Invocación de la lógica de creación de un objeto Acceso a un campo Read Lectura de un objeto o el campo de una clase Acceso a un campo Write Escritura de un objeto o el campo de una clase Proceso de excepciones Handler Bloque catch para manejar una excepción Inicialización Class init Proceso de carga de una clase (class loading) Inicialización Object init Inicialización de un objeto en un constructor Inicialización Object pre-init Pre-inicialización de un objeto en un constructor Advice Execution Ejecución de un advice 12
  13. 13. Aspect Oriented Programming Conceptos básicos A continuación se analizarán los elementos básicos que se deben conocer para definir un pointcut. 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. Operadores AspectJ 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 operandos. Signaturas: sintaxis y ejemplos 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 a 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 caractéres, 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. A lo largo de los siguientes subapartados veremos, a través de una serie de ejemplos, los diferentes patrones de signaturas ofrecidos por AspectJ que podemos utilizar para seleccionar dierentes joint points Signaturas de tipos Table 1.2. Ejemplos de signaturas de tipos Patrón de la Descripción Ejemplos de tipos concordantes signatura AstVisitor El tipo AstVisitor Sólo el tipo AstVisitor concuerda (ni tipos base ni derivados) *AstVisitor Cualquier tipo cuyo nombre termine en Por ejemplo, HighlightingAstVisitor o AstVisitor SemanticAstVisitor concuerdan con el patrón java.*.Date El tipo Date en cualquier subpaquete java.util.Date o java.sql.Date son directo del paquete java ejemplos de tipos concordantes javax..* Cualquier tipo en el paquete javax y en Cualquier tipo en el paquete sus subpaquetes (tanto directos como javax.security así como cualquiera indirectos) de sus subpaquetes indirectos como javax.security.auth.login 13
  14. 14. Aspect Oriented Programming Patrón de la Descripción Ejemplos de tipos concordantes signatura javax..*Model Todos los tipos en el paquete javax TableModel, TreeModel y sus (subpaquetes directos e indirectos) subtipos como DefaultTreeModel o cuyo nombre termine en Model, y todos DefaultTableModel. los subtipos de estos. Singaturas de tipos: anotaciones Table 1.3. Ejemplos de signaturas de tipos: anotaciones Patrón de la Descripción Ejemplos de tipos concorndantes signatura @Secured User El tipo User anotado con la anotación @Secured class User{. . .} Secured @Entity * Cualquier tipo anotado con la anotación @Entity class Section {. . .},@Entity Entity class Report {. . .} @Transactional* El tipo Manager y cualquiera de sus @TransactionalDefault class Manager+ clases derivadas que estén anotados con Manager{},@TransactionalOracle una anotación cuyo nombre comienze class OracleManager extends por Transactional Manager{} Signaturas de tipos: generics Table 1.4. Ejemplos de signaturas de tipos: generics Patrón de la Descripción Ejemplos de tipos concordantes signatura Map El tipo Map cuyo primer argumento En este caso únicamente concordará el <Integer,String> genérico está fijado a Integer y el tipo Map<Integer,String> segundo a String *<User> Cualquier tipo genérico cuyo único Collection<User>,List<User>, . . . argumento genérico sea de tipo User Collection<? El tipo Collection con un parámetro de Collection<User>, extends User> tipo User o derivado Collection<DomainUser>, . . . Collection<? El tipo Collection, cuyo parámetro será Collection<Serializable>, super User> uno de los tipos base de User Collection<BaseUser>, 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 Descripción Ejemplos de tipos concordantes signatura !Collection Cualquier tipo excepto Collection User,Model, List (aunque sea subclase de Collection) Set || Map Los tipos Set o Map Los tipos Set y Map únicamente !@Transactional Cualquier tipo que se encuentre anotado class NonTransactionalManager{} por por la anotación Transactional @Serializable Cualquier tipo que se encuentre anotado @Serializable @Encrypted class @Encrypted * por las dos anotaciones UserCredentials{} 14
  15. 15. Aspect Oriented Programming Patrón de la Descripción Ejemplos de tipos concordantes signatura (@Serializable || Cualquier tipo que esté anotado por @Serializable class User {} @Encrypted) * alguna de las dos anotaciones Signaturas de métodos y constructores Table 1.6. Signaturas de métodos Patrón de la Descripción Ejemplos de métodos concordantes signatura public void Cualquier método público de la clase class User{ public void setName(String User.set*(*) User cuyo nombre comienze por set, name){} } cuyo tipo de retorno sea void, y que espere un único argumento public void Cualquier método público de la clase class User { public void updateInfo() User.*() User cuyo tipo de retorno sea void y que {}} no espere argumentos public * User.*() Cualquier método público de la clase class User{ public UserInfo User que no espera argumentos y getUserInfo(){}} retorna cualquier tipo public * User.*(..) Cualquier método público de la clase class User{ public UserInfo User que retorna cualquier tipo, y getUserInfo(){},public Date espera cualquier número y tipo de updatesBetween(Date first,Date last) argumentos (incluido cero) {}} * *.*(..) o * * (..) Cualquier método independiente de su Cualquier método del sistema tipo, del tipo de retorno, de su nombre y de los argumentos que espere !public * User.*(..) Cualquier método que no sea público class User {protected getUserInfo(){}} (privado,protegido o paquete) * * (..) throws Cualquier método que declare que clas AntlRBasedParser { public RecognitionException puede lanzar una excepción de tipo void parser(String file) throws RecognitionException RecognitionException} * User+.*(..) Cualquier método en la clase User y sus class UserDetails extends User {} subclases User Cualquier método del tipo UserService class UserService{public User UserService.*(..) cuyo tipo de retorno sea User. Si alguna retrieveUser(String name){}} class de las subclases sobreescribe el tipo DetailUserService extends de retorno con un subtipo de User UserService{public DetailedUser (mediante el tipo de retorno covariante retrieveUser(String name){}} introducido en Java 5) también será seleccionado. 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 15
  16. 16. Aspect Oriented Programming Signaturas de campos Table 1.7. Signaturas de campos de clase Patrón de la Descripción Ejemplos de campos concordantes signatura private String Campo privado (tanto campo de una class User { private String username;} User.username instancia como estático) de la clase User * User.* Cualquier campo de la clase User class User{private String independiente de sus cualificadores, username;protected int credits;private tipo y nombre. UserDetails details;} * User+.* Cualquier campo de la El ejemplo de la fila anterior o class clase User y todas sus SpecificUser extends User {private subclases, independientemente de sus String address;} cualificadores, tipo y nombre. Implementación de pointcuts AspectJ ofrece dos mecanimos 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 sastisfagan la condición descrita De nuevo, al igual que en el apartado anterior, veremos las diferentes opciones disponibles, tanto para los kinded como para los non-kinded pointcuts. Kinded pointcuts Table 1.8. 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) Inicalizació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(. .)) 16
  17. 17. Aspect Oriented Programming Non-kinded pointcuts Este tipo de pointctus permiten 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. De nuevo, a través de ejemplos, veremos los diferentes tipos de non-kinded pointcuts ofrecios por AspectJ Non-kinded pointcuts basados en control de flujo Table 1.9. Non-kinded pointcuts basados en control de flujo Pointcut Descripción cflow(execution(* Selecciona todos los joint points en el flujo de la ejecución de TransactionManager.commit())) cualquier operación commit de la clase TransactionManager, incluyendo la ejecución del propio método. cflowbellow(execution(* Selecciona todos los joint points en el flujo de la ejecución de TransactionManager.commit())) 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 dentre de un método o un constructor, incluendo la definición de cualquier clase local que puediera aparecer en los mismos. Table 1.10. Non-kinded pointcuts basados en la estructura léxica Pointcut Descripció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(* Selecciona todos los joint points que parecen dentro de cualquier TransactionManager.retrieve*(..)) método de la clase TransactionManager cuyo nombre comience por retrieve Non-kinded pointcuts de ejecución Este tipo de pointcuts nos permiten seleccionar joint points en base al tipo de los objetos en tiempo de ejecución. De este modo, disponemos 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. 17
  18. 18. Aspect Oriented Programming • target(). Similar al concepto anterior, aunque en este caso, se utilizar el target del joint point en lugar del this Table 1.11. Pointctucts basados en la ejecución Pointcut Descripción this(User) Selecciona cualquier joint point en que se la expressió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 nos permiten 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 • Los accesos de modificación de un campo, el argumento será el nuevo valor que va a tomar dicho campo. Table 1.12. Ejemplos de pointctucts basados en argumentos Pointcut Descripción args(User, . . , Selecciona cualquier joint point de tipo método o constructor en el que el primer String) 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 una campo estableciendo un nuevo valor de tipo SqlException, y también seleccinaría un manejador de excepciones de tipo SqlException Non-kinded pointcuts condicionales Este tipo de pointcuts nos permiten seleccionar joint points basados en una expresión condicional Table 1.13. Ejemplos de pointcuts condicionales Pointcut Descripción if(debug) Selecciona cualquier joint point donde el campo estático debug (en la definición del aspecto) toma el valor cierto 3 Crosscutting dinámico A lo largo de las secciones anteriores hemos analizado el modelo de joint point de AspectJ y la manera de defininir las reglas que nos permitan seleccionar aquellos joint points que sean de nuestro interés. Durante este capítulo analizaremos el modo en el que podemos alterar el comportamiento de nuestro sistema en los joint points que hayamos seleccionado mediante la definición de nuestros pointcuts. Descripción general Las reglas de tejido están compuestas de dos partes: 3 El término crosscuting representa la naturaleza transversal de las funcionalidades que estamos añadiendo. Se ha optado por no realizar la traducción del término y utilizar la terminología orginal 18
  19. 19. Aspect Oriented Programming • advice: qué deseamos hacer • pointcuts: donde aplicamos el advice anterior AspectJ soporta el crosscutting dinámico mediante los advices, construcciones similares a los métodos que nos permiten definir las acciones que a ejecutar en los joint points seleccionados por un pointcut. Categorías de advices Dependiendo de las funcionalidades que estemos implementando necesitaremos ejecutar nuestra lógica en un determinado lugar del flujo de ejcución orginal; así por ejemplo, si estamos construyendo la seguridad de un sistema, nuestro código tendrá que verificar dicha seguridad antes de la ejecución del joint point. Si estuvieramos construyendo un sistema de cachés, nuestra nueva funcionalidad tendría que ejecutarse alrededor de 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 categorias 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 dicha ejecución • After returning se ejecuta tras la ejecución del joint point siempre y cuándo é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 estemos escribiendo, podríamos 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 y 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 un ejemplo sencillo de definición de un advice: En primer lugar defininamos 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 actua 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()); 19
  20. 20. Aspect Oriented Programming Object retValue = proceed(user); System.out.println("Finished secured operation on user " + user.toString()); return retValue; } En la definición anterior podemos ver la estructura de la declaración de un advice descrita anteriormente 1. 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. 2. La parte que sigue a los dos puntos representa el pointcut, es decir, la definición de los criterios que determinan cuando se ejecutará el advice 3. 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 explicitamente la invocación del mismo. Similitudes Analizemos las similitudes de 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: • Opcionalmente puede asigarse 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. En cuerpo de los advices también es muy parecido al de los métodos: • 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 el caso de los métodos, los advices : • No pueden declarar el disparo de una excepción que no está declarada en TODOS los joint points sobre los que actua • Pueden omitir algunas de las excepciones de tipo checked que han sido declaradas por alguno de los joint point sobre los que actua. • 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. 20
  21. 21. Aspect Oriented Programming Diferencias En comparación con los métodos, los advices: • 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 en el 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. Análisis detallado de los advices Hasta este momento disponemos de la información sobre las diferentes categorías de advices y su sintáxis general. A lo largo de las siguientes subsecciones profundizaremos en cada uno de los diferentes tipos de 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 una excepción, el joint point no se ejecutará. Este tipo de aspectos son comunmente utilizados en aspectos tales como seguridad o trazabilidad. After advice Se ejecutan después de la ejecución del joint point sobre el que actuan. 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: 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 21
  22. 22. Aspect Oriented Programming } registra el resultado de todas las operaciones de la clase ServiceManager que estén marcadas con la anotación Loggin, independientemente si retornan correctamente o terminan su ejecución de forma inesperada mediante el disparo de una excepción. 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 (si que lo podemos modificar, pero no retornar uno nuevo). 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 una modo de recuperar la excepción que ha sido disparada por el joint point de modo que esté disponible en el cuerpo del advice. Siguiendo una sintáxis 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: 22
  23. 23. Aspect Oriented Programming • 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 una gestión de excepciones, 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 necesitamos llevar a cabo. Ejecución del joint point Si desde el around advice deseamos llevar a cabo la ejecución del joint point, tendremos que hacer uso de la palabra reservada proceed() dentro del cuerpo del advice. Debemos recordar, que, puesto que la invocación de proceed() ejecuta el joint point, tendremos que pasarle 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. Veamos 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 comienze 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éditos que 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. Retornando valores Todos los around advices deben declarar un valor de retorno (puediendo 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 actua 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. 23
  24. 24. Aspect Oriented Programming • En el caso en el que el tipo de retorno no sea primitivo, AspectJ realizará los casts oportunos antes de retornar el valor. Contexto del joint point 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 cojunto de pointcuts que AspectJ ofrece para recuerar el contexto en los joint points. Table 1.14. Pointcuts para recuperar el contexto en un joint point Pointcut Contexto recuperado this(obj) Objecto 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 existen 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 mismo. 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. Aspectos Los aspectos representan la únidad básica de modularización en el mundo de la orientación a aspectos en general, y en AspectJ, en particular. Los siguientes subapartados nos mostrarán una definición más formal que la vista hasta este momento.4 Analizaremos en detalle los aspectos, realizaremos una comparativa con las clases y veremos como nos pueden ayudar a modularizar y construir componentes reutilizables. Trabajando con aspectos Los aspectos son las construcciones encargadas de encapular la implementación de todos los conceptos transversales. Será el weaver el encargado de modificar el comportamiento del núcleo del sistema en función de la información definida en nuestros aspectos. 4 Seguiremos analizando los nuevos conceptos, al igual que en las secciones anteriores, desde elpunto de vista de la sintaxis tradicional, dejando un poco de lado la sintaxis @AspectJ 24
  25. 25. Aspect Oriented Programming La sintaxis general de un aspecto es la siguiente: [access specification][abstract] aspect <AspectName> [extends class or aspect] [implements interface-list] [ [<association-speficier>(Pointcut)] | [pertypewithin(TypePattern) ] ]{ // aspect body } La palabra reservada aspect permite realizar la declaración de un nuevo aspecto. Cada uno de estos aspectos presenta las siguientes características: • Presenta un nombre para podere referirse a él desde otras partes del código. • Puede presentar especificadores de acceso (public,proteced o private) • Puede ser abstract • Puede aplicar el mecanismo de herencia, derivando de otros aspectos o de clases tradicionales. Asimismo puede implementar interfaces. • Puede especificar el modelo de instanciación • Su cuerpo puede estar compuesto por definiciones de pointcuts, miembros o tipos anidados. Diferencias con las clases A la vista del apartado anterior, en un principio, parece que las clases y los aspectos son prácticamente lo mismo, pero no es así, los aspectos no son clases. A continuación se describen algunas de las diferencias principales: Modelo de instanciación Es el sistema el encargado de la instanciación de los aspectos. En resumen, nunca utilizaremos la palabra reservada new para crear una instancia de un aspecto Restricciones en el mecanismo de herencia Los aspectos no pueden derivar de otros aspectos concretos, es decir, sólo pueden derivar de aspectos definidos como abstract. Esto es una restricción que se incluyó en la definición del lenguaje para evitar complejidades innecesarias. Parámetros genéricos Los aspectos concretos no pueden declarar parámetros genéricos, sólo aquellos marcados como abstract podran hacerlo. El motivo es el modelo de instanciación, puesto que, como los aspectos son instanciados por el sistema, éste último no es capaz de realizar la inferencia de tipos. privileged Los aspectos pueden ser definidos con el especificador de acceso privileged, el cuál otorga la capacidad de acceder a los miembros privados de las clases sobre las que se está actuando. Resumen Nos hemos introducido en el mundo AOP, hemos analizado su evolución a lo largo a lo largo del Hype Cycle, visto sus usos en el mundo real y hemos realizado un estudio de sus principales componentes y el modo en el que se utilizan cada uno de ellos. 25
  26. 26. Aspect Oriented Programming Evidentemente, esto pretende ser un primer acercamiento al mundo de AOP, y concretamente a AspectJ, a través del cual el lector pueda comprender los beneficios de esta tecnología y le permita utilizarlo para escribir sus propios programas. Soy consciente de que se han quedado muchas cosas en el tintero, que darían cabida para un libro completo, por lo que a continuación se enumeran algunas de ellas para las mentes inquietas que deseen profundizar en otros aspectos que no tienen cabida en este documento: • Técnicas como static crosscutting tales como: • Introducción de nuevos miembros en una clase • Complemento de anotaciones • Modificación de jerarquías de tipos • Respuesta a errores y warnings en tiempo de compilación • Debilitado de excepciones • Asociación y predencia de aspectos • Sintáxis @AspectJ • Análisis detallado de los procesos de weaving • Integración con Spring • ... Para una análisis exhaustivo de estos y otros muchos aspectos, el usuario puede acudir a la sección bibliografía El objetivo de este introducción no era dar una visión teórica y aburrida de AspectJ sino dar una visión pragmática del mismo. De este modo, la siguiente sección ilustrará la mayoría de conceptos vistos anteriormente mediante una serie de ejemplos reales. Ejemplos reales Hasta este momento hemos visto las posibilidades que nos ofrece AOP en general, y AspectJ en particular, mediante la exploración de sus elementos, sintáxis y ejemplos sencillos. Los siguientes subapartados ofrecerán un conjunto de ejemplos reales de aplicación de la tecnología así como algunos patrones habituales. La estructura de los ejemplos será la siguiente: se planteará un problema, se dará una solución general, y después se propondrá una alternativa basada en AOP y AspectJ. Pongámonos manos a la obra. 5 Patrón wormhole Este patrón consiste, básicamente, en habilitar un contexto de información desde un llamante a un llamado sin la necesidad de pasar dicha información como un conjunto de argumentos a lo largo de todas las invocaciones del los diferentes métodos del flujo de ejecución. Imaginemos por un momento que estamos implementando un sistema de seguridad en el que cada uno de los métodos tiene que comprobar quien les ha invocado para determinar si se les permite ejecutar la operación. Creando un canal de comunicación directa entre dos niveles cualesquiera en la pila de llamadas, nos evitará tener que ir moviendo la información necesaria por cada una de las capas. De este modo, evitaremos la necesidad de modificar la cadena de llamadas cuando necesitemos añadir algún parámetro adicional al contexto de información. 5 De nuevo, se ha optado por mantener la nomenclartura original del patrón, sin realizar una traducción al castellano, puesto que considero que el resultado de dicha traducción no resultaría demasiado intuitiva para el lector. 26
  27. 27. Aspect Oriented Programming Solución tradicional Sin la utilización de una tecnología como AOP disponemos de dos alternativas de solución. La primera de ellas movería toda la información necesaria a lo largo del contexto de llamadas añadiendo los parámetros que fueran necesarios, mientras que la segunda, haría uso de un almacenamiento espefíco de los hilos. La solución que pasa a través de parámetros el contexto de información necesario produce una contaminación del API, puesto que cada uno de los métodos que intervienen en el flujo que transcurre desde el llamante hasta el llamado necesitará de parámetros adicionales que permitan mover dicha información a lo largo de la pila de llamadas. La segunda solución propuesta, el uso de un almacenamiento específico, pasaría porque el llamante crease una variable ThreadLocal en la que se almacenase toda la información necesaria. Aunque esta segunda solución evita la contaminación del API, se necesita modicar tanto el llamante como el llamado, además de requerir un conocimiento de cómo se almacena el contexto de información. Descripción general La siguiente figura ilustra la idea básica de este patrón arquitectónico: Figure 1.5. Diagrama arquitectónico del patrón wormhole La idea general consiste en indentificar dos pointcuts: uno en el llamado y otro en el llamante. El primero de ellos será el encargado de recolectar toda la información que será transmitida a lo largo del "aguejero de gusano". El segundo pointcut tendrá que especificar aquellos joint point del llamado en los que debe actuar el wormhole. En la imagen anterior cada una de las cajas trasnversales representa un nivel en la pila de llamadas. Habitualemente, para comunicar el llamante y el llamado tendríamos que ir pasando toda la información de nivel en nivel, hasta que alcanzásemos el nivel deseado. El patrón descrito en esta sección establece un canal de comunicación directo a lo largo de todos los niveles de llamadas, evitando el paso de información nivel a nivel. Plantilla A continuación se propone una plantilla de solución que puede ser utilizada como base para la implementación de este patrón en nuestros sistemas 27
  28. 28. Aspect Oriented Programming public aspect WormholeAspect { pointcut callerSpace(caller context) : <caller pointcut> ; pointcut calleeSpace(callee context) : <callee pointcut> ; pointcut wormhole(<caller context>, <callee context>) : cflow(callerSpace(<caller context>)) && calleeSpace(<callee context<); // advices para el wormhole around(<caller context>, <callee context>) : wormhole(<caller context>, <callee context>) { // ... advice body } } En la plantilla anterior, los dos primeros pointcuts recolectan los contextos en el llamante y en el llamado respectivamente. El tercero de ellos construye el wormhole entre los dos espacios, capturando todos los joint point determinados por el pointcut calleeSpace que se encuentren en el flujo de ejecución de aquellos joint points capturados por el pointcut callerSpace. Puede que con un ejemplo nos queden las cosas un poquito más claras. Escribamos un pequeño aspecto que genere un canal de comunicación entre un sistema iniciador de transacciones y las ejecuciones de una query: public aspect SqlTransactionAspect { pointcut transactionSystemUsage(TransactionSystem ts) : execution(* TransactionSystem.*(..)) && this(ts) pointcut queryExecutions(SqlQuery query, String[ ] params) : this(query) && args(params) && execution(public * SqlQuery.exec*(String [ ])); pointcut wormhole(TransactionSystem ts, SqlQuery query, String[ ] params) : cflow(transactionSystemUsage(ts)) && queryExecutions(query, params); before(TransactionSystem ts, SqlQuery query, String[ ] params) returning : wormhole(ts, query, params) { // realizar las operaciones deseadas } • El pointcut transactionSystemUsage captura todos los joint point de ejecución en de la clase TransactionSystem. Además recolecta el objeto en si en el contexto de ejecución. • El pointcut queryExecutions() captura todas las llamadas a los métodos exec de la clase SqlQuery y recolecta la propia query y los argumentos • El pointcut wormhole genera un canal de comunicación directo entre el el sistema de transacciones y la ejecución de la query, capturando todos los joint points que se produzcan bajo el flujo de ejecución de transactionSystemUsage. Además, se captura el contexto de los dos pointcuts constituyentes. • El advice que actua sobre el pointcut wormhole dispone ahora de toda la información: la query y sus parámetros así como el sistema de trasancciones 28
  29. 29. Aspect Oriented Programming Hilos seguros La amplia difusión de los procesadores con múltiples núcleos, el uso de lenguajes especialidados como Erlang o Haskell, y la popularidad de numeros libros al respecto, han logrado que los sistemas de software alcancen niveles de concurrencia insospechados hasta hace poco tiempo. Si embargo, para muchos desarrolladores, incluso gente experimentada,la concurrencia supone un auténtico misterio. Implementar un sistema que presente un control de concurrencia es una tarea complicada, dado que, en muchas ocasiones, los conceptos se difuminan a lo largo de diferentes módulos. Estas circunstancias pueden ocasionar, la construcción de sistemas no demasiado ricos, con erros aparentes, caídas del sistema inesperadas, integridad comprometid, deadlocks, . . . Por norma general, la implementación de un sistema de concurrencia basado en los patrones arquitectónicos y de diseño apropiados suele ocasionar sistemas mucho más fiables que los descritos en el párrafo anterior. Muchos de los patrones de concurrencia presentan numerosos conceptos trasnversales, por lo que AOP puede ayudarnos a simplificar su diseño y posterior implementación. En este ejemplo, analizaremos el patrón read-write lock. La implentación clásica de este patrón supone la adición de código en todos aquellos métodos que necesiten modificar el estado de un objeto. Los siguientes subapartados nos mostarán como podemos realizar una implementación modular de este patrón basado en AspectJ. Implementación tradicional. Este patrón utiliza un par de bloqueos (locks): el bloqueo de lectura, y el bloqueo de escritura. Múltiples hilos lectores podrán adquirir el bloqueo de lectura siempre y cuando el bloqueo de escritura no haya sido adquirido. El otro bloqueo, el de escritura, únicamente podrá ser adquirido siempre y cuando ninguno de los hilos restantes haya tomado posesión del bloqueo de escritura. La siguiente clase ilustra, mediante una pequeña clase de ejemplo, la solución anterior. // package definition . . . import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author migue * */ public abstract class User { protected String username; protected String login; protected int credits; public User(int credits) { this.credits = credits; } private ReadWriteLock lock = new ReentrantReadWriteLock(); public void increaseCredit(int credit) { // adquirmos el bloqueo de lectura this.lock.writeLock().lock(); try { 29
  30. 30. Aspect Oriented Programming // realizamos la operación, en este caso, // aumentar el crédito del usuario this.credits += credit; } finally { // liberamos el recurso adquirido this.lock.writeLock().unlock(); } } public void decreaseCredit(int credit) { // adquirmos el bloqueo de lectura this.lock.writeLock().lock(); try { // realizamos la operación, en este caso, // disminuir el crédito del usuario this.credits -= credit; } finally { // liberamos el recurso adquirido this.lock.writeLock().unlock(); } } public boolean hasCredit() { // adquirimos el bloqueo de lectura this.lock.readLock().lock(); boolean hasCredit; try { hasCredit = this.credits > 0 ? true : false; } finally { this.lock.readLock().unlock(); } return hasCredit; } } Resulta evidente que la manera anterior de realizar los bloqueos resulta intrusiva, puesto que para cada uno de los métodos para los que en los que se desee gestionar la gestión de la concurrencia, tendremos que añadir los fragmentos de código vistos en el ejemplo. Cualquier método en el que nos olvidemos de añadir el código de gestió de la concurrencia puede ocasionarnos un incorrecto funcionamiento de nuestro sistema. Además, debemos asegurarnos que si un método adquire el bloqueo de lectura, sea este el que libere, y no el de lectura, y viceversa. Implementación basada en AspectJ Esta alternativa de solución propone a creación de un aspecto que encapule toda la lógica del patrón descrito; evitando de este modo la modificación de todas aquellas clases que necesiten hacer uso de diho patrón. Puesto que además, el patrón es reutilizable en numerosas situaciones, haremos que el aspecto también lo sea. La base de la solución, tal y como se indicaba anteriormente, consiste en disponer de un aspecto abstracto que implemente toda la lógica del patrón: public abstract aspect ReadWriteLockSynchronizationAspect perthis(readOperations() || writeOperations()) { 30

×