Un Framework para la Visualización de Patrones de Diseño

Anuncio
UN FRAMEWORK PARA LA VISUALIZACIÓN DE PATRONES DE
DISEÑO DISTRIBUIDOS Y CONCURRENTES IMPLEMENTADOS CON
PROGRAMACIÓN ORIENTADA A ASPECTOS:
ACVF(ASPECTUAL COMPONENT VISUALIZATION FRAMEWORK)
TESIS DE GRADO EN INGENIERIA INFORMATICA
FACULTAD DE INGENIERIA
UNIVERSIDAD DE BUENOS AIRES
TESISTA: Sr. Diego M.S. ERDÖDY
DIRECTORA: Prof. María FELDGEN
Laboratorio de Sistemas Distribuidos Heterogéneos
DICIEMBRE 2006
UN FRAMEWORK PARA LA VISUALIZACIÓN DE PATRONES DE
DISEÑO DISTRIBUIDOS Y CONCURRENTES IMPLEMENTADOS CON
PROGRAMACIÓN ORIENTADA A ASPECTOS:
ACVF(ASPECTUAL COMPONENT VISUALIZATION FRAMEWORK)
TESIS DE GRADO EN INGENIERIA INFORMATICA
Laboratorio de Sistemas Distribuidos Heterogéneos
FACULTAD DE INGENIERIA
UNIVERSIDAD DE BUENOS AIRES
Sr. Diego M.S. Erdody
Tesista
DICIEMBRE 2006
Prof. María Feldgen
Directora
Resumen
En este trabajo se hace un análisis exhaustivo de los beneficios y desventajas que
tiene una implementación de patrones de diseños distribuidos y concurrentes utilizando el
paradigma de programación orientada a aspectos. Junto con la implementación de las
aplicaciones de cada patrón de diseño, se modela y desarrolla el framework ACVF (bautizado con el nombre de “Aspectual Component Visualization Framework”) para poder
integrar gráficamente los componentes de cada patrón y observar la interacción resultante. Cada patrón se visualiza como una mini-aplicación. Los patrones a analizar se han
dividido en 3 categorías: Concurrentes, Manejo de Eventos y Fiabilidad. Como eje del
análisis, se analizarán las características de las implementaciones orientadas a aspectos
para determinar las mejoras en modularidad.
Palabras clave: programación orientada a aspectos, patrones de diseño distribuidos.
Abstract
This work consists of an exhaustive analysis about the benefits of applying aspect
oriented programming to distributed and concurrent design patterns. Along with the implementation of the study cases for the different design pattern, a framework called
ACVF (Aspectual Component Visualization Framework) was designed and developed.
The framework is used to visually integrate the pattern components and study its interactions. In this way, each pattern can be considered a mini-application. The design patterns
under study have been divided in three groups: Concurrent, Event Handling and Security.
The main objective of this work is to analyze the aspect oriented implementations and
establish its improvements in modularity.
Keywords: aspect oriented programming, distributed design patterns.
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Índice de Contenidos
Índice de Figuras ............................................................................................................... 1
1.
Introducción ............................................................................................................... 3
1.1. Estructura del trabajo ...................................................................................... 4
1.1.1.
Programación Orientada a Aspectos ........................................................... 5
1.1.2.
Patrones de diseño distribuidos ................................................................... 5
1.1.3.
Análisis de patrones .................................................................................... 5
1.1.4.
Framework de Visualización ....................................................................... 5
1.1.5.
Patrones de concurrencia............................................................................. 5
1.1.6.
Patrones de manejo de eventos.................................................................... 5
1.1.7.
Patrones de comunicación distribuida ......................................................... 5
1.1.8.
Patrones de seguridad y fiabilidad .............................................................. 5
1.1.9.
Resultados y conclusiones ........................................................................... 5
1.1.10. Futuros trabajos ........................................................................................... 6
2.
3.
Programación Orientada a Objetos ........................................................................... 7
2.1.
Fundamentos ..................................................................................................... 7
2.2.
Problemas........................................................................................................... 8
Programación Orientada a Aspectos ......................................................................... 8
3.1. Separación en intereses ..................................................................................... 8
3.1.1.
Programación de patrones ......................................................................... 10
3.1.2.
Filtros de composición .............................................................................. 11
3.1.3.
Programación de meta-objetos .................................................................. 12
3.1.4.
Conclusiones ............................................................................................. 13
3.2.
Estructura de un aspecto ................................................................................ 13
3.3.
Combinación de intereses ............................................................................... 14
3.4.
Ventajas y Problemas de la AOP ................................................................... 16
3.5. AspectJ ............................................................................................................. 18
3.5.1.
Sintaxis de un aspecto ............................................................................... 19
3.5.2.
Definición de un pointcut .......................................................................... 20
3.5.3.
Declaración de un advice .......................................................................... 25
3.5.4.
Ejemplos .................................................................................................... 26
3.5.5.
Declaraciones de miembros inter-tipo....................................................... 30
3.5.6.
Declaraciones de derivación...................................................................... 31
3.5.7.
Otras declaraciones ................................................................................... 33
3.5.8.
Intercalado ................................................................................................. 34
3.6. Comparación con otras implementaciones de AOP ..................................... 35
3.6.1.
AspectWerkz ............................................................................................. 35
3.6.2.
JBoss AOP ................................................................................................ 36
3.6.3.
Spring AOP ............................................................................................... 37
Diego M.S. Erdödy
1
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
3.6.4.
3.6.5.
4.
CaesarJ ....................................................................................................... 38
Conclusiones .............................................................................................. 40
Patrones de diseño distribuidos ................................................................................ 45
4.1. Patrones de diseño ........................................................................................... 45
4.1.1.
Beneficios y problemas de los patrones de diseño .................................... 45
4.1.2.
Descripción de patrones de diseño ............................................................ 46
4.2.
5.
Uso en sistemas distribuidos y concurrentes ................................................. 47
Metodología de Análisis de patrones ....................................................................... 49
5.1.
Objetivos ........................................................................................................... 49
5.2. Descripción de las características a analizar ................................................. 49
5.2.1.
Reusabilidad .............................................................................................. 49
5.2.2.
Transparencia de composición .................................................................. 49
5.2.3.
Independencia ............................................................................................ 49
5.2.4.
Localidad del código ................................................................................. 49
5.3. Notación UML.................................................................................................. 50
5.3.1.
Aspect ........................................................................................................ 50
5.3.2.
Pointcut ...................................................................................................... 51
5.3.3.
Advice ........................................................................................................ 51
5.3.4.
Introduction ............................................................................................... 52
5.3.5.
Declare ....................................................................................................... 53
6.
Framework de Visualización .................................................................................... 55
6.1.
Introducción ..................................................................................................... 55
6.2.
Frameworks ..................................................................................................... 55
6.3. Características de ACVF ................................................................................ 57
6.3.1.
Orientación a Componentes ....................................................................... 57
6.3.2.
Relaciones .................................................................................................. 60
6.3.3.
Simulación ................................................................................................. 60
6.3.4.
Vista de resumen........................................................................................ 61
6.4. Arquitectura y Diseño ..................................................................................... 62
6.4.1.
GEF Framework ........................................................................................ 63
6.4.2.
ACVF Framework ..................................................................................... 64
7.
6.5.
Otros Frameworks de Visualización .............................................................. 76
6.6.
Conclusión ........................................................................................................ 77
Patrones de concurrencia y sincronización ............................................................. 79
7.1. Rendezvous ....................................................................................................... 79
7.1.1.
Resumen .................................................................................................... 79
7.1.2.
Otras denominaciones................................................................................ 79
7.1.3.
Problema .................................................................................................... 79
7.1.4.
Solución ..................................................................................................... 79
7.1.5.
Caso de estudio .......................................................................................... 79
2
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7.1.6.
7.1.7.
7.1.8.
7.1.9.
Estructura .................................................................................................. 80
Estrategia de implementación Orientada a Aspectos ................................ 80
Análisis ...................................................................................................... 86
Componente gráfico .................................................................................. 87
7.2. Balking ............................................................................................................. 88
7.2.1.
Resumen .................................................................................................... 88
7.2.2.
Otras denominaciones ............................................................................... 88
7.2.3.
Problema.................................................................................................... 88
7.2.4.
Solución..................................................................................................... 88
7.2.5.
Caso de Estudio ......................................................................................... 89
7.2.6.
Estructura .................................................................................................. 89
7.2.7.
Estrategia de implementación Orientada a Aspectos ................................ 89
7.2.8.
Análisis ...................................................................................................... 91
7.2.9.
Componente gráfico .................................................................................. 92
7.3. Observador ...................................................................................................... 93
7.3.1.
Resumen .................................................................................................... 93
7.3.2.
Otras denominaciones ............................................................................... 93
7.3.3.
Problema.................................................................................................... 93
7.3.4.
Solución..................................................................................................... 94
7.3.5.
Caso de estudio ......................................................................................... 94
7.3.6.
Estructura .................................................................................................. 94
7.3.7.
Estrategia de implementación Orientada a Aspectos ................................ 95
7.3.8.
Análisis ...................................................................................................... 98
7.3.9.
Componente gráfico .................................................................................. 98
7.4. Optimistic Locking .......................................................................................... 99
7.4.1.
Resumen .................................................................................................... 99
7.4.2.
Otras denominaciones ............................................................................... 99
7.4.3.
Problema.................................................................................................... 99
7.4.4.
Solución..................................................................................................... 99
7.4.5.
Caso de estudio ....................................................................................... 100
7.4.6.
Estructura ................................................................................................ 100
7.4.7.
Estrategia de implementación Orientada a Aspectos .............................. 102
7.4.8.
Análisis .................................................................................................... 105
7.4.9.
Componente gráfico ................................................................................ 105
8.
Patrones de manejo de eventos .............................................................................. 107
8.1. Reactor ........................................................................................................... 107
8.1.1.
Resumen .................................................................................................. 107
8.1.2.
Otras denominaciones ............................................................................. 107
8.1.3.
Problema.................................................................................................. 107
8.1.4.
Solución................................................................................................... 108
8.1.5.
Caso de estudio ....................................................................................... 108
8.1.6.
Estructura ................................................................................................ 108
8.1.7.
Estrategia de implementación Orientada a Aspectos .............................. 109
8.1.8.
Análisis .................................................................................................... 112
Diego M.S. Erdödy
3
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
8.1.9.
9.
Componente gráfico................................................................................. 113
Patrones de seguridad y fiabilidad ......................................................................... 115
9.1. Watchdog ........................................................................................................ 115
9.1.1.
Resumen .................................................................................................. 115
9.1.2.
Otras denominaciones.............................................................................. 115
9.1.3.
Problema .................................................................................................. 115
9.1.4.
Solución ................................................................................................... 115
9.1.5.
Caso de estudio ........................................................................................ 115
9.1.6.
Estructura ................................................................................................. 116
9.1.7.
Estrategia de implementación Orientada a Aspectos............................... 117
9.1.8.
Análisis .................................................................................................... 119
9.1.9.
Componente gráfico................................................................................. 119
10. Resultados y Conclusiones ..................................................................................... 123
11. Futuros trabajos...................................................................................................... 125
Referencias ...................................................................................................................... 127
Glosario ........................................................................................................................... 131
4
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Índice de Figuras
Figura 3-1: Mapeo entre espacio de intereses y espacio de implementación...................... 9
Figura 3-2: Modelo Conceptual de Filtros de Composición ............................................. 12
Figura 3-3: Mecanismo de combinación de intereses ....................................................... 15
Figura 3-4: Impacto de cambios sobre sistemas convencionales y orientados a aspectos 16
Figura 3-5: Diagrama de estructura para los ejemplos de AspectJ ................................... 27
Figura 3-6: Captura de Eclipse mostrando el resultado de “declare error” ....................... 34
Figura 3-7: Mecanismo general de funcionamiento de AspectJ ....................................... 35
Figura 5-1: Ejemplo de notación UML para un Aspecto .................................................. 51
Figura 5-2: Ejemplo de notación UML para un Pointcut .................................................. 51
Figura 5-3: Ejemplo de notación UML para un Advice simple ........................................ 52
Figura 5-4: Ejemplo de notación UML para un Advice con argumentos ......................... 52
Figura 5-5: Ejemplo de notación UML para una Introducción ......................................... 53
Figura 5-6: Ejemplo de notación UML para una declaración de implementación ........... 54
Figura 6-1: Paleta de componentes ................................................................................... 57
Figura 6-2: Propiedades comunes a todos los componentes ............................................. 58
Figura 6-3: Propiedades particulares para el componente RoboArm ............................... 58
Figura 6-4: Ejemplo de un Componente ........................................................................... 59
Figura 6-5: Relaciones entre Componentes ...................................................................... 59
Figura 6-6: Ejemplo de un diagrama con varios componentes y relaciones ..................... 60
Figura 6-7: Vista de resumen ............................................................................................ 61
Figura 6-8: Capas de la arquitectura del Framework y sus características principales ..... 62
Figura 6-9: Interacción entre las distintas capas MVC del framework ............................. 63
Figura 6-10: Capa de Modelo de ACVF ........................................................................... 65
Figura 6-11: Compartimientos del componente ................................................................ 67
Figura 6-12: Capa de Vista de ACVF ............................................................................... 68
Figura 6-13: Capa de Controlador de ACVF .................................................................... 69
Figura 6-14: Capa de Componentes de ACVF.................................................................. 71
Figura 6-15: Diagrama de secuencia para el movimiento de un componente .................. 73
Figura 6-16: Diagrama de secuencia para el inicio de la simulación ................................ 75
Figura 6-17: Diagrama de estructura básico del framework de visualización JHotDraw . 76
Figura 7-1: Diagrama de Estructura del Patrón “Rendezvous”......................................... 80
Figura 7-2: Núcleo de la estructura Orientada a Aspectos del Patrón “Rendezvous” ...... 81
Figura 7-3: Estructura de la aplicación del patrón “Rendezvous” .................................... 83
Figura 7-4: Estructura de la distintas implementaciones del Rendezvous ........................ 84
Figura 7-5: Estructura de la implementación remota del Rendezvous.............................. 85
Figura 7-6: Estructura completa para el Rendezvous........................................................ 86
Figura 7-7: Diagrama ACVF del caso de estudio para el patrón “Rendezvous” .............. 88
Figura 7-8: Diagrama de estructura del patrón “Balking” ................................................ 89
Figura 7-9: Estructura del Núcleo del patrón “Balking” ................................................... 90
Figura 7-10: Estructura de la aplicación del patrón “Balking” ......................................... 91
Figura 7-11: Diagrama ACVF del caso de estudio para el patrón “Balking” ................... 93
Figura 7-12: Diagrama de estructura del patrón “Observador” ........................................ 94
Figura 7-13: Estructura del Núcleo del patrón “Observador” ........................................... 95
Diego M.S. Erdödy
Índice de Figuras
1
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 7-14: Estructura de la aplicación del patrón “Observador”.................................... 97
Figura 7-15: Diagrama ACVF del caso de estudio del patrón “Watchdog”...................... 99
Figura 7-16: Interacción del patrón de concurrencia optimista ....................................... 101
Figura 7-17: Estructura del Núcleo del patrón “Optimistic Locking” ............................. 102
Figura 7-18: Estructura de la aplicación del patrón “Optimistic Locking” ..................... 104
Figura 7-19: Diagrama del patrón “Optimistic Locking” ................................................ 106
Figura 8-1: Estructura del patrón “Reactor” .................................................................... 108
Figura 8-2: Estructura del Núcleo (sector Event Handling) del patrón “Reactor” .......... 110
Figura 8-3: Estructura del Núcleo (sector Event Dispatching) del patrón “Reactor”...... 111
Figura 8-4: Estructura de la aplicación concreta del patrón “Reactor” ........................... 112
Figura 8-5: Diagrama ACVF del caso de estudio para el patrón “Reactor” .................... 114
Figura 9-1: Diagrama de estructura del patrón “Watchdog” ........................................... 116
Figura 9-2: Estructura del Núcleo del patrón “Watchdog” ............................................. 117
Figura 9-3: Estructura de la aplicación del patrón “Watchdog” al caso de estudio ........ 118
Figura 9-4: Simulación de patrón Watchdog ................................................................... 120
Figura 9-5: Propiedades de los componentes gráficos Pacemaker y Watchdog ............. 120
2
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
1. Introducción
En los últimos años, la programación orientada a objetos (POO) ha sido el modelo
de lenguaje de programación predominante dada su gran capacidad para abstraer la complejidad de un problema determinado. La programación orientada a objetos clásica se
basa en el paradigma de que todo programa está compuesto de una colección de unidades
individuales llamadas objetos. Cada objeto es capaz de recibir mensajes, procesar información o enviar mensajes a otros objetos. Si bien este modelo ha sido un gran paso en la
evolución de los lenguajes de programación, no es de ninguna forma perfecto.
Uno de los grandes problemas que posee, por ejemplo, es la incapacidad de abstraer
problemas que afectan a varios sectores del sistema en forma transversal. La programación orientada a aspectos (POA) provee una solución para abstraer código “transversal”,
que se encuentra distribuido en varias clases sin relación funcional con ellas (por ej. instrucciones de seguridad o de bitácora). En vez de dispersar este código dentro de las clases, la POA permite abstraer estos fragmentos en módulos independientes, llamados aspectos, para luego poder aplicar este código dinámicamente cuando sea necesario. Esto se
logra definiendo lugares específicos (llamados puntos de corte o “pointcuts”) en el modelo de objetos donde el código transversal debe ser aplicado. Este código es insertado dentro de las clases tanto en tiempo de ejecución como en tiempo de compilación, dependiendo del framework POA utilizado y de la configuración. Esencialmente, la POA permite agregar nueva funcionalidad dentro de los objetos sin la necesidad de que ellos tengan conocimiento de dicha introducción.
Las implementaciones de POA más desarrolladas y activas que existen hasta este
momento son:
AspectJ [Ram 03] El proyecto fue iniciado por uno de los creadores de la POA, el
cual se basa en una extensión al lenguaje Java y se ha transformado en una de las
implementaciones más completas y genéricas de este paradigma1.
AspectWerks [AWerks 05] Es una herramienta concebida por el grupo de código
libre Codehaus.
JBossAOP [Bur 03] Es una extensión para el servidor de aplicaciones JBoss cuya
implementación se basa en interceptores configurables via XML.
SpringAOP [Rus 04] Es uno de los módulos del framework multipropósito
llamado Spring, el cual también está basado en interceptores configurables via
XML.
CaesarJ [Mez 03] Es la más reciente y prometedora implementación de AOP
basada en extensión al lenguaje Java, nacida en la Universidad de Darmstadt,
Alemania.
Dentro de la POO, los patrones de diseño son estructuras conocidas y definidas
como soluciones estándar para problemas comunes en el diseño de software. El objetivo
de estos patrones es disminuir los tiempos de diseño e implementación de los sistemas,
1
Patrón de pensamiento y conjunto de reglas que enmarcan y sirven de guía en una especialidad determinada.
Diego M.S. Erdödy
Introducción
3
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
proveyendo soluciones prefabricadas que resultaron eficientes para determinado tipo de
problema. Es por este motivo, que mientras más independientes y fáciles de “ensamblar”
sean estas soluciones, mayor será el tiempo ahorrado, tanto en diseño e implementación
como en mantenimiento.
Dado que los beneficios de la introducción de la POA en los patrones de diseño tradicionales, también llamados GoF (Gang of Four, debido a los autores libro [Gam 95])
han sido ampliamente verificados [Han 02], se demuestra, en este trabajo, que lo mismo
ocurre con patrones de diseño distribuidos y concurrentes, es decir, los que tratan con
problemas en donde intervienen más de una máquina o hilo de ejecución2.
El objetivo del trabajo es hacer un análisis exhaustivo de los beneficios y desventajas que tiene una implementación de patrones de diseños distribuidos y concurrentes
utilizando el paradigma de programación orientada a aspectos. Junto con la implementación de las aplicaciones de cada patrón de diseño, se modela y desarrolla un framework
(bautizado con el nombre de “Component Visualization Framework”) para poder integrar
gráficamente los componentes de cada patrón y observar la interacción resultante.
Cada patrón se implementa simulando una aplicación clásica. Por ejemplo, el
“Rendezvous” se visualiza como una mini-aplicación donde un conjunto de brazos robóticos esperan un evento para comenzar su tarea. Estas mini-aplicaciones pueden conectarse entre sí y con el ambiente. En la cadena así armada se puede evaluar el impacto de los
cambios según POO y POA. Los patrones a analizar se han dividido en 4 categorías:
Concurrentes, Distribuidos, Manejo de Eventos y Control o Tiempo Real [Dou 02]
[Sch 00]. Para cada uno se hace el desarrollo de una mini-aplicación del patrón usando
las mejores técnicas conocidas en POO y POA.
El framework está basado en una conjunción de caja blanca [Ras 03] para los componentes y caja negra para las utilidades asociadas al entorno. Se utiliza metainformación como mecanismo de extensión de ciertos puntos definidos, denominados
“hotspots”. Las simulaciones pueden ser ejecutadas tanto en modo automático como en
modo manual o interactivo, permitiendo una visualización con mayor detalle. En el modo
manual, el usuario es el encargado de hacer que los componentes actúen sobre otros, pudiendo ejecutar acciones sobre cada componente y ver las reacciones en el resto. En el
modo automático, cada componente ejecuta un comportamiento predeterminado, que en
la mayoría de los casos se puede configurar a través de las propiedades del componente,
que se inicializa al comenzar la simulación.
1.1.
Estructura del trabajo
Este trabajo consta de los siguientes capítulos:
2
4
Unidad de división de paralelización de procesamiento de un programa determinado.
Introducción
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
1.1.1.
Programación Orientada a Aspectos
Breve introducción a la programación orientada a objetos (POO) y su evolución
hacia la programación orientada a aspectos (POA). Las definiciones de elementos de la
POA y una comparación de herramientas que implementan la POA. Por último se justifica la elección de la herramienta elegida, AspectJ.
1.1.2.
Patrones de diseño distribuidos
Definición de un patrón de diseño. Características de los patrones de diseño distribuidos y sus distintos tipos.
1.1.3.
Análisis de patrones
Especificación de características a analizar, metodología de trabajo y lenguaje de
modelado que se utiliza para la especificación de los diseños orientados a aspectos.
1.1.4.
Framework de Visualización
Descripción de las características del framework desarrollado para la visualización
de los casos de estudio. Explicación de la arquitectura y diseño.
1.1.5.
Patrones de concurrencia
Descripción y análisis de patrones de diseño de concurrencia.
1.1.6.
Patrones de manejo de eventos
Descripción y análisis de patrones de diseño de manejo de eventos.
1.1.7.
Patrones de comunicación distribuida
Descripción y análisis de patrones de diseño de comunicación distribuida.
1.1.8.
Patrones de seguridad y fiabilidad
Descripción y análisis de patrones de diseño seguridad y fiabilidad.
1.1.9.
Resultados y conclusiones
Análisis comparativo de los resultados de los distintos casos de estudio. Conclusiones obtenidas aplicando distintas métricas.
Diego M.S. Erdödy
Introducción
5
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
1.1.10.
Futuros trabajos
Lineamientos para profundización del presente trabajo y trabajos relacionados.
6
Introducción
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
2. Programación Orientada a Objetos
2.1.
Fundamentos
La programación orientada a objetos consiste básicamente en descomponer un problema en un grupo de objetos que interactúan entre sí por medio de mensajes. Cada objeto puede contener atributos para almacenar su estado interno, el cual puede ser alterado a
través de los mensajes. Los mensajes, también llamados métodos, son subrutinas que se
ejecutan en el ámbito de un objeto en particular. Cada objeto debe estar asociado a un
tipo de objeto, llamado clase. La clase es la que contiene la definición de los métodos que
los objetos de su tipo poseerán así como también los atributos que contendrán.
Una vez definido los elementos fundamentales, se enumeran los conceptos principales presentes dentro de este paradigma:
 Abstracción: Al igual que en matemáticas, es el mecanismo por el cual se identifican características comunes en un cierto conjunto de elementos. Por este mecanismo, se pueden establecer formas homogéneas de ver a un objeto desde el exterior, llamadas interfaces. La abstracción permite independizar dichas interfaces,
de la forma de implementar el comportamiento semántico definido por ellas.
 Encapsulación: Asegura que los atributos internos de un objeto, también llamados privados, no puedan ser accedidos desde afuera, dando así la seguridad de que
futuros cambios sobre ellos no impactarán en el resto de los objetos. Para acceder
a dichos atributos se deberán proveer maneras específicas y controladas como, por
ejemplo, métodos públicos.
 Herencia: Los elementos de una clase (métodos y atributos) pueden ser heredados
por otra clase. Esto significa que la clase hija (la que hereda) contendrá implícitamente todos los elementos de la clase padre. Se dice que la clase hija “extiende”
o “especializa” a la clase padre, ya que es un mecanismo por el cual se incorporan
nuevos elementos a partir de los ya existentes.
 Polimorfismo: En su definición clásica, consiste en poder utilizar la misma definición con distintos tipos de datos indistintamente. Una de las técnicas de polimorfismo que provee la POO es la posibilidad de contar con distintas implementaciones del mismo método dentro de una jerarquía de clases. Dicha técnica pertenece al grupo del polimorfismo dinámico dado que la decisión, de que tipo de
comportamiento aplicar, se realiza en tiempo de ejecución. Otra de las formas de
polimorfismo presentes en la POO es el mecanismo denominado “sobrecarga”
que es la redefinición de un mismo método con distintos tipos de argumentos. De
esta forma se logra un comportamiento acorde a cada tipo de objeto. Esta última
técnica pertenece al grupo del polimorfismo “ad-hoc”, ya que la cantidad de variantes de tipos argumentos debe ser explícitamente declarada en tiempo de compilación (un método distinto por cada tipo).
Diego M.S. Erdödy
Programación Orientada a Objetos
7
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
2.2.
Problemas
Uno de los principales problemas de la POO, es la incapacidad de abstraer eficientemente la funcionalidad que se repite en distintos módulos que no estén relacionados en
la jerarquía de clases. Uno de los ejemplos más claros de este inconveniente son las instrucciones de bitácora y de traza de ejecución. Si en un sistema se quisiera mostrar un
mensaje cada vez que comienza la ejecución de un método, se tendría que agregar dicha
instrucción en todos los métodos del sistema. Supongamos, que se incorpora una instrucción en cada uno de los métodos del sistema que muestre el nombre del método al cual se
está invocando. Si en el futuro, se decide que es necesario ver además, los valores de los
argumentos con los cuales fue llamado cada método, se necesitaría hacer una modificación en cada uno de los lugares donde fue aplicada la instrucción originalmente. Dicha
modificación implica un esfuerzo lineal en comparación con el tamaño del sistema. Todos estos problemas surgen de la falta de mecanismos del paradigma para la abstracción
de comportamiento transversal.
3. Programación Orientada a Aspectos
3.1.
Separación en intereses
Es posible ver un sistema como la combinación de múltiples intereses. Se define interés como cualquier aspecto3 que le concierne a un programa, ya sea relacionado a la
infraestructura, al negocio, a los requerimientos o a las estructuras de diseño. Nótese que
la definición es genérica por naturaleza. Ejemplos típicos de intereses son lógica de negocio, performance, bitácora, persistencia, autenticación, restricciones de tiempo real y tolerancia a fallos.
Los intereses se pueden dividir en locales y transversales. Los intereses locales son
los que sólo le conciernen a un componente (o grupo reducido) en particular. El ejemplo
clásico de este tipo de intereses es la lógica de negocio. Los intereses transversales en
cambio, son aquellos que involucran a más de un componente a la vez. Un ejemplo es el
logging (o bitácora de ejecución) de una aplicación. La mayoría de los componentes, si
no todos, deben preocuparse por tener un registro persistente de su ejecución.
Los intereses se pueden ver en dos planos o espacios distintos, el conceptual y el de
la implementación. El primero es el espacio lógico en el cual se identifica el interés. El
segundo es el espacio físico en el cual se traduce el interés conceptual a una implementación específica, en un leguaje de programación determinado. El problema principal con el
mapeo entre ambos espacios utilizando lenguajes orientados a objetos, es que el espacio
conceptual posee múltiples dimensiones, una dimensión por cada interés existente. En
cambio, el espacio de implementación presenta una naturaleza “unidimensional” inherente. En el caso de la POO, el espacio de implementación se limita a un flujo lineal de traspaso de mensajes entre objetos. Por esta razón, el mapeo produce una pérdida de información valiosa. En la siguiente figura se puede apreciar gráficamente este efecto:
3
8
En su acepción tradicional, no confundir con el aspecto definido más adelante
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Lógica de
Negocio
Mapeo
Logging
Au
te
n
tic
ac
ió
n
Lógica de
Negocio
Autenticación
Logging
Espacio de intereses
Espacio de Implementación
Figura 3-1: Mapeo entre espacio de intereses y espacio de implementación
En el espacio de intereses, se observan tres intereses ortogonales que generan dicho
espacio. Al llevar estos intereses a un espacio de implementación, que como se indicó,
tiene una naturaleza unidimensional, éstos se deben mapear a un único eje de acción. Este
proceso conlleva una pérdida de identidad de cada interés.
El proceso de conversión de un espacio multidimensional a un espacio unidimensional produce problemas y efectos colaterales. Los principales síntomas de este fenómeno, aplicados al caso de logging, son los siguientes:
 Mezcla de código: La implementación del interés principal de un módulo contiene
en muchos casos, la implementación de otros intereses transversales intercalada.
Esto dificulta la lectura y el mantenimiento del mismo. Cada elemento que quiera
ser registrado en la bitácora, debe agregar instrucciones especiales, las cuales estarán mezcladas con las que implementan el interés especial.
 Dispersión de código: Es el caso contrario al anterior. La implementación del
comportamiento de un interés transversal se encuentra distribuido en distintos
módulos. Por este motivo resulta muy difícil identificar el interés. Para saber qué
componentes están siendo registrados en la bitácora, es necesario revisar todo el
sistema para rastrear en dónde se están utilizando tales instrucciones.
 Repetición de código: Una consecuencia del problema anterior es que el código
disperso, frecuentemente se encuentra repetido. Las mismas instrucciones de logging aparecerán una y otra vez en cada módulo que precise ser registrado.
Y las principales desventajas son:
 Problemas al analizar la implementación: Al implementar múltiples intereses se
hace cada vez más difícil distinguir cada uno de los intereses en su implementación. Este es un problema exponencial, dado que cada interés transversal incorporado al sistema, potencialmente dificulta cada uno de los intereses existentes.
 Baja productividad: Al momento de implementar un módulo, es necesario preocuparse por la correcta implementación del interés principal así como también de
los intereses transversales que afectan al módulo. Evidentemente preocuparse por
Diego M.S. Erdödy
Programación Orientada a Aspectos
9
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
un solo interés a la vez resultaría más sencillo y reduciría el riesgo de errores, lo
cual aumentaría considerablemente la productividad.
 Imposibilidad de reutilización: Dado que cada módulo contiene la implementación de los intereses transversales, otros sistemas que tengan distintos requerimientos de sistema, no podrán reutilizar la implementación del módulo.
 Mantenimiento difícil: En caso de necesidad de cambios en los requerimientos
del sistema, los cambios a nivel de implementación tienen un costo, en el mejor
de los casos, de orden lineal al tamaño del sistema. Esto se debe a que la modificación se debe hacer en todos los lugares en donde se encuentre intercalada la implementación del interés. Por ejemplo, para el caso del logging, si durante la evolución de un sistema se determina que aparte de registrar un mensaje, es necesario
identificar al usuario involucrado, cada una de las llamadas que el sistema realiza
al componente de log, tendrán que ser modificadas. Mientras más grande sea el
sistema, más llamadas habrá al componente de log y de la misma forma aumentará la cantidad de cambios necesarios.
 Evolución compleja: Incorporar nuevos intereses es también costoso y aumenta
con la cantidad de intereses transversales que existan. Siguiendo con el ejemplo
de logging, si una aplicación inicialmente no contempló la necesidad de registrar
su ejecución, el costo de la incorporación de dicho interés será proporcional al
tamaño del sistema.
Es por todas estas razones que se debe encontrar un método que permita implementar cada interés en forma independiente. De tal forma que cualquier modificación en un
interés no afecte al resto de los intereses, pero los intereses requieren interactuar entre
ellos. Esto requiere que existan mecanismos que permitan la interacción entre ellos sin
perder la independencia.
Existen varios métodos para lograr el objetivo planteado [Hur 95]. Estos métodos se
describen y analizan en las siguientes secciones.
3.1.1.
Programación de patrones
Lieberherr [Lie 96] describe la programación adaptativa basada en patrones de
código. Los patrones de código, que no son patrones de diseño, se pueden dividir en diferentes categorías:
 Patrones de propagación: Consiste en identificar subgrafos dentro de la estructura general de objetos, que están ligados a una operación en común. De esta forma, el acceso a la información de esa operación se hará de forma tal que no sea
necesario conocer el camino. Sólo es necesario saber a qué operación pertenece.
Por ejemplo, sea la estructura simple de tres clases: Alumno, Clase (que posee referencias a un conjunto de alumnos) y Escuela (que se asocia a un conjunto de
clases). En la POO, para acceder a un alumno a partir de una Escuela se debe conocer el camino, es decir a través de la clase “Clase”. Si se modifica la estructura
de objetos, y se agrega un nivel “Departamento” entre Escuela y Clase, debe modificarse la estrategia de acceso. Los patrones de propagación tratan este tipo de
10
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
problemas y permiten hacer llamadas tales como “a partir de una Escuela, obtener
el Alumno tal”, es decir, abstraen el camino dentro de la estructura.
 Patrones de transporte: El objetivo de estos patrones es la abstracción del concepto de parametrización. Se utilizan dentro de los patrones de propagación para
enviar los parámetros entre los subgrafos.
 Patrones de sincronización: Define esquemas de sincronización entre los objetos
