Descarga la aplicación para disfrutar aún más
Esta es una vista previa del archivo. Inicie sesión para ver el archivo original
LEEME.TXT NOTA SOBRE EL CÓDIGO DE EJEMPLO: Para ejecutar la aplicación de ejemplo debes instalar AspectWerkz. Tienes información sobre cómo hacerlo en la página del proyecto, aunque básicamente se reduce a 1. descomprimir el paquete en alguna carpeta de tu sistema 2. crear una variable de entorno ASPECTWERKZ_HOME que apunte a la carpeta que has utilizado 3. añadir la ruta ASPECTWERKZ_HOME/bin al PATH de tu sistema. ¡A disfrutar! articulo_aop.html La Programación Orientada a Aspectos ("Aspect Oriented Programming", o AOP) es, quizá, uno de los campos de investigación en el ámbito de la Ingeniería de Software que más interés despierta en la actualidad, tanto en círculos académicos como empresariales. La razón de ello es que propone una nueva visión de los sistemas informáticos que promete simplificar considerablemente el desarrollo y mantenimiento de aplicaciones, aumentando por tanto la productividad del desarrollador. La AOP lleva la separación de responsabilidades más allá que la programación orientada a objetos, aumentando el nivel de encapsulación y por tanto, permitiendo diseños más limpios, fáciles de entender y mantener. A la vez, la AOP permite adoptar esa visión de forma muy poco traumática para los equipos de desarrollo, pues los conceptos introducidos son relativamente sencillos y las herramientas requeridas se pueden construir sobre las plataformas orientadas a objetos tradicionales. Como veremos, las ventajas de la Programación Orientada a Aspectos son muchas y muy valiosas, y el esfuerzo requerido para adoptarla es razonablemente pequeño. INTRODUCCIÓN La programación orientada a objetos (OOP) utiliza un modelo lógico del mundo real para construír una solución a un problema concreto. Cuando diseñamos una aplicación orientada a objetos, empezamos por distinguir las distintas entidades (personajes) que componen el espacio del problema, y así obtenemos las clases que formarán el modelo de datos. Luego identificamos los procesos que involucran a las entidades para alterar el estado del sistema y los plasmamos en el modelo de lógica de negocio. Y empezamos a construír nuestra aplicación escribiendo el código de cada una de nuestras clases intentando mantener en la medida de lo posible una separación adecuada de las responsabilidades (una clase para cada cosa). Este método de trabajo lleva muchos años demostrando su eficacia en proyectos muy diferentes, pero también tiene sus limitaciones, derivadas del hecho de que estamos trabajando con modelos de la realidad, que nunca serán tan ricos como la realidad misma. En concreto, una de las limitaciones que se presentan es que existen determinados Aspectos de nuestra solución que se resistirán a ser encapsulados en clases estándar, normalmente porque afectan a un gran número de procesos y/o actores. Son asuntos que conceptualmente son ortogonales (independientes) a los demás, pero que al modelizarse en términos de objetos terminan plasmándose en forma de código repetido en distintos puntos del sistema, y dependencias entre módulos que no están relacionadas con el problema real que pretendíamos resolver. Consideremos como ejemplo la seguridad. Supongamos que debemos desarrollar una aplicación de gestión para una escuela, y que ésta debe tener un modelo de seguridad complejo en el que, además de distinguir entre "usuario autorizado" y "usuario no autorizado", cada usuario del sistema debe tener una política de acceso en función de sus características individuales, con reglas del tipo "un profesor puede acceder a los datos de un alumno, pero sólo si está matriculado en alguno de los cursos que imparte el profesor. Además, si no es el tutor del alumno, entonces sólo podrá consultar los datos, no modificarlos". Una regla así requiere muchas comprobaciones en tiempo de ejecución antes de permitir que un profesor modifique la ficha del alumno, lo que puede (aunque hay formas de evitarlo) provocar que el código relacionado con la seguridad termine desperdigado entre las clases de lógica de negocio, la clase Curso y la clase Alumno. Téngase en cuenta el énfasis en la palabra "puede": existen patrones de diseño orientado a objetos que permiten aislar este tipo de conceptos, como el patrón comando, que encapsula los procesos de negocio en clases individuales y permite ejercer un control muy fino sobre las condiciones de invocación de cada proceso. Pero como veremos la AOP nos proporciona formas más sencillas de resolverlos. Conceptualmente, la AOP nos proporciona nuevos elementos para representar conceptos que escapaban a las técnicas de análisis orientadas a objetos. Estructuralmente, los entornos de ejecución basados en Aspectos añaden a nuestros ejecutables puntos de captura ("hooks") a los que se puede asociar código arbitrario de forma dinámica o estática. En definitiva, la AOP nos da las herramientas necesarias para modelizar el "cuando se acceda a un método de la clase Alumno, si la llamada viene de un objeto de la clase Profesor, antes de ejecutarlo hay que comprobar si el alumno pertenece a algún curso de éste y lanzar una excepción si no es así" sin tener que introducir en las clases Profesor ni Alumno código relativo a unas funcionalidades que no les corresponden. La programación orientada a Aspectos surgió a partir del trabajo de investigación sobre lenguajes de programación orientados a objetos desarrollado entre 1972 y 1980 por el equipo de Gregor Kiczales en el Xerox Palo Alto Research Center CONCEPTOS Lo que la AOP introduce de nuevo respecto a la OOP tradicional es que conceptos que hasta ahora hemos estado representando por medio de clases, pasan a tener una representación propia de primer nivel: el Aspecto. Utilizamos los Aspectos para representar características globales de una aplicación que por serlo influyen y reciben influencia de distintos componentes de la misma, y que por tanto escapan a la ordenación tradicional de capas (presentación, negocio, persistencia, servicios). Un ejemplo muy habitual es el de los sistemas de trazas. Si necesitamos que nuestra aplicación proporcione información en tiempo de ejecución sobre las tareas que se van desarrollando, normalmente utilizamos frameworks como Log4J o java.util.logging, desperdigando por todo nuestro código llamadas a éstos sistemas de trazas para ir registrando los eventos. Pues bien, en términos de AOP, la generación de trazas es un Aspecto de nuestra aplicación, encapsulado en su propio -y único- módulo de código fuente y por tanto desacoplado respecto a las demás clases del sistema. Elementos ortogonales en el sistema real pasan a ser también ortogonales en el modelo de software. Una de las críticas más recurrentes que se escuchan sobre AOP es que en realidad no permite hacer nada nuevo que no se pueda hacer ya y que se exagera su importancia. Es importante destacar que la novedad -y la ventaja- de AOP no está en el qué, sino en el cómo, pues permite diseños mucho más limpios y fáciles de representar y comunicar que por tanto dan lugar a aplicaciones mejor construídas y más fáciles de implementar y mantener. Por ejemplo, hasta ahora la persistencia se ha implementado como un conjunto de clases de utilidad, gestores de esquema y una serie de técnicas repartidas por el código a la hora de instanciar objetos o modificar sus atributos (independientemente de si usamos JDBC directo, CMP, JDO o frameworks a medida). La consecuencia de esto es que cuando un fragmento de código invoca un método sobre un bean que representa una entidad del modelo de datos, al menos uno de los dos (y muchas veces ambos) deben ser conscientes del hecho de que el bean es persistente, ya sea porque hay que comenzar una transacción JDO, o enviar una consulta SQL a la base de datos o cualquier otro medio que hayamos elegido. Como veremos más adelante, la AOP nos proporciona herramientas para que la persistencia sea realmente un elemento ortogonal a los demás de nuestra aplicacion, lo que se ha venido a llamar "persistencia transparente". Ciertamente hay muy pocas cosas que se puedan hacer con AOP que no hayamos estado haciendo ya con la OOP tradicional. Debemos esperar un cambio en la forma de resolver los problemas, no en ámbito de éstos. A continuación se presentan los conceptos fundamentales introducidos por la AOP. Son las herramientas que podemos utilizar para representar esos Aspectos de nuestro sistema que antes se adaptaban a duras penas a la representación por medio de clases: Punto de unión (join point): Representa un "momento" en la ejecución de un programa, por ejemplo, una llamada a un método, o un constructor, o el acceso a un miembro de una clase en particular. Intersección (pointcut): Declaración de un conjunto de puntos de unión, por ejemplo, "llamadas a los métodos que empiecen por set". Guía/Consejo/Orientación (advice): Comportamiento que queremos invocar cuando se alcance un determinado punto de intersección. Aspecto (aspect): Módulo que define uno o varios puntos de intersección y las acciones (guías) que deben ejecutarse cuando la ejecución alcance cualquiera de ellos. Introducción (introduction): Comportamiento que añadimos a un objeto en tiempo de ejecución para que implemente una interfaz adicional aparte de la suya original. La programación orientada a Aspectos complementa la programación orientada a objetos, introduciendo nuevos conceptos que representan comportamientos que queremos asociar a determinados momentos de la ejecución de un programa, pero que no encajan en el modelado estático para el que tan buenos resultados proporcionan las clases tradicionales. APLICACIONES A estas alturas probablemente el lector ya tendrá en la cabeza algunas posibles aplicaciones de estos nuevos conceptos. Algunos de los más inmediatos son: Trazas (logging): si podemos asociar un determinado código a la invocación de un método, podemos incluír en ese código las trazas sobre la llamada al método, los parámetros, el tiempo que tarda en ejecutarse, etc, de forma que el propio método sólo contenga el código para completar la tarea que le corresponde. Seguridad: como ya comentamos antes, al interceptarse las llamadas a métodos, acceso a miembros y demás, podemos aplicar políticas de seguridad tan arbitrarias como necesitemos sin mezclar ese código con el existente. Persistencia: podemos interceptar las llamadas a constructores y mutadores ("setters" ) de una clase y aprovecharlas para lanzar consultas sql sincronizando los miembros con la información de la base de datos. Funcionalidad arbitraria: incluso con el sistema completamente desarrollado y en ejecución, podemos agregar nuevas funcionalidades para tratar casos especiales o de aparición tardía, sin modificar el código existente. Monitorización y administración: funcionalidades análogas a JMX se pueden implementar en términos de Aspectos. Cacheado de información: podemos aprovechar la intercepción de la llamada a un método para decidir si efectivamente lo ejecutamos o si devolvemos un resultado almacenado previamente. DIFERENTES ENFOQUES La AOP es una metodología emergente, alrededor de la cual aún existe una intensa labor investigadora en ámbitos académicos y empresariales. Esto ha derivado en la existencia de muchos y muy variados enfoques a la hora de aplicar las nuevas ideas. En mi opinión, y restringiéndome a los frameworks Java, los que quizá tengan más posibilidades de establecerse en un futuro cercano son los siguientes (en la sección referencias se pueden encontrar más información sobre ellos): AspectJ: Actualmente en la versión 1.1. Desarrollado inicialmente en Xerox PARC y hospedado actualmente en la fundación eclipse, AspectJ propone una extensión del lenguaje Java, con nuevas palabras reservadas y construcciones específicas para definir Aspectos, puntos de corte, guías, etc.En su sitio web podemos descargar toda una plataforma de desarrollo compuesta por un entorno de edición de código (independiente o integrado en eclipse IDE), un compilador y abundante documentación y ejemplos. Nanning:Actualmente en la versión 0.9. Framework basado en proxies dinámicos: clases que se generan dinámicamente en tiempo de ejecución y se interponen entre el autor de una llamada y el código de destino. Los Aspectos se implementan mediante clases Java, y pueden añadirse al sistema programáticamente o declararse en un fichero xml de configuración. AspectWerkz: Actualmente en la versión 0.10. Al igual que en Nanning, los Aspectos se definen como clases Java y pueden ser añadidos al sistema mediante un fichero de configuración xml, pero además, podemos declarar los Aspectos y guías en forma de atributos definidos en el código fuente. En la actualidad se utilizan etiquetas xDoclet, aunque se prevé adoptar el modelo de metadatos introducido con el JDK 1.5 (JSR 175). AspectWerkz soporta el enlazado de los Aspectos tanto en tiempo de ejecución (mediante proxies dinámicos) como de forma estática, para lo que proporciona un post-procesador que incorpora a las clases compiladas el bytecode correspondiente a la funcionalidad definidda en las guías en los puntos de corte seleccionados. JBossAOP: Actualmente en la versión 1.0Beta. La próxima versión 4.0 del servidor de aplicaciones JBoss contiene un entorno AOP gracias al cual se pueden proporcionar servicios como gestión de transacciones y persistencia a POJO's ("Plain Old Java Objects") de forma trasparente. Al generar los descriptores de despliegue para nuestra aplicación deberemos incorporar uno más en el que se definan qué servicios AOP va a requerir y JBoss los incorporará en tiempo de ejecución mediante proxies dinámicos.También podemos usar JBossAOP de forma independiente del servidor. Cuál de los enfoques mencionados gozará de mayor aceptación es algo que tendremos que ver con el tiempo. Lo que está claro es que las grandes firmas del Software están empezando a hacer una apuesta firme por la Programación Orientada a Aspectos, y que eventos como la Aspect Oriented Software Development Conference han trascendido los entornos meramente académicos para convertirse en encuentros empresariales donde, quizá, se esté definiendo el futuro de las técnicas en Ingeniería de Software. UN CASO PRÁCTICO Para ilustrar los conceptos introducidos, vamos a desarrollar un pequeño ejemplo consistente en una aplicación de gestión para un centro de enseñanza. Supongamos que queremos representar Profesores, Cursos y Alumnos, y las relaciones establecidas entre ellos. Cada alumno tiene un tutor y está matriculado en una serie de cursos. Cada profesor imparte una serie de cursos y además es tutor de una serie de alumnos, y por último cada curso tiene un profesor y una serie de alumnos matriculados. El modelo de datos estaría formado por las siguientes clases: Entidad.java public abstract class Entidad { /* * La clase Entidad define las características comunes a todas las * entidades. En particular, el hecho de que cada entidad debe tener un * identificador único que permita realizar búsquedas, por ejemplo. */ protected Integer ID; /** * Si nos pasan un id en el constructor, lo utilizamos para esta entidad. * * @param id */ public Entidad(Integer id) { ID = id; } /** * El constructor sin parámetros generará un nuevo ID. Cada entidad tendrá * un ID generado aleatoriamente entre 0 y 1.000.000 */ public Entidad() { ID = new Integer(10000000 * Math.random()); } // .. // } Alumno.java public class Alumno extends Entidad { protected String nombre; protected String apellidos; protected List cursos; protected Profesor tutor; /** * @param id */ public Alumno(Integer id) { super(id); } public Alumno() { //creamos el id... super(); cursos = new ArrayList(); } /** * Añade un curso a la lista del alumno. * * @param c */ public void addCurso(Curso c) { cursos.add(c); } //...getters y setters // } Profesor.java public class Profesor extends Entidad { protected String nombre; protected String apellidos; protected List cursos; protected List alumnosTutelados; /** * @param id */ public Profesor(Integer id) { super(id); } public Profesor() { super(); alumnosTutelados = new ArrayList(); cursos=new ArrayList(); } /** * * @param c */ public void addCurso(Curso c) { cursos.add(c); } /** * * @param a */ public void addAlumnoTutelado(Alumno a) { alumnosTutelados.add(a); } //... getters y setters // } Curso.java public class Curso extends Entidad { protected String nombre; protected Profesor profesor; protected List alumnos; /** * @param id */ public Curso(Integer id) { super(id); } public Curso() { super(); alumnos=new ArrayList(); } /** * * @param a */ public void addAlumno(Alumno a) { alumnos.add(a); } //...getters y setters // } Main.java public class Main { public static void main(String[] args) { //Creamos las entidades: System.out.println("Creando curso, profesor y alumno:"); Curso c = new Curso(); c.setNombre("Aspect Oriented Programming"); Profesor p = new Profesor(); p.setNombre("Nacho"); p.setApellidos("Brito"); Alumno a = new Alumno(); a.setNombre("Manolito"); a.setApellidos("Gafotas"); //establecemos las relaciones: System.out.println("Estableciendo relaciones:"); //El profesor p imparte el curso c, al que está matriculado el alumno // a, que además tiene a p como tutor. c.setProfesor(p); c.addAlumno(a); a.setTutor(p); a.addCurso(c); p.addAlumnoTutelado(a); p.addCurso(c); } } Hasta aquí todo es bastante sencillo pero poco interesante. Parece el típico proyecto de ejemplo en el que las partes complejas de las clases se omiten por simplicidad. Pero... ¿y si afirmase que podemos hacer persistentes estas entidades sin tocar una linea del código existente? Para este ejemplo vamos a utilizar AspectWerkz, quizá el framework más flexible de entre los existentes. Como vimos en el apartado 4, AspectWerkz nos permite definir nuestros Aspectos como clases tradicionales, añadiendo la metainformación sobre puntos de corte bien en un fichero xml externo o bien en forma de atributos insertados en el código fuente. Vamos a utilizar esta última manera por dos razones: la primera es que el manejo del fichero xml es trivial y está muy bien documentado en el sitio web de AspectWerkz (ver referencias), y la segunda porque tal como se comenta en la documentación, próximamente la declaración de puntos de corte se hará aprovechando las nuevas características sobre metadatos que aporta la nueva JVM 1.5, con lo cual tendremos Aspectos autocontenidos (definidos en un solo fichero de código fuente) y perfectamente integrados con la plataforma estándar de Java. Veamos como definir la persistencia en nuestra aplicación como un Aspecto. AspectWerkz trata los Aspectos como clases java normales, con la única restricción de que deben heredar de org.codehaus.aspectwerkz.aspect.Aspect. Las guías que defina un Aspecto serán métodos de la clase, y los puntos de corte podrán ser anónimos, en cuyo caso simplemente se anotarán como atributos en el metadata de la clase, o bien ser miembros de tipo JoinPoint. Así, nuestro Aspecto para la persistencia queda como sigue: PersistenceAspect.java public class PersistenceAspect extends Aspect { /** * Intercepta los accesos de escritura a los campos y almacena el valor * asignado en la base de datos. * * @After set(* org.tres.escuela.modelo.*.*) */ public void setterAdvice(JoinPoint joinPoint) throws Throwable { FieldSignature declaracion = (FieldSignature) joinPoint.getSignature(); String sql = null; String nombreCampo = declaracion.getField().getName(); System.out.println("Modificando el campo " + nombreCampo); DBHelper dbHelper = DBHelper.getInstance(); //obtenemos información sobre la invocación: String tabla = joinPoint.getTargetClass().getName(); Entidad sujeto = (Entidad) joinPoint.getTargetInstance(); Field campo = declaracion.getField(); Object resultado = campo.get(sujeto); if (nombreCampo.equals("ID")) { if (dbHelper.existeEntidad(sujeto)) dbHelper.poblar(sujeto); else dbHelper.insertar(sujeto); } else dbHelper.actualizarCampo(campo, sujeto); } /* * (non-Javadoc) * * @see java.lang.Object#finalize() */ protected void finalize() throws Throwable { DBHelper.getInstance().close(); super.finalize(); } Lo primero que tenemos que destacar es la nomenclatura de la guía "setterAdvice". En AspectWerkz, como en la mayoría de frameworks AOP existentes en la actualidad, se distinguen distintos tipos de guía en función de cuándo queremos ejecutarlas para un punto de corte: Before Advices: son guías que se ejecutarán antes que el punto de corte al que están asociadas. After Advices: guías que se ejecutarán después del punto de corte. Around Advices: guías que toman el control antes del punto de ejecución y lo pierden después, teniendo la posibilidad de modificar el flujo de ejecución en vez de ceder el control al código original. Si vamos a definir un Around Advice, el método deberá devolver un Object en lugar de ser void, como consecuencia de que una guía de este tipo puede incluso suplantar la ejecución del punto de unión haciendo que el objeto devuelto sea uno diferente Estas guías podrán aplicarse tanto a constructores como métodos y campos de una clase cualquiera. Es importante prestar atención a la etiqueta " @After set(* org.tres.escuela.modelo.*.*)" que se encuentra en el comentario javaDoc del método setterAdvice. En la actualidad AspectWerkz utiliza etiquetas XDoclet para la generación de metainformación sobre los Aspectos, aunque como hemos visto esto cambiará con el JDK 1.5. Con esta etiqueta estamos declarando que el método setterAdvice es una guía para el punto de corte representado por "set(* org.tres.escuela.modelo.*.*)", que comprende todos los accesos de escritura ("set") sobre campos de cualquier tipo (primer asterisco) de cualquier clase del paquete org.tres.escuela.modelo, sin ninguna restricción de nombre (penúltimo y último asteriscos, respectivamente). En la práctica totalidad de los frameworks AOP se contempla el empleo de expresiones regulares para declarar puntos de corte. Para una información más detallada sobre el funcionamiento de este framework, consultar las referencias anotadas al final del artículo. Como vemos, el método guía recibe un único parámetro, de tipo JoinPoint, que nos proporciona acceso al contexto de ejecución del punto de unión que se está ejecutando en el momento de invocar nuestro Aspecto. Podemos obtener información sobre la instancia sobre la que se modificaba el campo, su clase, el valor del campo modificado, etc. En este caso utilizamos esta información para generar una consulta SQL que mantendrá sincronizado nuestro objeto con la base de datos. En caso de que estemos modificando el campo ID, se asume que lo que se quiere es modificar la identidad del objeto, que pasará a representar una entidad distinta. Para ejecutar nuestra aplicación debemos primero declarar el Aspecto en un archivo "aspectwerkz.xml" y colocarlo en el classpath: <!DOCTYPE aspectwerkz PUBLIC "-//AspectWerkz//DTD 0.10//EN" "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_10.dtd"> <aspectwerkz> <system id="escuela"> <package name="org.tres.escuela"> <aspect class="aop.PersistenceAspect"/> </package> </system> </aspectwerkz> En este archivo estamos declarando un único Aspecto, definido en la clase org.tres.escuela.aop.PersistenceAspect. Para introducir los ganchos ("hooks"), y a la espera de JDK1.5, deberemos ejecutar el postprocesador: H:\AOP>java org.codehaus.aspectwerkz.definition.AspectC -verbose ./src ./bin AspectC::INFO - compiling attributes... AspectC::INFO - aspect [org.tres.escuela.aop.PersistenceAspect] AspectC::INFO - deployment model [perJVM] AspectC::INFO - after advice [setterAdvice::set(* org.tres.escuela.modelo.*.*)] AspectC::INFO - compiled classes written to ./bin AspectC::INFO - compilation successful Y, finalmente, ejecutamos nuestra aplicación sustituyendo el comando java por el script "aspectwerkz", que invoca a la máquina virtual sustituyendo el ClassLoader estándar por uno específico que se encargará de monitorizar la ejecución del código e invocar a cada Aspecto cuando se alcance el punto de intersección definido: H:\AOP>aspectwerkz -cp bin org.tres.escuela.Main NOTA: En la versión 0.10 de AspectWerkz existe un fallo en los scripts proporcionados (aspectwerkz y setEnv); al definir el CLASSPATH se olvidan de incluir la librería bcel.jar. Hay que modificar el script para incluírla en el CLASSPATH o el ejemplo no funcionará. CONCLUSIONES Vamos a seguir oyendo hablar sobre la AOP. La prueba de ello es el interés que han mostrado en la idea empresas com IBM, que lo manifestó abiertamente en la última Aspect Oriented Software Development Conference (AOSD), Bea, que recientemente ha comenzado su patrocinio del proyecto AspectWerkz, o JBoss, que basa la próxima versión 4.0 de su servidor de aplicaciones en aplicar Aspectos sobre los componentes del desarrollador que les proporcionen los servicios definidos por la especificación J2EE. Puede sin embargo que la adopción de la AOP sea desigual, al menos la adopción directa. Parece que donde primero veremos aparecer implementaciones estables basadas en Aspectos es en los servidores de aplicaciones, que de esta forma podrán proveer de distintos servicios a los componentes desplegados en su interior de una forma mucho menos intrusiva que hasta ahora. Pronto veremos contenedores que no necesitarán que implementemos interfaces extrañas para proporcionarnos servicios de persistencia, gestión de transacciones o seguridad. REFERENCIAS Aspect Oriented Software Development Aspect Oriented Software Development Conference Seguimiento de la AOSD en TheServerside AspectWerkz Artículo de Graham O'Regan sobre AspectWerkz, traducido al castellano por Juan Antonio Palos Cambios en la version 0.10 de AspectWerkz, despues de que Bea comenzara su patrocinio del proyecto. AspectJ Artículo de Nicholas Lesiecki sobre AspectJ Documentación sobre AspectJ en el sitio de Xerox PARC Introducción teórica a la AOP, por los miembros del equipo original de Xerox PARC JBossAOP Artículo de Bill Burke y Adrian Brock sobre AOP y JBoss Artículo de Yasser EL-Manzalawy sobre AOP en developer.com Artículo de Russ Milles sobre Lazy Loading con AspectJ ejecutar.bat java org.codehaus.aspectwerkz.definition.AspectC -verbose ./src ./bin aspectwerkz -cp bin org.tres.escuela.Main src/aspectwerkz.xml src/org/tres/escuela/Main.java src/org/tres/escuela/Main.java package org.tres.escuela; import org.tres.escuela.modelo.Alumno; import org.tres.escuela.modelo.Curso; import org.tres.escuela.modelo.Profesor; /** * @author Nacho */ public class Main { public static void main(String[] args) { //Creamos las entidades: System.out.println("Creando curso, profesor y alumno:"); Curso c = new Curso(); c.setNombre("Aspect Oriented Programming"); //System.out.println(c); Profesor p = new Profesor(); p.setNombre("Nacho"); p.setApellidos("Brito"); //System.out.println(p); Alumno a = new Alumno(); a.setNombre("Manolito"); a.setApellidos("Gafotas"); //System.out.println(a); //establecemos las relaciones: System.out.println("Estableciendo relaciones:"); //El profesor p imparte el curso c, al que está matriculado el alumno // a, que además tiene a p como tutor. c.setProfesor(p); c.addAlumno(a); a.setTutor(p); a.addCurso(c); p.addAlumnoTutelado(a); p.addCurso(c); //System.out.println(c); //System.out.println(p); //System.out.println(a); } } src/org/tres/escuela/aop/PersistenceAspect.java src/org/tres/escuela/aop/PersistenceAspect.java package org.tres.escuela.aop; import java.lang.reflect.Field; import org.codehaus.aspectwerkz.aspect.Aspect; import org.codehaus.aspectwerkz.joinpoint.FieldSignature; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; import org.tres.escuela.modelo.Entidad; import org.tres.escuela.persistencia.DBHelper; /** * @Aspect perJVM * @author Nacho. La persistencia se trata como un aspecto de la aplicación, de * forma que en esta clase se implementan los distintos "advices" que * defienen el comportamiento "persistente" */ public class PersistenceAspect extends Aspect { /** * Intercepta los accesos de escritura a los campos y almacena el valor * asignado en la base de datos. * * @param invocation * @return El resultado de invocar al siguiente consejo o el método final. * @throws Throwable * @After set(* org.tres.escuela.modelo.*.*) */ public void setterAdvice(JoinPoint joinPoint) throws Throwable { FieldSignature declaracion = (FieldSignature) joinPoint.getSignature(); String sql = null; String nombreCampo = declaracion.getField().getName(); System.out.println("Modificando el campo " + nombreCampo); DBHelper dbHelper = DBHelper.getInstance(); //obtenemos información sobre la invocación: String tabla = joinPoint.getTargetClass().getName(); Entidad sujeto = (Entidad) joinPoint.getTargetInstance(); Field campo = declaracion.getField(); Object resultado = campo.get(sujeto); if (nombreCampo.equals("ID")) { if (dbHelper.existeEntidad(sujeto)) dbHelper.poblar(sujeto); else dbHelper.insertar(sujeto); } else dbHelper.actualizarCampo(campo, sujeto); } /* * (non-Javadoc) * * @see java.lang.Object#finalize() */ protected void finalize() throws Throwable { DBHelper.getInstance().close(); super.finalize(); } } src/org/tres/escuela/modelo/Alumno.java src/org/tres/escuela/modelo/Alumno.java package org.tres.escuela.modelo; import java.util.ArrayList; import java.util.List; /** * @author Nacho */ public class Alumno extends Entidad { protected String nombre; protected String apellidos; protected List cursos; protected Profesor tutor; /** * @param id */ public Alumno(Integer id) { super(id); } public Alumno() { //creamos el id... super(); cursos = new ArrayList(); } /** * Añade un curso a la lista del alumno. * * @param c */ public void addCurso(Curso c) { cursos.add(c); } /** * @return Returns the apellidos. */ public String getApellidos() { return apellidos; } /** * @param apellidos * The apellidos to set. */ public void setApellidos(String apellidos) { this.apellidos = apellidos; } /** * @return Returns the nombre. */ public String getNombre() { return nombre; } /** * @param nombre * The nombre to set. */ public void setNombre(String nombre) { this.nombre = nombre; } /** * @return Returns the tutor. */ public Profesor getTutor() { return tutor; } /** * @param tutor * The tutor to set. */ public void setTutor(Profesor tutor) { this.tutor = tutor; } /** * @return Returns the cursos. */ public List getCursos() { return cursos; } } src/org/tres/escuela/modelo/Curso.java src/org/tres/escuela/modelo/Curso.java package org.tres.escuela.modelo; import java.util.ArrayList; import java.util.List; /** * @author Nacho */ public class Curso extends Entidad { protected String nombre; protected Profesor profesor; protected List alumnos; /** * @param id */ public Curso(Integer id) { super(id); } public Curso() { super(); System.out.println("Instanciando curso"); alumnos=new ArrayList(); } /** * * @param a */ public void addAlumno(Alumno a) { alumnos.add(a); } /** * @return Returns the nombre. */ public String getNombre() { return nombre; } /** * @param nombre The nombre to set. */ public void setNombre(String nombre) { this.nombre = nombre; } /** * @return Returns the profesor. */ public Profesor getProfesor() { return profesor; } /** * @param profesor The profesor to set. */ public void setProfesor(Profesor profesor) { this.profesor = profesor; } /** * @return Returns the alumnos. */ public List getAlumnos() { return alumnos; } } src/org/tres/escuela/modelo/Entidad.java src/org/tres/escuela/modelo/Entidad.java package org.tres.escuela.modelo; import java.lang.reflect.Field; /** * @author Nacho */ public abstract class Entidad { /* * La clase entidad define las características comunes a todas las * entidades. En particular, el hecho de que cada entidad debe tener un * identificador único que permita realizar búsquedas, por ejemplo. */ protected Integer ID; /** * Si nos pasan un id en el constructor, lo utilizamos para esta entidad. * * @param id */ public Entidad(Integer id) { ID = id; } /** * El constructor sin parámetros generará un nuevo ID. Cada entidad tendrá * un ID generado aleatoriamente entre 0 y 1.000.000 */ public Entidad() { ID = new Integer((int) (10000000 * Math.random())); System.out.println("ID generado: " + ID); } /** * @return Returns the iD. */ public Integer getID() { return ID; } /** * muy útil para depuración ;-) */ public String toString() { StringBuffer sb = new StringBuffer("\n" + getClass().getName() + ":\n"); Field[] campos = getClass().getDeclaredFields(); try { Class list = Class.forName("java.util.List"); for (int i = 0; i < campos.length; i++) { if (!list.isAssignableFrom(campos[i].getType())) { try { sb = sb.append(campos[i].getName() + ": " + campos[i].get(this) + "\n"); } catch (Exception e1) { sb = sb.append(campos[i].getName() + ": <inaccesible>\n"); } } else sb = sb.append(campos[i].getName() + ": <lista>\n"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } return sb.toString(); } } src/org/tres/escuela/modelo/Profesor.java src/org/tres/escuela/modelo/Profesor.java package org.tres.escuela.modelo; import java.util.ArrayList; import java.util.List; /** * @author Nacho */ public class Profesor extends Entidad { protected String nombre; protected String apellidos; protected List cursos; protected List alumnosTutelados; /** * @param id */ public Profesor(Integer id) { super(id); } public Profesor() { super(); alumnosTutelados = new ArrayList(); cursos=new ArrayList(); } /** * * @param c */ public void addCurso(Curso c) { cursos.add(c); } /** * * @param a */ public void addAlumnoTutelado(Alumno a) { alumnosTutelados.add(a); } /** * @return Returns the apellidos. */ public String getApellidos() { return apellidos; } /** * @param apellidos The apellidos to set. */ public void setApellidos(String apellidos) { this.apellidos = apellidos; } /** * @return Returns the nombre. */ public String getNombre() { return nombre; } /** * @param nombre The nombre to set. */ public void setNombre(String nombre) { this.nombre = nombre; } /** * @return Returns the alumnosTutelados. */ public List getAlumnosTutelados() { return alumnosTutelados; } /** * @return Returns the cursos. */ public List getCursos() { return cursos; } } src/org/tres/escuela/persistencia/DBHelper.java src/org/tres/escuela/persistencia/DBHelper.java package org.tres.escuela.persistencia; import java.lang.reflect.Field; import java.sql.Connection; import org.tres.escuela.modelo.Entidad; public class DBHelper { private static DBHelper instance = null; private Connection cn = null; /** * SINGLETON * * @return */ public static DBHelper getInstance() { if (instance == null) instance = new DBHelper(); return instance; } /** * * */ private DBHelper() { System.out.println("DBHelper: Instanciando DBHelper"); } /** * @param sql */ public void execute(String sql) throws Exception { System.out.println("DBHelper: Ejecutando " + sql); } /** * @param e */ public void insertar(Entidad e) { System.out.println("DBHelper: Insertando " + e.getClass().getName()); } /** * @param e */ public void poblar(Entidad e) { System.out.println("DBHelper: Poblando " + e.getClass().getName()); } /** * @param class1 */ public void comprobarQueExisteTabla(Class class1) { System.out.println("DBHelper: Comprobando si existe la tabla " + class1.getName()); } /** * */ public void close() { System.out.println("DBHelper: Cerrando conexión."); } /** * Comprueba si existe la entidad proporcionada * * @param resultado * @return */ public boolean existeEntidad(Entidad resultado) { System.out.println("DBHelper:Comprobando si existe el " + resultado.getClass().getName() + " con ID " + resultado.getID()); return false; } /** * @param campo * @param sujeto */ public void actualizarCampo(Field campo, Entidad sujeto) { System.out.println("DBHelper: Actualizando " + campo.getName() + " en el " + sujeto.getClass().getName() + " con ID " + sujeto.getID()); } }
Compartir