en aplicaciones concurrentes.
Este conjunto de patrones debe ser complementado con una estructura de clases sobre la cual aplicarlo. Un compilador de patrones tomará tanto la estructura de clases como
los patrones y generará un programa orientado a objetos.
Cada categoría trata con un conjunto de problemas distintos y puede considerarse
un interés de alto nivel dentro de una aplicación.
3.1.2.
Filtros de composición
El modelo de filtros de composición desarrollado por Bergmans y Aksit [Aks 92]
[Ber 94] en la Universidad de Twente, es una extensión del modelo convencional orientado a objetos. Este modelo fue aplicado a un sistema de pagos de subsidios médicos por
invalidez implementada en el lenguaje Sina [Ber 01].
El siguiente diagrama representa el modelo de un filtro de composición.
Mensajes
Recibidos
Filtros de entrada
Interface
Métodos y
Condiciones
Objetos
internos
Variables de
Instancia
Objetos
externos
Implementación
(objeto núcleo)
Filtros de salida
Mensajes
Enviados
Diego M.S. Erdödy
Programación Orientada a Aspectos
11
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 3-2: Modelo Conceptual de Filtros de Composición
El núcleo o implementación del objeto representa el comportamiento y sólo puede
ser modificado a través del procesamiento de los mensajes de entrada y salida. Esto se
logra a través de la capa externa del núcleo del objeto, llamada interface. La interface
contiene los elementos más importantes que son los filtros, tanto de entrada como de salida. Los filtros son los encargados de combinar el comportamiento del objeto con otros
objetos. Los otros objetos pueden ser tanto internos como externos.
El objeto núcleo contiene variables de instancia que representarán el estado del
componente y en el límite con la interface se encuentran los métodos y las condiciones.
Los métodos pueden ser invocados por mensajes, siempre y cuando no sean rechazados
por alguno de los filtros de entrada. Las condiciones son simplemente expresiones booleanas que se refieren al estado del objeto y son usadas por los filtros en el momento de
procesar los mensajes.
Los filtros, que son los encargados de combinar los intereses, aceptando, rechazando y modificando los mensajes, tienen un orden determinístico de aplicación formando
filtros en cascada. La composición de filtros en cascada posee un comportamiento booleano del tipo AND mientras que los distintos elementos dentro de un mismo filtro proveen el comportamiento OR. Esta propiedad hace que el modelo sea apropiado para representar intereses que se deben aplicar en un orden especificado.
3.1.3.
Programación de meta-objetos
Un “meta-objeto” es una entidad que puede crear o modificar a otros objetos. Los
protocolos de meta-objetos (MOP) son interfaces definidas en el lenguaje de programación que posibilita al usuario, cierto grado de libertad, para modificar el comportamiento
y la implementación del lenguaje. De esta forma los objetos que se encuentran en el nivel
primario, por lo general encargados de los algoritmos fundamentales de un programa,
pueden ser modificados por meta-objetos con el objetivo de adaptarlos a requerimientos
específicos de un entorno determinado.
Los protocolos de meta-objetos [Kic 92] se pueden clasificar en meta-objetos en
tiempo de ejecución y meta-objetos en tiempo de compilación. Uno de los lenguajes más
representativos del primer grupo es el Common Lisp Object System [Kic 91], el cual
permite alterar mecanismos del lenguaje LISP tales como la herencia, instanciación de
objetos o procesamiento de mensajes, en tiempo de ejecución.
Sullivan [Sul 01] enumera los siguientes beneficios de un MOP en tiempo de ejecución:
 Se permite la modificación de objetos en el momento de cargar las clases, agregando un nivel de extensibilidad mayor.
 Los meta-objetos pueden utilizar valores de tiempo de ejecución para tomar las
decisiones de cómo modificar los objetos.
12
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 Los meta-objetos pueden ser modificados en tiempo de ejecución. Esto tiene como corolario que el meta-comportamiento puede ser removido también en tiempo
de ejecución.
Además muestra cómo un lenguaje reflexivo como Java no cumple con los requerimientos de un MOP ya que si bien permite obtener información de otros objetos, el acceso que brinda es de “sólo lectura”, es decir, no admite que los objetos sean modificados. Kojarski [Koj 03], demuestra que todas las funciones que brinda la biblioteca de
reflexión de Java (CJR) se pueden realizar con un lenguaje basado en MOP como AspectJ, incluso con mejoras en la performance. Desde luego que hay una decisión de compromiso en dicho beneficio. Se requiere un aumento en la memoria necesaria ya que la
meta-información es almacenada completamente en un caché. Para minimizar este problema, se puede reducir el conjunto de clases que van a ser almacenadas. Esto se puede
lograr indicando explícitamente el conjunto de clases que podrá ser accedida reflexivamente. Esto no es posible con la CJR dado que la reflexión es implícita para todas las
clases.
3.1.4.
Conclusiones
El primer modelo, programación de patrones, tiene una gran limitación en la cantidad de intereses que puede representar dado que fue diseñado para tratar problemas específicos como la abstracción de caminos de acceso a ciertos elementos, pasaje de parámetros y sincronización general. Para la implementación de los patrones que se analizaron en este trabajo, no es conveniente dada la heterogeneidad de los mismos.
En el segundo modelo, filtros de composición, la única forma de combinar los intereses es por medio del procesamiento de los mensajes de entrada y salida. Otra de las
desventajas para la implementación de los patrones de este trabajo es que no permite la
modificación en la estructura de los objetos, limitando considerablemente las formas de
inserción del comportamiento de los nuevos intereses.
Se ha elegido un modelo similar a la programación de meta-objetos para la implementación de los patrones de diseño de este trabajo, dada la gran generalidad y flexibilidad del modelo.
3.2.
Estructura de un aspecto
Los términos “aspecto” y “AOP” fueron definidos por Gregor Kiczales en 1997
[Kic 97]. Estructuralmente, un aspecto es una clase con ciertos elementos adicionales que
lo caracterizan. Estos elementos le dan la expresividad que necesita para poder abstraer
intereses transversales. Los elementos son:
 Join point: es un punto específico dentro de la estructura o ejecución de un programa. Puede ser, por ejemplo, la ejecución de un método, la creación de un objeto o la modificación en el valor de un atributo. Mientras más tipos de join points
Diego M.S. Erdödy
Programación Orientada a Aspectos
13
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA




se puedan especificar con una herramienta AOP, más casos se podrán representar
en un aspecto y por ende más potente será dicha herramienta.
Pointcut: es una agrupación lógica de un grupo de join points dentro de un aspecto.
Advice: es un fragmento de código equivalente al contenido de un método, en
donde se especifica el comportamiento transversal que contendrá el aspecto. Este
comportamiento va acompañado de la indicación del pointcut donde debe ser insertado y la forma de insertarlo (antes, después o en reemplazo del pointcut).
Declaraciones de miembros inter-clase: son declaraciones que permiten agregar
métodos o atributos a interfaces o clases ya existentes. También conocido como
introducción.
Otras declaraciones: son declaraciones de distintos tipos cuya disponibilidad dependen del lenguaje orientado a aspectos que se utilice. Por ejemplo para el caso
de AspectJ se cuenta con los siguientes tipos:
o parents: que especifica que una clase determinada herede de otra en particular.
o implements: que agrega la implementación de una interface a una clase
dada
o soft: que elimina la obligación de capturar las excepciones.
o warning: que genera un aviso en tiempo de compilación cuando se cumple
la condición requerida.
o error: que genera un error en tiempo de compilación cuando se cumple la
condición requerida.
o precedence: que especifica el orden de aplicación de los distintos aspectos
para eliminar conflictos de precedencia de aspectos.
Matemáticamente, los aspectos constituyen una extensión de segundo orden en el
paradigma de programación, es decir que mientras los paradigmas conocidos utilizan
simples funciones, mensajes o equivalentes, la AOP permite razonar en términos de conjuntos de dichas entidades a través del uso de los puntos de corte. De esta forma se puede
ver a la AOP como una poderosa extensión lógica más que como un nuevo paradigma.
3.3.
Combinación de intereses
La combinación de intereses es el mecanismo por el cual distintos elementos de los
aspectos se insertan dentro de las clases. Existen dos grandes grupos de elementos a insertar, los estructurales y los advices. Los estructurales implican cambios en la estructura
de la clase, ya sea agregando la implementación de nuevas interfaces, nuevos atributos o
nuevos métodos. En el segundo caso, la inserción es más compleja ya que depende de la
incorporación de los advices en los distintos pointcuts que puede requerir el cumplimiento de distintas condiciones.
Las formas clásicas de llevar a cabo la combinación de intereses son:
Preprocesador de código fuente.
Postprocesador que modifique el código binario ya compilado.
14
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Compilador preparado para AOP que genere el código binario intercalado.
Intercalado en tiempo de carga de las clases (por ej., en el caso de Java, se intercala cada advice a medida que se cargan las clases en la máquina virtual (JVM))
Intercalado en tiempo de ejecución (capturar cada join point en tiempo de ejecución y ejecutar todos los advices correspondientes).
Las primeras dos opciones complican la tarea del programador mientras que las dos
últimas pueden afectar la performance del sistema. La última opción necesita de un entorno de ejecución especial para que sea factible (en el caso de Java esto implica una
JVM a medida).
Se resume el mecanismo de la combinación de intereses en el siguiente gráfico:
Implementación de
intereses principales
(objetos)
Compilador e
Intercalador
AOP
Sistema
Implementación de intereses
transversales (aspectos)
+ reglas de intercalado
Figura 3-3: Mecanismo de combinación de intereses
Todas estas soluciones implican modificaciones del código compilado en algún
punto. Esto quiere decir que el código binario resultante es distinto al generado por un
compilador tradicional. Esto es un grave problema al momento de depurar código con las
herramientas tradicionales.
Cohen y Gil [Coh 04] proponen una estrategia de combinación denominada “deploy-time weaving”. La diferencia principal con los métodos anteriores es que en vez de
modificar el código compilado inyectándole los aspectos, estos son introducidos a través
de clases que extienden las clases ya existentes y sobrescriben sus métodos. La ventaja de
este mecanismo es que el código original queda intacto y por ende se pueden utilizar
herramientas de depuración tradicionales sin mayor inconveniente. También se observa
como ventaja la compatibilidad con el código tradicional ya que los aspectos pasan a ser
objetos que extienden otros objetos. Como desventaja se puede mencionar el hecho de
que la creación de nuevas capas en la jerarquía de herencia reduce la eficiencia final.
Diego M.S. Erdödy
Programación Orientada a Aspectos
15
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
3.4.
Ventajas y Problemas de la AOP
La implementación de cada interés en forma independiente, facilita el mantenimiento del sistema y la introducción de modificaciones o nuevas funcionalidades. Esto se puede ver en el siguiente diagrama:
Caso con la POO
Caso con la POA
Sistema original
Sistema original
Sistema original
+ interés transversal
Sistema original
Sistema original
+ interes modificado
Sistema original
Implementación del
interés transversal
(aspecto)
Aspecto modificado
Figura 3-4: Impacto de cambios sobre sistemas convencionales y orientados a aspectos
Por un lado se muestra que con en el caso de la POO, una modificación sobre el
código concerniente a un interés transversal, impacta en diversos lugares del sistema. Es
decir que los mismos cambios se tienen que repetir más de una vez. En cambio, en el caso
de la POA, al poder modularizar el código asociado al interés transversal, las modificaciones que se le deban aplicar siempre estarán centralizadas en un único lugar físico.
La depuración de código es uno de los grandes problemas de la AOP. El código
fuente transversal en la AOP es independiente mientras que una vez compilado y en
tiempo de ejecución esto no es cierto debido al intercalado. Una solución a este problema
16
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
es utilizar herramientas especializadas de depuración para AOP, que puedan relacionar el
código fuente con el código binario correspondiente.
Existe otro problema asociado a la captura de pointcuts con comodines. Los pointcuts con comodines representan diversos puntos (llamada a métodos, asignación de atributos, etc.) dentro del sistema, agrupados por una convención de nombres que contiene
caracteres especiales denominados comodines. El comodín “*”, por ejemplo, representa
un grupo de caracteres. Si se usa por ejemplo, un esquema de pointcuts basado en el
nombre de los métodos (como en AspectJ 1.2) se corre el riesgo que en futuras modificaciones, se cambie el nombre de uno de los métodos referenciados por el pointcut. Esto
produciría el funcionamiento incorrecto del aspecto. Esto se puede controlar usando
estándares de nombres rígidos pero aún así no se elimina el problema completamente.
Otra alternativa es contar con herramientas de desarrollo que permitan visualizar fácilmente la integración entre clases y aspectos de forma tal de poder contar con controles
visuales al momento de hacer cambios. Sin embargo, la solución más prometedora a dicho inconveniente es la introducción de información adicional (o meta-información) a la
especificación de las clases, ya sea métodos o atributos. De esta forma se tiene un control
certero y flexible al mismo tiempo.
La forma de especificar esta meta-información se analizará más adelante, pero para
ejemplificar el problema, se tomará el ejemplo de una clase a la que se le debe aplicar
políticas de seguridad. Para ello se define que para llamar a cualquier método cuyo nombre empiece con “get”, se deben poseer privilegios especiales. Si con la evolución de la
clase, alguien que no tenga conocimiento de la política de seguridad, intenta agregar otro
método bajo la misma convención (una práctica habitual en Java), se verá forzado a hacer
entrar dicho método a la política de seguridad, aunque no sea el comportamiento esperado. Para solucionar el problema, se puede agregar meta-información a los métodos que
los categorice según su funcionalidad, por ejemplo, “Privilegiado”. De esta forma, los
nuevos métodos no se verán afectados.
La meta-información puede estructurarse en distintas formas. Una de las más complejas, actualmente en desarrollo, es la estructura semántica. En el framework SetPoint
[Alt 04], se hace uso de un lenguaje de ontologías llamado OWL [OWL 06] (Ontology
Web Language), aumentando considerablemente el grado de expresividad de la metainformación. Por ejemplo, se pueden vincular pointcuts con expresiones del tipo “todos
los objetos que representen una manufactura y cuyo costo sea mayor a 100”.
Como indica Roger Alexander [Ale 02], existe un peligro inherente a la abstracción
transversal que implica la orientación a aspectos. Este peligro se asocia a la carga cognitiva que se adiciona con la aplicación de nuevos intereses transversales. A esto se lo denomina “distancia cognitiva” [Kru 92] entre la abstracción base (la implementación antes
de aplicar funcionalidad transversal) y la que resulta luego del intercalado con intereses
transversales. Esta distancia cognitiva lleva a que invariantes asumidas por el autor de la
implementación original, ya no se cumplan al intercalar el o los intereses transversales.
Diego M.S. Erdödy
Programación Orientada a Aspectos
17
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Otro problema que ha sido identificado dentro de la AOP, se denomina “pointcuts
frágiles”. El problema se presenta al efectuar una reestructuración de código en un sistema que ha sido implementado en AOP. Los cambios introducidos por la reestructuración
pueden afectar la cantidad de joinpoints representados por cada pointcut y en última instancia hacer que el comportamiento no sea el esperado. Este problema ha sido analizado
por Koppen y Stoerzer [Kop 04] y han propuesto una herramienta para minimizar sus
efectos. La herramienta básicamente calcula la diferencia en número de joinpoints representados por cada pointcut antes y después de una reestructuración, permitiendo visualizar los resultados para poder tomar medidas correctivas.
3.5.
AspectJ
AspectJ [Asj 05] es una implementación en Java de AOP creada en los laboratorios
de XEROX Parc y que gradualmente se convirtió en el lenguaje orientado a aspectos más
popular. El proyecto ha sido integrado a la fundación Eclipse en diciembre de 2002 y
conjuntamente se le ha dado soporte dentro del entorno de desarrollo Eclipse (proyecto
llamado AspectJ Development Tools [AJDT 06]). Este proyecto provee un aporte importante hacia el mejor entendimiento de la interacción entre los aspectos y la base de código
existente, a través de ayudas gráficas y atajos.
AspectJ está diseñado como una extensión a la especificación del lenguaje Java. De
la misma forma que Java, se trata de un lenguaje multipropósito, es decir, no está ligado a
un tipo de dominio en particular. Es una implementación diseñada para ser compatible en
varios niveles [Kic 01]:
 Compatibilidad con versiones anteriores: todos los programas escritos en
Java, son también programas válidos en AspectJ.
 Compatibilidad de plataforma: todos los programas AspectJ corren en
máquinas virtuales de Java (JVM) tradicionales, sin ninguna modificación
adicional.
 Compatibilidad de herramientas: las herramientas de desarrollo de Java
son extensibles de una forma natural para poder trabajar con programas AspectJ.
 Compatibilidad para el programador: para el programador, AspectJ es
una extensión natural del lenguaje Java.
Las dos formas de implementación transversal que brinda AspectJ son:
 Implementación transversal dinámica: permite definir comportamiento
adicional que se ejecutará en puntos específicos dentro del programa.
 Implementación transversal estática: permite modificar la estructura estática del programa, agregando superclases o implementaciones de interfaces.
La implementación transversal dinámica se logra a partir de los join points, mientras que la implementación transversal estática se logra con las declaraciones de miembros inter-clase y las declaraciones de extensión e implementación. En todos estos
18
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
casos la modificación de la estructura de clases se realiza desde un elemento localizado y
modular como lo es un aspecto.
3.5.1.
Sintaxis de un aspecto
En AspectJ, un aspecto se define de manera similar a una clase y tiene la siguiente
sintaxis:
<Aspecto>
[
[
[
{
:=
privileged ] [ <Modificadores> ] aspect <Identificador>
extends <Tipo> ] [ implements <ListaDeInterfaces> ]
<ClausulaDeInstanciación> ]
<Cuerpo> }
La palabra reservada “class” que define una clase en Java es reemplazada por “aspect” para el caso de un aspecto en AspectJ. De la misma forma que una clase, un aspecto
debe tener un identificador único dentro de su paquete, que le dará el nombre al aspecto.
Los modificadores permitidos para un aspecto son similares a los que puede preceder a una clase:
 public: el aspecto es expuesto públicamente y por ende puede ser accedido
desde cualquier punto en la jerarquía de paquetes.
 default o package: el modificador por defecto indica que sólo es posible
acceder al aspecto dentro del paquete donde fue definido.
 final: al igual que el modificador de clases, indica que el aspecto no puede
ser extendido.
 abstract: de la misma forma que en una clase, los aspectos abstractos tienen
la característica de contener elementos abstractos, es decir, sólo definidos
pero no implementados. En el caso de los aspectos, estos elementos pueden
ser atributos, métodos o pointcuts.
La palabra reservada privileged es un modificador introducido por AspectJ exclusivamente para los aspectos e indica que el aspecto tiene el privilegio de acceder a miembros de clases que comúnmente no podría, ya sean privados, protegidos o “default” de
otros paquetes.
Los aspectos, al igual que una clase, pueden implementar interfaces y/o extender
otra clase. Además, pueden extender de otros aspectos, manteniendo la misma semántica
que en el caso de las clases, es decir, se heredan los elementos públicos y protegidos del
aspecto o clase base. Como restricciones, los aspectos no pueden implementar ni la interface “Serializable” ni la interface “Clonable”. La semántica de estas interfaces no tiene
sentido para el caso de un aspecto. Si bien un aspecto puede extender tanto de una clase
como de un aspecto, lo contrario no es cierto en AspectJ, una clase sólo puede extender
otra clase, ya que los aspectos tienen un grupo de elementos que incluyen a los de un clase. Es decir, un aspecto es una clase con elementos adicionales.
Diego M.S. Erdödy
Programación Orientada a Aspectos
19
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
A diferencia de una clase, un aspecto no puede poseer constructores debido a que
no puede ser instanciado explícitamente. En cambio, el aspecto es automáticamente instanciado. El momento y forma de su instanciación depende de la política que se defina en
la <ClausulaDeInstanciación>. Las distintas políticas disponibles son:
 issingleton(): política por defecto que indica que existirá sólo una instancia
del aspecto.
 perthis( <Pointcut> ): una instancia es creada y asociada a cada objeto que
actúa de iniciador al llegar al punto de ejecución definido por cualquiera de
los join points que forman el pointcut que se recibe como parámetro.
 pertarget( <Pointcut> ): similar al caso anterior, pero con la diferencia que
la instancia se asocia al objeto destino del join point perteneciente al pointcut pasado como parámetro.
 percflow( <Pointcut> ): es la abreviación de “per control flow” (por cada
control de flujo). El aspecto es instanciado al entrar en el flujo de ejecución
definido por el pointcut. A diferencia de los dos casos anteriores, puede
haber más de una instancia de un aspecto definido para el mismo objeto, si
el flujo del programa pasa más de una vez por los join points definidos en el
pointcut. En los últimos dos casos sólo puede haber una instancia del mismo
aspecto asociada a un objeto.
 percflowbelow( <Pointcut> ): Variación del caso anterior en donde la instanciación se hace inmediatamente después de llegar a uno de los join points
del pointcut.
 pertypewithin( <PatronTipo> ): A diferencia de los demás tipos de instanciación, pertypewithin recibe como parámetro una expresión del tipo <PatronTipo>. Dicha expresión representa un conjunto de Clases o Interfaces
con ciertas características en común y será explicada en detalle más adelante. Al utilizar este tipo de instanciación, una instancia del aspecto es creada
por cada Clase o Interface que concuerda con la expresión.
Por último, un aspecto debe poseer un cuerpo en donde se definen los elementos integrantes del aspecto. Estos elementos pueden ser, al igual que en una clase: atributos,
métodos, clases internas o interfaces internas, teniendo en cuenta que un aspecto no puede definir constructores. En el cuerpo de un aspecto también se pueden definir los siguientes elementos: pointcut, advice, declaraciones inter-tipo y otras declaraciones. En
las siguientes secciones se analiza la gramática y semántica de cada uno de estos elementos.
3.5.2.
Definición de un pointcut
Un pointcut, es la estructura que no se asemeja a ninguno de los miembros tradicionales de una clase. Básicamente, se trata de un identificador seguido de parámetros y la
definición de una expresión que indicará cuales son los join points a capturar. La estructura formal es:
20
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<Pointcut> := <PointcutAbstracto> | <PointcutConcreto>
<PointcutAbstracto> :=
abstract [ <Modificadores> ] pointcut <Id> ( <Parametros>
);
<PointcutConcreto> :=
[ <Modificadores> ] pointcut <Id> ( <Parametros> ) :
<PointcutExpression> ;
El primer caso corresponde a un pointcut abstracto el cual, de la misma forma que
un método abstracto, sólo declara la “firma” del pointcut, comprendida por el identificador y sus parámetros. Al igual que en un método abstracto, su objetivo es declarar la existencia del elemento para que pueda ser referenciado dentro del aspecto, dejando abierta la
posibilidad de definirlo en aspectos derivados.
El segundo caso se aplica para los pointcuts concretos. El pointcut se define de la
misma forma que en el primer caso, sin la palabra reservada “abstract”. Los modificadores posibles son los mismos que para el caso de un aspecto: public, private, default y final. Estos tienen la misma semántica. Los parámetros tienen la misma sintaxis que en el
caso de los métodos de Java, es decir, una lista separada por coma de parámetros los cuales se componen del tipo (clase o interface) y el identificador del parámetro.
Si bien los pointcuts se pueden sobrescribir en aspectos derivados, al igual que un
método, no existe la noción de sobrecarga. Eso quiere decir que no se puede declarar más
de un pointcut con el mismo identificador en un mismo aspecto o aspectos derivados, que
posean una lista de parámetros distinta, ya sea en cantidad o en tipos.
La expresión <PointcutExpression> está formada principalmente por elementos
llamados pointcuts primitivos. Hay varios tipos de pointcuts primitivos y cada uno define un conjunto específico de join points prefijado dentro del universo de join points posible en un programa. Dicho conjunto puede ser limitado a través de los parámetros que se
le proveen. Estos pointcuts primitivos pueden ser combinados entre sí con operaciones de
conjuntos para formar la expresión final que especificará exactamente los join points que
integrarán el pointcut en cuestión. Las operaciones disponibles son: unión (||), intersección (&&) y complemento (!). Por último, en el caso de utilizar parámetros dentro de la
declaración del pointcut, se deben utilizar operadores especiales que se encargan de enlazar los parámetros con elementos del join point, como se verá al final de la sección.
AspectJ permite utilizar los siguientes tipos de pointcuts primitivos:
 call( <PatronFirma> ): incluye los join points en donde se realice una llamada a métodos que coincidan con la firma pasada como parámetro. Esta
firma puede incluir comodines en distintos puntos, para poder agrupar varios métodos relacionados con una sola expresión. La expresión <PatronFirma> puede ser una patrón de método o de constructor y la sintaxis formal es:
Diego M.S. Erdödy
Programación Orientada a Aspectos
21
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<PatronFirma> := PatronMetodo | PatronConstructor
<PatronMetodo> :=
[ <PatronModificadoresMetodo> ] <PatronTipo>
[ <PatronTipo> . ] <PatronIdentificador> ( <PatronTipo> |
.. , ... )
[ throws <PatronThrow> ]
<PatronConstructor> :=
[ <PatronModificadoresConstructor> ] [ <PatronTipo> . ]
new ( <PatronTipo> | .. , ... )
[ throws <PatronThrow> ]
Como se puede ver en la sintaxis formal, la diferencia entre ambos es que
para el caso de un constructor, no hace falta el tipo de retorno y en vez de un
identificador, se utiliza la palabra “new”.
Las expresiones <PatronModificadoresMetodo> y <PatronModificadoresConstructor> son simplemente una enumeración de los modificadores de
Java que se pretenden incluir o el modificador precedido por el signo „!‟ si
se lo quiere excluir. Los modificadores posibles para un constructor son
“public”, “protected” y “private”. Para el caso de un método se agregan los
modificadores “static”, “final” y “synchronized”.
La expresión <PatronIdentificador> es una secuencia de caracteres válidos
para un identificador en Java con la posibilidad de utilizar el carácter comodín „*‟ para reemplazar un grupo de caracteres.
La expresión <PatronThrow> es una lista separadas por coma de <PatronTipo> en donde cada elemento puede ir precedido del signo „!‟ en caso de
querer excluirlo.
Por último, la expresión <PatronTipo> conceptualmente representa un
patrón que agrupa clases o interfaces. Este patrón es utilizado en la expresiones <PatronMétodo> y <PatronConstructor> para reemplazar el tipo de
retorno del método, el calificador (a qué tipo pertenece el método) y a los tipos de cada uno de los parámetros. A partir de la versión 1.5 de AspectJ se
introdujo compatibilidad con Java 1.5 y es por eso que se aceptan anotaciones dentro de este patrón. La sintaxis es:
22
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<PatronTipo> := <PatronTipoSimple> |
! <PatronTipo> |
( [ <PatronAnotacion> ] <PatronTipo> )
<PatronTipo> '&&' <PatronTipo> |
<PatronTipo> '||' <PatronTipo>
<PatronTipoSimple> := <PatronNombreTipo> [ + ] [ '[]' ... ]
Esta definición recursiva, muestra que el <PatronTipo> consiste de expresiones simples de tipos que pueden ser combinadas con operadores booleanos y a los cuales se les puede indicar un patrón de anotación. El <PatronTipoSimple> consta de una expresión <PatronNombreTipo> que consta de
una cadena de caracteres para especificar un identificador de tipo, opcionalmente con su paquete y puede contener dos tipos de comodines, „*‟ y „..‟.
El comodín „*‟ reemplaza una cadena de caracteres que no incluyan el
carácter „.‟ mientras que el comodín „..‟ reemplaza un grupo de términos separados por el carácter „.‟. Si bien se pueden agrupar distintos tipos de esta
manera (basados en patrones de nombre o de paquete), el signo „+‟ permite
hacer coincidir además, a todos los tipos derivados de las coincidencias originales, es decir clases o interfaces hijas según sea el caso. En caso de tratarse de vectores se deben agregar la cantidad correspondientes de pares de
corchetes según la dimensión.
La expresión <PatronAnotacion> permite reducir el grupo de coincidencias
basándose en las anotaciones que posee el elemento. La sintaxis es:
<PatronAnotación> := [ ! ] @ <PatronNombreTipo> | (
<ExpresionTipo> ) ...
Es una lista de términos encabezados por el signo „@‟. La semántica en el
caso de los términos de la lista es la conjunción, es decir una coincidencia se
dará cuando se cumplan las condiciones de todos los términos. Opcionalmente se puede preceder a cada término por el signo „!‟ para negarlo. Cada
término puede ser simple o complejo. En el primer caso consta de un identificador de tipo. En el segundo caso, la expresión debe ir encerrada entre
paréntesis y consta de una expresión booleana de identificadores del primer
tipo con la posibilidad de combinarlos con los operadores „||‟, „&&‟ o „!‟.
 execution( <PatronFirma> ): similar al pointcut primitivo del tipo call, con
la diferencia de que el join point se encuentra inmediatamente después de
haber realizado la llamada e inmediatamente antes de iniciar la ejecución del
Diego M.S. Erdödy
Programación Orientada a Aspectos
23
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
método. La expresión <PatronFirma> es la misma que se describió en el
caso anterior.
 get( <PatronAtributo> ): representa los join points en donde se efectúa una
lectura de los atributos que coinciden con la expresión <PatronAtributo>.
Dicha expresión tiene la siguiente sintaxis:
<PatronAtributo> :=
[ <PatronAnotacion> ] [ <PatronModificadoresAtributo> ]
<PatronTipo> [ <PatronTipo> ] . <PatronIdentificador>
La expresión consta de un patrón de anotación opcional seguido de un <PatronModificadoresAtributo> que tiene la misma sintaxis que en los casos
anteriores y las posibilidades son equivalentes a los modificadores permitidos para un atributo en Java, es decir, “public”, “private”, “protected”, “static”, “transient” y “final”.
La expresión se completa con un término <PatronTipo>, explicado anteriormente, que indica el tipo de los atributos a capturar y otra expresión opcional del mismo tipo que especifica a qué clase pertenecen los atributos a
capturar. Por último se especifica el patrón que aplicará al identificador del
atributo.
 set( <PatronAtributo> ): similar al pointcut primitivo del tipo “get”, con la
diferencia que se seleccionan los join points de escritura en vez de lectura,
de los atributos que coincidan con el patrón especificado.
 handler( <PatronTipo> ): representa los join points en donde se captura
una excepción del tipo indicado por la expresión <PatronTipo>. Esta captura está siempre especificada por una cláusula “catch” en Java.
 adviceexecution(): representa los join points donde se inicia la ejecución de
un advice. No requiere argumentos.
 within( <PatronTipo> ): representa el conjunto de todos los join points que
tienen como elemento en la cadena de llamadas al tipo especificado en la
expresión pasada como argumento.
 withincode( <PatronFirma> ): similar al pointcut primitivo anterior, con la
diferencia que en vez de aplicarse a un tipo, se aplica a una firma, es decir
un método o constructor. Formalmente, el conjunto de todos los join points
que tienen como elemento en la cadena de llamadas a un método o constructor que coincida con la expresión pasada como argumento.
 cflow( <PointcutExpression> ): representa los join points que están dentro
del flujo de ejecución de los join points capturados por la expresión pasada
como parámetro, incluyendo a estos últimos. Dentro del flujo de ejecución
significa que los join points figuran dentro de la cadena de llamada.
24
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 cflowbelow( <PointcutExpression> ): similar al caso anterior, con la diferencia que los join points representados por la expresión pasada como parámetro, son excluidos del conjunto.
 staticinitialization( <PatronTipo> ): captura los join points del inicio de la
ejecución de un inicializador estático para los tipos que coinciden con el
patrón pasado como parámetro.
 initialization( <PatronFirma> ): captura los join points del inicio de la ejecución de un constructor desde que retorna la ejecución del constructor padre (super) hasta el retorno del primer constructor, para los constructores
que coinciden con el patrón pasado como parámetro.
 preinitialization( <PatronFirma> ): a diferencia del anterior, los join
points capturados son los de la ejecución desde el inicio del constructor hasta la llamada del constructor padre (super).
Para poder enlazar los parámetros que se le proveen al pointcut con la expresión
que lo definirá, se cuenta con un proceso denominado “Exposición de Contexto”. En
dicho proceso, ciertos operadores llevan a cabo la tarea de relacionar los parámetros del
pointcut con objetos definidos según el tipo de operador. Estos operadores son:
 this( <Tipo> | < PointcutVar> ): representa el conjunto de join points en
donde el objeto que se está ejecutando es del tipo indicado por el parámetro.
En el caso de pasar como parámetro un identificador de variable del pointcut, se asigna a dicha variable el objeto en ejecución al momento de alcanzar el join point.
 target( <Tipo> | < PointcutVar> ): similar al caso anterior con la diferencia
de que en vez de referirse al objeto que se está ejecutando, se refiere al objeto destino del join point. Por ejemplo, en un join point del tipo “call” corresponderá al objeto sobre el cual se efectúa la llamada.
 args( <Tipo> | <PointcutVar> , … ): en este caso, se trata de referenciar los
argumentos del pointcut. A diferencia de los otros dos casos, recibe una lista
de argumentos que pueden ser tipos o identificadores. Semánticamente representa los join points cuyos tipos parámetros coinciden con los tipos (o los
tipos de los identificadores) pasados como parámetro. En el caso de proveerse identificadores como parámetro, a dicha variable se le asigna el valor
que posee el argumento correspondiente en el join point al momento de ser
capturado, por ejemplo al ejecutar un advice.
3.5.3.
Declaración de un advice
Un advice define el comportamiento que deberá ejecutarse en los join points capturados por un pointcut. La declaración es muy similar a un método, con la diferencia que
se debe especificar a qué tipo pertenece y sobre qué pointcuts actuará. La sintaxis formal
de la declaración de un advice es:
Diego M.S. Erdödy
Programación Orientada a Aspectos
25
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<Advice> := [ strictfp ] <AdviceSpec> [ throws <ListaDeTipos> ] :
<PointcutExpression> { <CuerpoAdvice> }
<AdviceSpec> :=
before ( <Argumentos> ) |
after ( <Argumentos> ) |
after ( <Argumentos> ) returning [ ( <Argumento> ) ]
after ( <Argumentos> ) throwing [ ( <Argumento> ) ]
<Tipo> around ( <Argumentos> )
|
|
El modificador opcional strictfp, al igual que en Java, indica que las operaciones de
punto flotante realizadas dentro del código del advice deben tener estrictamente la precisión indicada por el estándar, independientemente del hardware utilizado.
La expresión <AdviceSpec> contiene el tipo del advice a declarar junto con los
parámetros necesarios. Los tipos disponibles de advice en AspectJ son los siguientes:
 before: el código se ejecuta antes de los join points capturados.
 after: el código se ejecuta después de los join points capturados. Por defecto, el advice se ejecuta sin importar si el código que sigue al join point, finalizó normalmente o por una excepción. Para indicar específicamente cada
uno de estos dos casos, se utilizan las palabras reservadas “returning” y
“throwing” respectivamente al final de la expresión, seguidos opcionalmente de un argumento entre paréntesis que representa el valor de retorno o la
excepción capturada.
 around: el método se ejecutará en vez del join point capturado. Por ejemplo, si el join point es del tipo “call”, la llamada original no se realizaría (a
no ser que se especifique explícitamente en el método del advice) y en cambio se ejecutaría el código indicado en el advice.
Al igual que un método, se puede indicar opcionalmente la lista de excepciones que
pueden ocurrir dentro del cuerpo del advice, precedida de la palabra throws. Por último,
se completa la declaración del advice con un signo „:‟ seguido de una <PointcutExpression> que ya fue definida anteriormente y el cuerpo de advice encerrado entre llaves.
3.5.4.
Ejemplos
Una vez definidos los tres elementos fundamentales (aspecto, pointcut y advice) se
muestran distintos ejemplos para clarificar las definiciones hasta aquí presentadas. Los
ejemplos están basados en una implementación de un modelo sencillo de cuentas bancarias. La estructura del ejemplo es la siguiente:
26
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Customer
Account
-id : String
-name : String
-address : String
-accounts : List<E->Account>
-amount : double
-id : String
<<getter>>+getAccounts() : List<E->Account>
<<getter>>+getAddress() : String
<<setter>>+setAddress( address : String ) : void
<<getter>>+getId() : String
<<setter>>+setId( id : String ) : void
<<getter>>+getName() : String
<<setter>>+setName( name : String ) : void
-has
0..*
<<getter>>+getAmount() : double
<<getter>>+getId() : String
<<setter>>+setId( id : String ) : void
<<JavaElement>>+deposit( dAmount : double ) : void{JavaAnnotations = @Transaction}
<<JavaElement>>+withdraw( dAmount : double ) : void{JavaAnnotations = @Transaction}
+transferTo( destination : Account, dAmount : double ) : void
SavingAccount
SecurityManager
CurrentAccount
+userLogged( user : User ) : boolean
User
<<getter>>+getId() : String
Sentinel
Teller
-id : String
-id : String
<<constructor>>+Sentinel( nid : String )
<<getter>>+getId() : String
<<getter>>+getAccounts( cust : Customer ) : void
<<constructor>>+Teller( nid : String )
<<getter>>+getId() : String
<<getter>>+getAccounts( cust : Customer ) : List<E->Account>
Figura 3-5: Diagrama de estructura para los ejemplos de AspectJ
Los clientes están representados por la clase Customer que tiene atributos como
identificación, nombre, dirección y una lista de cuentas. La clase abstracta Account encapsula el comportamiento de una cuenta y posee dos clases derivadas SavingAccount y
CurrentAccount, para una caja de ahorro y una cuenta corriente respectivamente. La
cuenta posee una identificación y un monto. Además de consultar el saldo, se puede depositar o extraer dinero mediante los métodos deposit() y withdraw().
Por otro lado, los usuarios internos del sistema están representados por la interface
User. Existen dos tipos: Sentinel y Teller, que representan un empleado de seguridad y
un cajero, en ese orden. Por último, la clase SecurityManager, permite averiguar si un
usuario está registrado en el sistema a través del método estático userLogged().
El siguiente ejemplo muestra la implementación más simple de un interés transversal de auditoría sobre todas las llamadas críticas, es decir los movimientos de fondos.
Diego M.S. Erdödy
Programación Orientada a Aspectos
27
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
public aspect Audit {
public pointcut auditableCall():
call(public * Account+.deposit(..)) ||
call(public * Account+.withdraw(..));
before(): auditableCall() {
audit("Audit transaction: " + thisJoinPoint);
}
}
El interés es implementado por el aspecto Audit, el cual consta del poincut auditableCall() que representa las llamadas a auditar. En este caso, por simplicidad, las llamadas
a los métodos de depósito y extracción son enumeradas (vinculadas con el operador „||‟).
Nótese que se utilizan comodines para el tipo de devolución de los métodos, así como
también para los argumentos. Esto garantiza que en caso de sobrecargar los métodos en
futuras implementaciones, los métodos sobrecargados seguirán formando parte del poincut. También cabe destacar que al utilizar el operador „+‟, el pointcut abarca las llamadas
a los métodos de la clase Account y a todas las clases derivadas, en este caso SavingAccount y CurrentAccount. Por último, el aspecto define un advice del tipo “before” sobre
el poincut, para realizar la operación de auditoría, representada por el método audit().
Para hacer la implementación más flexible, se puede usar metainformación para
describir la funcionalidad de los participantes del sistema. De esta manera, se pueden usar
construcciones que consuman esta metainformación y así evitar la enumeración. Si los
métodos deposit() y withdraw() de la clase Account, estuvieran modificados con la anotación de negocio @Transaction, describiendo a las operaciones monetarias atómicas, el
poincut se podía reescribir de la siguiente manera:
public pointcut auditableCall():
call(@Transaction public * Account+.*(..));
De esta forma se están capturando todas las llamadas a métodos públicos de la clase
Account o derivadas, que estén modificados con dicha anotación.
Por último se analiza el caso de la implementación de un interés de seguridad. Se
pretende que el acceso a cuentas de un cliente, sea permitido sólo a los cajeros que están
registrados en el sistema. El aspecto que lo implementa es el siguiente:
28
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
public aspect AccessRestriction {
public pointcut securityCalls():
call( * Customer+.getAccounts(..));
public pointcut checkedCalls(User usr):
within(Teller+) && securityCalls() && this(usr);
public pointcut forbiddenCalls():
!within(Teller+) && securityCalls();
before(User usr): checkedCalls(usr) {
if (!SecurityManager.userLogged(usr)) {
throw new BankingException("User should be logged");
}
}
before(): forbiddenCalls() {
throw new BankingException("Access denied");
}
}
En el aspecto AccessRestriction, se comienza declarando el pointcut securityCalls()
que encapsula las llamadas que se pretenden controlar. En este caso son las llamadas a
métodos de acceso a las cuentas getAccounts() de la clase Customer o derivadas. A continuación, se define el pointcut checkedCalls() que representa las llamadas que se deben
permitir solamente cuando el usuario esté registrado en el sistema. Está compuesto por
todos los puntos contenidas en securityCalls() que sean llamados desde un objeto del tipo
Teller o derivado. Además, se vincula un parámetro del tipo usuario, con el objeto origen
de la invocación, es decir el cajero. Para el pointcut checkedCalls(), se define un advice del
tipo “before” que consulta con el SecurityManager si el usuario está registrado. De no
ser así, se lanza una excepción indicando la situación. El segundo advice lanza una excepción cuando se llega a un punto contenido en forbiddenCalls(), indicando que no se tiene acceso.
Para la definición de las llamadas a controlar, en el pointcut securityCalls(), existe
el peligro de que en el futuro se agreguen métodos que accedan a la lista interna del objeto, pero bajo otra convención de nombres (un iterador, por ejemplo). Esto se puede solucionar con el mecanismo de las anotaciones, aunque en este caso la anotación no tiene
una semántica definida, es decir sería una solución forzada. Otra posible solución al problema, es definir el pointcut a partir del acceso a la lista interna, utilizando el join point
del tipo “get”. El pointcut sería:
public pointcut securityCalls():
cflow(call( * Customer+.*(..))) &&
get(private List<Account> Customer+.accounts);
Diego M.S. Erdödy
Programación Orientada a Aspectos
29
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
En esta nueva versión se está incluyendo a todos los puntos que precedan a una
llamada a un método de la clase Customer o derivada (indicado con el join point “cflow”)
y en cuya ejecución se acceda al atributo privado “accounts” de la misma clase. Esta implementación tiene el problema de depender de la estructura interna del objeto, aunque en
este caso no es muy probable que se vaya a modificar.
3.5.5.
Declaraciones de miembros inter-tipo
Para la implementación transversal estática, AspectJ cuenta con las declaraciones
de miembros inter-tipo. Estas declaraciones consisten en agregar miembros, ya sea atributos o métodos, a clases externas al aspecto. De esta forma se logra introducir en la clase,
la lógica específica al interés que implementa el aspecto, sin que dicha clase tenga conocimiento.
Existen varias estructuras sintácticas para este tipo de declaraciones, según sea el
tipo de miembro que se quiera agregar. Estas son:
1) abstract [ <Modificadores> ] <Tipo> <Tipo> . <Id> ( <Parametros> )
[ throws <ListaDeTipos> ] ;
2) [ <Modificadores> ] <Tipo> <Tipo> . <Id> ( <Parametros> )
[ throws <ListaDeTipos> ] { <Cuerpo> }
3) [ <Modificadores> ] <Tipo> . new ( <Parametros> )
[ throws <ListaDeTipos> ] { <Cuerpo> }
4) [ <Modificadores> ] <Tipo> <Tipo> . <Id> [ = <Expresion> ] ;
El primer y el segundo caso corresponden a declaraciones de introducción de métodos abstracto y concreto, respectivamente. En estos casos la sintaxis es casi idéntica a la
declaración de un método en Java. La única diferencia es que el nombre o identificador
del método debe ir precedido del nombre de la clase en donde se desea introducir.
El tercer caso corresponde a la introducción de un constructor. La declaración cuenta con la particularidad de que en vez de requerir un identificador, como en el caso de un
método, se debe utilizar la palabra reservada new, para indicar que se trata de un constructor. Al igual que el identificador de un método, la palabra debe ir precedida de la clase en donde se quiere introducir el constructor.
El cuarto caso representa la introducción de un atributo. Sigue la forma de una declaración de atributos en Java, con la diferencia de anteponer el tipo al que se quiere introducir, antes del identificador del atributo.
30
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
3.5.6.
Declaraciones de derivación
Llamamos declaraciones de derivación a las sentencias de un aspecto que permiten
introducir extensión de padres o implementación de interfaces en una clase dada. La sintaxis de estas declaraciones es:
1) declare parents : <PatronTipo> extends <Tipo> ;
2) declare parents : <PatronTipo> implements <ListaDeInterfaces> ;
En ambos casos, la declaración comienza con las palabras reservadas “declare
parents : ” y es seguida de una expresión del tipo <PatronTipo>. Dicha expresión representa un conjunto de clases a las cuales se les efectuará la introducción.
El primer caso corresponde a la introducción de la extensión de la clase especificada. Como el código compilado que genera AspectJ debe ser compatible con cualquier
JVM tradicional, no se pueden introducir extensiones a clases que ya poseen un padre. En
otras palabras, si una clase extiende de otra, no se puede forzar a que extienda de una
tercera ya que Java no permite le herencia múltiple. Existe, sin embargo, una excepción a
dicha regla que consiste en introducir una extensión de una subclase del padre original.
Por ejemplo, si A extiende de B, un aspecto puede hacer que A extienda de C, siempre y
cuando C sea subclase de B.
El segundo caso corresponde a la introducción de implementación de nuevas interfaces. Al igual que en Java, se puede especificar una lista de interfaces a implementar. La
única restricción que existe, es que los métodos definidos por las nuevas interfaces, deben
ser implementados, o bien con métodos preexistentes, o con métodos introducidos por el
aspecto.
Siguiendo con el ejemplo propuesto, se analiza la implementación del interés “cobro de servicios”, para brindar un ejemplo de declaraciones de miembros inter-tipo como
de declaraciones de derivación. El objetivo es registrar los accesos a los servicios de las
cuentas corrientes de cada cliente y acumularlo para poder realizar los resúmenes a fin de
mes. Si bien esta implementación se podría realizar sin la ayuda de aspectos, el código
asociado al interés se mezclaría con el interés principal de una cuenta que es administrar
fondos. El aspecto que lo implementa es el siguiente:
Diego M.S. Erdödy
Programación Orientada a Aspectos
31
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
public aspect Billing {
interface Billable {
double getBill();
void addToBill(double amount);
}
public double Billable.bill;
public double CurrentAccount.getBill() {
return bill;
}
public void CurrentAccount.addToBill(double amount) {
bill += amount;
}
declare parents: CurrentAccount implements Billable;
public pointcut billableCall(Billable billable):
call(@Transaction public * CurrentAccount.*(..)) &&
target(billable);
before(Billable billable): billableCall(billable) {
billable.addToBill(1);
}
}
En el aspecto Billing, se declara la interface Billable que representa un objeto contable. Esta interface posee el método getBill() para obtener el acumulado de consumo y
addToBill() para agregar un nuevo consumo. En las siguientes tres declaraciones de intertipo, se realiza la implementación de la interface, agregando un atributo que representa el
acumulado de consumo y la implementación de los métodos. Este mecanismo es similar a
implementar una clase abstracta, pero brinda la facilidad de poder utilizar varias implementaciones al mismo tiempo, como se haría en un esquema de herencia múltiple. La
siguiente línea indica que la clase CurrentAccount debe implementar la interface Billable. Luego se declara el poincut billableCall(), similar al utilizado en el ejemplo de auditoría, el cual captura los métodos modificados con la anotación @Transaction. Por último,
se define un advice de tipo “before” sobre el pointcut, para adicionar un costo arbitrario
al consumo de la cuenta.
Vale aclarar que los ejemplos utilizados son ilustrativos para comprender el uso de
la sintáxis de AspectJ. La mayoría de los ejemplos se pueden resolver con aspectos abstractos que hacen que la implementación sea reutilizable, y aumentan las propiedades de
modularidad, pero esto aumenta considerablemente la complejidad de los mismos.
32
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
3.5.7.
Otras declaraciones
Por último, se describen las declaraciones generales que provee AspectJ. La sintaxis
de las declaraciones restantes es:
1) declare warning : <PointcutExpression> : <String> ;
2) declare error : <PointcutExpression> : <String> ;
3) declare soft : <Excepcion> : <Pointcut> ;
4) declare precedence : <ListaDeAspectos>;
La primera y la segunda declaración, tienen semánticas y sintaxis muy similares.
Ambas definen que cierto conjunto de join points, especificados por la expresión <PoincutExpression>, deben ser tratados por el compilador, al momento de compilación como
advertencias o como errores, según se trate del primer o del segundo caso respectivamente. El mensaje a mostrar es el último término de la declaración. Este tipo de declaraciones
sirven, por ejemplo, para reforzar la modularidad de cierto componente y asegurarse que
sus métodos sean utilizados sólo desde algunas clases seleccionadas, es decir, casos complejos donde la visibilidad no alcanza para cumplir dicho propósito.
La declaración del tipo “soft”, convierte al tipo org.aspectj.lang.SoftException la
excepción especificada en la declaración que se lanza en los join points que representa el
pointcut. Dicha excepción es del tipo RuntimeException, por lo que no es necesario capturarla.
Por último, la declaración “precedence” indica en qué orden se deben aplicar los
aspectos, en caso que haya alguna ambigüedad al respecto. Simplemente se enumera la
lista de aspectos en el orden de su aplicación, pudiendo utilizar opcionalmente comodines
para hacer más simples las instrucciones del tipo “primero aplicar el aspecto A y después
cualquier otro que deba aplicarse”.
Continuando con el ejemplo del interés de seguridad, para las llamadas prohibidas,
(es decir, los pedidos de cuentas a usuarios por parte de objetos que no sean cajeros ni
derivados) la acción que se tomó fue lanzar una excepción. El inconveniente que trae esta
decisión es que las llamadas ilegales serán identificadas en tiempo de ejecución, cuando
tal vez sea demasiado tarde. Para poder identificar dichas llamadas en tiempo de compilación, se puede usar una llamada del tipo “declare error” de la siguiente forma:
declare error: forbiddenCalls() : "Access denied";
Diego M.S. Erdödy
Programación Orientada a Aspectos
33
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
De esta manera utilizando extensiones que soportan el análisis sintáctico en tiempo
de edición, como por ejemplo AJDT en la IDE de Eclipse, las llamadas prohibidas serán
identificadas como errores de compilación tradicionales. La siguiente captura muestra
este caso:
Figura 3-6: Captura de Eclipse mostrando el resultado de “declare error”
3.5.8.
Intercalado
Con respecto a la técnica de intercalado, AspectJ utiliza un compilador dedicado, el
cual genera código Java binario a partir del conjunto de clases y aspectos, ya sea en código fuente o archivos compilados, que están disponibles en tiempo de compilación.
34
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Código fuente Java (.java)
AspectJ Compiler
(ajc)
Sistema intercalado
(archivos .class + .jar )
JVM tradicional
(java)
Código fuente AspectJ (.aj)
Archivos compilados
(.jar + .class)
Figura 3-7: Mecanismo general de funcionamiento de AspectJ
La gran desventaja de este mecanismo es la necesidad de contar con todas las clases
que intervienen en el sistema al momento de compilación, en vez de poder hacer el intercalado a medida que se van cargando las clases en tiempo de ejecución. Sin embargo, de
este modo se logra una mayor performance del sistema ya que todo el código ha sido procesado e intercalado en tiempo de compilación y no existe ninguna penalidad en cuanto a
performance en tiempo de ejecución.
3.6.
Comparación con otras implementaciones de AOP
Para poder seleccionar la herramienta que se utiliza en este trabajo, se realizó un
análisis de otras implementaciones AOP, similar al trabajo de Mik Kersten [Ker 05], sobre versiones actualizadas y nuevas implementaciones. Sin entrar en los detalles sintácticos de cada una de las implementaciones, se realiza un análisis y comparan las características de cada implementación, las cuales se resumen al final en una tabla comparativa.
3.6.1.
AspectWerkz
AspectWerkz [AWerks 05] (AW) es un framework AOP para Java, diseñado para
ser dinámico, liviano y de alta performance.
La primera diferencia importante que presenta frente a AspectJ, es que la implementación no está basada en una extensión a la sintaxis del lenguaje Java. Existen dos
opciones para realizar declaraciones de aspectos.
La primera opción consiste en un archivo de configuración en formato XML que
contiene todas las instrucciones que indican las clases que implementarán los aspectos,
pointcuts, advices y demás declaraciones de AOP. Es decir que los aspectos son en realidad clases tradicionales de Java, las cuales deben cumplir con alguna convención, como
por ejemplo poseer cierto tipo específico de constructor.
La otra opción, incorporada recientemente para la definición de declaraciones AOP
de AW, es usar anotaciones de Java 5. Básicamente, se traslada la información del archiDiego M.S. Erdödy
Programación Orientada a Aspectos
35
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
vo XML a anotaciones dentro de clases tradicionales de Java. De esta forma, la información AOP se encuentra mucho más localizada que en el caso del archivo de configuración, permitiendo un mejor entendimiento y facilitando el mantenimiento.
Ambas opciones tienen la desventaja de no proveer un chequeo sintáctico de las expresiones de pointcuts en tiempo de compilación. Dado que dichas expresiones son atributos de XML o argumentos de anotaciones, según la opción que se prefiera, los chequeos que se realizan en tiempo de compilación no analizan las expresiones. En AW en cambio, las expresiones son verificadas en tiempo de ejecución. En el caso de AJDT, el plugin de AspectJ para Eclipse, los errores son marcados en tiempo de edición. Esto no se
puede lograr con anotaciones ni con archivos XML.
En cuanto a la estrategia de intercalado, AW utiliza varios modelos según la versión
de Java y el tipo de JVM que se utilice. La opción más moderna, es el intercalado en
tiempo de carga que se puede realizar con la nueva API de instrumentación de Java 5,
especificado en el Java Specification Request número 163 del Java Community Process
[JSR 163]. Dicha API, permite especificar al iniciar la JVM, una o más clases con ciertas
características que actuarán de “agentes java”. Estas clases deben contener un método
llamado “premain” el cual será llamado al iniciar la JVM. Uno de los argumentos de este
método, es un objeto que implementa la interface “Instrumentation” que brinda los servicios específicos de instrumentación. Entre estos servicios, se encuentra la posibilidad de
incorporar implementaciones propias de la interface “ClassFileTransformer” con la habilidad de modificar el código intermedio de las clases cargadas.
AW soporta la mayoría de los pointcuts primitivos soportados por AspectJ con las
excepciones de adviceexecution, cflowbelow, initialization y preinitialization. Los únicos
dos pointcuts de AW que no posee AspectJ son hasmethod y hasfield. Ambos ayudan a
limitar la cantidad de join points seleccionados en relación a propiedades estructurales
como es la existencia de otros métodos o atributos. AW no soporta las políticas de instanciación del tipo pertarget ni percflow que posee AspectJ.
3.6.2.
JBoss AOP
JBoss AOP [JBA 06][Bur 03], es un módulo de soporte de AOP ideado para utilizar con el Servidor de Aplicaciones JBoss [JBS 06]. Si bien los conceptos básicos de
AOP están presentes en JBoss AOP (JBA), la forma de presentarlos tiene ciertas diferencias con AspectJ.
En primer lugar, JBA al igual que AW, ofrece dos opciones para declarar los elementos AOP que acompañan a las clases que los implementan: anotaciones de Java5 (o
un equivalente de XDoclet [XDoc 06] en Java 1.4) o un archivo de configuración XML.
La funcionalidad que se logra con cualquiera de las dos opciones es la misma, ya que
existe un mapeo entre ambas notaciones.
En segundo lugar, en JBA las declaraciones de AOP se pueden realizar en dos tipos distintos de clases, a diferencia de AJ o AW en donde sólo existe el aspecto. En JBA
se utiliza la noción de “interceptores” para poder declarar advices, aunque sigue existiendo la noción de aspecto. Estos interceptores son simplemente clases que deben implementar una interface específica (Interceptor) y ser afectadas por la anotación que la identifi36
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
cará como tal. La mayoría de los elementos AOP restantes pueden declararse tanto dentro
de un aspecto como dentro de un interceptor.
A diferencia de las implementaciones analizadas anteriormente, JBA sólo soporta
los advices del tipo “around”, es decir, los interceptores reemplazan a los join points interceptados. La interface Interceptor contiene un método que se ejecutará al momento de
llegar al join point y tiene como argumento un objeto del tipo Invocation. Este objeto
brinda por un lado meta-información sobre el join point actual (p. ej. tipo de miembro,
nombre del método/constructor, argumentos, etc.) y por otro lado acciones tales como
“seguir con el joinpoint”. De esta forma se logran simular los advices del tipo “after” o
“before”por medio de programación. En otras implementaciones se puede hacer también
en forma “declarativa”, que resulta más sencillo y visual.
Los pointcuts tienen una sintaxis similar a la de AJ y son representados por un
atributo estático de tipo “Pointcut” modificado por la anotación que lo identifica. Los
advices deben relacionarse con los pointcuts a través de declaraciones especiales denominadas “Bind” en JBA. Los pointcuts primitivos de AJ que no son soportados por JBA
son: handler, adviceexecution, staticinitialization y preinitialization. Al igual que AW,
adiciona los siguientes pointcuts primitivos “has” y “hasfield” que sirven para relacionar
con la estructura del tipo, es decir si poseen cierto atributo, método o constructor. Por
último, JBA posee un pointcut primitivo llamado “field” que es equivalente a la expresión “get(x) || set(x)” en AJ.
En cuanto a la introducción de elementos, JBA permite introducir solamente nuevas implementaciones de interfaces, no soporta la extensión de una nueva clase. Para poder implementar los métodos de las interfaces introducidas, JBA utiliza el concepto de
“mixin”. Un mixin en JBA, consta de una clase que implementa las interfaces a introducir
y que cuenta con un constructor que recibe el objeto en donde las interfaces serán introducidas. Esta clase debe ir acompañada de la declaración del mixin en donde se indica
qué interfaces se quiere introducir en qué clase y cual mixin lo implementará. No es posible introducir métodos o atributos a una clase, si no es por medio de la introducción de un
nueva interface y de un mixin que la implemente.
Las políticas de instanciación son muy similares a las de AJ, salvo que no permite
“percflow”. En cambio, permite instanciación por clase a diferencia de AJ. Las declaraciones adicionales de AJ también son soportadas en su mayoría, excepto las del tipo
“soft”.
El tipo de intercalado que utiliza JBA, es en tiempo de carga, llevado a cabo por
un classloader especial que debe indicarse en el momento de inicializar la VM.
3.6.3.
Spring AOP
Spring nació como un framework genérico y modular de soluciones elegantes a
problemas comunes en aplicaciones de empresa, como por ejemplo la configuración de
componentes. Este problema es resuelto por uno de los módulos más populares de Spring
llamado IoC (inversión de control) [Fow 04]. El módulo de AOP [Rus 04], según la propia documentación, no fue diseñado para ser la solución más completa en AOP, sino para
proveer una buena integración con el módulo de IoC. Es por esto, que la declaración de
Diego M.S. Erdödy
Programación Orientada a Aspectos
37
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
elementos AOP en Spring, se realiza en el mismo archivo de configuración xml en donde
se definen y configuran los componentes.
En Spring, los pointcuts se declaran a través de clases que deben implementar la
interface “Poincut”. Esta interface tiene dos métodos destinados a seleccionar las clases y
métodos que incluirá el pointcut. La implementación más usada, se basa en expresiones
regulares. Los advice se deben implementar también en una clase que implemente a la
interface del tipo de advice que se desee aplicar, por ejemplo “AfterReturningAdvice”.
Dentro de dicha clase se debe implementar el método declarado que contendrá la lógica
del advice. Este método cambia de nombre dependiendo del tipo de advice. Para el caso
de AfterReturningAdvice, el método que se declara se denomina “afterReturning”. Los
otros tipos de advice soportados son “before” y “throws”. Este último cumple la misma
función que la declaración “after throwing” de AspectJ.
Al igual que JBoss AOP, Spring incorpora el concepto de interceptores. La idea es
muy similar, existe una interface por cada tipo de interceptor (de método, atributo o constructor) las cuales definen métodos que serán llamados al momento de llegar al punto
interceptado. Estos métodos proveen, a través de los argumentos, la meta información
sobre el punto de intersección que se está utilizando (nombre del método, argumentos,
etc.).
Para introducir nuevas interfaces en Spring, se utiliza un mecanismo similar al caso del advice. Se debe también implementar una interface específica que define varios
métodos. Uno de los métodos está destinado a seleccionar las clases destino de la introducción. Otro de los métodos indica la lista de clases que serán las interfaces introducidas. Por último, un tercer método, similar a los utilizados para interceptores de métodos,
implementa los métodos de las interfaces introducidas, utilizando un mecanismo de identificación por nombre de los métodos, lo cual resulta engorroso.
Si bien en Spring no existe la noción de aspecto, la conjunción de implementación
de advices y pointcuts en una misma clase se la conoce como “Advisor”. En Spring, los
Advisors son las unidades de modularización y reutilización, similares a los aspectos de
AspectJ.
El intercalado de los elementos AOP en Spring, está inherentemente relacionado
al módulo de inversión de control que provee. Como la instanciación de los componentes
está controlada por el framework, para poder intercalar elementos foráneos a la clase definida, se utiliza una clase provista por el módulo AOP que actúa de Proxy y realiza los
intercalados necesarios. De esta forma, el usuario del componente puede utilizar la implementación del mismo de forma transparente y al mismo tiempo se pueden intercalar
dinámicamente los elementos AOP.
Una de las grandes falencias de Spring AOP es la falta de un verdadero lenguaje
de pointcuts. Es por esto que en la nueva versión 2.0, actualmente en fase beta, se incorporará la implementación de AspectJ dentro del framework.
3.6.4.
CaesarJ
CaesarJ [Mez 03] es un proyecto nacido en la Universidad Alemana de Darmstadt.
Actualmente se encuentra en la fase de desarrollo (versión 0.8) y esa fue una de las grandes desventajas al momento de seleccionar la implementación AOP para este trabajo.
38
Programación Orientada a Aspectos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Al igual que AspectJ, CaesarJ es una extensión al lenguaje Java cuyo compilador
genera código intermedio compatible con cualquier JVM tradicional. CaesarJ también
utiliza el mismo modelo de pointcuts propuesto por AspectJ, es decir que soporta los
mismos pointcuts primitivos y la misma sintaxis de declaración tanto de pointcuts como
de advices.
Si bien los diseñadores de CaesarJ reconocen la importancia del modelo de pointcuts dentro de una implementación AOP, también consideran importante una clara separación entre la implementación de un aspecto y su vinculación dentro de una aplicación.
Implementaciones AOP como AspectJ no cuentan con un mecanismo explícito para dicha
tarea. Para solucionar este problema es que se plantea el concepto de “Interfaces de Colaboración de Aspectos” (ACI, del inglés Aspectual Collaboration Interfaces).
Las ACI son interfaces cuyas declaraciones de métodos se dividen en “provistos”
(provided) y “requeridos” (required), es decir lo que el aspecto brinda al contexto en donde es aplicado y lo que espera obtener de dicho contexto. La implementación (o implementaciones) de la interface deberá definir los métodos “provistos”, aunque puede también referenciar métodos “requeridos” como si se trataran de métodos abstractos. Los
métodos “requeridos” deben ser definidos en módulos especiales denominados “bindings”. En dichos módulos es también donde se definen detalladamente los pointcuts utilizados por el aspecto. A través de las ACIs y su implementación dual, se permite desacoplar de una manera clara, la lógica propia del aspecto de la lógica específica a la aplicación dentro del contexto.
Existen clases especiales denominadas “weavelets” que son las encargadas de
definir la implementación y el binding que se utilizará con un ACI determinado. El mecanismo es similar a una clase con una plantilla de 2 argumentos de Java 5. Es en este
punto en donde se observa claramente la flexibilidad que se logra al poder combinar una
implementación con cualquier binding y viceversa. Tener un “weavelet” dentro del grupo
de compilación no es suficiente para que el aspecto sea debidamente intercalado.
CaesarJ provee dos mecanismos para el intercalado de los aspectos. La opción
tradicional es realizar el intercalado en tiempo de compilación, definiendo un atributo con
los modificadores „static‟ y „final‟ en cualquier clase, del tipo del weavelet que se quiere
desplegar. La segunda opción consta de una cláusula llamada “deploy” que permite activar a un determinado aspecto en tiempo de ejecución, y dentro de un bloque perfectamente delimitado.
A pesar de que CaesarJ no provee cláusulas para especificar la política de instanciación que se aplicará a un aspecto, todas las políticas provistas por AspectJ pueden ser
emuladas mediante construcciones particulares. Un weavelet en CaesarJ, por defecto es
instanciado en modo “singleton”. Si se quisiera instanciar en modo “perTarget” se debería utilizar un wrapper dentro de la implementación de la interface. Esto tiene como desventaja un mayor trabajo al momento de querer aplicar una política no soportada directamente por el lenguaje, lo que trae aparejado una dificultad adicional al momento de leer
la implementación de un aspecto. Por otro lado, es una ventaja el hecho de que las políticas de instanciación puedan ser expresadas en términos de elementos primitivos y no de
cláusulas artificiales.
Diego M.S. Erdödy
Programación Orientada a Aspectos
39
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
3.6.5.
Conclusiones
Una de las características requeridas de la herramienta de AOP para este trabajo es
la mayor expresividad posible en la definición de pointcuts. Es requisito que la herramienta brinde la mayor cantidad de recursos posibles. Esto se debe a que se necesita explorar cada una de las opciones que brinda AOP al momento de abstraer y modularizar un
patrón de diseño. En esta categoría, AspectJ es el más apropiado, seguido por CaesarJ, ya
que este último utiliza el mismo modelo de pointcuts.
Las razones de la elección de AspectJ por sobre CaesarJ, son la madurez de la implementación y el soporte que brinda la herramienta. En cuanto a soporte para el desarrollo, ambas cuentan con plug-ins que facilitan la tarea. El plug-in de AspectJ tiene considerable ventaja en cuanto a la estabilidad y el testeo ya que fue uno de los primeros y más
completos plug-ins producidos.
Las siguientes tablas contienen un resumen de la comparación entre las herramientas AOP analizadas en este trabajo. La primera tabla resume las características de la
implementación y cantidad de elementos que soporta mientras que la segunda muestra el
detalle de dichos elementos.
40
Programación Orientada a Aspectos
Diego M.S. Erdödy
AspectWerkz
2.0
Configuración XML /
Anotaciones
de Java 5
Configuración XML /
Anotaciones
de Java 5
JBossAOP
1.5
SpringAOP
1.2.8
Configuración XML
CaesarJ
0.8.5
Extensión
de Java
Dinámico
Sí
13
3
5
4
2
4
 Una de las herramientas más maduras y con mejor documentación
 Mejor integración a herramientas de desarrollo
 Mayor flexibilidad en cuanto a características AOP
En tiempo
de carga
(javaagent)
Sí
11
3
6
3
0
0
 No admite chequeo sintáctico de expresiones
 Mejor integración a herramientas de desarrollo
Sí
11
-
1
4
1
3
 No admite chequeo sintáctico de expresiones
 Introducción de interfaces a través de Mixins
No
5
-
4
2
1
0
 Integración al contenedor IoC
 Orientado a componentes
 Modelo de pointcuts rudimentario
No
13
3
5
2
-
0
 Implementación relativamente reciente, falta de madurez
 Incorpora el modelo de pointcuts de AspectJ
 Resuelve ciertos problemas de AspectJ
En tiempo
de carga
(classloader)
En tiempo
de carga
(proxies
dinámicos)
Tiempo de
compilación
y ejecución
DD
OD
AspectJ
Conclusiones
TI
1.5
Extensión
de Java
Intercalado
AD
Lenguaje
PE
Ver.
PP
Impl. AOP
Anot.
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Referencias:
PP: Pointcuts Primitivos
PE: Pointcuts de entorno
AD: Tipos de Advices
TI: Tipos de instanciación
DD: Declaraciones de derivación
OD: Otras declaraciones
Diego M.S. Erdödy
Programación Orientada a Aspectos
41
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Detalle de la cantidad de elementos AOP soportado por cada implementación
AspectJ
AspectWerkz
JBoss AOP
Spring AOP
CaesarJ
call
x
x
x
x
execution
get
set
handler
adviceexecution
within
withincode
cflow
cflowbelow
staticinitialization
initialization
preinitialization
x
x
x
x
x
x
x
x (Field Interc.)
x (Field Interc.)
x
x
x
x
x
x
x
x (Constr.)
hasmethod
hasfield
13
this
target
args
3
before
after
after throwing
after returning
around
5
issingleton
perthis
pertarget
percflow
4
parents implements
parents extends
2
error
warning
soft
precedence
4
Diego M.S. Erdödy
11
x
x
x
3
x
x
x
x
x
after finally
6
perJVM
perInstance
perClass
3
0
0
all (any member)
has (Method or
Constr.)
hasfield
field (get or set)
11
Acceso Reflexivo
Acceso Reflexivo
Acceso Reflexivo
3
x
x (Constr. Interc.)
5
Acceso Reflexivo
Acceso Reflexivo
Acceso Reflexivo
3
x
x
x
x (Method Interc.)
x
x
x
x
x
x
x
x
x
x
x
x
13
x
x
x
3
x
x
x
x
x
1
PER_VM
PER_INSTANCE
PER_JOINPOINT
4
5
x
PER_CLASS
4
per class
2
x
x (Introd. Interc.)
1
x
x
1
0
0
0
x
x
3
Programación Orientada a Aspectos
1
43
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
4. Patrones de diseño distribuidos
4.1.
Patrones de diseño
Un patrón de diseño es la descripción de una solución repetible a un problema frecuente dentro del diseño de software. Sin embargo no se puede considerar que sea una
solución final aplicable directamente. Se puede ver como una plantilla con una solución
efectiva y probada a una clase específica de problemas dentro de un entorno determinado.
Un patrón tiene cuatro componentes principales, según Gamma et. al [Gam 93], que
son:
I. Nombre: Es el identificador utilizado para describir la solución propuesta por
el patrón. Conocer el nombre de los patrones tradicionales es importante para
crear un vocabulario común. Dicho vocabulario puede ser utilizado en la documentación de un framework o incluso en la interacción entre colegas.
II. Problema: Describe el entorno al cual el patrón está dirigido. Dicho entorno
puede incluir prerrequisitos para que la aplicación del patrón tenga sentido.
III. Solución: Conjunto de elementos que forman el patrón de diseño. Estos elementos incluyen los elementos del patrón con sus roles, sus responsabilidades
y las relaciones entre ellos.
IV. Consecuencias: Describe los beneficios y las desventajas de aplicar el patrón.
Documenta las alternativas que presenta el patrón y como afectan las decisiones de compromiso que deben tomarse.
Los 23 patrones tradicionales documentados están divididos en tres grupos, según
el propósito. Los patrones “de creación” tratan con los problemas de instanciación de
objetos. Los patrones “estructurales” están relacionados con la composición de clases u
objetos. Por último, los patrones “de comportamiento” describen los problemas asociados
a la vinculación y distribución de responsabilidades entre objetos.
Los micropatrones de diseño [Gil 05], a diferencia de los patrones de diseño, corresponden a un menor nivel de abstracción. Los micropatrones pertenecen al grupo de
los patrones “traceables”, es decir, los patrones que pueden ser identificados automáticamente y de una manera simple, dentro del código fuente de un sistema. Esto es posible,
ya que los micropatrones se relacionan con la cantidad y tipo de elementos que posee una
clase.
Por ejemplo, uno de los 27 micropatrones identificados se denomina “Designator” y
se define como una interface sin ningún miembro. Esta interface solamente sirve para
marcar roles dentro de un sistema. Este mecanismo es utilizado por diferentes patrones
del grupo GoF. Los micropatrones ayudan en la implementación en los patrones de diseño y es por eso que se dice que los micropatrones se encuentran en un nivel intermedio
entre la codificación y los patrones de diseño.
4.1.1.
Beneficios y problemas de los patrones de diseño
Diego M.S. Erdödy
Patrones de diseño distribuidos
45
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Los patrones de diseño permiten la reutilización del conocimiento aplicado al diseño de una solución particular. Es por eso que la documentación de dichas soluciones
permite un vocabulario universal que facilita la comprensión de arquitecturas complejas.
Los patrones pueden ser utilizados de forma “reactiva” o “proactiva” [Cli 96]. El
primer caso corresponde a la documentación de un diseño terminado, con un formato
estándar para poder almacenar el trabajo de análisis y resolución del problema en cuestión. El segundo implica la aplicación de los patrones previamente conocidos en un problema en particular, adaptándolos al entorno específico según los lineamientos que provee.
Uno de los grandes inconvenientes asociados a la mayoría de los patrones de diseño
es la imposibilidad de realizar una implementación reutilizable utilizando POO. Esto
quiere decir que cada vez que se desee implementar un patrón en un entorno distinto, se
deberá repetir el código utilizado en otros entornos. Si bien el problema tiene varios motivos que dependen del patrón en particular, se ha hallado que muchos de esos casos tienen que ver con la falta de un mecanismo de abstracción de intereses transversales. En
este trabajo se analizará el grado de avance en este sentido con la aplicación de la POA
como técnica de abstracción transversal.
4.1.2.
Descripción de patrones de diseño
Para la descripción de los patrones de diseño se analizaron las metodologías utilizadas por Gamma [Gam 95], Schmidt [Sch 00] y Powel Douglas [Dou 02]. Para este trabajo
se eligió una combinación de las metodologías antes mencionadas dado que el objetivo
principal no es analizar los beneficios del patrón en sí, sino explicar el uso del patrón y
analizar los beneficios o desventajas de la implementación orientada a aspectos. Las secciones que se describen por cada patrón son las siguientes:
 Resumen: Breve descripción del patrón que tiene como objetivo dar una idea resumida del propósito del mismo.
 Otras denominaciones: Otros nombres bajo los cuales se puede conocer el
patrón, en caso de existir.
 Problema: Descripción del conjunto de problemas que busca resolver el patrón de
diseño.
 Solución: Descripción de la solución que propone el patrón.
 Caso de estudio: Descripción del caso de estudio a analizar para el patrón. Este
caso de estudio es un ejemplo práctico de la aplicación del patrón, útil para describir el patrón de manera más sencilla y para poder desarrollar una implementación que será el elemento de análisis principal.
 Estructura: Diagrama UML de estructura en donde se describen los elementos
intervinientes en el patrón. Se da una breve explicación general del diagrama en
su totalidad. También se describen en detalle los participantes involucrados en el
diagrama de estructura.
 Estrategia de implementación orientada a aspectos: Se describen la estructura
general de la implementación orientada a aspectos junto con el detalle de cada
elemento interviniente en dicha implementación. La implementación estará en
46
Patrones de diseño distribuidos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
inglés porque actualmente es un estándar y para poder compartir dicha implementación con otros grupos de estudio.
 Análisis de la implementación: Se verifica en qué grado se cumple con las características a analizar en cada patrón. Para cada una de las características se da una
justificación del nivel encontrado.
 Componente gráfico: Detalle y descripción del componente gráfico desarrollado
con la ayuda del framework construido y explicación de su funcionamiento y características.
4.2.
Uso en sistemas distribuidos y concurrentes
Se dice que un sistema es concurrente cuando está compuesto por tareas que deben
interactuar y que son ejecutadas simultáneamente. Estas tareas pueden ser, por ejemplo,
procesos o hilos de ejecución. Cuando dichas tareas se ejecutan en más de una máquina,
las cuales se encuentran conectadas a través de una red, se dice que el sistema es distribuido.
Si el sistema está sujeto a restricciones temporales, se lo llama de tiempo real. Es
decir que las operaciones tienen límites de tiempo claramente especificados. Los sistemas
de tiempo real se dividir en dos tipos. En el primer caso, si las operaciones deben realizarse indefectiblemente dentro de un límite de tiempo prefijado, entonces se dice que es
un sistema de tiempo real duro. Un ejemplo de este tipo de sistemas es el cálculo de la
presión de frenado en un automóvil. La presión debe ser calculada a tiempo, para evitar el
peligro de colisión. En el segundo caso, si las restricciones, aunque son altamente deseadas, tienen un grado de flexibilidad, se dice que es un sistema de tiempo real blando. En
dicha categoría se encuentran los algoritmos de codificación y decodificación de audio y
video. Si se pierde una muestra, es probable que resulte imperceptible a la vista o audición humanas.
El objeto de este trabajo es analizar los patrones de diseño aplicado a entornos concurrentes y distribuidos, sin tener en cuenta el estudio de las restricciones temporales.
Dichos patrones se pueden clasificar en los siguientes grupos:
 Patrones de Concurrencia y Sincronización: Son los patrones destinados a resolver problemas que surgen en la sincronización de recursos compartidos y tareas
que interactúan concurrentemente. En estos ambientes se debe asegurar que la integridad de los datos de los recursos compartidos se garantice y que las tareas que
acceden o interactúan entre si no entren en deadlock o starvation. El primero de
los problemas ocurre cuando todas las tareas están esperando por algún evento sin
que ninguna de las partes pueda proseguir. El segundo inconveniente se produce
cuando una tarea espera por un recurso, que si bien es liberado, nunca le es asignado.
 Patrones de Manejo de Eventos: Son los patrones que permiten la comunicación
distribuida entre tareas utilizando el modelo de eventos. Los eventos permiten independizar el comportamiento y las responsabilidades de los participantes de un
sistema distribuido. Se utiliza especialmente al momento de interactuar con una
capa de transporte sincrónica. En el análisis previo de los patrones se detectó una
Diego M.S. Erdödy
Patrones de diseño distribuidos
47
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
similitud en el esquema de trabajo de los distintos patrones por lo que se eligió
analizar en profundidad el patrón más representativo del grupo.
 Patrones de Seguridad y Fiabilidad: Son los patrones encargados de la integridad de un sistema distribuido. Proveen soluciones para mantener la integridad del
sistema ante fallos, tanto aleatorios como sistemáticos. Los fallos aleatorios se basan generalmente en mal funcionamiento de hardware o condiciones externas. Los
sistemáticos en cambio, se deben habitualmente a errores en el diseño o construcción del sistema. Para poder mantener la integridad, estos patrones se basan en
proveer redundancia para actuar en caso de que ocurran los fallos. Dado que el
esquema que utilizan estos patrones es muy similar en todos los casos, se tomó un
patrón en representación del grupo.
Algunos de los patrones que se han elegido, son representantes de un grupo de patrones que poseen características similares. Los patrones analizados en este trabajo son:
Categoría
Concurrencia
y sincronización
Patrón
Rendezvous
Observer
Optimistic Locking
Balking
Manejo de
Eventos
Reactor
Seguridad y
Fiabilidad
Watchdog
48
Notas
Versión distribuida del patrón descripto por GoF
Patrones similares:
Guarded Suspension,
Single Threaded Execution
Patrones similares:
Proactor,
Acceptor,
Half Synch/ Half Asynch,
Leader/Followers
Patrones similares:
Monitor Actuator,
Sanity Check,
Safety Executive
Patrones de diseño distribuidos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
5. Metodología de Análisis de patrones
5.1.
Objetivos
El objetivo principal de este trabajo consta en analizar características específicas de
las implementaciones orientadas a aspectos de los distintos patrones de diseño distribuidos y concurrentes seleccionados y determinar las ventajas y desventajas de estas implementaciones. En este capítulo se describen los elementos a analizar en cada patrón de
diseño. También se especifica el lenguaje de modelado que se utilizará para describir la
implementación orientada a aspectos de cada caso de estudio, el cual estará basado en
UML [UML 06].
5.2.
Descripción de las características a analizar
Siguiendo el trabajo de Hannemann [Han 02], las características que se analizarán
en cada caso son las que se describen a continuación.
5.2.1.
Reusabilidad
Indica la posibilidad de reutilizar en otro entorno, el código fuente que compone el
patrón de diseño. En el patrón de diseño GoF “Facade” [Gam 95], por ejemplo, que consta de una clase que encapsula la complejidad interna de un componente para un usuario,
la reutilización es nula. Esto se debe a que la clase de fachada, tiene siempre lógica atada
al entorno en donde se está aplicando.
5.2.2.
Transparencia de composición
La transparencia de composición se define como la habilidad de combinar patrones
de diseño sin que ello afecte el comportamiento que presenta cada uno por separado. Por
ejemplo, el patrón “Observer”, puede ser aplicado más de una vez sobre el mismo sujeto,
para exponer cambios sobre distintas propiedades. La posibilidad de aplicar el patrón
varias veces sobre el mismo sujeto, prueba que cumple con la propiedad de transparencia
de composición.
5.2.3.
Independencia
El nivel de acoplamiento que hay entre los roles que componen el patrón de diseño
y las probabilidades de supervivencia que tiene cada rol fuera del patrón. Para el caso del
patrón “Singleton”, el objeto sobre el cual se aplica el patrón sigue teniendo sentido fuera
de éste. Si la implementación del patrón puede lograr dicho desacoplamiento de forma
transparente, para el objeto sobre el cual se aplicó, se dice que es independiente.
5.2.4.
Localidad del código
Diego M.S. Erdödy
Metodología de Análisis de patrones
49
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La proximidad física del código fuente que implementa el patrón de diseño. Si el
patrón de diseño puede ser implementado con una sola unidad de implementación dentro
del lenguaje elegido (por ejemplo un aspecto en el caso de AspectJ o una clase en Java)
entonces se dice que la localidad es absoluta. Si en cambio, el código se encuentra diseminado en varias unidades de implementación entonces la localidad es menor. Mientras
más unidades de implementación se encuentren involucradas, menor será el grado de localidad de la implementación del patrón. Para el caso de la implementación clásica del
patrón “Observer”, no existe localidad ya que la implementación se encuentra dividida en
más de un módulo (sujeto y observador).
5.3.
Notación UML
La notación de diseño que se utiliza es una extensión compatible con UML 1.2 basada en el trabajo hecho por Stein [Ste 02]. La extensión definida en dicho trabajo se basa
en utilizar las herramientas de personalización del lenguaje UML, es decir, estereotipos,
etiquetas y plantillas. La incorporación de comportamiento transversal, se lleva a cabo a
través de colaboraciones que permiten vincular ambas partes. Para algunos elementos,
como las declaraciones de derivación hubo que extender el trabajo y definir la forma de
representarlos. Dichos elementos serán explicados en las siguientes secciones.
Se elige adoptar una política basada en la notación UML existente principalmente
por la disponibilidad de herramientas para trabajar y la estandarización lograda por el
lenguaje de modelado.
Para poder hacer más legibles los diagramas, se utilizó un color distinto de relleno
para cada elemento que se encuentra al mismo nivel de una clase, dependiendo del estereotipo que posea. Los colores elegidos son los siguientes:




Naranja para las clases. Esta es la coloración estándar.
Verde para las interfaces.
Celeste para los aspectos.
Amarillo para las introducciones.
A continuación se define la notación utilizada en este trabajo, para cada elemento
de la POA. Los ejemplos han sido tomados de los patrones que serán analizados más adelante.
5.3.1.
Aspect
El aspecto se representa como el estereotipo « aspect », aplicable a elementos del
tipo “clase”. Tanto el aspecto como la clase, comparten gran parte de los atributos ya que
ambos pueden tener métodos, atributos y clases internas. Sin embargo, un aspecto posee
elementos adicionales tales como el pointcut, advice o introduction. Otra diferencia es
que a un aspecto se le puede indicar la política de instanciación requerida por medio de
una etiqueta de UML, la cual llamaremos “instantiation”. El valor de dicha etiqueta contiene la instrucción en AspectJ que indica el método de instanciación especificado. Para
50
Metodología de Análisis de patrones
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
hacer más visible la diferencia con una clase regular en un diagrama complejo, se utiliza
el color celeste de relleno.
El siguiente diagrama muestra un ejemplo de un aspecto llamado PacemakerWatchDogSignal con una política de instanciación prefijada (pertarget).
<<instantiation>>
<<aspect>>
PacemakerWatchDogSignal
{instantiation = pertarget(initCall(Resetable))}
<<base>> <<pointcut>>+initCall( resetable : Resetable ){base = execution (void Pacemaker+.init()) && target(r)}
<<base>> <<pointcut>>+watchableCall( r : Resetable ){base = execution (void Pacemaker+.markBeat()) && target(r)}
Figura 5-1: Ejemplo de notación UML para un Aspecto
5.3.2.
Pointcut
El aspecto es una clase de UML estereotipada. Por esa razón los puntos de corte se
modelarán como métodos de un aspecto, indicándolo por medio del estereotipo « pointcut
». Este estereotipo sólo se puede aplicar a métodos y el nombre del método deberá coincidir con el nombre del punto de corte.
Para puntos de corte concretos, el join point se indicará por medio de la etiqueta de
UML “base”. El siguiente diagrama contiene un aspecto llamado BalkingHandler, con
un ejemplo de un punto de corte concreto:
<<aspect>>
BalkingHandler
<<pointcut>> <<base>>+executionCall(){base = call (void Flusher.flush())}
Figura 5-2: Ejemplo de notación UML para un Pointcut
El punto de corte corresponde a la siguiente línea en AspectJ:
public pointcut executionCall() : call (void Flusher.flush());
Los puntos de corte abstractos se representarán de la misma forma que un método
abstracto (en UML se escriben en letra itálica) y sin utilizar la etiqueta “base”.
5.3.3.
Advice
Los elementos del tipo advice se representarán también con estereotipos aplicados a
métodos. El nombre del método en este caso corresponderá con el tipo de advice (after,
before, etc.) precedido opcionalmente por un identificador del advice. El identificador
surge de la necesidad de que no haya dos métodos con el mismo nombre. El punto de
Diego M.S. Erdödy
Metodología de Análisis de patrones
51
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
corte o la expresión compuesta por puntos de corte, será especificado como valor de la
etiqueta “base” del método.
El siguiente diagrama representa un ejemplo para este caso:
<<aspect>>
BalkingProtocol
-busy : boolean
<<pointcut>>+executionCall()
<<base>> <<advice>>+around(){base = executionCall()}
Figura 5-3: Ejemplo de notación UML para un Advice simple
La última línea indica que el aspecto BalkingProtocol posee un advice alrededor
del punto de corte executionCall(), sin especificar ningún parámetro. Esto corresponde a la
siguiente línea de AspectJ:
void around() : executionCall() { …
}
Si es necesario, se pueden agregar los argumentos del advice de la misma manera
que se haría con un método. En ese caso, se deben usar dichos argumentos dentro de la
expresión indicada en la etiqueta base. El siguiente diagrama representa un ejemplo de
este caso:
<<aspect>>
RendezvousProtocol
-rvs : Map<String, Rendezvous>
<<pointcut>>+synchronizingCall( memb er : RendezvousMemb er )
<<getter>>+getRendezvous( groupName : String ) : Rendezvous
<<setter>>+setRendezvous( groupName : String, rendezvous : Rendezvous ) : void
<<advice>> <<base>>~before( member : RendezvousMember ){base = synchronizingCall(member)}
Figura 5-4: Ejemplo de notación UML para un Advice con argumentos
El advice del tipo “before” de la última línea, utiliza un argumento llamado “member” que luego es usado como parámetro para el punto de corte synchronizingCall(), sobre
el cual se aplica el advice. Esto corresponde a la siguiente línea en AspectJ:
before(RendezvousMember o) : synchronizingCall(o) { … }
5.3.4.
Introduction
52
Metodología de Análisis de patrones
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Las introducciones de elementos en clases externas son los elementos con menor
soporte en la especificación actual de UML. Para poder representarlas se usan “colaboraciones” de UML. Por un lado se especifica sobre cuál de las clases se realiza la introducción, dentro del aspecto correspondiente, con una colaboración especializada con el estereotipo « introduction ». Dicha colaboración sólo contiene el nombre de la clase afectada.
Por otro lado se especifica una colaboración conteniendo el detalle de los elementos a
introducir sobre la clase afectada. Esto se hace agregando una clase dentro de la colaboración con el nombre BaseType que contendrá los métodos o atributos a introducir. Esta
colaboración debe estar asociada a la clase o interface que está afectando para dar más
claridad al diagrama.
El siguiente es un diagrama de estructura que contiene la especificación de una introducción:
<<aspect>>
RendezvousProtocol
-rvs : Map<String, Rendezvous>
<<pointcut>>+synchronizingCall( memb er : RendezvousMemb er )
<<getter>>+getRendezvous( groupName : String ) : Rendezvous
<<setter>>+setRendezvous( groupName : String, rendezvous : Rendezvous ) : void
<<advice>> <<base>>~before( member : RendezvousMember ){base = synchronizingCall(member)}
<<introduction>>
RendezvousMember
BaseType
<<introduction>>
RendezvousMember
-group : String
<<getter>>+getGroup() : String
<<setter>>+setGroup( groupName : String ) : void
<<crosscut>>
RendezvousMember
<<getter>>+getGroup() : String
Figura 5-5: Ejemplo de notación UML para una Introducción
En este ejemplo se está indicando que el aspecto RendezvousProtocol realiza una
introducción sobre la interface RendezvousMember. A esta interface, que sólo contiene el
método getGroup(), se le introduce un atributo privado llamado group y dos métodos, getGroup() y setGroup(). Nótese que originalmente la interface sólo contenía la declaración
del método getGroup() pero en la introducción se le está agregando la implementación del
método.
5.3.5.
Declare
Para representar un elemento del tipo declare, también se usa una colaboración pero
en este caso especializada con el estereotipo « declare » y se agrega el tipo de declaración que se esté utilizando (parents, warning, error, soft y precedence). Dentro de la colaboración, se introduce el cuerpo de la declaración. Dicha colaboración deberá ir dentro
del aspecto que especifique la declaración.
Diego M.S. Erdödy
Metodología de Análisis de patrones
53
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<<aspect>>
RendezvousHandler
<<pointcut>> <<base>>+synchronizingCall( member : RendezvousMember ){base = call (void RoboArm.executeAction()) && target(o)}
<<declare parents>>
RoboArm implements RendezvousMember
Figura 5-6: Ejemplo de notación UML para una declaración de implementación
El ejemplo anterior indica que dentro del aspecto RendezvousHandler se especifica
una declaración que indica que la clase RoboArm implementa la interface RendezvousHandler.
54
Metodología de Análisis de patrones
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
6. Framework de Visualización
6.1.
Introducción
Junto con el análisis de los distintos patrones de diseño distribuidos y concurrentes,
se ha desarrollado un framework de visualización de componentes para la mejor comprensión de los casos de estudio. Este framework permite visualizar, configurar y relacionar los componentes pertenecientes a cada patrón y al mismo tiempo analizar los resultados de su ejecución.
6.2.
Frameworks
Dada la dificultad que existe para definir un framework, se han tomado las siguientes definiciones complementarias [Joh 97] [Sch 03]:
I. “diseño reutilizable completo o parcial de un sistema representado por un
conjunto de clases abstractas y la forma en que interactúan”
II. “esqueleto de una aplicación que puede ser personalizado para cumplir con
los requerimientos de un sistema”
Un framework es una de las técnicas de reutilización que existen en la POO. La diferencia con otras técnicas, como los patrones de diseño, los componentes o un middleware, es el contenido que permite reutilizar y la forma de llevarlo a cabo.
La primera definición indica que un framework permite reutilizar el diseño. También nos indica que se puede llevar a cabo a partir de clases abstractas, interfaces y/o la
interacción que existe entre los distintos componentes o unidades funcionales. Un framework permite reutilizar el diseño aplicado a un dominio en particular, utilizando las técnicas básicas de la POO (abstracción, polimorfismo y herencia) y aplicando también las
unidades de diseño que le siguen en complejidad, o sea, los patrones de diseño. Una de
las herramientas principales que presenta un framework para la reutilización de diseño, es
la “inversión de control”. El mecanismo consiste en que los componentes provistos por el
usuario del framework, sean invocados por éste cuando sea necesario, en vez de producirse el flujo inverso. Dicha invocación se puede producir ante un evento externo, como por
ejemplo, la llegada de una petición HTTP en un framework de aplicaciones Web. De esta
forma, el modo en que el evento es manejado y redireccionado, es decir, el diseño del
manejo de peticiones, puede ser reutilizado y el usuario puede independizarse de su problemática.
La segunda definición, por otro lado, cubre intuitivamente el otro tipo de contenido
que un framework permite reutilizar, o sea, la funcionalidad o código. Esta funcionalidad
base o primaria es la que se implementa en el “esqueleto” que representa al framework,
junto con las bases de la interacción que debe existir entre los componentes y la forma en
que se deben distribuir los métodos.
Diego M.S. Erdödy
Framework de Visualización
55
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
En tercer lugar, un framework permite la reutilización del análisis del dominio, ya
que permite identificar rápidamente cuáles son los objetos importantes y provee una terminología para enfocar el problema.
Desde el punto de vista de la reutilización, existe el concepto clásico de que los patrones de diseño solamente permiten la reutilización del diseño de software. Con la introducción de la POA [Han 02], se ha demostrado que la reutilización de código a nivel de
patrón de diseño, es posible en la mayoría de los casos, por lo menos en los patrones tradicionales (GoF). Dado que una implementación AOP de un patrón de diseño se modulariza en una biblioteca totalmente reutilizable, es necesario marcar una diferencia entre
dicha implementación y un framework. En este sentido, los elementos que componen la
implementación abstracta de un patrón de diseño, difieren de un framework principalmente en tamaño (un framework está compuesto por muchos patrones de diseño) y en la
carencia de dominio específico.
Si bien un framework permite la reutilización de diversos elementos, el problema
típico que padece es la alta complejidad en el diseño e implementación. Esto es fácil de
comprender si se tiene en cuenta que el objetivo es que, tanto el diseño como la implementación, puedan ser reutilizados por diversos sistemas. Es por eso que a menudo existe
una fuerte decisión de compromiso entre la flexibilidad o generalidad y la verticalidad en
los problemas que soluciona. Es decir, que mientras el framework intente resolver más
detalles de un dominio, más difícil será hacerlo lo suficientemente flexible para que sea
adecuadamente reutilizado por distintas aplicaciones.
Los frameworks pueden ser categorizados según la forma en que se deben utilizar.
Si se pueden utilizar los componentes que brinda el framework sin necesidad de conocer
su implementación, entonces se los denomina del tipo “caja negra”. Si en cambio, el framework requiere que el usuario conozca y utilice elementos de la implementación, se los
conoce como del tipo “caja blanca”. En el primer caso, por ejemplo, solo sería necesario
instanciar clases y utilizar los objetos resultantes. Un ejemplo clásico del segundo son las
extensiones de clases abstractas por parte del usuario, en donde debe conocer cuál es el
mecanismo en la implementación de dicha clase, para poder insertar la nueva funcionalidad correctamente. Muchos de los frameworks se encuentran en un punto intermedio entre ambas categorías.
En resumen, los siguientes elementos han sido reconocidos como característicos de
un framework:
 Orientado a ayudar en un dominio en particular
 Representa una solución parcialmente completa para un dominio específico
que puede ser personalizada
 Permite la reutilización de diseño a partir de técnicas básicas de POO y patrones de diseño
 Permite la reutilización de funcionalidad
 Permite la reutilización de análisis del dominio
Una vez definido y analizado el concepto de framework, se describe el framework
desarrollado para este trabajo.
56
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
6.3.
Características de ACVF
El framework de visualización está implementado en forma de plug-in (o extensión)
de Eclipse 3.1+ y en esta sección se describen sus características principales.
6.3.1.
Orientación a Componentes
El framework desarrollado, se denomina “orientado a componentes” ya que el
componente es el elemento atómico fundamental, que representa visualmente un rol o un
actor dentro del caso de estudio. El segundo elemento importante dentro del framework,
es el diagrama, en donde los componentes pueden ser desplegados y asociados entre sí a
través de relaciones. Una vez desplegados, relacionados y configurados apropiadamente,
el diagrama puede ser “ejecutado” a través de una simulación, la cual mostrará a los
componentes interactuar entre sí.
El plug-in que se desarrolló como implementación del framework, posee una paleta
principal asociada al diagrama, que contiene una lista de los componentes disponibles,
agrupados por categorías, como muestra el ejemplo en la siguiente figura:
Figura 6-1: Paleta de componentes
Los componentes pueden ser arrastrados al diagrama para armar un entorno que represente el patrón a estudiar.
Cada componente puede tener asociados los siguientes tipos de elementos:
Diego M.S. Erdödy
Framework de Visualización
57
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 Propiedades comunes: Son atributos comunes para todos los componentes que
controlan la visualización básica. Se pueden observar y modificar en la vista de
propiedades de Eclipse.





Nombre: Nombre identificador del componente
Posición: Valores absolutos de las componentes X e Y dentro del diagrama.
Tamaño: Valores de ancho y alto del componente donde corresponda.
Color: Color de fondo del componente para hacer más sencilla la visualización.
Modo Interactivo: Propiedad booleana que especifica si el componente va
a ser ejecutado en modo interactivo o no. Más adelante se detalla el significado del modo interactivo.
Figura 6-2: Propiedades comunes a todos los componentes
 Propiedades propias del componente: Son propiedades inherentes al componente.
Estas se pueden dividir en dos tipos:

Estáticas: Atributos que controlan el comportamiento del componente y
son de escritura/lectura. Se visualizan dentro del panel de propiedades bajo
una agrupación particular en forma de pestaña. Puede haber más de una
agrupación. El siguiente es un ejemplo de las propiedades estáticas de un
componente del tipo “RoboArm”.
Figura 6-3: Propiedades particulares para el componente RoboArm
58
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA

Dinámicas: Propiedades de salida del componente cuya evolución se puede apreciar a lo largo de una simulación, interactiva o no, en tiempo real.
Se visualizan dentro del componente mismo. La siguiente figura muestra
un componente del tipo “OptimisticRecord” con tres propiedades dinámicas, denominadas “id”, “attrib” y “version”.
Figura 6-4: Ejemplo de un Componente
 Acciones: Son pedidos que se le pueden hacer al componente en el modo de simulación interactiva. Están representados por un botón dentro del componente. La
figura anterior muestra un componente con dos acciones denominadas “save” y
“retrieve”. Vale la pena aclarar que solo los componentes que posean acciones,
pueden ejecutarse en modo interactivo, ya que de lo contrario no habría forma de
interactuar con el componente.
 Consola: Panel dentro del componente en donde se muestran mensajes informativos de su estado. La siguiente figura muestra un ejemplo de información mostrada
en la consola de dos componentes:

Figura 6-5: Relaciones entre Componentes
 Representación gráfica: Cada componente puede reflejar su estado también por
medio de una representación gráfica. Dicha representación constituye un modelo
gráfico del componente que provee una respuesta visual a los cambios de estado y
hace al componente más intuitivo y didáctico.
Diego M.S. Erdödy
Framework de Visualización
59
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La siguiente es una vista de la edición de un diagrama con varios componentes interactuando entre sí.
Figura 6-6: Ejemplo de un diagrama con varios componentes y relaciones
6.3.2.
Relaciones
Cada componente interactúa con otros por medio de relaciones. Las relaciones se
indican con flechas dirigidas que unen componentes. Cada componente tiene habilitada la
interacción con un conjunto de tipos de componentes en particular, por lo general son los
que pertenecen al mismo grupo de la paleta de componentes. Esto quiere decir que un
componente puede relacionarse sólo con otro componente que sea de un tipo específico.
Por ejemplo, un registro optimista se puede relacionar con una tabla, pero no con un brazo robótico. Por otro lado, hay casos en que un componente puede relacionarse con más
de un componente. En otros casos la relación puede ser sólo uno a uno.
6.3.3.
Simulación
60
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
El objetivo del diagrama es que los componentes puedan ejecutarse, interactúen entre si y que dicha interacción sea visible. La simulación se inicia con el botón “Run” de la
barra de herramientas, que se habilita cuando se está editando un diagrama. Una vez iniciada la simulación, se puede detener con el botón “Stop”.
Cuando la simulación se está ejecutando, cada componente se comporta de la manera preestablecida a partir de los valores proporcionados para sus propiedades específicas.
Esto quiere decir que es en este momento en donde entra en juego la lógica propia de
cada componente.
Ciertos componentes tienen dos modos de actuar frente a una simulación: preestablecido o interactivo.
 En el modo preestablecido, el componente ejecuta la rutina que tiene prefijada y
el usuario sólo puede ver su comportamiento pero no puede interactuar con el
componente.
 En el modo interactivo el componente espera a que el usuario ejecute alguna de
sus acciones.
Sólo los componentes que tienen definidas acciones, tienen la propiedad “Interactive” en donde se puede definir el modo de ejecución.
Por ejemplo, para el caso de estudio del patrón “optimistic locking”, se dispone de
dos componentes: la tabla y el registro. Un registro se puede asociar a una tabla. La tabla
no posee modo de simulación interactiva dado que no posee acciones, sólo se puede visualizar el contenido. En cambio, el registro posee un modo interactivo ya que se puede
interactuar con el componente, pidiéndole por ejemplo que almacene su valor local en la
tabla asociado que lo obtenga de la tabla.
6.3.4.
Vista de resumen
El panel de vista de resumen (“outline” en inglés) provee un listado de los componentes existentes en el diagrama. La lista contiene el nombre de cada componente precedido por el tipo de componente. De esta forma se puede acceder rápidamente a un componente en particular haciendo doble click en el elemento deseado.
Figura 6-7: Vista de resumen
Diego M.S. Erdödy
Framework de Visualización
61
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
6.4.
Arquitectura y Diseño
El framework provee interfaces y clases de ayuda para la creación de componentes.
Las clases abstractas provistas encapsulan el comportamiento por defecto, con el objetivo
de minimizar la cantidad de código necesario para implementar un componente relativamente sencillo.
El framework está implementado sobre la base del subproyecto de Eclipse denominado GEF (Graphical Editing Framework). GEF es un framework que ayuda al desarrollo
de editores gráficos y a su vez, está basado sobre Draw2D. Este último es un estándar en
el modelado en dos dimensiones y representa una capa de abstracción sobre la biblioteca
de Eclipse denominada SWT (Standard Widget Toolkit).
La siguiente figura muestra un resumen de la interacción de las distintas capas y sus
funcionalidades principales:

CVF





GEF


Draw2D




SWT

Contiene el modelo de componentes e
interacciones
Soporta el uso de simulaciones
Incorpora figuras para visualización y manejo
de componentes
Implementa los patrones de diseño
distribuídos y concurrentes estudiados
Implementa MVC de editores gráficos
Se integra con el Workbench de Eclipse
(acciones, menúes, barra de herramientas,
etc).
Permite la interacción entre el usuario y la
representación visual del modelo
Responsable del renderizado de figuras y
conexiones
Permite el control de presentación de figuras
Posee un sistema de coordenadas flexible
Permite el ajuste de tamaño
Biblioteca nativa para interfaces gráficas de
la plataforma Eclipse
Utiliza los controles nativos del sistema
operativo siempre que sea posible
Figura 6-8: Capas de la arquitectura del Framework y sus características principales
GEF implementa el patrón Model-View-Controller para el desarrollo de editores
gráficos y por ende permite una clara separación de responsabilidades. También permite
62
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
una integración con el Workbench de Eclipse lo que posibilita el manejo de diagramas
dentro de un entorno integrado.
6.4.1.
GEF Framework
GEF pone énfasis en desacoplar las distintas capas de un editor gráfico y brinda especial soporte para la implementación del controlador. En GEF, el controlador es el encargado, entre otras cosas, de hacer de intermediario entre la vista y el modelo. El esquema de trabajo de alto nivel es el siguiente:
Vista
(IFigure)
GUI
Usuario
Notificaciones
Modelo
(BaseElement)
Comandos
Controlador
(EditPart)
Figura 6-9: Interacción entre las distintas capas MVC del framework
A continuación se detalla cada una de las capas:
Modelo
El Modelo consta de la información de base que interesa almacenar en un editor. La
información que contenga el Modelo es la que va a ser persistida al momento de guardar
o cerrar un editor y con la que se contará al momento de abrirlo.
Si bien GEF no pone restricciones en cuanto a las interfaces o clases abstractas que
debe extender el Modelo, es necesario que dicho Modelo cuente con un mecanismo para
notificar adecuadamente sus cambios a terceras partes. Este mecanismo es necesario para
que el Controlador pueda actuar al momento de registrar un cambio en el Modelo.
Dado que GEF no suministra ninguna clase base para el armado del modelo, se confeccionó una clase abstracta denominada BaseElement, la cual brinda las características
principales comunes para todos los elementos del modelo. Una de las características más
importantes, es la posibilidad de registrarse para ser notificado ante el cambio de cualquiera de las propiedades del elemento, como se verá más adelante.
Diego M.S. Erdödy
Framework de Visualización
63
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Los cambios realizados sobre el modelo por parte del Controlador (generalmente a
pedido del usuario) son representados por Comandos. Los comandos, sólo deben conocer
la existencia del modelo (es por eso que se pueden considerar una extensión de éste), y
deben contener la lógica para efectuar o deshacer los cambios requeridos por el Controlador.
Vista
La Vista compone la capa visual del editor. Dada la gran separación entre capas que
propone GEF, la vista no puede conocer o hacer referencia a ningún objeto del modelo, ni
tampoco tener lógica asociada al editor. Sí debe contener información propia de la representación visual de los elementos que se pretenden graficar.
GEF utiliza Draw2D como capa visual base y en ella, el elemento atómico es la figura, representada por la interface IFigure. Draw2D provee una amplia gama de figuras
elementales prediseñadas y la composición entre ellas permite un amplio espectro de posibilidades con un mínimo esfuerzo. Por lo general, un elemento del modelo tiene una
asociado una figura de la Vista, aunque no es una limitación del framework.
Controlador
El Controlador tiene el papel de mediador entre el usuario, la vista y el modelo. En
GEF, el controlador es llamado EditPart.
Principalmente, actúa de observador del modelo, reflejando los cambios en la vista
cuando es necesario. Por lo general existe un EditPart por cada elemento del modelo
(aunque no es una limitación), y entre los distintos EditParts se forma una estructura de
árbol, siendo el nodo raíz el correspondiente al diagrama.
Los EditParts también son los encargados de administrar los pedidos de cambio del
usuario, denominados Requests, y transformarlos en Comandos. Por ejemplo para el tipo
de pedido “delete” (borrar), la política de edición deberá identificar el tipo elemento a
borrar y crear el comando de borrado, correspondientemente. La administración también
consta de aceptar, ignorar o rechazar los Requests, a través de clases que encapsulan las
políticas de edición.
6.4.2.
ACVF Framework
El ACVF está basado en GEF, y por lo tanto provee extensiones de cada una de las
capas MVC.
Modelo
El modelo de ACVF consta de una clase abstracta base que contiene el comportamiento común a todos los elementos, como el soporte de propiedades, ser “serializable” y
la habilidad de ser “observable”. Luego existe un elemento raíz, que representa el diagrama y que contiene una lista de los elementos que lo componen. Estos elementos pueden ser Componentes o Conexiones. Vale la pena aclarar que el framework deja abierta la
64
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
posibilidad de incorporar nuevos tipos de elementos en caso de querer extender el editor,
como por ejemplo, etiquetas de texto, figuras para dibujo libre, etc.
El siguiente es el diagrama de estructura con una descripción de los elementos principales:
BaseElement
...
+addPropertyChangeListener( l : PropertyChangeListener ) : void{guarded}
<<getter>>+getEditableValue() : Object
+removePropertyChangeListener( l : PropertyChangeListener ) : void{guarded}
<<setter>>+setPropertyValue( id : Object, value : Object ) : void
<<getter>>+getPropertyDescriptors() : IPropertyDescriptor"[]"
<<getter>>+getPropertyValue( id : Object ) : Object
+resetPropertyValue( id : Object ) : void
<<getter>>+isPropertySet( id : Object ) : boolean
...
Connection
Element
PatternWorkbenchDiagram
<<constructor>>+Connection( source : Element, target : Element )
+disconnect() : void
<<getter>>+getLineStyle() : int
<<getter>>+getSource() : Element
<<getter>>+getTarget() : Element
+reconnect() : void
+reconnect( newSource : Element, newTarget : Element ) : void
<<setter>>+setLineStyle( lineStyle : int ) : void
<<setter>>+setPropertyValue( id : Object, value : Object ) : void
<<getter>>+getPropertyValue( id : Object ) : Object
<<getter>>+getIcon() : Image
<<getter>>+getLocation() : Point
<<getter>>+getPropertyDescriptors() : IPropertyDescriptor"[]"
<<getter>>+getPropertyValue( propertyId : Object ) : Object
<<getter>>+getSize() : Dimension
<<getter>>+getSourceConnections() : List<E->Connection>
<<getter>>+getTargetConnections() : List<E->Connection>
<<setter>>+setPropertyValue( propertyId : Object, value : Object ) : void
<<getter>>+getName() : String
<<setter>>+setName( name_ : String ) : void
<<setter>>+setColor( newColor : RGB ) : void
<<getter>>+getColor() : RGB
<<setter>>+setLocation( newLocation : Point ) : void
<<setter>>+setSize( newSize : Dimension ) : void
+addChild( elem : Element ) : boolean
<<getter>>+getChildren() : List<E->Element>
+removeChild( s : Element ) : boolean
...
ComponentElement
<<constructor>>+ComponentElement( pc : PatternComponent )
<<getter>>+getThreadGroup() : ThreadGroup
<<JavaElement>> <<getter>>+getPropertyDescriptors() : IPropertyDescriptor"[]"{JavaAnnotations = @Override}
<<getter>>+getIcon() : Image
+preStart() : void
+start() : void
+stop() : void
<<getter>>+getTypeName() : String
<<getter>>+getAllowedSources()
<<getter>>+getComponent() : PatternComponent
<<getter>>+getRunnable() : ComponentRunnable
+toString() : String
<<JavaElement>> <<getter>>+getPropertyValue( propertyId : Object ) : Object{JavaAnnotations = @Override}
<<JavaElement>> <<setter>>+setPropertyValue( propertyId : Object, value : Object ) : void{JavaAnnotations = @Override}
<<setter>>+setRunning( value : boolean ) : void
<<getter>>+getRunning() : boolean
<<setter>>+setStatus( value : String ) : void
<<getter>>+getStatus() : String
<<setter>>+setInteractive( value : boolean ) : void
<<getter>>+getInteractive() : boolean
...
Figura 6-10: Capa de Modelo de ACVF
 BaseElement: Clase abstracta que contiene la funcionalidad común de todos los
elementos del modelo como el soporte de propiedades, de ser observable y la capacidad de ser serializable para poder mantener un estado persistente. Los métodos que provee la clase son:
 addPropertyChangeListener(), removePropertyChangeListener(): Permiten agregar o
quitar un “Observador” de los cambios de valores de las propiedades del elemento, a través de la interface PropertyChangeListener, que contiene un
método para informar dicho tipo de eventos.
 getPropertyValue(), setPropertyValue(), getPropertyDescriptors(), isPropertySet(), resetPropertyValue(): Métodos asociado al manejo de propiedades de los elemen-
Diego M.S. Erdödy
Framework de Visualización
65
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
tos (obtener el valor de una propiedad determinada, cambiar el valor, obtener
la lista de descriptores de propiedades, consultar si la propiedad tiene algún
valor y limpiar el valor de una propiedad, respectivamente). Si bien esta clase
no define ningún tipo de propiedades, implementa los métodos con valores por
defecto para poder ser referenciados por las clases derivadas que componen el
modelo.
 PatternWorkbenchDiagram: Elemento raíz del modelo que actúa de contenedor
del resto de los elementos. Los métodos provistos son:
 addChild(), removeChild(), getChildren(): Métodos de acceso a los elementos hijo
del modelo. Permiten agregar, quitar u obtener los hijos, respectivamente.
 Connection: Representa una conexión dirigida entre dos elementos del modelo, el
origen (source) y el destino (target). Posee además una propiedad que representa
el tipo de conexión que se visualizará (sólida, punteada, etc.). Los métodos principales son:
 Connection(): La conexión debe construirse indicando los elementos origen y
destino.
 disconnect(), reconnect(): Permiten indicar a los elementos que están asociados
por esta conexión, que dicho vínculo debe eliminarse o volverse a establecer.
 getSource(), getTarget(): Métodos de acceso a los elementos origen y destino.
 Element: Clase abstracta que incorpora el comportamiento de un elemento del
modelo genérico, visible gráficamente. Cuenta con referencias a las conexiones de
las que participa y métodos para acceder a dichas conexiones. Además posee propiedades y métodos de acceso para los siguientes atributos:
 height, width: Dimensiones del elemento, en alto y ancho.
 xpos, ypos: Posición del elemento dentro del diagrama (coordenadas X e Y).
 color: Color distintivo del elemento (para distinguir los elementos del mismo
tipo de una manera sencilla y visual).
 instanceName: Nombre del elemento que lo identificará en el diagrama.
 ComponentElement: Elemento del modelo principal de ACVF que representa un
componente. Cuenta con las siguientes propiedades:
 running: Indica que el componente se encuentra ejecutando la simulación.
 status: Texto que muestra el estado en el componente.
 interactive: Modo en que se ejecutará la simulación.
Además, posee los siguientes métodos:
66
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 getComponent(): Devuelve la instancia de la clase PatternComponent asociada a
este componente. Dentro de dicha instancia se almacena la información del
componente, provista por el usuario del framework.
 getRunnable(): Devuelve el objeto de simulación que se ejecutará cuando se inicie la simulación del diagrama.
 getTypeName(): Indentificador único del componente.
 getAllowedSources(): Devuelve una lista con los tipos de componentes que se
pueden conectar con este componente.
 getIcon(): Obtiene el icono a utilizar en las representaciones visuales del componente.
 start(): Inicia el flujo de simulación para este componente.
 preStart(): Preinicialización del componente, inmediatamente previo al inicio
de la simulación. La tarea típica de preinicialización de un componente es crear la instancia del objeto de simulación.
 stop(): Detiene la ejecución de la simulación para este componente.
Más adelante se explica en detalle el ciclo de vida de una simulación, resumido en
un diagrama de secuencia.
Vista
La vista es la capa responsable de representar visualmente el estado del modelo.
Debe ser actualizada cada vez que el modelo cambie, ya sea por acción de la interacción
con el usuario o por cambios internos. La vista no debe almacenar información sobre el
modelo y solamente debe poseer el comportamiento asociado a la visualización.
Para la implementación del framework, la vista se compone de dos elementos principales: los componentes y las conexiones entre ellos. Los componentes se representan
con figuras similares a las de una clase en UML y se dividen en secciones denominadas
compartimientos. Los compartimientos disponibles para un componente son:
Encabezado del componente (Color,
Tipo y nombre de instancia)
Figura personalizada del componente
Estado del componente
componente
Propiedades dinámicas del componente
componente
Acciones del componente
Figura 6-11: Compartimientos del componente
Diego M.S. Erdödy
Framework de Visualización
67
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
El siguiente es un diagrama de estructura que representa el diseño de la vista utilizado y una descripción de su composición:
IFigure
(org.eclipse.draw2d)
Figure
(org.eclipse.draw2d)
ComponentFigure
CompartmentFigure
~layout : ToolbarLayout = new ToolbarLayout()
<<constructor>>+CompartmentFigure()
<<setter>>+setAlign( al : int ) : void
<<setter>>+setMsgEnabled( value : boolean ) : void
<<setter>>+setStatus( st : String ) : void
+addProperty( msg : Method ) : void
<<setter>>+setPropertyValue( prop : String, newValue : Object, oldValue : Object ) : void
<<JavaElement>> <<setter>>+setBackgroundColor( bg : Color ) : void{JavaAnnotations = @Override}
<<constructor>>+ComponentFigure( name : Label, custFig_ : IFigure )
+addMessage( msg : Method, actList : ActionListener ) : void
...
Figura 6-12: Capa de Vista de ACVF
 IFigure, Figure: Interface que define el comportamiento de una figura en
Draw2D y su implementación abstracta.
 CompartmentFigure: Figura que representa un compartimiento dentro del
rectángulo principal. Es muy similar a la representación en UML de una clase, en
la cual un compartimiento agrupa elementos de naturaleza semejante, como ser
atributos o acciones del componente.
 ComponentFigure: Figura que representa un componente. Posee los siguientes
métodos:
 ComponentFigure(): Constructor de la figura. Debe recibir como parámetros, el
nombre del componente junto con la figura personalizada que lo representa.
 setMsgEnabled(): Indica el estado en que deben estar los botones que representan las acciones. El estado puede ser “habilitado” o “deshabilitado”.
 setStatus(): Especifica un mensaje de estado pasado como parámetro de texto,
que se debe mostrar dentro del componente.
 addProperty(): Agrega una propiedad al compartimiento correspondiente.
 setPropertyValue(): Especifica el valor que debe tener una propiedad previamente adicionada.
 addMessage(): Agrega una acción al componente. Los parámetros que requiere
son el método a ejecutar y el observador que se encargará de ejecutar dicho
método cuando se dispare la acción.
 setBackgroundColor(): Establece el color de fondo del componente.
Controlador
68
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La capa del controlador de ACVF consta básicamente de un EditPart por cada elemento del modelo. Para poder actuar en caso de que alguna de las propiedades de los
elementos del modelo cambie, los EditParts implementan la interface PropertyChangeListener para poder registrarse como observadores de dichas propiedades. La interface
PropertyChangeListener forma parte de la biblioteca integrada de Java y posee un método
“propertyChange” que es llamado por el objeto observable cada vez que se produce un
cambio sobre los atributos observados, para poder notificar a los observadores. El diagrama de estructura y el detalle de los elementos es el siguiente:
AbstractGraphicalEditPart
ElementEditPart
AbstractConnectionEditPart
DiagramEditPart
+activate() : void
#createEditPolicies() : void
#createFigure() : IFigure
+deactivate() : void
<<getter>>-getCastedModel() : PatternWorkbenchDiagram
<<getter>>#getModelChildren() : List
+propertyChange( evt : PropertyChangeEvent ) : void
ConnectionEditPart
+activate() : void
#createEditPolicies() : void
#createFigure() : IFigure
+deactivate() : void
<<getter>>-getCastedModel() : Connection
+propertyChange( event : PropertyChangeEvent ) : void
<<getter>>#getConnectionAnchor() : ConnectionAnchor
+activate() : void
#createEditPolicies() : void
#createFigure() : IFigure
-createFigureForModel() : IFigure
+deactivate() : void
<<getter>>-getCastedModel() : ComponentElement
<<getter>>#getModelSourceConnections() : List
<<getter>>#getModelTargetConnections() : List
<<getter>>+getSourceConnectionAnchor( connection : ConnectionEditPart ) : ConnectionAnchor
<<getter>>+getSourceConnectionAnchor( request : Request ) : ConnectionAnchor
<<getter>>+getTargetConnectionAnchor( connection : ConnectionEditPart ) : ConnectionAnchor
<<getter>>+getTargetConnectionAnchor( request : Request ) : ConnectionAnchor
+propertyChange( evt : PropertyChangeEvent ) : void
-refreshProperty( prop : String, newValue : Object, oldValue : Object ) : void
<<getter>>-getCastedFigure() : ComponentFigure
#refreshRunning() : void
#refreshStatus() : void
#refreshVisuals() : void
<<getter>>+getColor( rgb : RGB ) : Color
+actionPerformed( event : ActionEvent ) : void
PropertyChangeListener
+propertyChange( event : PropertyChangeEvent ) : void
Figura 6-13: Capa de Controlador de ACVF
 AbstractGraphicalEditPart, AbstractConnectionEditPart: Clases abstractas que
implementan la funcionalidad común de un EditPart gráfico en general y de conexión en particular, respectivamente. Los métodos principales que provee son:
 activate(): Indica que el controlador ha sido activado y permite llevar a cabo tareas de inicialización como la registración en calidad de observador al elemento del modelo correspondiente.
 deactivate(): Marca la finalización del ciclo de vida del controlador, para poder
realizar tareas de limpieza.
 createEditPolicies(): Se crean las políticas de edición, que son básicamente las
encargadas de convertir los pedidos del usuario (requests) en comandos entendibles por el modelo.
 createFigure(): Método destinado a la construcción de la figura que representará
gráficamente al componente.
Diego M.S. Erdödy
Framework de Visualización
69
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 DiagramEditPart: Controlador que representa al diagrama en su totalidad. Juega
el papel de nodo raíz en la jerarquía de controladores y brinda la política de edición que permite resolver los distintos tipos de elementos del diagrama. Agrega
los siguientes métodos, aparte de la personalización de los provistos por las clases
base:
 getModelChildren(): Permite acceder a los elementos del modelo del primer nivel, por debajo del nodo raíz.
 getCastedModel(): Obtiene la referencia al elemento raíz del modelo transformado al tipo correcto.
 ElementEditPart: Controlador principal del framework, encargado de la mediación entre la capa de vista y modelo de cada componente del diagrama. Los métodos adicionales provistos son:
 getModelSourceConnections(), getModelTargetConnections(): Métodos para acceder
a la lista de conexiones entrantes o salientes del componente, respectivamente.
 getSourceConnectionAnchor() y derivados: Especifica el tipo de terminación que
tendrá la conexión.
 propertyChange(), refresh*(): Método que indica que una propiedad ha cambiado
en el componente asociado. Para actualizar la capa visual de los tipos de propiedades conocidos se invoca al método refresh correspondiente.
 actionPerformed(): Ejecuta la acción especificada sobre el componente.
 ConnectionEditPart: Controlador para las conexiones entre componentes.
Componentes
La capa de componentes brinda la funcionalidad común a la administración de los
componentes. Algunos ejemplos de las dicha funcionalidad son: el descubrimiento de
propiedades dinámicas y acciones, vinculación automática de las propiedades, manejo del
ciclo de vida de la simulación y ayuda para la representación gráfica.
También establece una interface con la información que necesita de cada componente adicional que se desee agregar al framework.
El diagrama de estructura y la descripción de los elementos principales es el siguiente:
70
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
PatternComponent
<<getter>>+getType() : String
<<getter>>+getRunnab leClass() : Class<T->ComponentRunnab le>
+createRunnab le() : ComponentRunnab le
+postCreate( runnab le : ComponentRunnab le ) : void
<<getter>>+getLinkSources()
<<getter>>+getLinkDestinations()
<<getter>>+getActions() : List<E->Method>
<<getter>>+getPropertyDescriptors() : IPropertyDescriptor"[]"
<<getter>>+getPropertyValue( propertyId : Ob ject ) : Ob ject
<<setter>>+setPropertyValue( propertyId : Ob ject, value : Ob ject ) : void
<<getter>>+getFigure() : IFigure
<<getter>>+getIconSmall() : ImageDescriptor
<<getter>>+getIconLarge() : ImageDescriptor
<<setter>>+setElement( element : ComponentElement ) : void
<<introduction>>
ComponentRunnable
BaseType
-display : Display
-element : ComponentElement
+getDisplay() : Display
+setDisplay( display : Display )
+getElement() : ComponentElement
+setElement( element : ComponentElement )
+writeAsync( text : String )
AbstractComponent
ComponentRunnable
<<getter>>+getLinkSources()
<<getter>>+getLinkDestinations()
<<setter>>+setElement( element_ : ComponentElement ) : void
<<getter>>+getElement() : ComponentElement
<<getter>>+getFigure() : IFigure
<<getter>>#getTargets( type : String ) : Set<E->ComponentElement>
<<getter>>#getSources( type : String ) : Set<E->ComponentElement>
+postCreate( runnable : ComponentRunnable ) : void
+propertyChange( arg0 : PropertyChangeEvent ) : void
+createRunnable() : ComponentRunnable
<<getter>>+getActions() : List<E->Method>
Component1
Component2
...
Component N
+stopComponent() : void
<<setter>>+setDisplay( display : Display ) : void
<<getter>>+getDisplay() : Display
<<setter>>+setElement( element : ComponentElement ) : void
<<getter>>+getElement() : ComponentElement
<<aspect>>
ComponentRunnableImpl
...
<<introduction>>
ComponentRunnable
Component1 UI
Component2 UI
ComponentN UI
Figura 6-14: Capa de Componentes de ACVF
 PatternComponent: Es la interface que define las operaciones que debe implementar un componente para cumplir con los requerimientos del framework. Los
métodos necesarios son:
 getType(): Debe proveer la identificación única del tipo de componente, en
forma de texto.
 getRunnableClass(): Especifica la clase que hará de objeto ejecutable durante la
simulación y se encargará de representar visualmente el estado a lo largo del
tiempo. Dicha clase debe implementar la interface ComponentRunnable.
 createRunnable(): Método encargado de crear el objeto de ejecución del componente.
 postCreate(): Método que se ejecuta una vez que ha sido creado el objeto ejecutable e inmediatamente posterior a iniciar la simulación.
 getLinkSources(): Provee un grupo de tipos de componentes que pueden conectarse como origen a este componente.
 getLinkDestinations(): Provee un grupo de tipos de componentes que pueden conectarse como destino a este componente.
 getActions(): Especifica el conjunto de acciones que se pueden ejecutar sobre
este componente.
 getPropertyDescriptors(): A través de este método se especifican las propiedades
que publicará el componente.
 getPropertyValue(): Método para obtener el valor de una propiedad publicada.
 setPropertyValue(): Método para establecer el valor de una propiedad publicada.
 getFigure(): Especifica la figura que representará visualmente al componente.
Diego M.S. Erdödy
Framework de Visualización
71
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 getIconSmall(): Representación iconográfica del componente, en su versión pequeña. Se utiliza para mostrar en las listas como, por ejemplo, la paleta o el
sumario.
 getIconLarge():Representación iconográfica del componente, en su versión aumentada. Se utiliza para mostrar en el diagrama.
 setElement(): Le asigna el elemento de la capa de modelo, para poder obtener
información como, por ejemplo, los componentes conectados.
 AbstractComponent: Implementa las operaciones básicas de un componente
haciendo mucho más sencilla la tarea de crear un nuevo componente.
 getLinkSources(): Devuelve un grupo vacío.
 getLinkDestinations(): Devuelve un grupo vacío.
 setElement(): Asigna el elemento de modelo a un atributo protegido para poder
ser usado por las clases derivadas.
 getElement(): Acceso al elemento de modelo.
 getFigure(): Devuelve la figura por defecto.
 getTargets(),getSources(): Devuelve una lista de los elementos del modelo que
actúan en una conexión como destino u origen, respectivamente,.
 postCreate(): Por defecto, especifica el atributo “element” en el objeto de simulación. También asigna el “display” activo, para poder utilizarlo dentro de la
simulación al hacer llamadas asíncronas, ya que se deben correr en un hilo de
ejecución distinto al principal.
 propertyChange(): Implementación vacía del método, en el caso que no se requieran propiedades.
 createRunnable():Instancia un nuevo objeto de la clase devuelta por getRunnableClass() llamando al constructor por defecto (sin argumentos).
 getActions(): Por defecto, devuelve la lista de métodos de la clase definida por
getRunnableClass() que están modificados con la anotación “Action”.
 ComponentRunnable: Es la interface que especifica la interacción con los objetos
que implementarán el comportamiento dinámico del componente, utilizado en el
momento de la simulación.
 stopComponent(): Método para poder detener el objeto de simulación.
 setElement(), getElement(): Permite especificar u obtener el objeto del modelo
asociado al componente.
 setDisplay(),getDisplay(): Permite especificar u obtener el “display” a utilizar para realizar las operaciones que impliquen cambios visuales.
 ComponentRunnableImpl: Es el aspecto que implementa la interface ComponentRunnable. De esta forma se utiliza el mecanismo que provee AOP para poder
asociar comportamiento directamente a una interface. Es un mecanismo similar a
la herencia múltiple pero implementado de forma más natural.
72
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Anotaciones Utilizadas
El ACVF utiliza anotaciones de Java 1.5 para identificar de forma clara, ciertos
atributos de los componentes. Las anotaciones introducidas por ACVF son:
 Action: Se aplica a los métodos de los objetos de simulación (los que implementan ComponentRunnable) que se deseen publicar como acciones del
componente. El método getActions() de la clase AbstractComponent, obtiene
los métodos que estén marcados con esta anotación y los devuelve para ser
incluidos en el compartimiento correspondiente de la representación gráfica.
 DynamicProperty: Se aplica a los métodos del tipo “setter” de los objetos de
simulación cuyo atributo se desee publicar dinámicamente. Esto implica que
el atributo aparecerá en el compartimiento de propiedades dinámicas de la
vista y su valor podrá ser seguido visualmente en tiempo de simulación.
 Property: Diseñado para ser aplicado a los atributos del componente que se
deseen mostrar en el panel de propiedades. Dichos atributos componen la
configuración del componente. Posee los siguientes argumentos:
o name: identificador único interno de la propiedad.
o description: descripción a mostrar en el panel de propiedades
o caption: nombre de la propiedad
o category: nombre de la categoría del panel de propiedades en donde
se ubicará la propiedad.
Interacción de las distintas capas en la ejecución de un Componente
En el siguiente diagrama se muestra la interacción entre los distintos elementos de
cada capa MVC, para el movimiento de un componente dentro de un diagrama.
MODELO
GEF
: ElementXYLayoutEditPolicy
CONTROLADOR
GEF
: Element
VISTA
: ElementEditPart
: ComponentFigure
1: createChangeConstraintCommand(child=, constraint=)
2: (Element, )
: ElementSetConstraintCommand
3: execute()
4: redo()
5: setSize(newSize=)
6: firePropertyChange(property=, oldValue=, newValue=)
7: propertyChange(evt=)
8: refreshVisuals()
9: setBounds(rectangle=)
10: setLocation(newLocation=)
Figura 6-15: Diagrama de secuencia para el movimiento de un componente
Diego M.S. Erdödy
Framework de Visualización
73
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Los pasos se pueden dividir en dos grupos, por un lado la creación del comando,
que se realiza al iniciar el movimiento del componente dentro del diagrama (pasos 1 y 2),
y por otro lado la ejecución del comando, realizada al “soltar” el componente (pasos del 3
al 10). A su vez, los elementos que intervienen en la interacción se dividen en tres grupos
correspondientes a las distintas capas MVC, como lo indica el diagrama.
1) El framework GEF, invoca a la política de edición (EditPolicy) provista por
ACVF, requiriéndole la creación de un comando del tipo “ChangeConstraint”, correspondiente al pedido del usuario de cambiar la posición del
componente.
2) El EditPolicy crea el comando correspondiente, pasándole como parámetro
una referencia del elemento en cuestión.
3) Un vez establecido el nuevo lugar del componente (cuando el usuario lo
“suelta” dentro del diagrama, el framework GEF envía un mensaje al comando para que se ejecute.
4) Internamente, el comando invoca al método “redo” que contiene la lógica de
ejecución del comando.
5) El comando le informa el nuevo tamaño al elemento, a través del mensaje
setSize().
6) El cambio en el valor de la propiedad del elemento, dispara la notificación
de todos los observadores registrados al elemento.
7) El EditPart asociado al elemento, al ser uno de los observadores, es notificado del nuevo valor a través del mensaje propertyChange().
8) El EditPart, al enterarse del cambio de valor, invoca al método que informará a la capa visual de dicho cambio, llamado refreshVisuals().
9) El controlador informa el cambio a la figura que representa el componente,
enviando el mensaje setBounds()
10) El proceso se repite para el atributo “location”.
A continuación se describe la interacción que se produce al momento de iniciar una
simulación dentro del diagrama ACVF:
74
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
user : User
action : RunAction
editor : PWEditor
model : ComponentElement
Para todos los componentes
del diagrama
component : PatternComponent
1: run()
2: startCycle()
loop
[]
3: preStart()
4: createRunnable()
5: getRunnableClass()
6:
runnable : ComponentRunnable
Un thread por
cada
componente
7: start()
8: postCreate(runnable=)
par
9:
runnableThread : Thread
[]
10: start()
11: run()
Figura 6-16: Diagrama de secuencia para el inicio de la simulación
1) El usuario hace click en el botón de “Run” de la barra de herramientas para
iniciar la simulación.
2) La acción asociada al botón “Run” obtiene una referencia al editor y le envía un pedido de inicio de simulación.
3) El editor, itera sobre todos los elementos hijos del modelo del tipo ComponentElement y les envía un mensaje de preinicialización.
4) En este paso, el elemento del modelo invoca al componente para crear el objeto de simulación.
5) Internamente, la instancia del componente, obtiene la clase que va a representar al objeto de simulación (patrón Template Method).
6) El objeto de simulación es instanciado, utilizando el mecanismo de reflexión.
7) Nuevamente el editor itera sobre todos los elementos hijos del modelo del
tipo ComponentElement y les envía un mensaje de inicio de simulación.
8) El componente envía un mensaje de postCreate() al objeto de simulación para
que pueda inicializar sus valores, por ejemplo, a partir de componentes conectados.
9) 10) 11) Por último, se crea un nuevo hilo de ejecución para correr la simulación para ese componente y se inicia. De esta forma cada componente del
diagrama, se corre simultáneamente, en un hilo de ejecución distinto. Internamente, el hilo llama al método run() de la interface Runnable que implementa el objeto de simulación.
Diego M.S. Erdödy
Framework de Visualización
75
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
6.5.
Otros Frameworks de Visualización
JHotDraw [JHD 06] es un framework de visualización implementado en Java y
orientado a diagramas técnicos y estructurados. Fue desarrollado originalmente por Erich
Gamma y Thomas Eggenschwiler como un caso de estudio para mostrar la aplicación de
los patrones de diseño.
Si bien la versión original estaba basada en la biblioteca de gráficos AWT, las nuevas versiones incorporaron la biblioteca más moderna Swing. JHotDraw está basado en
dos proyectos, HotDraw, un framework de visualización implementado en SmallTalk, y
E++, un framework de aplicaciones hecho en C++.
El framework soporta el uso de una barra de herramientas, diferentes vistas, figuras
personalizadas y manejo de persistencia de diagramas (guardado, carga e impresión). La
arquitectura del framework, al igual que GEF y ACVF está basada en el patrón MVC
brindado especial soporte en la capa de control. La estructura básica del framework es la
siguiente:
Figura 6-17: Diagrama de estructura básico del framework de visualización JHotDraw
En JHotDraw las aplicaciones están compuestas por un diagrama (Drawing), una
vista del diagrama (DrawingView), un conjunto de figuras (Figure) y al menos una
herramienta (Tool). Las herramientas permiten ejecutar acciones sobre el diagrama, como
por ejemplo, crear una figura. Por último, los Handles son porciones de la figura que
permiten interactuar con la misma, por ejemplo para agregar una conexión.
Si bien técnicamente es un producto bien construido y las características son similares a las provistas por GEF, las desventajas que se le encontraron, por las cuales no fue
utilizado para la implementación de ACVF son:
 JHotDraw está diseñado para trabajar como una aplicación independiente. No
posee integración con un entorno de desarrollo como GEF y Eclipse, en don-
76
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
de se puede optar por desarrollar un plug-in integrable o transformar dicho
plug-in en una aplicación cliente, con un pequeño cambio en configuración.
 La documentación existente no es muy detallada y se basa en la autodocumentación de las clases dentro de los patrones de diseño utilizados.
 El estado actual y futuro del proyecto es incierto. Se encontraron distintos
emprendimientos cuyo objetivo es extender y continuar con el framework,
pero no existe un plan de soporte a largo plazo.
6.6.
Conclusión
Para verificar que ACVF es un framework, a continuación se analiza si cumple con
las propiedades que lo definen como tal:
 Orientado a ayudar en un dominio en particular: ACVF está diseñado para
proveer una infraestructura que soporte la visualización de componentes tanto
de una forma estática como dinámica, posibilitando el mejor entendimiento
de las interacciones intervinientes. De esta forma cumple con la propiedad
por estar específicamente construido con tal propósito.
 Representa una solución parcialmente completa para un dominio específico
que puede ser personalizada: El dominio de ACVF es el descripto en el punto anterior. ACVF provee comportamiento de base para la implementación de
representaciones visuales de componentes, compuesto por clases abstractas y
por implementación de elementos comunes como la vista de “outline”. La
personalización consiste de los distintos componentes “acoplables” (pluggable) y de la extensión del comportamiento de base.
 Permite la reutilización de diseño a partir de técnicas básicas de POO y patrones de diseño: El diseño de ACVF provee una separación en capas bien
definidas y desacopladas, la interacción definida entre ellas y la definición de
la interface con los servicios que se espera de cada componente. Este es el
mayor aporte que este framework provee a los usuarios. En él se utiliza una
gran variedad de patrones, entre los que se encuentran:
o Abstract Factory: Este patrón se utiliza en la creación de los controladores, a partir de los elementos del modelo.
o Template Method: Utilizado, por ejemplo, por la clase AbstractComponent, para poder crear el objeto de simulación a partir de la clase
provista por el método getRunnableClass(). Cada componente debe sobrescribir el método para especificar la clase adecuada.
o Mediador: La capa del controlador está basada en este concepto, en
donde coordina y desacopla la interacción entre las capas del modelo
y de la vista.
o Composite: El modelo está implementado bajo este patrón, ya que posee una naturaleza inherentemente jerárquica.
o Command: Cada pedido del usuario es transformado en un comando
que luego es aplicado al modelo. De esta forma se puede almacenar
Diego M.S. Erdödy
Framework de Visualización
77
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
un historial de comandos ejecutados e implementar un mecanismo de
“deshacer” y “rehacer” de manera simple.
o Obervador: Implementado, por ejemplo, por el controlador para actuar frente a cambios en el modelo.
 Permite la reutilización de funcionalidad: La funcionalidad o comportamiento que provee el framework se reparte entre las clases abstractas, que deberán
implementar los usuarios, y los elementos comunes que resuelven problemas
específicos del dominio y no necesitan modificaciones adicionales. Un ejemplo del primer caso es la clase AbstractComponent, mientras que la clase de
modelo Connection se encuentra en el segundo grupo.
 Permite la reutilización de análisis del dominio: Tal vez no es muy evidente
en este caso, pero las posibilidades que brinda el framework, como armar un
diagrama, editar componentes, conectarlos entre si y visualizar su interacción
en una simulación, son todos casos de uso que se tuvieron que definir antes
de desarrollar el framework, y es un valor agregado que éste le provee al
usuario.
78
Framework de Visualización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7. Patrones de concurrencia y sincronización
7.1.
7.1.1.
Rendezvous
Resumen
El patrón de diseño “Rendezvous” tiene como objetivo modelar las precondiciones para la sincronización o encuentro de distintos hilos de ejecución. Es un patrón
general y fácil de aplicar para asegurar que las condiciones requeridas (las cuales pueden ser complejas) sean cumplidas en tiempo de ejecución. El modelo de comportamiento consiste en que a medida de que cada hilo está listo para “encontrarse” con el
resto, éste se registra ante el Rendezvous y es bloqueado. Una vez que las restricciones de concurrencia se cumplen el Rendezvous libera a los participantes registrados,
por ejemplo al llegar a un número predefinido de participantes registrados.
7.1.2.
Otras denominaciones
Punto de encuentro.
7.1.3.
Problema
El problema que soluciona el patrón de diseño es la abstracción de la aplicación
de precondiciones en la ejecución de una acción dada que se encuentra compartida
entre más de un componente.
7.1.4.
Solución
La solución consiste en delegar la sincronización en un objeto externo, llamado
rendezvous, que administre en forma centralizada la información de cada una de las
partes involucradas. Cada hilo de ejecución deberá registrarse con el rendezvous y
requerir permiso para proseguir en el momento en que la sincronización sea necesaria.
El rendezvous, con la ayuda de una política de sincronización encapsulada en otro
componente, será el encargado de informarle a cada hilo de ejecución, cuándo es correcto proceder.
7.1.5.
Caso de estudio
Como ejemplo ilustrativo del patrón se utilizó una línea de montaje en donde el
procesamiento de cada etapa se encuentra a cargo de un brazo robótico. En dicho entorno existe la limitación de que cada etapa no puede ser iniciada hasta no tener la
confirmación que el resto de las etapas han terminado. En este caso, el punto de encuentro está indicado por la finalización de las tareas de cada etapa, en cada ciclo de
procesamiento. De esta forma todos los brazos empezarán su tarea simultáneamente,
sin importar cuánto dure cada tarea.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
79
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7.1.6.
Estructura
La estructura clásica del patrón es la siguiente:
Synch Policy
1
1
Rendezvous
Client Thread
+reset()
+register(in callback : Callback)
+release()
1
1..*
+notify()
1
*
Callback
Figura 7-1: Diagrama de Estructura del Patrón “Rendezvous”
Este diagrama, sugerido por Powell Douglass [Dou 02], muestra a una clase
central llamada Rendezvous que representa el punto de encuentro de los objetos que
necesiten sincronizarse. Es el Rendezvous el que verifica que en cada registración de
un participante se cumpla o no la política de sincronización y en caso afirmativo libera a dichos participantes. Los participantes están representados por la clase ClientThread y el mecanismo para liberarlos por la interface Callback.
A continuación se detallan las funciones de cada uno de los participantes de la
estructura del patrón.
 Client Thread: Representa los hilos de ejecución, por lo menos dos, que necesitan un mecanismo de sincronización. Al llegar al punto de sincronización se
registran con el Rendezvous pasando como argumento su referencia. Este mecanismo recibe el nombre de Callback. Al cumplirse con las precondiciones,
los hilos son liberados para poder proseguir con su ejecución.
 Rendezvous: Administra la sincronización entre los hilos participantes. Posee
un método de registración el cual es invocado por los hilos de ejecución para
indicar que han llegado al punto de sincronización.
 Synch Policy: Es una abstracción del conjunto de precondiciones que necesita
verificar el Rendezvous para poder liberar a los integrantes. La política más
sencilla es simplemente contar los integrantes registrados y liberarlos al llegar
al número preestablecido de miembros.
 Callback: Interface utilizada para la reactivación de los hilos de ejecución.
7.1.7.
Estrategia de implementación Orientada a Aspectos
80
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La implementación realizada, posee dos modalidades: la versión local o simple,
en donde todos los brazos robóticos se encuentran en la misma máquina; y por otro
lado la versión distribuida, que puede sincronizar brazos robóticos en distintas máquinas.
En el trabajo realizado por Jiménez-Peris et al. [Jim 98] se analiza una implementación distribuida y orientada a objetos del patrón de diseño rendezvous, en
ADA95. En ese trabajo se optó por estudiar las ventajas de implementar el servidor
con un hilo de ejecución por cliente (multithreaded). En el presente trabajó se utilizó
un solo hilo para implementar el servidor ya que el tiempo de procesamiento de cada
petición de un cliente no es significativo y el objetivo no es realizar una implementación de alta performance.
NÚCLEO
El núcleo del patrón de diseño se encuentra en el aspecto abstracto RendezvousProtocol junto con las interfaces Rendezvous y RendezvousMember. El siguiente
es el diagrama de estructura de dichos elementos:
Rendezvous
+register( ob j : Ob ject ) : void
<<setter>>+setParticipants( part : int ) : void
+reset() : void
+release() : void
+close() : void
<<aspect>>
RendezvousProtocol
-rvs : Map<String, Rendezvous>
<<pointcut>>+synchronizingCall( memb er : RendezvousMemb er )
<<getter>>+getRendezvous( groupName : String ) : Rendezvous
<<setter>>+setRendezvous( groupName : String, rendezvous : Rendezvous ) : void
<<advice>> <<base>>~before( member : RendezvousMember ){base = synchronizingCall(member)}
<<introduction>>
RendezvousMember
BaseType
<<introduction>>
RendezvousMember
-group : String
<<getter>>+getGroup() : String
<<setter>>+setGroup( groupName : String ) : void
RendezvousMember
<<crosscut>>
<<getter>>+getGroup() : String
Figura 7-2: Núcleo de la estructura Orientada a Aspectos del Patrón “Rendezvous”
La idea principal de la implementación consta en desacoplar la lógica de sincronización del objeto a sincronizar. Esto se logra por medio del punto de corte syncronizingCall() definido en el aspecto abstracto, que representa el conjunto de sitios donde
la sincronización es requerida. De esta forma, el objeto a sincronizar, en nuestro caso
el brazo robótico, no necesita contener lógica asociada a la sincronización con el resto
de los brazos, ni tampoco saber de su existencia.
El Rendezvous es el objeto encargado de aplicar la política de sincronización a
los participantes que se registran en el mediante el método register(). Los participantes
deben implementar la interface RendezvousMember para poder identificar a qué grupo de sincronización pertenecen. Un grupo de sincronización es una entidad lógica
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
81
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
que permite al aspecto poder correlacionar un participante con un Rendezvous específico y de esta forma poder servir a más de un Rendezvous a la vez. Como se ve en el
diagrama, el aspecto abstracto implementa el método que la interface requiere, llamado getGroup() y provee el método setGroup() para poder especificar el grupo de sincronización sobre un participante.
A continuación se detalla cada elemento:
Rendezvous: Interface que representa un objeto de sincronización en donde distintos
componentes pueden registrarse y expresar su necesidad de suspender su ejecución
hasta que le sea dicho que es correcto proseguir. Esto se logra a través del método
register() cuyo argumento es el componente registrado. El método reset() brinda la posibilidad de volver al rendezvous a su estado original mientras que release() es usado
cuando se necesite liberar a los participantes registrados. Por último, el método close()
realiza todas las operaciones de limpieza necesarias para finalizar el ciclo de vida del
rendezvous.
RendezvousMember: Interface que identifica a un miembro capaz de registrarse ante
un rendezvous. Los miembros setGroup() y getGroup() son los que permiten obtener y
especificar a qué grupo lógico de encuentro pertenece este miembro. Este nombre de
grupo permite tener más de un conjunto de miembros capaces de registrarse simultáneamente. La implementación de esta interface, que para aspectos se denomina “introducción”, se efectúa en el aspecto abstracto que implementa el protocolo. La realización, en este caso por parte de la clase RoboArm, se lleva a cabo en el aspecto concreto permitiendo así indicar qué clase podrá ser miembro del Rendezvous sin que
ésta tenga que ser alterada con código específico del patrón de diseño.
RendezvousProtocol: Aspecto abstracto encargado de definir las interacciones comunes del patrón de diseño. En primer lugar se define el punto de corte abstracto llamado
synchronizingCall() que representa el punto de sincronización del patrón. De esta forma,
en vez de agregar código adicional en el miembro para registrarse en el Rendezvous y
agregar un nivel de dependencia entre ambos objetos, la llamada queda a cargo del
aspecto permitiendo la abstracción del mecanismo. Por otro lado se definen los métodos getRendezvous() y setRendezvous(), encargados de especificar qué Rendezvous manejará la sincronización del grupo cuyo nombre es pasado como argumento. El advice
que se agrega sobre el punto de corte es el encargado de obtener el Rendezvous especificado para el miembro que se está procesando. Una vez obtenido el Rendezvous
llama al método register(). Por último, se implementa la interface RendezvousMember
que posee métodos para administrar a qué grupo pertenece el miembro.
APLICACIÓN del NÚCLEO sobre EL CASO DE ESTUDIO
Si bien se logró encapsular la lógica general del patrón en el aspecto abstracto
previamente explicado, es necesario algún elemento que vincule esa lógica con el
problema específico a atacar, en este caso la sincronización de brazos robóticos. El
aspecto concreto RendezvousHandler se encarga de dicha tarea, es decir que aplica la
lógica del rendezvousProtocol a objetos del tipo RoboArm, clase que representa el
brazo robótico. Las funciones del aspecto concreto son:
Transformar la clase RoboArm en un participante de un Rendezvous
haciendo que dicha clase implemente la interface RendezvousMember.
82
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Indicar que el punto de corte syncronizingCall() incluya las llamadas al
método executeAction() del brazo robótico.
En el siguiente diagrama de estructura se representan dichos elementos con las
asociaciones correspondientes:
RoboArm
<<aspect>>
RendezvousProtocol
-id : String
~log : Logger = Logger.getLogger(RoboArm.class)
-maxTaskTime : long = 5000
-minTaskTime : long = 1000
-random : Random
-stopped : boolean = false
-rvs : Map<String, Rendezvous>
#calculateTime() : long
-createRandom() : Random
+executeAction() : void
<<getter>>+getMaxTaskTime() : long
<<getter>>+getMinTaskTime() : long
<<getter>>+getRAId() : String
<<constructor>>+RoboArm( id_ : String )
+run() : void
<<setter>>+setMaxTaskTime( maxTaskTime_ : long ) : void
<<setter>>+setMinTaskTime( minTaskTime_ : long ) : void
+stop() : void
+toString() : String
<<pointcut>>+synchronizingCall( memb er : RendezvousMemb er )
<<getter>>+getRendezvous( groupName : String ) : Rendezvous
<<setter>>+setRendezvous( groupName : String, rendezvous : Rendezvous ) : void
<<advice>> <<base>>~before( member : RendezvousMember ){base = synchronizingCall(member)}
<<introduction>>
RendezvousMember
<<aspect>>
RendezvousHandler
<<pointcut>> <<base>>+synchronizingCall( member : RendezvousMember ){base = call (void RoboArm.executeAction()) && target(o)}
RendezvousMember
<<getter>>+getGroup() : String
<<declare parents>>
RoboArm implements RendezvousMember
Figura 7-3: Estructura de la aplicación del patrón “Rendezvous”
RoboArm: Representa el brazo robótico. Se le asigna un identificador (atributo id) y
se puede configurar el límite superior e inferior del tiempo de su acción principal
(atributos minTaskTime y maxTaskTime). Por defecto estos valores están especificados en 1 seg. y 5 seg., respectivamente. Cada vez que se ejecute, el tiempo real de
duración de la tarea será un número aleatorio comprendido entre dichos límites. Posee
un método run() para iniciar la simulación y otro stop() para detenerla.
RendezvousHandler: Aspecto concreto que agrega los detalles necesarios para aplicar
el patrón definido en el aspecto abstracto. Aquí se determina qué clase se utilizará
como miembro del Rendezvous y cuál será el punto de sincronización. En nuestro
caso es la clase RoboArm y su método execute().
IMPLEMENTACIONES DEL RENDEZVOUS
Hasta ahora se ha mostrado solo la interface que debe implementar un Rendezvous. Para este caso de estudio se han analizado y desarrollado dos implementaciones
distintas. La primera, llamada SimpleRendezvous, es una versión local que permite la
sincronización de hilos de ejecución dentro de una misma máquina. La segunda, llamada RemoteRendezvous, consiste en una versión remota que permite sincronizar
hilos que se ejecuten en distintas máquinas.
Para el caso remoto, el objeto RemoteRendezvous se deberá conectar con un
servidor detallado en la siguiente sección, que coordinará las acciones de las distintas
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
83
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
instancias de RemoteRendezvous. A diferencia de la versión simple, el Rendezvous
remoto implementa la interface Callback, dado que se necesita que sea llamado por el
servidor de Rendezvous una vez que la política de sincronización ha sido cumplida y
los participantes del Rendezvous pueden ser liberados. Esto se logra a través de una
llamada remota utilizando RMI desde el servidor al método finished() de la instancia de
RemoteRendezvous. Téngase en cuenta que en el caso del Rendezvous simple este
mecanismo no era necesario ya que el Rendezvous directamente bloqueaba el hilo de
ejecución al registrarse el participante y luego cuando debía ser liberado, lo despertaba.
El siguiente es el diagrama de estructura:
RemoteRendezvous
Callback
-objs : Set<E->Object> = new HashSet<Object>()
-maxParticipants : int = 2
~log : Logger = Logger.getLogger(RemoteRendezvous.class)
-server : RendezvousServer
+finished() : void
Rendezvous
+register( ob j : Ob ject ) : void
<<setter>>+setParticipants( part : int ) : void
+reset() : void
+release() : void
+close() : void
+register( obj : Object ) : void
+release() : void
+reset() : void
<<setter>>+setParticipants( part : int ) : void
<<constructor>>+RemoteRendezvous( serverAddress : String )
+finished() : void
+close() : void
SimpleRendezvous
#objs : Set<E->Object> = new HashSet<Object>()
-maxParticipants : int = -1
<<constructor>>+SimpleRendezvous( maxParticipants_ : int )
<<constructor>>+SimpleRendezvous()
+register( obj : Object ) : void{guarded}
+release() : void
+reset() : void
<<setter>>+setParticipants( part : int ) : void
+close() : void
-checkParticipants() : void
Figura 7-4: Estructura de la distintas implementaciones del Rendezvous
Callback: Interface de retro-llamada que declara el método finished(). Cuando en un
mensaje se pasa como argumento una referencia del objeto iniciador de la llamada,
esta interface permite que el objeto al cual se está llamando pueda informarle al objeto
iniciador en una manera asincrónica que el procesamiento asociado a la llamada ha
sido finalizado. En este caso el objeto que necesita ser notificado es el RemoteRendezvous y el notificador es el servidor que a través del método finished() le indicará al
Rendezvous que sus participantes pueden ser liberados.
SimpleRendezvous: Versión sencilla o local del rendezvous. Simplemente bloquea al
hilo de ejecución de cada miembro registrado hasta que las precondiciones sean cumplidas, usando una llamada al método wait(). Cuando se cumplen las precondiciones
llama al método notifyAll() sobre el monitor liberando así todos los hilos.
RemoteRendezvous: Versión remota del rendezvous. En este caso las llamadas se
delegan a un servidor de rendezvous via RMI el cual es el encargado de coordinar las
acciones. El constructor recibe como argumento la dirección de la máquina donde
84
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
reside el servidor de Rendezvous. Implementa la interface Callback para permitir ser
llamado por el servidor al momento de ser liberado, a través del método finished().
IMPLEMENTACIÓN REMOTA
Para la implementación remota del Rendezvous, es necesario especificar un servidor que pueda coordinar los Rendezvous sobre distintas máquinas de una forma
centralizada. Dicho servidor es accesible via RMI y permite que los RemoteRendezvous actúen de proxy entre los participantes del rendezvous y él. De esta forma al
servidor le llega la información las registraciones de participantes en cada una de las
máquinas y él puede distribuir la orden de liberación una vez que la política de sincronización ha sido cumplida. El diagrama de estructura es el siguiente:
RendezvousServer
+register( client : Callb ack ) : void
+addClient( client : Callb ack, part : int ) : void
+removeClient( client : Callb ack ) : void
RendezvousServerImpl
~serialVersionUID : long = 1{readOnly}
~log : Logger = Logger.getLogger(RemoteRendezvous.class)
-maxParticipants : int
-registered : int
-participantsByClient : Map<Publisher, List<Subscriber>><K->Callback, V->Integer> = new HashMap<Callback,Integer>()
-name : String
<<constructor>>+RendezvousServerImpl( name_ : String )
#register() : void
#unregister() : void
+register( client : Callback ) : void{guarded}
-release() : void
+addClient( client : Callback, part : int ) : void
+removeClient( client : Callback ) : void
Figura 7-5: Estructura de la implementación remota del Rendezvous
RendezvousServer: Es la interface que define a un servidor de rendezvous. Esta se
aplica en los casos en que los miembros pueden encontrarse en máquinas distintas y
por lo tanto es necesario coordinar hilos de ejecución remotos. Al igual que un rendezvous tiene un método register() para indicar que un miembro ha llegado al punto de
sincronización. Los otros dos métodos, addClient() y removeClient(), agregan o quitan
clientes activos, es decir instancias de RemoteRendezvous, a este servidor. Para poder
agregar un cliente, es necesario indicarle al servidor cuántos participantes tiene bajo
su control para que pueda calcular el número total de participantes y de esta forma
saber en qué momento están todos registrados.
RendezvousServerImpl: Implementación del servidor de rendezvous utilizando RMI.
El diagrama de estructura completo es el siguiente:
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
85
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
RendezvousServer
+register( client : Callb ack ) : void
+addClient( client : Callb ack, part : int ) : void
+removeClient( client : Callb ack ) : void
RemoteRendezvous
Callback
+finished() : void
RendezvousServerImpl
~serialVersionUID : long = 1{readOnly}
~log : Logger = Logger.getLogger(RemoteRendezvous.class)
-maxParticipants : int
-registered : int
-participantsByClient : Map<Publisher, List<Subscriber>><K->Callback, V->Integer> = new HashMap<Callback,Integer>()
-name : String
<<constructor>>+RendezvousServerImpl( name_ : String )
#register() : void
#unregister() : void
+register( client : Callback ) : void{guarded}
-release() : void
+addClient( client : Callback, part : int ) : void
+removeClient( client : Callback ) : void
Rendezvous
+register( ob j : Ob ject ) : void
<<setter>>+setParticipants( part : int ) : void
+reset() : void
+release() : void
+close() : void
-objs : Set<E->Object> = new HashSet<Object>()
-maxParticipants : int = 2
~log : Logger = Logger.getLogger(RemoteRendezvous.class)
-server : RendezvousServer
+register( obj : Object ) : void
+release() : void
+reset() : void
<<setter>>+setParticipants( part : int ) : void
<<constructor>>+RemoteRendezvous( serverAddress : String )
+finished() : void
+close() : void
SimpleRendezvous
#objs : Set<E->Object> = new HashSet<Object>()
-maxParticipants : int = -1
RoboArm
<<aspect>>
RendezvousProtocol
-id : String
~log : Logger = Logger.getLogger(RoboArm.class)
-maxTaskTime : long = 5000
-minTaskTime : long = 1000
-random : Random
-stopped : boolean = false
-rvs : Map<String, Rendezvous>
#calculateTime() : long
-createRandom() : Random
+executeAction() : void
<<getter>>+getMaxTaskTime() : long
<<getter>>+getMinTaskTime() : long
<<getter>>+getRAId() : String
<<constructor>>+RoboArm( id_ : String )
+run() : void
<<setter>>+setMaxTaskTime( maxTaskTime_ : long ) : void
<<setter>>+setMinTaskTime( minTaskTime_ : long ) : void
+stop() : void
+toString() : String
<<pointcut>>+synchronizingCall( memb er : RendezvousMemb er )
<<getter>>+getRendezvous( groupName : String ) : Rendezvous
<<setter>>+setRendezvous( groupName : String, rendezvous : Rendezvous ) : void
<<advice>> <<base>>~before( member : RendezvousMember ){base = synchronizingCall(member)}
<<constructor>>+SimpleRendezvous( maxParticipants_ : int )
<<constructor>>+SimpleRendezvous()
+register( obj : Object ) : void{guarded}
+release() : void
+reset() : void
<<setter>>+setParticipants( part : int ) : void
+close() : void
-checkParticipants() : void
<<introduction>>
RendezvousMember
<<introduction>>
RendezvousMember
BaseType
-group : String
<<getter>>+getGroup() : String
<<setter>>+setGroup( groupName : String ) : void
<<aspect>>
RendezvousHandler
<<pointcut>> <<base>>+synchronizingCall( member : RendezvousMember ){base = call (void RoboArm.executeAction()) && target(o)}
RendezvousMember
<<getter>>+getGroup() : String
<<declare parents>>
RoboArm implements RendezvousMember
<<crosscut>>
Figura 7-6: Estructura completa para el Rendezvous
7.1.8.
Análisis
Como se vio en la descripción de la implementación, la utilización de un aspecto abstracto para encapsular la lógica general del patrón ha hecho factible tener un elemento
reutilizable y localizado. Esto implica que al momento de aplicar el patrón solo haga
falta indicar sobre qué lugares del sistema actuarán los puntos de corte definidos en el
aspecto abstracto y distribuir los roles especificados por el mismo. El detalle de cada
una de las características es el siguiente:
 Localidad del código: Todo el código relacionado al manejo del Rendezvous
se encuentra concentrado en el protocolo así como también en las implementaciones específicas de la interface.
 Reusabilidad: Dada la abstracción que se logró del protocolo principal del
Rendezvous, el aspecto abstracto es totalmente reutilizable e incluso configurable con una implementación particular de la interface.
 Independencia: Desde el momento que el brazo robótico no tiene ningún conocimiento de las restricciones que se le está aplicando, se puede aseverar que
es totalmente independiente del patrón. Si se necesitara que funcione un brazo
independientemente, no haría falta ningún cambio en absoluto en el código de
la clase RoboArm.
 Transparencia de composición: Un brazo puede participar en más de un Rendevous sin necesidad de agregar restricciones adicionales ni afectar el comportamiento del mismo.
86
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7.1.9.
Componente gráfico
Existen tres componentes implementados:
 RoboArm: Brazo robótico. Se puede configurar los límites de tiempo de la duración de su tarea así como también el nombre de la instancia.
 Rendezvous: Representación del rendezvous. Si no se especifica una dirección
de servidor actúa en modo “simple”, es decir en forma local. De lo contrario se
conecta con el servidor especificado y pasa a actuar en forma remota pudiendo
sincronizarse con todos los rendezvous conectados a ese servidor. Es necesario
que se le especifique un nombre de grupo en caso de que exista más de un
rendezvous.
 RendezvousServer: Servidor coordinador y aceptor de llamadas de rendezvous
remotos.
Cada roboArm que se quiera sincronizar debe ser conectado con el correspondiente rendezvous. El nombre del grupo para el brazo robótico se obtiene automáticamente del Rendezvous, por eso es importante especificar un nombre distinto para
cada Rendezvous en caso de que haya más de uno en el diagrama.
Al momento de iniciar la simulación, cada brazo robótico comenzará a ejecutar
su acción y se representa dicho estado con el color rojo. Una vez finalizada la acción,
que tardará un número aleatorio de milisegundos acotado por los límites impuestos,
vuelve al estado libre, representado por el color verde. Es en este momento actúa la
sincronización y no permite que el brazo vuelva a ejecutar su acción hasta que todos
los otros brazos participantes del rendezvous hayan finalizado. Si el brazo no formara
parte de un rendezvous, se lo vería constantemente ocupado, es decir, en color rojo.
A continuación se puede observar una captura de una configuración con un
RendezvousServer por un lado (diagrama superior) y un Rendezvous que sincroniza a
tres brazos robóticos por otro lado (diagrama inferior).
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
87
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 7-7: Diagrama ACVF del caso de estudio para el patrón “Rendezvous”
7.2.
7.2.1.
Balking
Resumen
Si se llama a un método sobre un objeto dado, cuando éste se encuentra en un
estado inapropiado, entonces la llamada al método no se ejecutará.
7.2.2.
Otras denominaciones
Barrera de llamada.
7.2.3.
Problema
Un objeto puede encontrarse en un estado inapropiado para atender una llamada
a un método determinado. Una posible solución sería dejar en espera a la llamada hasta que llegue el momento en que el estado del objeto sea el adecuado, pero esto no
siempre cumple los requerimientos deseados (por ejemplo si hay restricciones en el
tiempo de respuesta).
7.2.4.
Solución
88
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La solución que plantea este patrón de diseño, es simplemente ignorar la llamada y es por eso que sólo se puede aplicar en entornos donde la ejecución no sea crítica
dentro del sistema. No podría aplicarse en una situación tan crítica como es, por
ejemplo, el latido de un marcapasos.
7.2.5.
Caso de Estudio
El ejemplo que se eligió para analizar este patrón, es la interacción entre un retrete y las distintas formas que pueden existir de activar su vaciado. Ejemplos de formas de activación son un botón o un sensor de proximidad. Evidentemente, no tiene
sentido iniciar un vaciado una vez que otro vaciado se encuentra en curso. Una solución posible es verificar el estado del retrete antes de realizar el pedido de vaciado. El
problema con esta solución es que se necesita introducir lógica que no pertenece al
interés principal del cliente, que es simplemente realizar el vaciado del retrete. Es por
eso que se propone encapsular en un aspecto dicho mecanismo para poder aplicarlo de
una forma transparente al cliente.
7.2.6.
Estructura
Veamos el diagrama de colaboración de la estructura típica:
Figura 7-8: Diagrama de estructura del patrón “Balking”
El patrón se basa sobre una estructura en la cual un componente es accedido, ya
sea en forma remota o local, por un objeto cliente.
A continuación se describen los roles de los participantes involucrados en el
patrón.
 Component: Es el objeto sobre el cual se va a aplicar la llamada y el cual necesita una política de manejo de las llamadas en momentos inapropiados.
 Client: Objeto que actúa como cliente del componente y por ende el responsable de efectuar la llamada al método en cuestión. Según el patrón de diseño
puede esperar que el resultado sea nulo o hasta recibir una excepción en el caso de que el estado del componente no sea el adecuado para procesar la llamada.
7.2.7.
Estrategia de implementación Orientada a Aspectos
La implementación se basa en extraer el mecanismo básico del patrón en un aspecto abstracto llamado BalkingProtocol. La implementación además incluye los
objetos representantes del retrete y sus activadores, junto con el aspecto concreto que
indica sobre cuales elementos debe aplicarse el mecanismo abstraído en el protocolo.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
89
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
NÚCLEO
Para la implementación del núcleo del patrón se ha optado por ignorar la llamada en cuestión si es que ya se encuentra en ejecución al momento de iniciar el pedido.
Una opción más compleja hubiese sido abstraer el comportamiento que se ejecuta en
dicho caso y permitir al usuario aplicar otras políticas alternativas, como por ejemplo
registrar el pedido o llamar a otro componente. Se ha elegido el camino simple ya que
de cualquiera de las dos maneras se pueden observar los mecanismos básicos de la
implementación AOP, que se pretenden analizar.
El diagrama de estructura es el siguiente:
<<aspect>>
BalkingProtocol
-busy : boolean
<<pointcut>>+executionCall()
<<base>> <<advice>>+around(){base = executionCall()}
Figura 7-9: Estructura del Núcleo del patrón “Balking”
BalkingProtocol: Aspecto abstracto que define el mecanismo principal del patrón.
Posee una definición abstracta de pointcut llamada executionCall() la cual representa la
llamada que se quiere controlar. Asimismo cuenta con un advice aplicado alrededor
de dicho pointcut. El pointcut controla que la llamada sea ejecutada si no hay otra
llamada del mismo tipo en proceso, y que sea ignorada en cualquier otro caso.
APLICACIÓN CONCRETA al CASO DE ESTUDIO
En la aplicación del patrón de diseño se implementó un objeto simulador del retrete, en el cual se pueden configurar características tales como la duración del vaciado. El retrete puede contener uno o más tipos de activación, representado por la interface FlusherActivator al cual se le han implementado dos modos, FlashButton y
LightSensor, es decir, a través de un botón mecánico o un sensor de proximidad. Por
último se cuenta con la implementación concreta del aspecto que indica sobre cuales
elementos se debe aplicar el patrón.
El diagrama de estructura es el siguiente:
90
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<<aspect>>
FlusherActivator
BalkingProtocol
~log : Logger = Logger.getLogger(FlusherActivator.class)
#flushers : Set<E->Flusher> = new HashSet<Flusher>()
#meanTime : int
-stopRequested : boolean = false
<<aspect>>
BalkingHandler
<<pointcut>> <<base>>+executionCall(){base = call (void Flusher.flush())}
+actionPerformed() : void
+run() : void
<<constructor>>+FlusherActivator()
<<constructor>>+FlusherActivator( flusher : Flusher )
+addFlusher( fl : Flusher ) : void
+removeFlusher( fl : Flusher ) : void
<<setter>>+setMeanTime( ms : int ) : void
+stopActivator() : void
Flusher
~log : Logger = Logger.getLogger(Flusher.class)
-flushTime : int = 3000
+flush() : void
<<setter>>+setFlushTime( ms : int ) : void
FlashButton
LightSensor
<<constructor>>+FlashButton( flusher : Flusher )
<<constructor>>+LightSensor( flusher : Flusher )
Figura 7-10: Estructura de la aplicación del patrón “Balking”
Clases
Flusher: Clase que representa el funcionamiento de un retrete. El mensaje flush() es el
que realiza el proceso de vaciado del mismo. Es posible configurar el tiempo que toma este proceso por medio del método setFlushTime().
FlusherActivator: Clase abstracta que representa una entidad capaz de accionar la
cadena del retrete. Permite vincular (y desvincular) Flushers a los cuales se quieren
controlar, a través del método addFlusher(). Implementa también la funcionalidad básica de simulación de la entidad en la cual cada cierto tiempo (especificado con el
método setMeanTime()), se acciona la cadena de los retretes vinculados. La simulación
se puede iniciar o parar con los métodos run() (de la interface Runnable) y stopActivator().
FlusherButton: Especialización de la clase FlusherActivator que representa un botón
para el accionamiento de la cadena del retrete.
LightSensor: Especialización de la clase FlusherActivator que representa un sensor
de movimiento para el accionamiento de la cadena del retrete.
Aspectos
BalkingHandler: Aspecto concreto que representa la aplicación del patrón a nuestro
ejemplo particular. Para ello lo único que necesita especificar es cuál será la llamada a
controlar. Por eso es que define el pointcut executionCall() y lo asigna al método flush()
de la clase Flusher.
7.2.8.
Análisis
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
91
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
La lógica asociada al patrón se ha podido abstraer utilizando el mismo mecanismo del aspecto abstracto. A continuación se detalla el análisis de la implementación:
 Localidad del código: Todo el código relacionado al manejo del Balking se
encuentra concentrado en el aspecto abstracto sin necesidad de complicar la
lógica propia del Flusher ni otro componente.
 Reusabilidad: Dada la abstracción que se logró del protocolo principal del
BalkingProtocol, el aspecto abstracto es totalmente reutilizable e incluso configurable con una implementación particular de la interface.
 Independencia: El código que maneja el procedimiento a seguir en caso de que
el retrete se encuentre en un estado inadecuado para procesar el pedido de vaciado, se encuentra desacoplado de la lógica de dicho procedimiento. Es por
eso que cada interés logra el nivel de independencia necesario para poder evolucionar por separado.
 Transparencia de composición: Si fuera necesario aplicar el patrón a otro
método o aplicarlo al mismo método para manejar otros estados, no habría interferencia entre cada uno, ya que se encuentran modularizados y el único punto de encuentro es la invocación al método.
7.2.9.
Componente gráfico
Los componentes que fueron implementados para este caso son:
 Flusher: Representación del retrete. Se puede configurar la duración del vaciado.
 FlusherActivator: Activador de retrete. Cuenta con dos modos de activación,
uno interactivo en donde el usuario puede activar el componente a través de
una acción y otro automático en donde el componente se activa aleatoriamente
cada cierto tiempo.
Es posible conectar un activador con uno o varios retretes y de la misma forma
cada retrete puede ser activado por uno o más activadores. Se puede comprobar, dada
la aplicación del patrón de diseño en cuestión, que cualquier pedido de vaciado a un
retrete que se encuentra en proceso de vaciamiento, es ignorado.
El siguiente es un ejemplo que muestra todas las posibles combinaciones de enlace:
92
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 7-11: Diagrama ACVF del caso de estudio para el patrón “Balking”
El diagrama corresponde a una simulación en el modo interactivo. El primer activador actúa sobre ambos retretes simultáneamente, mientras que el segundo retrete
es activado por cualquiera de los dos activadores.
7.3.
7.3.1.
Observador
Resumen
El concepto central de este patrón de diseño es que uno o más objetos llamados
observadores puedan registrarse con otro objeto llamado „sujeto‟, el cual notificará un
determinado evento a dichos observadores.
7.3.2.
Otras denominaciones
Publisher – Subscriber
7.3.3.
Problema
El problema consiste en hallar una forma de poder notificar a un grupo de objetos, en el momento en que ocurre un cierto evento asociado a otro objeto dado. Dicho
evento puede ser el cambio de valor de uno de sus atributos, la llamada a un método o
cualquier acción que pueda llegar a afectar su estado. Una alternativa es que cada objeto cliente trate de averiguar el estado de dicho evento activamente, lo cual trae aparejado un gasto de recursos innecesario.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
93
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7.3.4.
Solución
La solución que propone el patrón consta de dos fases. Por un lado, los objetos
que necesitan ser notificados de los cambios (observadores), deben “registrarse” al
objeto fuente de dichos eventos (sujeto). De esta forma, solicitan que se les “avise”
cuando un cambio pertinente tenga lugar. Por otro lado, el sujeto debe “notificar” a
los observadores cuando dicho cambio ocurre. Para ello, invocará el método prefijado
de la referencia que le hayan provisto los observadores al momento de la registración.
En dicha invocación, se provee información acerca del evento que originó la llamada.
7.3.5.
Caso de estudio
Se eligió como caso de estudio, la interacción entre un sensor de velocidad de
un auto y un medidor que actúa de interface con el usuario. El problema es que el medidor debe ser informado cada vez que el sensor detecta un cambio.
7.3.6.
Estructura
La estructura que sugiere el patrón propone un desacoplamiento entre los observadores y el sujeto, al incorporar un mecanismo de suscripción basado en abstracciones de cada rol. Al mismo tiempo, permite notificar a los observadores en el momento
preciso en que el evento es capturado, o poco tiempo después. El diagrama de estructura es el siguiente:
Puede ser una referencia o una copia del dato
1
Data
-value
1
AbstractSubject
AbstractClient
+accept(in data)
*
ConcreteClient
1
+subscribe(in client : AbstractClient)
+unsubscribe(in client : AbstractClient)
ConcreteSubject
Figura 7-12: Diagrama de estructura del patrón “Observador”
Los participantes del patrón son:
94
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 AbstractSubject: Actúa como servidor de información para los Observadores




7.3.7.
(AbstractClient) que consumen dicha información. Permite incorporar o eliminar asociaciones con clientes mediante los métodos subscribe() y unsubscribe(). Es su tarea informar a los clientes cuando está disponible la información.
Un ejemplo típico de dicha información es el cambio en el valor de uno de sus
atributos.
AbstractClient: Representa un objeto que desea conocer la información que un
sujeto (AbstractSubject) tiene para brindar. Contiene el método accept() que es
llamado para proveerle la información deseada.
ConcreteClient: Subclase específica de la aplicación de la clase abstracta AbtractClient. Contiene la funcionalidad específica de la aplicación del patrón.
ConcreteSubject: Subclase especifica de la aplicación de la clase abstracta
AbstractSubject. Contiene la funcionalidad específica de la aplicación del
patrón.
Data: Contiene la información que el Sujeto conoce y le transmitirá a todos
los Observadores subscriptos.
Estrategia de implementación Orientada a Aspectos
La implementación orientada a aspectos, se divide en dos partes. Por un lado, el
núcleo del patrón, con la lógica que se abstrae y modulariza. Por otro lado, la aplicación de dicho núcleo y los objetos propios del caso de estudio.
NÚCLEO
La implementación del núcleo, consta de un aspecto abstracto que contiene el
protocolo del patrón y por otro lado dos interfaces de “marca” que define los roles
definidos por el patrón. Una interface de marca es aquella que no contiene métodos ni
atributos, y sólo sirve para identificar tipos de objetos. El diagrama de estructura es el
siguiente:
<<aspect>>
ObserverProtocol
-perPublisherSubscribers : Map<Publisher, List<Subscriber>>
#getSubscribers( publisher ) : List<Subscriber>
#updateSub scrib er( pub lisher : Pub lisher, ob server : Sub scrib er ) : void
+subscribe( publisher : Publisher, subscriber : Subscriber ) : void
+unsubscribe( publisher : Publisher, observer : Subscriber ) : void
<<pointcut>>+pub lisherChanges( pub lisher : Pub lisher )
<<base>> <<advice>>+after( publisher : Publisher ){base = publisherChanges(publisher)}
Publisher
Subscriber
Figura 7-13: Estructura del Núcleo del patrón “Observador”
Interfaces
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
95
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Publisher: Indica que el objeto será observado frente a determinados cambios.
Subscriber: Indica que el objeto actuará de suscriptor, es decir, podrá ser notificado
cuando el objeto del tipo Publisher al cual se suscribió cambie en alguna de las formas predeterminadas.
Aspectos
ObserverProtocol: Aspecto abstracto que define el mecanismo principal del patrón.
Posee un atributo, perPublisherSubscriber, que almacena las relaciones entre Publishers
y Subscribers para las suscripciones realizadas. Para acceder a dichas suscripciones
desde un aspecto derivado, posee un método llamado getSubscribers() el cual recibe el
Publisher del cual se quieren consultar suscriptores, como parámetro.
Posee también dos métodos para el manejo de suscripciones, suscribe() y unsuscribe()
los cuales reciben el par Publisher, Subscriber como parámetros. Nótese que ambos
métodos son parte del aspecto, por lo que el manejo de suscripciones es ahora desacoplado tanto del sujeto como del suscriptor para pasar a formar parte de una entidad
localizada que maneje el vínculo entre ellos. El único pointcut abstracto definido en el
aspecto, publisherChanges(Publisher), representa los puntos indicadores de un cambio en
la clase (o las clases) definida como Publisher. Por último se define e implementa un
advice sobre este pointcut, el cual simplemente llama al método protegido abstracto
(Template Method [Gam 95]) updateSubscriber (Publisher, Subscriber) para cada subscriptor asociado al Publisher en cuestión.
APLICACIÓN CONCRETA al CASO DE ESTUDIO
La aplicación del patrón al caso de uso, se realiza con un aspecto concreto que
especifica que join points debe capturar el pointcut y como notificará a los suscriptores del evento esperado. También se indica, a través del aspecto concreto, quien cumple cada rol especificado en el núcleo del patrón.
Para el modelado del caso de estudio, se cuenta con dos objetos que cumplen
con las funciones de velocímetro y sensor de velocidad. Estos objetos cumplirán el
papel de observador y sujeto, respectivamente. El diagrama de estructura es el siguiente:
96
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
<<aspect>>
ObserverProtocol
Publisher
Subscriber
<<aspect>>
ObserverHandler
<<base>> <<pointcut>>+publisherChanges( publisher : Publisher ){base = execution (void SpeedSensor.speedChanged()) && target(publisher)}
#updateSubscriber( publisher : Publisher, observer : Subscriber ) : void
<<declare parents>>
SpeedSensor implements Publisher
<<declare parents>>
SpeedMeter implements Subscriber
SpeedSensor
~log : Logger = Logger.getLogger(SpeedSensor.class)
-_speed : int
-stopped : boolean = false
SpeedMeter
~log : Logger = Logger.getLogger(SpeedMeter.class)
+speedChanged( speed : int ) : void
+run() : void
+speedChanged() : void
<<getter>>+getSpeed() : int
+stop() : void
Figura 7-14: Estructura de la aplicación del patrón “Observador”
Clases
SpeedSensor: Clase que representa un sensor de velocidad. Es este caso el atributo
que nos interesa monitorear es precisamente la velocidad (speed). El método run()
provee la simulación del cambio de velocidad a períodos regulares y el método speedChanged() indica que dicha velocidad se ha modificado. El método getSpeed() permite
obtener la velocidad actual y por último el método stop() detiene la simulación.
SpeedMeter: Clase que representa un medidor de velocidad el cual actúa como interface hacia el usuario y requiere información externa, en nuestro caso un sensor. Posee
un método llamado speedChanged(int) el cual provee la información externa sobre cambios en la velocidad a mostrar.
Aspectos
ObserverHandler: Aspecto concreto derivado de ObserverProtocol. Por un lado específica las clases que cumplen los roles de Publisher y Subscriber, es decir, SpeedSensor y SpeedMeter. Por otro lado determina que el join point para el pointcut publisherChanges será la llamada al método speedChanged() de la clase SpeedSensor e im-
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
97
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
plementa el método updateSubscriber() con una llamada al método speedChanged(int) de
la clase SpeedMeter. Este aspecto actúa de “mediador” entre el aspecto abstracto que
implementa el mecanismo general del patrón y las clases a las cuales se lo quiere aplicar.
7.3.8.
Análisis
Este patrón es un caso de abstracción total del comportamiento del aspecto y se ha
podido modularizar en un aspecto reutilizable, como se demuestra en la aplicación al
caso de estudio. A continuación se analizan los distintos puntos de estudio.
 Localidad del código: Este patrón es un claro ejemplo de localidad de código
generada de forma inherente a las características de la programación orientada
a aspectos. El interés transversal (mecanismo de observación) ha sido abstraído y materializado en un único aspecto, ObserverProtocol.
 Reusabilidad: El aspecto abstraído, además de estar localizado, es totalmente
reutilizable dado que para una nueva aplicación sólo basta con crear un aspecto concreto derivado que indique cuales van a ser los participantes de la observación y que se va a observar.
 Independencia: Al no tener ninguno de los componentes conocimiento alguno
sobre las interacciones involucradas en la implementación del patrón, cada uno
de ellos puede evolucionar de manera independiente sin afectar el comportamiento de ninguna de las partes.
 Transparencia de composición: La forma de manejo implementada de las referencias suscriptas garantiza que no haya interferencia entre distintas aplicaciones del patrón. Por otro lado, al mantener los niveles de invasión por parte
del aspecto sobre los componentes que actúan en cada rol (observador y sujeto), la composición con otros patrones no traería inconvenientes.
7.3.9.
Componente gráfico
Los siguientes componentes fueron implementados para este patrón:
 SpeedSensor: Dispositivo sensor de velocidad. Obtiene una lectura periódica
de velocidad cuyo valor es el atributo “observable” del componente.
 SpeedMeter: Representación del velocímetro que mostrará la información
provista por el sensor.
El medidor de velocidad mostrará el registro obtenido del sensor que tenga asociado, de manera casi simultánea a la obtención de dicha medición por parte del sensor. Nótese que cada sensor puede tener más de un medidor asociado en cuyo caso
todos mostrarán el mismo registro simultáneamente. El siguiente es un ejemplo que
muestra todas las posibles combinaciones de enlace:
98
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 7-15: Diagrama ACVF del caso de estudio del patrón “Watchdog”
La simulación sólo puede correrse en modo no-interactivo, y consta de valores
aleatorios que genera el sensor y muestra en el compartimiento de estado. Los velocímetros, simplemente reproducen el valor del sensor que tengan asociado, también
en el compartimiento de estado.
7.4.
7.4.1.
Optimistic Locking
Resumen
El acceso y modificación concurrente del contenido de un recurso compartido
(por ejemplo una tabla de una base de datos), puede generar inconsistencia en el contenido de dicho recurso, si no se toman las precauciones pertinentes. El bloqueo optimista es una solución intermedia que supone el caso más optimista (nadie modificó el
elemento desde la última vez que se accedió) y se toman acciones en caso de que dicho supuesto sea incorrecto.
7.4.2.
Otras denominaciones
Control Optimista de Concurrencia
7.4.3.
Problema
El acceso a un recurso de lectura-escritura compartido trae aparejado problemas
de consistencia si no se establece una política de sincronización adecuada.
7.4.4.
Solución
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
99
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Cuando es necesario tener acceso de lectura y escritura concurrente sobre elementos compartidos en un entorno distribuido, existen 3 políticas posibles a aplicar.
La política simplista consiste en no tomar ninguna acción si el recurso es modificado
por dos o más clientes a la vez. Se obtiene como resultado un esquema en donde “el
último en modificar es el que gana”. Es decir, un cliente puede acceder a la información, manipularla, luego almacenarla y estar sobrescribiendo una actualización que se
hizo sobre la misma información en el transcurso de la manipulación. Esta política
trae aparejada pérdida de modificaciones por manipulación de información local desactualizada. En el otro extremo del espectro, se sitúa la política pesimista. Esta política consiste en bloquear el elemento a acceder y no liberarlo hasta no haber terminado de manipularlo y almacenarlo. Por un lado, esta política asegura la consistencia en
todo momento pero tiene como gran desventaja una disminución considerable de la
performance en los casos donde accesos de lectura concurrentes al mismo elemento
son altamente probables.
En un punto intermedio se encuentra la política optimista. El concepto principal es suponer que el recurso no cambiará mientras se está actualizando. Por consiguiente, para mantener la consistencia, es necesario detectar cuando no se cumple.
Para realizar dicha verificación, al momento de almacenar el nuevo valor, se debe
constatar que no haya sido modificado por otro desde la última lectura. Para ello se
debe agregar un indicador al recurso a modo de meta-información, como por ejemplo,
el número de versión o la fecha de última modificación. Este indicador sirve para verificar que no hubo una operación de actualización simultánea. Si ninguna modificación
ha existido, entonces se procede de manera normal (la suposición optimista se ha
cumplido). En caso contrario, se debe informar al usuario (por ej. a través de una excepción o código de error) que debe repetir la operación, lo cual implica obtener nuevamente el recurso.
La política optimista se debe aplicar en un entorno en el cual los cambios sobre
un mismo recurso no sean muy frecuentes. En caso contrario, se corre el riesgo que
una actualización requiera demasiados intentos para poder concretarse.
7.4.5.
Caso de estudio
El caso de estudio que se ha elegido para este patrón es el acceso concurrente a
un registro genérico en una tabla de una base de datos. Dicho registro solo contiene un
atributo a modo de ejemplo y una clave primaria.
7.4.6.
Estructura
Los participantes del patrón no tienen una relación estructural entre sí, por lo
cual se eligió un diagrama de interacción para representar al patrón. En este caso, la
interacción de los participantes es más relevante que la estructura de los mismos. El
diagrama de interacción es el siguiente:
100
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Cliente
Recurso
Controlador de
Concurrencia Optimista
1: obtener()
2: modificar
3: validar
4: controlar
5: almacenar
Figura 7-16: Interacción del patrón de concurrencia optimista
El diagrama muestra a los siguientes participantes que componen el patrón:
 Cliente: Usuario del recurso compartido.
 Recurso: Recurso compartido que se pretende sincronizar. Se parte de la base
de que no hay restricciones sobre el acceso al mismo.
 Administrador de Concurrencia Optimista: Encargado de controlar que la suposición optimista se cumpla, y en consecuencia mantener la consistencia de
los datos. Esto quiere decir que al momento de realizar una operación de almacenamiento sobre el recurso, el controlador debe asegurarse que no ha
habido una modificación desde la última vez que ese cliente accedió al mismo.
La interacción que representa al patrón posee los siguientes pasos:
1) El cliente accede a los datos del recurso, con una operación de lectura.
2) El cliente procesa y modifica el valor local de los datos obtenidos del recurso.
3) Antes de poder almacenar el valor, el cliente le pide al controlador que valide el estado del recurso.
4) El controlador debe obtener meta-información sobre el recurso, para controlar que no se haya modificado desde que se realizó el paso 1.
5) En el caso de que la suposición optimista se cumple, el cliente procede a almacenar los nuevos valores en el recurso compartido.
La interacción previamente descripta corresponde al caso exitoso. En cambio, si la
suposición optimista no se cumpla en el paso 4, el controlador debe informar la situación al cliente y el recurso debe ser leído nuevamente para retomar la secuencia desde
el paso 2.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
101
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
7.4.7.
Estrategia de implementación Orientada a Aspectos
La implementación se divide en un núcleo reusable y la aplicación del mismo al
caso de estudio.
NÚCLEO
El núcleo consta de un aspecto abstracto que contiene los pointcuts que deben
ser especificados y la lógica reusable del controlador optimista de concurrencia. Por
otro lado, se definen las interfaces para el recurso (BaseRecord) y se provee implementación para dichas interfaces. A continuación se muestra el diagrama de interacción y la descripción de los componentes:
<<aspect>>
OptimisticProtocol
~log : Logger = Logger.getLogger(OptimisticProtocol.class)
<<pointcut>>+update( record : OptimisticRecord )
+getCurrentVersion( id : OptimisticRecord ) : int
#updateVersion( record : OptimisticRecord ) : void
<<pointcut>>+ implementationLoad( record : OptimisticRecord, result : ResultSet )
<<pointcut>>+ implementationCreate( record : OptimisticRecord )
<<pointcut>>+ implementationUpdate( record : OptimisticRecord )
<<advice>> <<base>>+before( record : OptimisticRecord ){base = update(record)}
<<advice>> <<base>>+after( record : OptimisticRecord, rs : ResultSet ){base = implementationLoad(record, rs)}
<<advice>> <<base>>+after( record : OptimisticRecord ){base = implementationUpdate(record)}
<<advice>> <<base>>+after ( record : OptimisticRecord ){base = implementationCreate(record)}
<<introduction>>
OptimisticRecord
BaseRecord
<<getter>>+getId() : int
<<introduction>>
OptimisticRecord
BaseType
-version : int
-increaseVersion() : void
-setVersion( version : int ) : void
<<getter>>+getVersion() : int
OptimisticRecord
<<getter>>+getVersion() : int
Figura 7-17: Estructura del Núcleo del patrón “Optimistic Locking”
Interfaces
BaseRecord: Define los atributos comunes a todos los tipos de registros a definir en el
sistema. El único atributo identificado es el “id” que contiene la clave del registro, con
un acceso de sólo-lectura (solamente posee un método getter).
102
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
OptimisticRecord: Identifica a los registros que van a ser tratados de forma optimista,
adicionándole un atributo de sólo-lectura que identificará la versión del registro (“version”).
Aspectos
OptimisticProtocol: Aspecto abstracto que posee el comportamiento común del
patrón. Define un pointcut llamado update que sirve para especificar el momento en el
cual se guarda el registro sobre el cual se aplica el patrón. Sobre este pointcut se especifica un advice, que es la parte principal del patrón, en donde se verifica que la versión actual del registro sea igual a la de la información a guardar, sino, se lanza una
excepción para informar al usuario que el registro fue actualizado por un tercero y que
debe obtener la última versión y volver a procesarlo antes de poder guardarlo.
En este aspecto también se realiza la implementación (o introducción) del atributo
version de la interface OptimisticRecord.
Por último, para el manejo automático de la versión, se definen tres pointcuts en donde se definirán los momentos de carga, creación y actualización de los registros (implementationLoad, implementationCreate e implementationUpdate). Para cada uno se especifica un advice que agrega la lógica de manejo de la versión. Para ello se agregan dos
métodos abstractos getCurrentVersion() y updateVersion() que serán invocados cuando se
necesite obtener la versión actual o actualizar dicha versión, respectivamente.
APLICACIÓN CONCRETA al CASO DE ESTUDIO
La aplicación al caso de estudio consiste en la definición del registro y el aspecto concreto que especifica los pointcuts y comportamiento necesario para el manejo
de la meta-información del recurso. Como se verá más adelante, el manejo de la metainformación hace que la implementación del patrón no sea completamente localizada.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
103
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
BaseRecord
<<getter>>+getId() : int
Record
<<getter>>+getAttrib () : String
<<setter>>+setAttrib ( attrib : String ) : void
OptimisticRecord
<<getter>>+getVersion() : int
RecordManager
<<constructor>>-RecordManager()
+newRecord( id : int ) : Record
+saveRecord( record : Record ) : void
+deleteRecord( id : String ) : void
RecordImpl
~log : Logger = Logger.getLogger(RecordImpl.class)
-id : int
-attrib : String
-isDirty : boolean = false
-isNew : boolean = true
<<constructor>>+RecordImpl( id : int )
<<getter>>+getAttrib() : String
<<setter>>+setAttrib( attrib : String ) : void
<<getter>>+getId() : int
+load() : boolean
#fillAttributes( rs : ResultSet ) : void
#update() : void
#create() : void
+save() : void
+delete() : void
<<aspect>>
OptimisticProtocol
<<aspect>>
OptimisticRecordAspect
+getCurrentVersion( id : OptimisticRecord ) : int
#updateVersion( record : OptimisticRecord ) : void
<<pointcut>> <<base>>+update( record : OptimisticRecord ){base = execution (public void RecordManager+.saveRecord(Record)) && args(record)}
<<pointcut>> <<base>>+ implementationLoad( record : OptimisticRecord, result : ResultSet ){base = execution(void RecordImpl.fillAttributes(ResultSet)) && args(rs) && target(record)}
<<pointcut>> <<base>>+ implementationUpdate( record : OptimisticRecord ){base = execution(void RecordImpl.update()) && target(record)}
<<pointcut>> <<base>>+ implementationCreate( record : OptimisticRecord ){base = execution(void RecordImpl.create()) && target(record);}
<<declare parents>>
Record implements OptimisticRecord
Figura 7-18: Estructura de la aplicación del patrón “Optimistic Locking”
Interfaces
Record: Define los atributos correspondientes al registro. En el ejemplo sólo posee un
atributo llamado “attrib”.
Clases
RecordManager: Responsable de administrar los registros encapsulando así el acceso
a la base de datos. Posee un método por cada operación posible sobre un registro. El
método newRecord() tiene una doble función, en el caso de que el id ya exista, se obtiene el registro; si por el contrario el id no existe, entonces se procede a crear un nuevo
registro vacío. El método saveRecord() permite guardar el contenido de un registro y
por último se puede borrar un registro llamando a deleteRecord()
RecordImpl: Implementación del registro. Además de implementar el atributo requerido por la interface, posee la lógica protegida para obtener, crear o actualizar los datos del registro (load(), save() y update()). El método save() efectúa una creación o actualización del registro según el estado en el que se encuentre (si ya fue creado o no).
Estos métodos son utilizados por el RecordManager cada vez que una operación es
requerida sobre un registro. Por último, el método fillAttributes() se encarga de popular
el registro con los datos que se obtienen de la base de datos.
104
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
ConnectionPool: Implementación para el soporte de un repositorio de conexiones de
base de datos. Para obtener una nueva conexión basta con llamar al método estático
getConnection(). Nótese que la implementación sólo soporta una base de datos y esto es
intencional, para simplificar el caso de estudio.
Aspectos
OptimisticRecordAspect: Aspecto concreto que extiende de OptimisticProtocol. Este
aspecto realiza la vinculación necesaria para aplicar el patrón al registro del caso de
estudio. La primera acción que toma, es extender la interface Record de OptimisticRecord para poder hacer uso del atributo version. Luego, como definición del pointcut update, define la llamada al método save() de la clase RecordManager o cualquiera
de sus derivados. También agrega la implementación específica de los métodos getCurrentVersion() y updateVersion(), conectándose directamente a la base de datos y obteniendo o actualizando el atributo que representa el número de versión. Por último, se
mapean los pointcuts implementationLoad, implementationCreate e implementationUpdate
con la llamada a los métodos fillAttributes(), create() y update() de la clase RecordImpl.
7.4.8.
Análisis
El patrón se ha podido modularizar y localizar, pero no en forma completa.
Quedan implementaciones tales como el manejo de la meta-información del recurso
que tiene que ser implementado para cada caso en particular, siendo parte central del
patrón. El análisis de las características es el siguiente:
 Localidad del código: En este caso se puede catalogar el código de la implementación como „muy localizado‟ dado que ciertos métodos han tenido que
ser implementados en el aspecto concreto, pero el comportamiento asociado a
la sincronización optimista, se pudo abstraer en un modulo reusable.
 Reusabilidad: La localidad del código lograda, permite alcanzar un alto nivel
de reusabilidad. La reusabilidad no es total, ya que los métodos getCurrentVersion() y updateVersion() del aspecto concreto deben ser implementados por cada
aplicación.
 Independencia: El nivel de independencia es alto pero no es total. La independencia se pierde, por ejemplo, cuando la estructura del registro incorpora un
nuevo atributo que utiliza la misma denominación que el atributo utilizado para identificar el número de versión.
 Transparencia de composición: En este caso, no tiene sentido aplicar más de
una vez el patrón al mismo tipo de registro.
7.4.9.
Componente gráfico
En este caso se implementaron los siguientes componentes visuales:
 Table: Representa una tabla en la base de datos que contiene registros del tipo
“Record”. Se puede configurar el nombre de la tabla y se visualiza el contenido de los registros.
Diego M.S. Erdödy
Patrones de concurrencia y sincronización
105
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 OptimisticRecord: Registro del tipo “Record” en su versión optimista el cual
se puede asociar a un elemento del tipo Table. Posee las siguientes propiedades:
o Record id: clave del registro con el que se desea trabajar.
o Record value: valor del atributo del registro.
En tiempo de simulación se pueden invocar las siguientes acciones:
o Save: Intenta guardar el registro en la base de datos.
o Retrieve: Sobrescribe la instancia del registro con el valor de la base de
datos.
En la siguiente figura se observa un diagrama con una tabla y dos registros, “rec1” y “rec2”, los cuales poseen el mismo id (0 en este caso):
Figura 7-19: Diagrama del patrón “Optimistic Locking”
Una vez iniciada la simulación, en caso de existir el id de algún registro, cada
registro obtiene su información de la base de datos. De manera interactiva, se puede
guardar cualquiera de los registros presionando el botón “save”. Como consecuencia,
se incrementa en uno el número de versión almacenado en la base de datos. En el diagrama se muestra que “rec1” ha sido recientemente guardado y posee el número de
versión 3. De la misma forma, se puede cargar el nuevo valor en “rec2” presionando
el botón “retrieve” del componente. Si en lugar de esto se hubiese querido quiere
guardar el valor de “rec2”, el mecanismo optimista se encargaría de avisar que se debe
actualizar el registro antes de poder guardarlo, dado que el número de versión que
contiene es menor al que se encuentra en la base de datos. Esta discrepancia indica
que alguien ha hecho una modificación desde que fue cargado por última vez. Este
caso es el que muestra la figura.
106
Patrones de concurrencia y sincronización
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
8. Patrones de manejo de eventos
8.1.
8.1.1.
Reactor
Resumen
El patrón de diseño Reactor [Sch 95] separa las responsabilidades de un proveedor
de servicios en un entorno cliente-servidor distribuido, con acceso concurrente a los distintos servicios. El patrón está compuesto por un demultiplexor sincrónico, que tiene como responsabilidad acceder a los distintos canales de comunicación de manera sincrónica
e identificar y decodificar los eventos relevantes. También está compuesto por un notificador, es el encargado de recibir y administrar dichos eventos así como también de notificar a los procesadores correspondientes. Por último, el EventHandler es el que procesa un
evento en particular, sin necesidad de intervenir en la administración de los eventos.
8.1.2.
Otras denominaciones
Notificador, Despachante (“Dispatcher”).
8.1.3.
Problema
El desarrollo de servidores concurrentes plantea ciertos problemas en cuanto al modo de atender los pedidos. Las necesidades a tener en cuenta son:
 El servidor debe brindar una separación clara de responsabilidades entre la
recepción de pedidos, el manejo de los eventos y el procesamiento de los
mismos por parte de los distintos servicios.
 El servidor debe tener una alta disponibilidad. Esto implica que no debe
bloquear el ingreso de nuevos pedidos, al esperar el ingreso de un pedido
en particular o el procesamiento de otro pedido en curso.
 El servidor se debe adaptar de manera sencilla a los cambios en el formato
de los requerimientos o a la incorporación de nuevos servicios.
 El servidor debe brindar una alta performance. Se debe maximizar la cantidad de pedidos procesados por unidad de tiempo y al mismo tiempo minimizar la latencia y el consumo de procesador.
Una de las implementaciones más simples consta de un componente que esté escuchando alternativamente en cada canal, por un período de tiempo prefijado, y que al momento de recibir un pedido, inicie un hilo de ejecución en donde se procese dicho evento.
Un problema asociado a esta implementación es que la creación de nuevos hilos de ejecución es generalmente costosa en cuanto a recursos del sistema operativo (desde ya que
dependerá de la implementación del SO que se esté utilizando) y limita la cantidad de
pedidos que se pueden procesar concurrentemente.
Diego M.S. Erdödy
Patrones de manejo de eventos
107
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
8.1.4.
Solución
La solución propuesta por el patrón consiste en desacoplar los componentes de la
siguiente manera. Los proveedores del servicio, responsables de procesar un tipo de evento específico, serán implementaciones de una interfaz común denominada EventHandler.
Los EventHandler se registran a un componente llamado InitiationDispatcher, que será el
encargado de interceptar los eventos y transmitirlos al EventHandler adecuado. Para esta
tarea cuenta con un Event Demultiplexer cuyo objetivo es la recepción física y decodificación del evento de una manera sincrónica.
Esta solución aplica el concepto de Inversión de Control (IoC) ya que los proveedores de los servicios no tienen que preguntar periódicamente por la llegada de un evento,
sino que la responsabilidad es delegada en otro componente que lo notificará en el momento adecuado. El mecanismo es similar al patrón Observador.
8.1.5.
Caso de estudio
El caso de estudio elegido, basado en el trabajo original [Sch 95], es un servidor de
bitácora (log) distribuido. El servidor recibe mensajes de clientes, a través del protocolo
orientado a conexión TCP, y registra un archivo histórico de los mismos.
8.1.6.
Estructura
El patrón se basa en separar los roles de recepción e identificación de eventos y el
procesamiento de los mismos. A continuación se muestra el diagrama de estructura correspondiente al patrón y la descripción de los participantes que cumplirán dichos roles:
InitiationDispatcher
+handleEvents()
+registerHandler(in handler : EventHandler, in event : Event)
+deregisterHandler(in handler : EventHandler, in event : Event)
1
*
«interface»
EventHandler
+handleEvent(in event : Event)
+getHandle() : Handle
1
1
1
1
«interface»
Handle
Synchronous Event Demultiplexer
1
*
Figura 8-1: Estructura del patrón “Reactor”
Los participantes del patrón son los siguientes:
 EventHandler: Encargado de procesar un determinado tipo de evento. El procesamiento es llevado a cabo en el método handleEvent().
108
Patrones de manejo de eventos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 Handle: Objeto encargado del acceso a un canal de comunicaciones de entrada
(como por ejemplo un socket TCP), por el cual ingresan los eventos.
 Synchronous Event Demultiplexer: Espera sobre un grupo de Handles e informa
cuando se recibió un evento relevante que puede ser procesado.
 InitiationDispatcher: Objeto mediador entre el canal de comunicación y los
EventHandlers. Contiene un conjunto de pares “Event-EventHandler” que especifican cual EventHandler deberá atender cierto tipo de evento. Define los métodos
registerHandler() y deregisterHandler() para agregar o quitar pares al conjunto, respectivamente. El método handleEvents() utiliza el demultiplexer para esperar la
llegada de eventos y los entrega al EventHandler correspondiente.
8.1.7.
Estrategia de implementación Orientada a Aspectos
El núcleo del patrón se divide en dos partes. Por un lado, los elementos involucrados en el procesamiento de los eventos y por otro, los concernientes a la recepción y derivación de dichos eventos.
NÚCLEO - Procesamiento de Eventos
La parte de procesamiento de eventos del núcleo, contiene las interfaces que definirán los puntos de extensión del patrón. También define los elementos básicos como el
evento a utilizar y la encapsulación de la conexión (Handle).
El diagrama de estructura es el siguiente:
Diego M.S. Erdödy
Patrones de manejo de eventos
109
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Event
EventHandlerImpl
EventHandler
+handleEvent( e : Event ) : void
<<getter>>+getHandle() : Handle
<<introduction>>
EventHandler
<<constructor>>+Event( code : byte )
<<getter>>+getCode() : byte
<<JavaElement>>+toString() : String{JavaAnnotations = @Override}
<<JavaElement>>+equals( ev : Object ) : boolean{JavaAnnotations = @Override}
<<JavaElement>>+hashCode() : int{JavaAnnotations = @Override}
<<crosscut>>
Handle
<<introduction>>
<<constructor>>+Handle( host : String, port : int )
<<constructor>>+Handle( port : int )
<<constructor>>+Handle( s : Socket )
+waitForEvent( timeout : int, dispatcher : InitiationDispatcher ) : boolean
+read( b : byte"[]" ) : void
+read() : Object
+write( e : Event ) : void
+write( b : byte"[]" ) : void
+write( o : Object ) : void
<<setter>>+setInUse( b : boolean ) : void
+close() : void
+waitForDeath() : void
<<getter>>+getLastEvent() : Event
EventHandler
BaseTypeEH
-reactor : Reactor
-handle : Handle
<<getter>>+getReactor() : Reactor
<<getter>>+getHandle() : Handle
<<setter>>+setReactor( reactor : Reactor )
<<setter>>+setHandle( handle : Handle )
AcceptorImpl
Acceptor
<<setter>>+setPeerHandle( h : Handle ) : void
<<introduction>>
Acceptor
<<crosscut>>
<<introduction>>
Acceptor
BaseTypeA
-peerHandle : Handle
<<getter>>+getPeerHandle() : Handle
<<setter>>+setPeerHandle( handle : Handle )
Figura 8-2: Estructura del Núcleo (sector Event Handling) del patrón “Reactor”
Interfaces
EventHandler: Procesador de eventos que implementará un servicio en particular. Posee
un método handleEvent() dedicado al procesamiento propiamente dicho del evento al cual
se registró. El método getHandle() permite obtener la referencia del canal asociado al
evento, para poder acceder a los datos que formarán parte de la entrada del proceso.
Acceptor: Tipo especial de EventHandler cuya responsabilidad es recibir nuevas conexiones. El Handle asociado debe ser de tipo servidor (para recibir conexiones) y cuando se recibe una nueva conexión, el nuevo Handle creado se almacena bajo el atributo
peerHandle. Generalmente, se registra a eventos del tipo OPEN_SERVICE (nueva conexión) y el procesamiento implica la registración de un EventHandler específico del
servicio que atenderá los eventos de datos de la conexión cliente.
Clases
Event: Clase que encapsula el evento que será transmitido. El evento se identifica por un
código numérico. Ya que se trata de un objeto inmutable (que no puede cambiar de estado), dicho código se debe proveer al momento de construirlo y solo se puede acceder a
través del método getCode().
Handle: Encargado del acceso a un canal TCP. El acceso al canal se puede iniciar en modo servidor, especificando solamente el puerto en el constructor, o en modo cliente, en
110
Patrones de manejo de eventos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
cuyo caso se debe proveer además la dirección de destino. Posee métodos de lectura/escritura (read() y write() y de recepción sincrónica de eventos (waitForEvent()). También
se puede acceder al último evento recibido (getLastEvent()).
Aspectos
EventHandlerImpl: Aspecto encargado de la implementación de la interface EventHandler. Introduce los atributos reactor y handle, y los métodos getter y setter de dichos atributos.
AcceptorImpl: Implementación de la interface Acceptor. Introduce el atributo peerHandle
y los métodos getter y setter asociados.
NÚCLEO – Direccionamiento de Eventos
La parte de direccionamiento de eventos del núcleo tiene como responsabilidad la
recepción y entrega de los eventos a los EventHandlers correspondientes. Para ello cuenta
con un mecanismo de registración de EventHandlers a eventos de un tipo específico.
El patrón original está pensado para lenguajes como C, que poseen mecanismos tales como el select, el cual permite capturar eventos sobre múltiples Handles de manera
simultánea. Para reemplazar dicho mecanismo, se utilizó un esquema de round-robin sobre los handles con un tiempo de expiración ajustable.
El diagrama de estructura es el siguiente:
InitiationDispatcher
HandleInfoManager
HandleInfo
+run() : void
+kill() : void
<<getter>>+getEventInfoManager() : HandleInfoManager
<<getter>>+getCurrentHandleInfo() : HandleInfo
+registerHandler( eh : EventHandler, e : Event ) : void
+removeHandler( eh : EventHandler, e : Event ) : void
<<getter>>+getCurrentHandleInfo() : HandleInfo
<<getter>>+getNextHandleInfo() : HandleInfo
<<constructor>>+HandleInfo( eh : EventHandler )
+addEvent( event : Event ) : void
+removeEvent( event : Event ) : void
<<getter>>+getEvents() : Collection
<<getter>>+getHandle() : Handle
<<getter>>+getEventHandler() : EventHandler
Reactor
+registerHandler( handler : EventHandler, type : Event ) : void
+removeHandler( handler : EventHandler, type : Event ) : void
+handleEvents( timeout : long ) : void
+stopHandling() : void
Handle
Event
EventHandler
Figura 8-3: Estructura del Núcleo (sector Event Dispatching) del patrón “Reactor”
Clases
Reactor: Componente central del patrón. Actúa de fachada para el InitiationDispatcher
que opera por detrás. Posee métodos para registrar y quitar EventHandlers al mecanismo
de reacción. El método handleEvents() inicia dicho mecanismo. El método stopHandling()
detiene el mecanismo.
InitiationDispatcher: Encargado del mecanismo de reacción. Extiende de la clase Thread
para poder correr en un hilo de ejecución independiente. El método start() inicia el mecanismo que consiste en recorrer los distintos EventHandlers y esperar un determinado
tiempo a que ocurra algún evento. Ante la llegada de un evento se ejecuta el EventHandler registrado. De lo contrario se sigue con el siguiente.
Diego M.S. Erdödy
Patrones de manejo de eventos
111
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
HandleInfoManager: Encargado de administrar la información de registraciones de
EventHandlers y el recorrido de los mismos. Los métodos getCurrentHandleInfo() y getNextHandleInfo() permiten recorrer los EventHandlers.
HandleInfo: Contenedor de la asociación entre un EventHandler y los distintos eventos
a los cuales se registró. Posee métodos para el acceso al EventHandler y agregar o quitar
eventos. El método getHandle() devuelve el Handle asociado al EventHandler.
APLICACIÓN CONCRETA al CASO DE ESTUDIO
La aplicación del patrón consta de las implementaciones de las dos interfaces que
definen los puntos de extensión y un objeto que representa a un cliente del servicio. El
diagrama de estructura es el siguiente:
LoggingClient
EventHandler
Acceptor
<<constructor>>+LoggingClient( host : String, port : int )
<<constructor>>+LoggingClient()
+log( s : String ) : void
+close() : void
+init() : void
<<getter>>+getHostName() : String
<<setter>>+setHostName( hostName : String ) : void
<<getter>>+getPort() : int
<<setter>>+setPort( port : int ) : void
LoggingHandler
LoggingAcceptor
<<constructor>>+LoggingHandler( handle : Handle )
<<constructor>>+LogginHandler()
+handleEvent( evt : Event ) : void
+init()
+logMessage( message : String )
<<constructor>>+LoggingAcceptor( port : int )
<<constructor>>+LoggingAcceptor()
+handleEvent( e : Event ) : void
+init()
Figura 8-4: Estructura de la aplicación concreta del patrón “Reactor”
Clases
LoggingClient: Cliente encargado de enviar los pedidos de log al servidor. El constructor
debe recibir la información necesaria para conectarse con el servidor remoto, es decir, el
nombre de host y el puerto. El método log() envía el mensaje especificado mientras que el
método close() cierra la conexión.
LogginAcceptor: Aceptor de conexiones para el servicio de logging que implementa la
interface Acceptor. El método handleEvent() crea y registra un nuevo handler del tipo LoggingHandler sobre el peerHandler.
LoggingHandler: Procesador del servicio de logging. El método handleEvent() registra el
mensaje recibido.
8.1.8.
Análisis
La implementación AOP de este patrón tiene poca relevancia ya que los aspectos
solamente se utilizaron para la implementación de las interfaces (en vez de usar clases
abstractas). Si bien existen características positivas en la implementación, como localidad
y reusablidad moderadas, es mérito del diseño OOP y no es una contribución por parte de
AOP. A continuación se detalla el análisis de la implementación:
112
Patrones de manejo de eventos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
 Localidad del código: La localidad lograda con OOP no se pudo mejorar con
AOP. El manejo de eventos dentro del Handler y el Acceptor en su totalidad, son
componentes propios del patrón que no han podido desacoplarse de la implementación del caso de estudio. Por lo tanto se considera que no hay localidad.
 Reusabilidad: De la misma forma, la reusabilidad no ha mejorado con la implementación AOP y existe código propio del patrón que debe implementarse cada
vez que se aplique a un caso en particular.
 Independencia: No existe la independencia, ya que el código no asociado al
patrón (el método log()) no tiene sentido sin los componentes que lo rodean.
 Transparencia de composición: El mismo EventHandler puede intervenir en más
de un reactor de una manera transparente.
8.1.9.
Componente gráfico
Los siguientes componentes fueron implementados:
 LogClient: Cliente del servicio de logging. Permite enviar un mensaje de log a un
server en particular. Posee las siguientes propiedades:
o hostname: dirección del host donde reside el servidor, en caso de una conexión remota.
o port: puerto TCP donde está instalado el servidor, para el caso de una conexión remota.
o message: mensaje que se enviará al servidor.
El componente posee una acción para enviar el mensaje, denominada log(). Ya que
el componente no contiene lógica asociada a la ejecución de la simulación, sólo
tiene sentido ejecutarlo en modo interactivo y poder activarlo por medio de la acción.
El componente puede operar de forma local o remota. La primera opción se realiza a través de una conexión en el diagrama a un componente LogServer. Para la
opción remota, se deben configurar las propiedades hostname y port del servidor
remoto.
 LogServer: Servidor de logging. Permite conexiones con componentes LogClient,
en cuyo caso se establece una conexión local. También permite conexiones remotas, via TCP. La historia de los mensajes recibidos la muestra en el compartimiento de estado.
El siguiente es un ejemplo que muestra a un servidor conectado localmente a dos
clientes:
Diego M.S. Erdödy
Patrones de manejo de eventos
113
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Figura 8-5: Diagrama ACVF del caso de estudio para el patrón “Reactor”
El diagrama corresponde a una simulación en donde se ha invocado la acción del
componente logclient dos veces y luego se ha repetido la operación sobre el componente
logclient2. En el componente logserver se puede ver la historia de los mensajes recibidos.
114
Patrones de manejo de eventos
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
9. Patrones de seguridad y fiabilidad
9.1.
9.1.1.
Watchdog
Resumen
Un Watchdog es un componente que supervisa el comportamiento de una característica determinada de otro componente. Dicha supervisión tiene como objetivo eliminar errores groseros que pueden ocurrir dentro del sistema, como por ejemplo, el orden de
ejecución de diferentes pasos o restricciones de tiempo.
9.1.2.
Otras denominaciones
Sentinela
9.1.3.
Problema
Un sistema de tiempo real se podría simplificar en eventos que tienen que ocurrir
dentro de un período específico de tiempo, fuera del cual dichos eventos no tienen relevancia o sentido. En estos sistemas, es necesario verificar que el funcionamiento básico
del sistema sea el correcto (ya sea en el dominio del tiempo, en la sucesión de eventos o
en algún otro criterio) y actuar, aunque sea de manera drástica, en consecuencia.
9.1.4.
Solución
El fundamento del patrón, consiste en contar con un participante llamado Watchdog, que se encargará de verificar periódicamente el estado de salud del sistema. En caso
de detectar una anomalía, procederá con alguna acción predeterminada como puede ser la
reinicialización del sistema.
En sistemas de tiempo real, las verificaciones que se realizan sobre el sistema, generalmente están relacionadas a una base de tiempo que actúa de referencia.
9.1.5.
Caso de estudio
El caso de estudio que se ha elegido para este patrón es el de un marcapasos vigilado por un supervisor. El supervisor debe asegurar que los latidos que indique el marcapasos estén en un rango determinado de tiempo, es decir, la distancia entre dos latidos consecutivos no debe superar el límite superior ni estar por debajo del límite inferior.
Diego M.S. Erdödy
Patrones de seguridad y fiabilidad
115
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
9.1.6.
Estructura
El siguiente es un diagrama de componentes del patrón:
Canal de actuación
Transición de
procesamiento
Tansformación
de datos
Procesamiento
de entrada
Procesamiento
de salida
Origen de
datos de actuación
Señal de
control
Actuador
Reportar
anomalía
Watchdog
Base de tiempo
Figura 9-1: Diagrama de estructura del patrón “Watchdog”
El diagrama presenta un caso general de procesamiento en tiempo real a verificar,
representado por el canal de actuación y sus componentes. El watchdog recibe por un
lado, la información enviada por los componentes de transformación y procesamiento de
salida, así como también, una referencia temporal independiente, para poder tomar decisiones en ese dominio. En caso de que el watchdog detecte una anomalía, inmediatamente
informará al sistema para que este tome las acciones necesarias, que pueden ser registrar
el incidente, apagar o efectuar una verificación de integridad del sistema.
Los participantes del patrón son:
 Canal de Actuación: Canal a través del cual se comunican los distintos compo-






116
nentes que forman parte del circuito de actuación. Dicho circuito comprende tanto
las señales de entrada de los sensores, el procesamiento de los datos como así
también las señales de salida hacia los componentes de actuación.
Actuation Data Source: Representa la entrada de datos al sistema desde los dispositivos de sensado.
Procesador de Entrada: Transforma los datos enviados por los dispositivos de
sensado en datos que puedan ser comprendidos por el procesador.
Actuator: Dispositivo físico que ejecuta una acción sobre el sistema a partir de los
datos provistos por el canal de actuación.
Procesador de Salida: Encargado de transformar los datos de salida de forma tal
que sean correctamente comprendidos por el dispositivo actuador.
Procesador de Datos: Procesa los datos recibidos por los sensores, ya sea de manera secuencial (un solo dato permanece en el canal a la vez) o paralela (puede
haber múltiples datos en diferente etapa de procesamiento en el canal para un determinado momento).
Base de tiempo: Referencia temporal independiente que guía el funcionamiento
del Watchdog.
Patrones de seguridad y fiabilidad
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA

9.1.7.
Watchdog: Espera por una señal periódica enviada por los componentes del canal
de actuación. Si dicha señal no es recibida dentro del tiempo esperado, debe indicar la anomalía al canal.
Estrategia de implementación Orientada a Aspectos
El patrón se divide en una capa reusable o núcleo y la aplicación al caso de estudio
correspondiente.
NÚCLEO
El núcleo consta de un aspecto abstracto que contiene la lógica reusable del patrón
y dos interfaces que definen los roles del patrón. El siguiente es el diagrama de estructura
y la descripción de los elementos:
<<aspect>>
WatchDogProtocol
#wd : WatchDog
#sendSignal( r : Resetable ) : void
#init( r : Resetable ) : void
+setWatchDog( watchdog : WatchDog ) : void
<<advice>> <<base>>+after returning( r : Resetable ){base = watchableCall(r)}
<<advice>> <<base>>+before( r : Resetable ){base = initCall(r)}
<<pointcut>>+initCall( resetab le : Resetab le )
<<pointcut>>+watchab leCall( r : Resetab le )
WatchDog
+init() : void
+processSignal( r : Resetab le ) : void
Resetable
+reset() : void
Figura 9-2: Estructura del Núcleo del patrón “Watchdog”
Interfaces
WatchDog: Definición de la interface de un watchdog. Posee un método init() para realizar tareas de inicialización y el método principal processSignal() que será llamado cada vez
que la señal que supervisa sea activada.
Resetable: Indica que un componente puede ser reiniciado. Esto se realiza a través del
método reset().
Aspectos
WatchDogProtocol: Aspecto abstracto que posee el comportamiento generalizable del
patrón. Posee un atributo que almacena el Watchdog a utilizar y un método setWatchdog
Diego M.S. Erdödy
Patrones de seguridad y fiabilidad
117
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
para asignarlo. Define dos pointcuts abstractos, initCall(Resetable) y watchableCall(Resetable). El primero permite especificar cuál será el momento de inicialización del
componente a supervisar para que el watchdog pueda hacer alguna tarea de inicialización
acorde. El segundo permite especificar cuál será el punto a supervisar dentro del componente. También posee un advice asociado a cada uno de los pointcuts abstractos. El primer advice indica que antes de llamar al pointcut initCall, se llame al método protegido
init() del aspecto. Este llama al método init() del watchDog. El segundo advice indica que
luego de terminar con el pointcut watchableCall(), se llame al método protegido sendSignal(), el cual llama al método processSignal() del watchDog.
APLICACIÓN CONCRETA al CASO DE ESTUDIO
La aplicación del patrón se lleva a cabo con la implementación de las interfaces
provistas para la definición de los roles y la especialización del aspecto abstracto, que
define los valores para los dos pointcuts. También se proveen los objetos que representan
a los participantes del caso de estudio, el Watchdog y el Pacemaker. El diagrama de estructura y la descripción es el siguiente:
WatchDog
+init() : void
+processSignal( r : Resetab le ) : void
TimerWatchdog
<<aspect>>
<<constructor>>+TimerWatchdog()
<<getter>>+getMaxLevel() : long
<<setter>>+setMaxLevel( maxLevel : long ) : void
<<getter>>+getMinLevel() : long
<<setter>>+setMinLevel( minLevel : long ) : void
<<getter>>+getLastInterval() : long
+init() : void
+processSignal( r : Resetable ) : void
WatchDogProtocol
<<instantiation>>
<<aspect>>
PacemakerWatchDogSignal
{instantiation = pertarget(initCall(Resetable))}
Pacemaker
#markBeat() : void
+reset() : void
+run() : void
+toString() : String
+init() : void
+stop() : void
<<getter>>+getBeatInterval() : long
<<setter>>+setBeatInterval( interval : long ) : void
<<getter>>+getBeatLength() : long
<<setter>>+setBeatLength( beatLength : long ) : void
<<base>> <<pointcut>>+initCall( resetable : Resetable ){base = execution (void Pacemaker+.init()) && target(r)}
<<base>> <<pointcut>>+watchableCall( r : Resetable ){base = execution (void Pacemaker+.markBeat()) && target(r)}
<<declare parents>>
Pacemaker implements Resetable
Resetable
+reset() : void
Figura 9-3: Estructura de la aplicación del patrón “Watchdog” al caso de estudio
Clases
Pacemaker: Representación de un marcapasos. Posee un método para inicializar el marcapasos llamado init() y uno para reiniciarlo, en caso de alguna falla, llamado reset(). El
método run() pone en funcionamiento la actividad del marcapasos, mientras que el método stop() lo detiene. Permite configurar tanto el tiempo medio de cada latido, como así
también el intervalo entre latidos, a través de los getters y setters de las propiedades bea-
118
Patrones de seguridad y fiabilidad
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
tLength y beatInterval. Por último, el método markBeat() representa la funcionalidad del lati-
do en sí mismo.
TimerWatchdog: Implementación de la interface WatchDog. Se basa en la revisión del
tiempo de los intervalos delimitados por cada señal. Si en algún caso, dicho intervalo se
encuentra fuera de los límites prefijados, entonces se dispara la reinicialización del componente. Posee dos propiedades que representan el límite mínimo (minLevel) y el máximo (maxLevel) de tolerancia. También posee un método para obtener el intervalo medido
entre las dos últimas señales llamado getLastInterval().
Aspectos
PacemakerWatchDogSignal: Aspecto concreto que extiende de WatchDogProtocol y
configura la aplicación del patrón. Por un lado hace que Pacemaker implemente la interface Resetable. Por otro lado, especifica que el pointcut a supervisar será la llamada al
método markBeat() de Pacemaker o cualquier clase derivada y que el pointcut de inicialización será el método init() del mismo conjunto de clases. Por último, se especifica que el
modo de instanciación del aspecto sea “por target” dado que el Watchdog a utilizar es
único. De no ser así, el mismo watchdog se haría cargo de todas las aplicaciones del
patrón dentro de la misma JVM, sin la posibilidad de configurar cada uno por separado.
9.1.8.
Análisis
De los diagramas de estructura, se desprende que el patrón ha podido modularizarse de
forma completa, ya que en la aplicación del patrón sólo se definen puntos de inserción. A
continuación se analizan las características de la implementación:
 Localidad del código: Hay localidad completa de la lógica inherente al patrón en
el aspecto abstracto.
 Reusabilidad: Es total dado que sólo hace falta indicar los roles y pointcuts para
poder aplicar el patrón.
 Independencia: El rol definido como elemento a observar sólo requiere que implemente la interface Resetable. Dado que este comportamiento puede ser insertado por medio de un aspecto, el objeto que debe implementar el rol se independiza
de manera completa del patrón.
 Transparencia de composición: Varias aplicaciones del patrón pueden convivir
transparentemente dentro del mismo componente ya que no tiene requerimiento
alguno.
9.1.9.
Componente gráfico
Para este caso se han implementado dos componentes gráficos. El primero, llamado
“Pacemaker” representa un marcapasos. Este tiene dos propiedades configurables, la duración de cada latido y el intervalo entre latidos, ambos expresados en milisegundos. El
segundo, simula un Watchdog y se puede vincular al marcapasos que se quiera controlar.
Tiene como propiedades configurables, los límites de tiempo máximo y mínimo tolera-
Diego M.S. Erdödy
Patrones de seguridad y fiabilidad
119
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
bles entre latidos. El siguiente es un diagrama que muestra a ambos funcionando en una
simulación:
Figura 9-4: Simulación de patrón Watchdog
Los latidos del marcapasos se pueden visualizar en la zona de texto del componente
mientras que el Watchdog indica el tiempo que ha pasado desde la detección del último
latido. En caso de que este tiempo esté fuera de los límites prefijados, el Watchdog procede a reinicializar el componente controlado.
Vale aclarar que un marcapasos puede ser controlado por más de un Watchdog a la
vez, lo que deja en claro el alto nivel de transparencia de composición que presenta el
patrón.
Figura 9-5: Propiedades de los componentes gráficos Pacemaker y Watchdog
La simulación permite visualizar la interacción entre ambos componentes. Para el
caso del marcapasos, el compartimiento de estado muestra un mensaje cada vez que se
marca un latido, por el tiempo que dure. Dichos valores dependerán de la configuración
120
Patrones de seguridad y fiabilidad
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
del componente. El Watchdog, por su parte, muestra en el compartimiento de estado, la
duración entre los últimos dos latidos. También muestra si se ha salido de los parámetros
configurados y qué acción se ha tomado en caso afirmativo.
Diego M.S. Erdödy
Patrones de seguridad y fiabilidad
121
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
10. Resultados y Conclusiones
La siguiente es una tabla comparativa de los resultados obtenidos luego del análisis
de los distintos patrones de diseño:
Nombre del
patrón
Localidad
Propiedades de modularidad
TransparenIndeReusabilidad cia de com- pendenposición
cia
Tipos de Roles
Únicos
Balking
Sí
Sí
Sí
Sí
-
Observador
Sí
Sí
Sí
Sí
-
Rendezvous
Sí
Sí
Sí
Sí
Rendezvous
Watchdog
Sí
Sí
Sí
Sí
Watchdog
Optimistic
Alta4
Alta
Sí
Alta
No
No
Sí
No
Reactor
OptimisticController
Reactor,
Acceptor,
Handler
Superpuestos
BalkingClient,
BalkingComponent
Publisher,
Subscriber
RendezvousMember
WatchdogComponent
Client,
Resource
EventHandler
En la primera columna se listan todos los patrones de diseño analizados. Luego le
sigue el listado de las cuatro propiedades que han sido analizadas y por último una separación de los roles de cada patrón según su tipo.
Al igual que en el trabajo hecho sobre los patrones GoF [Han 02], se han dividido
los roles que componen cada patrón en dos tipos y a continuación se verá qué relación
existe entre la cantidad de roles de cada tipo por patrón y la capacidad de cumplir con
cada una de las características analizadas.
Los roles únicos son los roles del patrón que son implementados por un componente que no tiene funcionalidad más allá del rol que le es asignado. Esto quiere decir que
sólo existe para cumplir el rol dentro del patrón y, por lo tanto, es definido por él. En
cambio, los roles superpuestos son los roles cuyos componentes comparten la funcionalidad con roles fuera del patrón. Por ejemplo, el rol de Sujeto, en el patrón Observador, es
un rol superpuesto ya que quien lo implemente, siempre va a cumplir uno o más roles
adicionales. Si no fuera así, no habría nada que observar. Por otro lado, la implementación del rol Rendezvous en el patrón del mismo nombre, sólo cumple esa función, no
tiene ninguna responsabilidad fuera del patrón.
En el trabajo de Hannemann [Han 02], se dividen los patrones en tres grupos según
los tipos de roles que posean: (a) los que sólo poseen roles superpuestos, (b) los que poseen roles de ambos tipos y por último, (c) los que únicamente poseen roles únicos. En
este trabajo no se han identificado patrones del grupo c, aunque el más cercano por cantidad de roles únicos, es el patrón Reactor.
La clasificación “Alta” indica que faltaron algunos detalles para lograr el objetivo de la propiedad. No se
intenta utilizar una escala de graduación para cada propiedad. Sólo si se cumplió o no, con la salvedad de la
calificación “Alta” para identificar casos cercanos al Sí.
4
Diego M.S. Erdödy
Resultados y Conclusiones
123
Generación automática de redes neuronales con ajuste de parámetros basado en algoritmos genéticos
Los resultados obtenidos para los patrones analizados es similar al encontrado por
Hannemann. Para los patrones del grupo (a), todas las propiedades se cumplen completamente. Esto indica que la modularización ha sido total. Para el grupo (b), el resultado
varía levemente en algunos patrones, en nuestro caso para el patrón Optimista. Si bien el
grado de modularidad es elevado, existen detalles que no han podido abstraerse. Por último, para el grupo (c), la utilidad de la aplicación de aspectos es limitada o nula. El patrón
Reactor se puede tomar como ejemplo de este caso.
124
Resultados y Conclusiones
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
11. Futuros trabajos
El presente trabajo se realizó con una implementación de AOP “genérica” o de
propósito general, como lo es AspectJ. Para la aplicación en patrones concurrentes y distribuidos especialmente, sería más adecuada la utilización de una implementación orientada específicamente a un entorno distribuido. Al comienzo del trabajo se analizó la
herramienta DJCutter, pero fue descartada por no contar con el nivel de madurez, soporte
y documentación suficiente.
DJCutter [Mug 03], es una herramienta pensada para la aplicación de AOP en entornos distribuidos. DJCutter es una extensión al lenguaje AspectJ que permite declarar
pointcuts distribuidos, es decir, que capture joinpoints de JVMs situados en máquinas
diversas. Para dicha tarea provee de agentes que se encargan de la sincronización que
deben correr en cada máquina que participe.
En futuros trabajos, se pueden analizar los beneficios de implementar con DJCutter
(u otra herramienta de características similares) los patrones revisados en el presente trabajo, para determinar si dicha implementación realmente provee ventajas sobre una implementación de propósito general.
Diego M.S. Erdödy
Futuros trabajos
125
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Referencias
[Ajdt 06] www.eclipse.org/ajdt (2006)
[Aks 92] Mehmet Aksit Lodewijk Bergmans and Sinan Vural (1992) An ObjectOriented Language-Database Integration Model: The Composition-Filters
Approach, in Proceedings of ECOOP ‟92, Springer-Verlag
[Ale 02] Roger Alexander and James Bieman (2002) Challenges of Aspect-oriented
Technology, Proc. Internacional Conference of Software Engineering 2002
Workshop on Software Quality, Orlando, Florida, USA
[Alt 04] Rubén Altman, Alan Cyment (2004) SetPoint: Un enfoque semántico para la
resolución de pointcuts en AOP, Tésis de Licenciatura, Facultad de Ciencias
Exactas, UBA
[Asj 06] www.eclipse.org/aspectj (2006)
[AWerkz 05] http://aspectwerkz.codehaus.org (Noviembre 2005)
[Ber 01] Lodewijk Bergmans and Mehmet Aksit (2001) Composing Crosscutting Concerns Using Composition Filters, Communications of the ACM, Oct.
2001/Vol. 44 No. 10, pp. 51-57, ACM Press
[Ber 94] Lodewijk Bergmans (1994) The Composition-Filters Object Model, Technical
report, Department of Computer Science, University of Twente, Netherlands
[Bur 03] Burke Bill, Brock Adrian (2003) Aspect-Oriented Programming and JBoss www.onjava.com/pub/a/onjava/2003/05/28/aop_jboss.html
[Cli 96] Marshall P. Cline (1996) The Pros and Cons of Adopting and Applying Design
Patterns in the Real World, Communications of the ACM, Oct. 1996/Vol. 39
No. 10, pp. 47-49, ACM Press
[Coh 04] Tal Cohen and Joseph (Yossi) Gil (2004) AspectJ2EE = AOP + J2EE, in the
8th European Conference on Object-Oriented Programming (ECOOP 2004).
[Dou 02] Bruce Powel Douglass (2002) Real-Time Design Patterns: Robust Scalable
Architecture for Real-Time Systems, Addison-Wesley
[Fow 04] Fowler M. (2004) Inversion of Control Containers and the Dependency Injection pattern, www.martinfowler.com/articles/injection.html
[Gam 93] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1993) Design Patterns: Abstraction and Reuse of Object-Oriented Design, in Proceedings of the 7th European Conference on Object-Oriented Programming
(ECOOP 2003), pp. 406-431, Kaiserslautern, Alemania.
[Gam 95] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995) Design Patterns: Elements of reusable Object-Oriented Software, AddisonWesley
[Gef 06] www.eclipse.org/gef/ (2006)
[Gil 05] Joseph Gil, Itay Maman (2005) Micro Patterns in Java Code, in Proceedings
of the 20th annual ACM SIGPLAN conference on Object oriented programming, systems, languages, and applications, San Diego, CA, USA, pp 97 –
116, ACM Press
[Han 02] Hannemann J., -Kiczales G. (2002) Design Pattern Implementation in Java
and AspectJ, Proceedings of the 17th ACM SIGPLAN conference on Object-
Diego M.S. Erdödy
Referencias
127
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
oriented programming, systems, languages, and applications, pp. 161-173,
ACM Press
[Hur 95] Walter L. Hürsch and Cristina Videira Lopes (1995) Separation of Concerns, Technical report by the College of Computer Science, Northeastern
University, Boston MA
[JBA 06] labs.jboss.com/portal/jbossaop (2006)
[JBS 06] jboss.com (2006)
[JHD 06] www.jhotdraw.org (2006)
[Jim 98] Ricardo Jiménez-Peris, Marta Patiño-Martínez y Sergio Arévalo (2002)
Multithreaded Rendezvous: A Design Pattern for Distributed Rendezvous,
Universidad Politécnica de Madrid, Facultad de Informática, España
[Joh 97] Ralph E. Jonson (1997) Components, Frameworks, Patterns, Proceedings of
the 1997 Symposium on Software reusability, Boston, Massachusetts, United
Status, pp 10-17, ACM Press
[JSR 163] jcp.org/en/jsr/detail?id=163 (2006)
[Ker 05] Mik Kersten (2005), AOP@Work: AOP tools comparison, part 1,
www.ibm.com/developerworks/java/library/j-aopwork1
[Kic 01] Gregor Kiczales et al. (2001) An Overview of AspectJ, Lecture Notes In Computer Science; Vol. 2072, Proceedings of the 15th European Conference on
Object-Oriented Programming, pp 327 – 353, Springer-Verlag
[Kic 91] Gregor Kiczales, Jim Des Rivieres, Daniel G. Bobrow (1991) The Art of the
Metaobject Protocol, MIT Press
[Kic 92] Gregor Kiczales (1992) Towards a New Model of Abstraction in Software Engineering, en Proceedings of the IMSA'92 Workshop on Reflection and Metalevel Architectures
[Kic 97] Gregor Kiczales et al. (1997) Aspect-Oriented Programming, in Proceedings of
the European Conference on Object-Oriented Programming (ECOOP), Finland. Springer-Verlag
[Koj 03] Sergei Kojarski, Karl Lieberherr, David H. Lorenz, Robert Hirschfeld
(2003) Aspectual Reflection, Proceedings of the AOSD 2003 Workshop on
Software-engineering Properties of Languages for Aspect Technologies,
March 17-21, 2003, Boston, Massachusetts
[Kop 04] Christian Koppen and Maximilian Stoerzer (2004) PCDiff: Attacking the
Fragile Pointcut Problem, in European Interactive Workshop on Aspects in
Software, Berlin, Germany, September 2004.
[Kru 92] Charles W. Krueger (1992) Software reuse, ACM Computing Surveys, Junio
1992, pp. 131-183, ACM Press
[Lie 96] Karl Lieberherr (1996) Adaptive Object-Oriented Software The Demeter Method, PWS Publishing Company
[Mez 03] Mira Mezini, Klaus Ostermann (2003) Conquering aspects with Caesar, Proceedings of the 2nd international conference on Aspect-oriented software development, Boston, Massachusetts, pp. 90-99, ACM Press
[Mug 03] Muga Nishizawa, Shigeru Chiba and Michiaki Tatsubori (2003) Remote
Pointcut - A Language Construct for Distributed AOP, Proceedings of the 3rd
international conference on Aspect-oriented software development, pp. 7-15,
ACM Press
128
Referencias
Diego M.S. Erdödy
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
[OWL 06] www.w3.org/2004/OWL/ (2006)
[Ram 03] Ramnivas Laddad (2003), AspectJ in Action, Manning
[Rus 04] Miles Russell (2004) An Introduction to Aspect-Oriented Programming with
the Spring Framework www.onjava.com/pub/a/onjava/2004/07/14/springaop.html
[Sch 00] Douglas C. Schmidt, Michael Stal, Hans Rohnert and Frank Buschmann
(2000) Pattern-Oriented Software Architecture, Patterns for Concurrent and
Networked Objects, Volume 2, Wiley & Sons
[Sch 03] Douglas C. Schmidt, Frank Buschmann (2003) Patterns, frameworks, and
middleware: their synergistic relationships, en Proceedings of the 25th International Conference on Software Engineering, Portland, Oregon, pp. 694 –
704, IEEE Computer Society
[Sch 95] Douglas C. Schmidt (1995) Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Dispatching, Pattern languages of program
design, pp. 529 – 545, ACM Press/Addison-Wesley Publishing Co.
[Ste 02] Dominik Stein, Stefan Hanenberg, and Rainer Unland (2002) Designing Aspect-Oriented Crosscutting in UML, In AOSD-UML Workshop at AOSD '02,
Enschede, The Netherlands, Apr. 2002
[Sul 01] Gregory T. Sullivan (2001) Aspect-Oriented Programming Using Reflection
and Metaobject Protocols, Communications of the ACM, Oct. 2001/Vol. 44
No. 10, pp. 95-97, ACM Press
[UML 06] www.uml.org (2006)
[XDoc 06] xdoclet.sourceforge.net (2006)
Diego M.S. Erdödy
Referencias
129
TESIS DE GRADO EN INGENIERÍA INFORMÁTICA
Glosario
AJDT: AspectJ Development Tool
AOP: Aspect Oriented Programming (Programación Orientada a Aspectos)
AOSD: Aspect Oriented Software Development (Desarrollo de Software Orientado a
Aspectos)
AWT: Abstract Window Toolkit. Biblioteca gráfica original de Java.
C3: Cross-Cutting Concern (Interés Transversal)
CJR: Core Java Reflection
ACVF: Aspectual Component Visualization Framework
EditPart: Elemento integrante de la capa de controlador perteneciente al modelo MVS
de GEF
GEF: Graphical Editing Framework
GoF: Gang of Four. Acrónimo con el que se reconoce a los cuatro autores de [Gam 95].
GUI: Graphic User Interface (Interface Gráfica de Usuario)
IoC: Inversion of Control (Inversión de Control)
MOP: Meta-Object Protocols (Protocolo de Meta-objetos)
MVC: Patrón de diseño Model-View-Controller
POA: ver AOP
POO: Programación Orientada a Objetos
POP: Procedural Oriented Programming (Programación Orientada a Procedimientos)
SoC: Separation of Concerns (Separación de Intereses)
SWT: Standard Widget Toolkit
UML: Unified Modeling Language (Lenguaje de Modelado Unificado)
Diego M.S. Erdödy
Glosario
131
Descargar