Copyright y permisos de reproducción: los trabajos contenidos en estas actas se hallan bajo la licencia de javaHispano (http://www.javahispano.org/licencias/index.html). En lo relativo javaHispano se permite la reproducción total o parcial de los trabajos siempre que se referencie debidamente el lugar original de publicación del trabajo y a sus autores. Los respectivos autores de cada trabajo pueden imponer restricciones adicionales para su reproducción, por lo que para reproducir total o parcialmente alguno de los trabajos de esta acta javaHispano recomienda contactar directamente con sus respectivos autores.
Editado por Abraham Otero Quintana.
ISBN 84-689-0035-4
Actas del II congreso javaHispano
Actas del II congreso javaHispano
Índice
Prefacio…………………………………………………………………..………………………………....… 5
Comité de Organización……………..………………………..………..…………….…………......… 6
Comité de revisión…………….…………………..…………….....………………………………...… ..7
Presentaciones invitadas………………….……………………..………………………………...… ..8
Annotation Driven AOP (AOP orientado a anotaciones)..…………………………………………..….......9
Alexandre Vasseur
Modern Java Bottom-up Software Composition Techniques: Revisiting Jini, AOP-Style
(Técnicas de composición de software modernas, del detalle al concepto: repaso a Jini,
con estilo AOP) …………………….………………………………………………………………………..... 10
Hugo Pinto
Taming the Tiger (Domesticando al Tigre)…………………….…………………………………………… 11
Neal Gafter y Joshua Bloch
Still More Programming Puzzlers (Más soluciones para los rompecabezas de programación)..……. 12
Neal Gafter y Joshua Bloch
¿Has dicho Middleware?...................................................................................................................13
Miguel Valdes-Faura
Articulos…………………..…………………………………………………………………………….…… 17
SIMToolkit: la última frontera para la integración total, en la palma de tu mano……….................. 19
Alejandro Seco Calero y David Fraga Aydillo
Aplicaciones de tratamiento de imagen en terminales J2ME con cámara…….…………………..…... 27
Jonatan Tierno Alvite y Celeste Campo Vázquez
Programación de dispositivos Bluetooth a través de Java……………………………….…..…….…….. 35
Alberto Gimeno
Memoria dinámica en JavaCard: una herramienta para superar las limitaciones…………..…….…... 41
Borja Bravo Alférez y David Fraga Aydillo
JVMTI, Creación avanzada de profilers de aplicaciones con la nueva API de Tiger …………..…...…. 49
Daniel Glez-Peña y Florentino Fdez-Riverola
Actas del II congreso javaHispano
JNIEasy: Aspect Oriented Programming a la ayuda de Java como ciudadano de
primera clase en el desktop ……………………………………………………………………………. 57
Jose Maria Arranz
Guasaj: Un framework de programación basado en componentes ………………..……….…..... 65
Urko Benito Mateo, Ángel Blesa Jarque y José Javier Lop lis Extensión del patrón Observador para la integración de eventos de componentes
heterogéneos ……………………………………………………………………………………...….…. 73
Luis Rodero, Miguel A. Ortuño y Luis López
Seguridad no intrusiva con Acegi Security System for Spring …………………….…..……...…... 79
Carlos Sánchez
Estándares libres y Java: ¿Es el JCP un organismo que crea estándares libres?………...……..... 87
A. Otero
Integración continua utilizando herramientas Open Source……………………..…………..….... 99
Jesús Perez
Caso de uso: Empleo de tecnologías J2EE para el desarrollo de una plataforma
para la gestión tecnológica………………………………………………………….....………..….... 107
Rafael Pedraza, Alberto Planas, Antonio Navarro, Benjamín de la Fuente y Jose David Fernández
Actas del II congreso javaHispano
Prefacio
Organizar el congreso hispano independiente sobre Java más grande del mundo no es una tarea fácil. Cuando alguien llega al mostrador de un congreso como este no solo recoge una acreditación de plástico, sino los frutos del trabajo de mucha gente. Un trabajo constante de varios meses al que multitud de personas se han dedicado con un único objetivo: extender el uso de Java en todo el mundo hispano. Con este mantra en la cabeza desde la organización del congreso, hemos coordinado el esfuerzo de ponentes de varios países de habla hispana. Especial mención y agradecimiento merecen en este apartado las ponencias que hemos recibido de países como Colombia, México y Ecuador entre otros, y que debido a limitaciones presupuestarias y organizativas nos hemos visto tristemente empujados a desestimar en esta edición. Por supuesto los agradecimientos no se pueden quedar ahí. Esta magnifica reunión hubiera sido imposible sin la gente que habla - nuestros grandes ponentes -, sin la gente que nos apoya - nuestros patrocinadores - y por supuesto sin los que escuchan, asistentes que han empleado sus vacaciones, sus recursos o simplemente su tiempo libre para que todo el esfuerzo del resto de personas no fuera en vano. Y el resultado de las interacciones de todas estas personas es el que tiene ahora en sus manos. Unas actas que recogen al detalle las diferentes charlas que, siempre siguiendo un riguroso nivel de calidad, exponen diferentes temas de máxima actualidad en el presente. Áreas como AOP, arquitectura, software libre, movilidad y JDK 5.0, todo ello expuesto por ponentes punteros en sus respectivos campos que se han prestado a compartir con todos nosotros sus conocimientos y experiencias. Y es precisamente ese espíritu de comunidad y el deseo de promover un intercambio de ideas es el que se haya al fin y al cabo detrás de la propia asociación javaHispano. Siempre hemos pensado que la tecnología es una herramienta para construir futuro, que en los países de habla hispana esta requiere de una mayor atención debido al retraso acumulado con respecto a los países anglosajones y que nosotros podíamos hacer algo para cambiar esta situación y convertir al castellano en una lengua que no sea remolcada, sino tractora dentro del campo de la tecnología en general y de Java particular. Esperamos haberlo conseguido. Al menos hasta la siguiente edición.
Aitor García Rey Pedro del Gallego Vida
Presidente del comité de organización Presidente del comité de programa
Actas del II congreso javaHispano
5
Comité de organización
• Presidente: Aitor García Rey. javaHispano, España.
• Alberto Molpeceres Touris, javaHispano, España.
• Álvaro Sánchez-Mariscal Arnaiz, javaHispano, España.
• Martín Pérez Mariñán, javaHispano, España.
• Abraham Otero Quintana, javaHispano, España.
• Isaac Ruiz Guerra, javaHispano, México.
• Enrique Rodríguez Lasterra, javaHispano, España.
• Pedro del Gallego Vida, javaHispano, España.
• Ignacio Brito Calahorro, javaHispano, España.
• Eduardo Millán Martínez, javaHispano, España.
• Emilio Escobar Reyero, javaHispano, España.
• Jesús Navarrete Izquierdo, javaHispano, España.
• Jose Luis Mondelo, javaHispano, España.
• Roberto Andradas Izquierdo, Capítulo de Estudiantes de ACM de la Universidad Rey Juan Carlos
• Álvaro Navarro Clemente , Capítulo de Estudiantes de ACM de la Universidad Rey Juan Carlos
Actas del II congreso javaHispano
6
Comité de revisión
• Presidente: Pedro del Gallego Vida, javaHispano, España.
• Aitor García Rey. Responsable de Proyectos, Grupo AIGE.
• Alberto Molpeceres Touris, Arquitecto Jefe de Software, NHT-Norwick.
• Álvaro Sánchez-Mariscal Arnaiz, Analista, IT Deusto.
• Emilio Escobar Reyero, Analista, ISOTROL SA.
• Ignacio Brito Calahorro, Ingeniero de Software Senior, QualityObjects.
• Isaac Ruiz Guerra, javaHispano, México.
• Jesús Navarrete Izquierdo, javaHispano, España.
• Martín Pérez Mariñán, Arquitecto de Software, DINSA Soluciones.
Actas del II congreso javaHispano
7
Actas del II congreso javaHispano
8
__________________________________________________________________________________
__________________________________________________________________________________
Actas del II congreso javaHispano
9
Actas del II congreso javaHispano
10
Annotation Driven AOP
(AOP orientado a anotaciones)
Alexandre Vasseur
Abstract
Esta ponencia proporciona una introducción a los conceptos de la Programación Orientada a Aspectos (AOP) e intenta explicar cómo las Anotaciones en Java 5 (JSR- 175) y AOP pueden ser utilizados conjuntamente. La ponencia ofrece varios ejemplos de código basados en AspectWerkz, un marco de trabajo de código abierto Java/XML AOP puro. La primera parte trata de proporcionar un conocimiento básico acerca de los conceptos AOP en general, y cómo dichos conceptos se concretan en código Java, en los que se mejoran las clases regulares con Anotaciones Java 5 y se transforman en aspectos. Se detallan conceptos de Anotaciones predefinidas, proporcionadas por el marco de trabajo para definir nuevas construcciones – exactamente en el caso de los web services (JSR-181). Las Anotaciones también pueden ser definidas por el usuario y utilizadas en aplicaciones para implementar un comportamiento específico, basado en acceso en tiempo de ejecución a las Anotaciones. El marco de trabajo AOP AspectWerkz soporta coincidencias en esas Anotaciones de propósito específico, de forma que provee de un robusto mecanísmo de coincidencias basado en tipos. AOP y las Anotaciones no son contendientes sino que se complementan mútuamente y, cuando se utilizan conjuntamente, pueden ser una herramienta muy potente; nosotros creemos que esto va a jugar un papel importante en los futuros desarrollos y estándares Java. Esta ponencia proporciona una introducción a los conceptos de la Programación Orientada a Aspectos (AOP) e intenta explicar cómo las Anotaciones en Java 5 (JSR- La charla concluye con una explicación de cómo estos conceptos se pueden utilizar hoy en día, y cómo las mismas funcionalidades pueden ser utilizadas gracias a las Anotaciones basadas en Java 1.3/1.4 doclet.
Actas del II congreso javaHispano
11
Modern Java Bottom-up Software Composition Techniques: Revisiting Jini, AOP-Style
(Técnicas de composición de software modernas, del detalle al concepto: repaso a Jini, con estilo AOP)
Hugo Pinto
Abstract
En esta charla, Hugo Pinto analiza las tendencias actuales en técnicas de Separación de Conceptos (Separation of Concerns, SoC) basadas en Java(TM), mayormente enfocadas a la descomposición multidimensional (tal como se hace en AOSD), y argumenta que tales aproximaciones, si se aplican a los componentes de sistema además del propio código, amplía enormemente las posibilidades de distribuir y hacer disponible software Java orientado a SOA, muy en la línea de cómo fue propuesto por la Tecnología de Red Jini(TM) en sus orígenes. Hugo establece un paralelismo entre una aproximación Java basada en AOP y Jini 2, y construye el caso en el que se puede crear software con características Jini basándose en componentes de servicio AOP cuidadosamente diseñados, que de esta forma permiten una máxima flexibilidad e interoperabilidad en el diseño de software Java.
Actas del II congreso javaHispano
12
Taming the Tigre (Domesticando al Tigre)
Neal Gafter y Joshua Bloch
Abstract
La reciente aparición de la nueva versión de la plataforma Java (J2SE 5.0, también llamada Tiger) incluye nuevas y excitantes características: generics, enums, autoboxing, for-each, varargs, static import y annotations (metadatos). Unidas, todas estas características incrementan en gran medida la facilidad de programación, mientras que disminuyen la probabilidad de error. Esta charla presenta una introducción a todas las nuevas características, y cuenta cuándo y cuándo no se debe utilizar cada una de ellas. Se proporciona gran cantidad de ejemplos de código.
Actas del II congreso javaHispano
13
Still More Programming Puzzlers
(Más soluciones para los rompecabezas de programación)
Neal Gafter y Joshua Bloch
Abstract
Joshua Bloch y Neal Gafter (también conocidos como "clic and Hack, the Type-It brothers") presentan ocho rompecabezas más para nuestro entretenimiento y aclaración. El formato de la demostración del juego nos mantiene atraídos mientras que los rompecabezas nos enseñan las delicadezas del lenguaje de programación de Java y de sus librerías fundamentales. Cualquier persona con un conocimiento práctico del lenguaje será capaz de entender los rompecabezas, pero incluso desafiarán a los veteranos más experimentados. Las lecciones ofrecidas en esta ponencia serán directamente aplicables a vuestros programas y diseños. Y algunas de las bromas pueden incluso ser divertidas.
Actas del II congreso javaHispano
14
¿Has dicho Middleware?
Miguel Valdes-Faura
Abstract
Actas del II congreso javaHispano
15
Actas del II congreso javaHispano
16
__________________________________________________________________________________
__________________________________________________________________________________
Actas del II congreso javaHispano
17
Actas del II congreso javaHispano
18
SIMToolkit: la última frontera para la integración total, en la palma SIMToolkit: la última frontera para la integración total, en la palma SIMToolkit: la última frontera para la integración total, en la palma SIMToolkit: la última frontera para la integración total, en la palma
de tu mano.de tu mano.de tu mano.de tu mano.
Alejandro Seco CaleroAlejandro Seco CaleroAlejandro Seco CaleroAlejandro Seco Calero [email protected]
David Fraga AydilloDavid Fraga AydilloDavid Fraga AydilloDavid Fraga Aydillo [email protected]
AbstractAbstractAbstractAbstract
EEEEl avance de la tecnología móvil parece no tener freno a día l avance de la tecnología móvil parece no tener freno a día l avance de la tecnología móvil parece no tener freno a día l avance de la tecnología móvil parece no tener freno a día
de hoy. de hoy. de hoy. de hoy. Los terminaLos terminaLos terminaLos terminales móvilesles móvilesles móvilesles móviles se renuevan cada menos se renuevan cada menos se renuevan cada menos se renuevan cada menos
tiempotiempotiempotiempo y las posibilidades que ofrecen se multiplican de y las posibilidades que ofrecen se multiplican de y las posibilidades que ofrecen se multiplican de y las posibilidades que ofrecen se multiplican de
igual manera. En este documento realizaremos un análisis igual manera. En este documento realizaremos un análisis igual manera. En este documento realizaremos un análisis igual manera. En este documento realizaremos un análisis
de las posibilidades ofrecidas por tecnologde las posibilidades ofrecidas por tecnologde las posibilidades ofrecidas por tecnologde las posibilidades ofrecidas por tecnologías incluídas enías incluídas enías incluídas enías incluídas en
nuestro móvil como sonnuestro móvil como sonnuestro móvil como sonnuestro móvil como son Java Card (SIMToolkit) Java Card (SIMToolkit) Java Card (SIMToolkit) Java Card (SIMToolkit) y y y y J2ME. La J2ME. La J2ME. La J2ME. La
tecnologítecnologítecnologítecnología SIMToolkit nos ofrece laa SIMToolkit nos ofrece laa SIMToolkit nos ofrece laa SIMToolkit nos ofrece la posibilidad de crear en posibilidad de crear en posibilidad de crear en posibilidad de crear en
lalalala t t t tarjeta SIM una aplicación diseñada por nosotrosarjeta SIM una aplicación diseñada por nosotrosarjeta SIM una aplicación diseñada por nosotrosarjeta SIM una aplicación diseñada por nosotros, y , y , y , y
realizar desde ella operaciones desde nuestro menúrealizar desde ella operaciones desde nuestro menúrealizar desde ella operaciones desde nuestro menúrealizar desde ella operaciones desde nuestro menú. P. P. P. Por or or or
otro lado el desarrollo de aplicaciones para J2ME (midlets) otro lado el desarrollo de aplicaciones para J2ME (midlets) otro lado el desarrollo de aplicaciones para J2ME (midlets) otro lado el desarrollo de aplicaciones para J2ME (midlets)
nos presenta tonos presenta tonos presenta tonos presenta todo el potencial del API de MIDP tanto a do el potencial del API de MIDP tanto a do el potencial del API de MIDP tanto a do el potencial del API de MIDP tanto a
nivel gráfico como de comunicaciones. nivel gráfico como de comunicaciones. nivel gráfico como de comunicaciones. nivel gráfico como de comunicaciones. Estudiaremos Estudiaremos Estudiaremos Estudiaremos
oportunidades que nos ofrecen ambas plataformas de cara oportunidades que nos ofrecen ambas plataformas de cara oportunidades que nos ofrecen ambas plataformas de cara oportunidades que nos ofrecen ambas plataformas de cara
a la comunicacia la comunicacia la comunicacia la comunicación con el exteriorón con el exteriorón con el exteriorón con el exterior, y de igual forma , y de igual forma , y de igual forma , y de igual forma
analizaremos la viabilidad de unir ambos mundos.analizaremos la viabilidad de unir ambos mundos.analizaremos la viabilidad de unir ambos mundos.analizaremos la viabilidad de unir ambos mundos. Las Las Las Las
posposposposibilidades que se nos ofrecen son muy amplias, y en ibilidades que se nos ofrecen son muy amplias, y en ibilidades que se nos ofrecen son muy amplias, y en ibilidades que se nos ofrecen son muy amplias, y en
base a ellas propondremos líneas futuras y de aplicación.base a ellas propondremos líneas futuras y de aplicación.base a ellas propondremos líneas futuras y de aplicación.base a ellas propondremos líneas futuras y de aplicación.
Keywords: Keywords: Keywords: Keywords: J2ME, Midlet, MIDP, GPRS, CSD, JavaCard,
Smart Card, SMS, evento, proactivo, SIMToolkit(STK).
1 Introducción al entorno de aplicac1 Introducción al entorno de aplicac1 Introducción al entorno de aplicac1 Introducción al entorno de aplicaciónióniónión
Hoy en día nos movemos cada vez más en un
entorno móvil donde los dispositivos de reducido
tamaño, inalámbricos, y de altas prestaciones son
cada vez más comunes en nuestros bolsillos. Dentro
de dicho mundo, no cabe duda, que el gran
protagonista en la actualidad es el teléfono móvil.
La ventaja sustancial que nos ofrece la movilidad
frente a otros dispositivos no quedaría reflejada si
no desarrolláramos convenientemente la capacidad
de comunicación entre nuestros terminales. El
intercambio de información entre dispositivos
móviles es imprescindible a la vez que lógico. Desde
nuestro móvil de bolsillo podemos, a día de hoy,
desde mandar un mensaje de texto, navegar por un
buscador de Internet o establecer una
videoconferencia con nuestro colega americano,
hasta descargarnos nuestra melodía favorita y todo
ello lo asumiremos como servicios en su mayor
parte habituales. Así pues el término
comunicaciones junto a movilidad van unidos de
forma irrevocable.
El planteamiento desde el punto de vista de usuario
de cara a su terminal parece sencillo. Los requisitos
fundamentales son interfaces gráficas vistosas, y
funcionalidades cada vez más avanzadas, pero la
base de todos estos elementos se desarrolla en un
motor de procesado cada vez más potente. Cada vez
más, el terminal móvil adquiere funcionalidades
similares a un ordenador convencional, y entre ellas
hay que destacar las librerías de comunicaciones.
Nuestro sistema va a estar dotado de un amplio
abanico de posibilidades a la hora de conectarse al
exterior. Deberemos tener siempre presente las
posibles redes de comunicaciones que nos vamos a
encontrar: red GSM, red GPRS (sobre GSM), red
UMTS, Internet, redes privadas y redes de área
personal (IrDa, Bluetooth, … ). Hoy en día una de las
redes de mayor uso es la red GPRS dimensionada
sobre su predecesora GSM.
Figura 1: Estructura de red GPRS
Actas del II congreso javaHispano
19
Otro factor clave en el mundo de las tecnologías
móviles es la tendencia hacia la universalización y
estandarización de dispositivos. Los terminales van
a ser desarrollados bajo plataformas muy similares,
de forma que el desarrollo de aplicaciones para cada
uno de ellos sea idéntico y no sea necesario
realizarlo y adaptarlo para cada uno.
Entre las tecnologías que mejor resumen todos los
aspectos que se han reseñado encontramos aquellas
que se sitúan tanto a nivel de terminal: Java2 ME
(plataforma miembro de la familia de productos Java
adaptado a terminales o dispositivos de reducido
tamaño) y Symbian (sistema operativo desarrollado
por un consorcio de compañías del sector, el cual
permite la programación de aplicaciones por parte
del usuario), como tecnologías en el ámbito de
nuestra tarjeta SIM: Java Card [2] (plataforma de Java
adaptada para tarjetas inteligentes o smart cards,
que cuenta con su propia maquina virtual) y, a un
nivel superior a Java Card, encontramos la interfaz
SIMToolkit [1], que adapta las posibilidades de una
tarjeta SIM al mundo Java. Adentrándonos en el
mundo de la tarjeta SIM lo que nos ofrece el entorno
de desarrollo SIMToolkit [1] es la unión de los
mundos de tarjetas inteligentes y nuestro terminal.
Mediante una API propia vamos a ser capaces de
desarrollar aplicaciones que puedan correr en
nuestra tarjeta, y que posean una interfaz de cara al
usuario en el display del móvil, siendo los aspectos
visuales opcionales.
Veamos a continuación la estructura de la familia de
productos Java 2 , donde aparecen desmarcados en
la parte derecha los productos destinados a
dispositivos móviles y smart cards.
Figura 2 : Plataforma Java 2
La tarjeta SIM va a ser considerada como un
periférico adicional dentro de los elementos que
componen un terminal móvil. La tarjeta adquirirá un
carácter pasivo. Este rol pasivo se define así ya que
para que la tarjeta pueda enviar información hacia el
exterior (terminal) recurrimos a un petición de
estado continua desde el terminal y en nuestra
respuesta desde la tarjeta le indicaremos nuestro
estado junto con la invocación de alguna función a
realizar (comandos proactivos).
Entenderemos nuestro terminal móvil como un
microprocesador (como hemos comentado,
actualmente cada vez son más potentes) con el cual
interactuamos mediante distintos periféricos, tales
como el teclado, el display, audífono o,
concretamente, nuestra tarjeta SIM. Por ello cada
uno de estos elementos adquiere gran importancia
para la composición final del dispositivo, pero
especialmente la tarjeta SIM será la que nos permita
comunicarnos hacia el exterior. Desde la tarjeta
vamos a poder controlar el acceso a la red. La
operadora nos va a permitir acceder a sus servicios a
través de la clave encriptada que reside en nuestra
tarjeta. Así pues tendremos acceso a los
mecanismos de comunicaciones de nuestra red:
SMS, llamadas de voz, llamadas de datos,
señalizaciones, etc. No solo se va a poder acceder a
estos servicios si no además la tarjeta va a disponer
de acceso a información sensible como agenda o
mensajes, almacenados en la propia SIM
Desde el punto de vista del terminal vamos a
concebir el dispositivo como un pequeño ordenador
desde el que podremos ejecutar aplicaciones
gráficas, descargar contenidos o conectar con un
servidor de correo, todo ello por la capacidad de
procesado de la que dispone.
La estandarización de tarjetas SIM parece obvia
mientras que por el contrario, a nivel de terminales
encontramos en el mercado mucha mayor
diversificación. Cada terminal de un fabricante
distinto llevará muy posiblemente un procesador
diferente pero nos basamos en un de los pilares
fundamentales de J2ME, su portabilidad entre
distintas máquinas. Todo el desarrollo realizado
para nuestra investigación se ha realizado siempre
en diversos tipos de móviles encontrando variedad
de recursos implementados por los mismo, pero
manteniendo el núcleo del API de J2ME siempre que
Actas del II congreso javaHispano
20
este estuviera soportado (con su correspondiente
perfil MIDP)
Así pues, con todo lo aquí planteado, parece verse
que delante de nosotros tenemos dos mundos a
simple vista diferentes, en primer lugar el lado
terminal, y por otro el lado tarjeta SIM. Los dos nos
ofrecen numerosas posibilidades, y por ello, nuestro
primer objetivo será el control de ambos campos,
desde la programación básica general hasta el
control de librería de comunicaciones. Una vez
alcanzado este objetivo nuestra línea de desarrollo
tenderá hacia la unión de ambos entornos, con el fin
de poder procesar información desde un lado y
recibirla en el otro, obtener conectividad desde la
tarjeta SIM hacia nuestro terminal móvil y viceversa.
2 SIMToolkit2 SIMToolkit2 SIMToolkit2 SIMToolkit (STK) (STK) (STK) (STK)
La comunicación entre nuestra tarjeta y el terminal
móvil se lleva acabo gracias a una API concreta, STK.
Esta API está estructurada dentro de las normas de
Java Card, por lo que el desarrollo de una applet
seguirá el mismo procedimiento que el de una
aplicación en el entorno de tarjetas inteligentes.
Puntualizaremos que para el desarrollo de una
aplicación para Smart Cards deberemos implementar
una serie de funciones guión pues el entorno de
ejecución estará predeterminado para buscar estas
funciones. Entre estos métodos encontramos:
install(), destroy(), o process(). Métodos que no
vamos a entrar a desarrollar por entender que
forman parte del entorno de tarjetas inteligentes y
salen del contexto de nuestro artículo. Destacar que
una diferencia de STK respecto a Java Card simple es
que la primera implementa la función
processToolkit() , similar a process() en Java Card y
será ésta la que se ejecute en el momento de
activación de nuestra aplicación.
El formato de transferencia de información desde
una Smart Card (SIM en nuestro caso) recibe el
nombre de APDU. Por consiguiente el terminal móvil
también va a esperar recibir este tipo de paquetes
desde nuestra tarjeta. La estructura de un ADPU es
muy sencilla. Poseen dos campos iniciales, uno de
clase de instrucción (CLA) y otro de de operación a
ejecutar (INS), seguidos de dos campos P1 y P2 que
serán parámetros opcionales, Le (tamaño en bytes
de la respuesta esperada, Lc (tamaño en bytes del
campo de datos) adjunto y el campo de datos en
cuestión. Tras el envío de un APDU se esperan
respuestas con un formato concreto y sencillo (Ej,
0x9000 Ok). Será sobre este perfil dónde interactúe
STK definiendo un protocolo entre ambos lados. En
el cuerpo de los datos enviados en el APDU
mandaremos información con cabeceras definidas
por STK, y de la misma forma en los códigos de
respuesta podrá venir información adicional con
formato STK. La comunicación en éste ámbito se va
a ver diferenciada por el origen y destino que tenga,
así, diferenciaremos entre mensajes de SIM a
terminal y vicerversa, dando lugar respectivamente,
a los que llamaremos comandos proactivos y
eventos.
Comandos Proactivos
Un comando proactivo es aquel lanzado por la
tarjeta SIM y cuyo destino es el propio terminal,
pidiéndole a éste que realice alguna operación
concreta. Existen 31 comandos proactivos posibles
para poder enviar a nuestro terminal. Los
dividiremos en 4 categorias:
• Applications commands, para desarrollar
aplicaciones típicas de STK.
• Smart-card commands, destinado a
interoperar con otras tarjetas SIM
introducidas en nuestro terminal.
• General communications commands,
interfaz para los distintos tipos de
portadoras soportadas.
• System Commands, orientados a la
sincronización con el terminal y la red.
Una vez nos adentramos dentro del bloque de datos
del APDU, observamos que los comandos proactivos
están divididos en una nueva estructura. Los
bloques que vamos a encontrar dentro del Data load
del APDU son denominados TLV (Tag-Length-
Values). Cada TLV va a venir definido por un campo
Tag, a modo de identificador, un campo de bytes
indicando el tamaño del siguiente campo, y el
campo value, donde encontraremos el verdadero
valor a nivel de datos. Podremos encontrar varios
TLV’s concatenados. A modo de ejemplo: podemos
encontrarnos un Data load en un APDU(SIM->MOVIL)
donde un primer TLV nos indique el tipo de
proactivo que se envía (Tag = comando proactivo,
length = 1, value = Display text), un segundo TLV
Actas del II congreso javaHispano
21
Móvil SIM
Tag Length Value
Data CLA INS P1 P2 Lc
TLV
APDU
Tag Length Value
INS P1 P2 Lc
TLV
APDU
CLA Data
STK
Smart Card
indicaría una seria de comandos que no entramos a
valorar y un tercer TLV nos indicaría qué texto
queremos sacar por el display.
Figura 3 : Estructura del APDU
Eventos
Mas allá de poder dar la tarjeta SIM órdenes a
nuestro terminal a través de comandos proactivos, la
propia tarjeta podrá registrar una serie de eventos
que serán lanzados desde el terminal. Entendemos
este proceso como el paso de información desde el
terminal hacia la tarjeta lo cual siempre parece más
lógico y por ello su mayor facilidad para
implementar. Recordamos que la tarjeta adquiere un
modo pasivo a la hora de poder enviar proactivos
hacia el terminal. Realizando una caracterización de
los eventos disponibles según su categoría,
encontramos los siguientes grupos principales:
- Notificaciones de mensajes.
- Notificaciones de llamadas, y datos.
- Notificaciones de actividad o selecciones de
menú por parte del usuario.
- Cambios de estado a nivel de celda
Entraremos a continuación a valorar posibles
desarrollos y funcionalidades aplicando
directamente los conceptos aquí descritos.
GESTIÓN DE LA INFORMACIÓN DE LA TARJETA
Como hemos mencionado anteriormente contamos
en nuestro desarrollo con el API ofrecido por STK.
Este API nos ofrecerá el entorno necesario para
manejar comandos proactivos y eventos. De la
misma forma nos ampliará posibilidades al
ofrecernos una interfaz Java que nos permitirá
acceder a la información contenido en la tarjeta SIM:
-La tarjeta como elemento de almacenaje que es,
estará compuesta por un sistema de ficheros con
unas características especiales, debido a su limitada
capacidad, pudiendo acceder a este sistema de
ficheros desde el API de STK (apertura, lectura,
escritura)
–Seremos capaces de poder acceder a los mensajes
cortos almacenados en nuestra tarjeta y de igual
manera a almacenamientos de mensajes entrantes
en la misma. (recordamos que tendremos manejo
total sobre mensajes entrantes, evento SMS-PP)
-Podremos editar y obtener información de las
entradas en nuestra agenda personal SIM.
-La tarjeta telefónica convencional guarda en
memoria las últimas llamadas de voz realizadas, y
de la misma forma podremos acceder a ellas para
comprobar sus destinatarios y duraciones.
GESTIÓN DE LOS PROCESOS DE COMUNICACIÓN CON
EL TERMINAL
El planteamiento que acabamos de realizar para el
desarrollo de aplicaciones desde STK (información
de la tarjeta) no lleva acabo transferencia de
información a través de eventos y proactivos. Todo
se realiza desde la misma tarjeta la cual obtiene
información propia, aunque si necesitamos de
proactivos para el envío de esta información hasta el
display (proactivo DISPLAY TEXT).
Las posibilidades que nos ofrece STK son mucho
más amplias, y gran parte de ellas tienen que ver
con las funciones de comunicación de nuestro móvil.
Los tres canales fundamentales de flujo de
información (llamadas de voz, SMS y conexiones de
datos) podrán ser controlados desde nuestra
aplicación STK.
EL API STK nos ofrece la posibilidad de controlar
tanto los mensajes cortos salientes como los
entrantes, pudiendo actuar dependiendo de su
naturaleza de forma distinta. Existirán dos eventos
distintos para cada una de las direcciones de envío
del mensaje (saliente-entrante). Puntualizaremos,
que al igual que todos los procesos anteriormente
descritos, estas funcionalidades quedan fuera de la
vista del usuario, al cual podremos informarle de las
funciones llevadas acabo a través del display
únicamente (proactivo que deberemos ejecutar).
Actas del II congreso javaHispano
22
Si nuestros mensajes pueden estar bajo control, no
iban a ser menos las llamadas. Tendremos la
posibilidad de realizar llamadas a un número
concreto, y de tomar acciones a nuestra elección si
una determinada llamada llega a nuestro móvil.
Por último destacar la posibilidad de controlar flujos
de datos desde nuestro terminal. Los canales
permitidos serán bien CSD o GPRS, determinando en
una configuración de parámetros sus respectivas
calidades de servicios. Seremos capaces por tanto de
abrir el canal de datos, obtener información y
enviarla para posteriormente cerrar nosotros
mismos el propio canal. Reseñamos numerosas
dificultades que hemos encontrado en este aspecto
en los desarrollos realizamos en el laboratorio por
motivos de configuración de operador, soporte de
transmisión de datos por parte del móvil, y
configuración adecuada de las calidades de servicio
en el propio canal.
OTRAS FUNCIONALIDADES
Que nuestra tarjeta SIM soporte JavaCard y
SIMToolkit no implica que el terminal móvil en el que
esté insertada soporte todos los comandos
proactivos y eventos que están contemplados. Con
ello queremos indicar que, cada terminal,
dependiendo de sus prestaciones nos va a ofrecer la
posibilidad de interactuar con la tarjeta hasta cierto
punto. Se implementarán por parte de fabricante un
cierto grupo de proactivos y eventos a los que
responder. Se ha realizado una pequeña aplicación
capaz de mostrarnos por pantalla aquellos servicios
que el móvil es capaz de procesar.
Una vez planteado que nuestro móvil no tiene
porqué implementar todas las funciones, veremos
algunas de las más interesantes de cara al
programador STK, aparte de las ya vistas:
-Presentación en pantalla de texto, menús, listas y
formularios.
-Respuestas a eventos del usuario: acción sobre
teclas.
-Obtención de parámetro de localización (a nivel de
celdas).
-Obtención de nivel de potencia de las bases más
cercanas.
DESARROLLOS/APLICACIONES
Veremos ahora algunas de las aplicaciones
realizadas basándonos en toda la estructura aquí
resumida:
MECapabilitiesMECapabilitiesMECapabilitiesMECapabilities: El desarrollo de esta aplicación nos
permite obtener una visión completa de las
funcionalidades implementadas en nuestro terminal,
tanto a nivel de proactivos como de eventos. Nos
muestra por pantalla el listado completo o
personalizado de los proactivos/eventos que
deseemos consultar y en base a una consulta en el
propio móvil nos confirma si está soportado.
Los mecanismos para el desarrollo de esta pequeña
aplicación son muy simples. Basándonos en el
evento de acción por parte del usuario (cuando entra
en nuestro menú de aplicación) interactuamos con él
presentándole gráficamente el menú selección desde
el que puede chequear cualquier opción. Desde el
API de STK accedemos a la clase ME Profile y desde
allí podremos chequear cualquier tipo de
evento/proactivo que deseemos.
GestSIMGestSIMGestSIMGestSIM: Completa aplicación basada en la
administración, almacenaje y transferencia de
mensajes y en el control de entradas en la agenda de
la propia tarjeta. Funcionalidades que podemos
aportar con esta aplicación son, por ejemplo, la
actualización de contactos de nuestra agenda al
llegar a una región distinta donde nuestra empresa
posee un grupo de comerciales distintos a la zona
de donde provenimos. Se accede también al control
de llamadas realizadas y salientes, siendo accesibles
para estas los contactos almacenados en nuestra
tarjeta. A través del manejo de información de
mensajes se pueden obtener funciones de
localización derivadas de nuestra posición en el
momento de envío.
Toda la aplicación se basa en la gestión de los
procesos de comunicación del terminal
anteriormente explicados, junto con la interfaz
gráfico común a todas las aplicaciones de cara al
usuario.
ConGPRSConGPRSConGPRSConGPRS: Aplicación aún en proceso de depuración,
que nos permite establecer una conexión de datos a
un servidor remoto, enviando información contenida
en nuestro móvil y cerrando posteriormente todas
las comunicaciones.
Actas del II congreso javaHispano
23
Para este desarrollo utilizamos los proactivos
destinados a canales de datos (clase ‘e’) , y de igual
forma presentamos los datos por nuestro display.
3 Integraci3 Integraci3 Integraci3 Integración ón ón ón
J2ME bajo sus perfiles y configuraciones nos permite
el desarrollo de aplicaciones dentro del propio
terminal. Estas aplicaciones pueden barajar desde
interfaces gráficos hasta soporte de comunicaciones
vía sockets TCP, es por ello que hemos trabajado en
este sentido para poder explotar especialmente los
recursos de comunicaciones del móvil. Hemos
realizado implementaciones de aplicaciones cliente-
servidor vía sockets TCP, vía protocolo http y envío-
recepción de datagramas. Hasta la versión MIDP2.0
estas son, junto con https, las implementaciones
posibles de elementos de comunicaciones.
Las plataformas desarrolladas en este artículo (J2ME
y STK) nos ofrecen altas posibilidades de desarrollo
respectivamente, estando cada una de ellas en
mundos distintos a pesar de convivir dentro del
propio terminal (móvil y tarjeta). Nos planteamos la
unión de ambos mundos con las ventajas que ello
conllevaría. Realizaríamos la ampliación de cada una
de las API’s por separado, pudiendo interactuar
desde nuestro móvil con la tarjeta SIM y viceversa.
Entre los mecanismos para realizar esta integración
podemos encontrar por ejemplo la implementación
de protocolos propios que utilizando eventos de
captura de llamadas o de mensajes, y de esta forma
interactuar con nuestra tarjeta. Este planteamiento
sería sólo en una dirección de comunicación.
Dejamos al lector discernir sobre posibles formas de
comunicación desde nuestra tarjeta al terminal.
4 Conclusiones4 Conclusiones4 Conclusiones4 Conclusiones
Dos mundos tan aparentemente separados, pero tan
cerca el uno del otro. Siguiendo la línea de la
integración total o parcial de ambas tecnologías
observamos las posibilidades que ofrecerían ambas
juntas. La implementación, especialmente en STK, a
priori puede parecer algo mas difusa que el
desarrollo en J2ME pues en esta última la aportación
por la comunidad Java en esta es mucho mayor, pero
gracias a las normas GSM [5][6], y a la transparencia
y practicidad de lo que se pretende hacer, resulta
muy agradecido el desarrollo de aplicaciones para
tarjetas inteligentes, y entendemos, desde nuestro
punto de vista, que queda mucho camino por
recorrer para STK, teniendo ya una buena base para
empezar a andar.
Por último hacer mención al entorno de desarrollo
de todo nuestro sistema aquí descrito, y es que por
muy bien que diseñemos una applet para nuestra
tarjeta SIM, deberemos transferirla e instalarla en la
tarjeta. SIMAlliance es la organización que ha
estandarizado sus herramientas, de uso público y
con las que recientemente estamos trabajando. Son
de fácil acceso, pero al estar poco extendida entre
desarrolladores esta tecnología, las dificultades que
aún se encuentran para el desarrollo no son pocas.
5 Líneas futuras5 Líneas futuras5 Líneas futuras5 Líneas futuras
No cabe duda que hoy en día la telefonía móvil ha
adquirido un ritmo de crecimiento tecnológico muy
alto. Es por ello por lo que movernos de un concepto
tecnológico a otro nos lleva muy poco tiempo y, a
día de hoy, la telefonía de tercera generación (3G)
está demasiado cerca nuestra como para obviarla.
La adaptación de STK a este nuevo entorno es
necesaria pues todas nuestras tarjetas en breve
estarán insertadas en teléfonos capaces de
conectarse a la red UMTS.
Orientándonos hacia la integración de STK junto a
J2ME, dos plataformas que ya empiezan a convivir
en nuestro dispositivos, encontramos vías de
estudio muy interesantes como el JSR de J2ME,
SATSA. Security and Trust Services API for J2ME es la
especifiación de un paquete de java que puede tener
mucho que decir en el futuro de estas tecnologías.
Con un final Release por parte del JCP de Sun, y a
tan sólo la espera de su implementación por parte
de un fabricante en algún dispositivo, SATSA ofrece
vías de comunicación a nivel APDU desde nuestro
terminal hasta nuestra tarjeta SIM. Seremos capaces
de, realizando la programación de nuestro módulo
Java, realizar ediciones en los mensajes o agenda de
la tarjeta, enviar eventos determinados o realizar un
propio protocolo de comunicaciones entre ambos
dispositivos con el fin de acceder a información en
un momento determinado. Dejaremos en manos del
lector analizar y evaluar otras posibles aplicaciones
bajo esta línea de desarrollo.
Actas del II congreso javaHispano
24
AgradecimientosAgradecimientosAgradecimientosAgradecimientos
Me gustaría agradecer desde aquí a toda la gente del
Laboratorio de Sistemas Integrados, perteneciente al
Departamento de Ingeniería Electrónica de la
Universidad Politécnica de Madrid la ayuda prestada
para el desarrollo de todas las investigaciones
relacionadas con esta ponencia. Gracias por hacerme
salir cada día sonriendo del laboratorio.
Especialmente a David, la figura que mejor
representa a un tutor cercano, y siempre dispuesto a
ayudar, gracias. Va por vosotros.
ReferenciasReferenciasReferenciasReferencias
[1] Scott B. Guthery, Mary J. Cronin. Mobile Application
Development with SMS and the SIM Toolkit. McGraw-
Hill.
[2] Zhiqun Chen. Java Card Technology for Smart Cards.
Addison Wesley.
[3] Roger Riggs, Antero Taivalsaari, Mark VandenBrink.
Programming with the Java 2 Platform, Micro Edition.
Addison Wesley.
[4] Luis Javier Herrera Maldonado. Tutorial de MIDP ,
Conexión a redes.
http://flanagan.ugr.es/J2ME/MIDP/conexion.htm
[5] ETSI TS 101 267 – GSM 11.14 “Specification of the SIM
Application Toolkit for the Subscriber Identity Module”
[6] ETS 300 608 – GSM 11.11 “Specification of the
subscriber Identity Module”
Actas del II congreso javaHispano
25
Actas del II congreso javaHispano
26
Aplicaciones de tratamiento de imagen en terminales J2ME con cámara
Jonatan Tierno Alvite [email protected]
Celeste Campo Vázquez [email protected]
Abstract
En este documento vamos a estudiar la posibilidad de
realizar aplicaciones de tratamiento de imagen sobre
teléfonos móviles con cámara incorporada en J2ME. Esto
implica implementar algoritmos que típicamente tienen alta
carga computacional sobre dispositivos limitados en
memoria y capacidad de proceso, y además sobre J2ME,
que suele ser considerado un lenguaje de programación
poco eficiente, y de posibilidades limitadas dada su
reducida API. Hemos realizado dos aplicaciones de ejemplo:
Un Lector de Colores y un Detector de Movimiento. Después
hemos realizado un estudio sobre las capacidades de un
terminal concreto, tanto en capacidad de proceso como en
la calidad de imagen que podemos obtener. Con estos
datos sacamos conclusiones sobre qué tipo de aplicaciones
es posible realizar en estas condiciones y para qué otras es
necesario buscar una plataforma más potente.
Keywords: Tratamiento de imagen, teléfonos móviles con cámara, MMAPI, ayuda a invidentes.
1 Introducción
Hoy en día muchos teléfonos móviles (y algunas PDAs)
pueden obtener información no sólo de las redes de
comunicaciones a las que están conectados, si no
también del entorno físico, mediante grabación de audio
y video.
Hasta ahora, y centrándonos en la cámara de fotos y
vídeo, esta nueva capacidad se ha utilizado de la misma
forma que en una cámara convencional: se guarda la
fotografía o el archivo de vídeo en el terminal para
recuperarla más tarde o para enviarla a otro dispositivo.
En este documento vamos a discutir la posibilidad de
realizar un cierto procesado sobre esa información antes
de pasar los resultados al usuario.
En este trabajo hemos utilizado terminales de la Serie 60
de Nokia, principalmente el Nokia 6600, y también el
Nokia 3650. Como lenguaje de programación, hemos
utilizado Java 2 Micro Edition (J2ME).
Uno de los posibles usos de estas aplicaciones es la
ayuda a invidentes, pues estamos hablando de
manipular información visual sobre un dispositivo que la
gente usa en su vida cotidiana. Por ello, hemos
implementado dos aplicaciones dirigidas a este fin: La
primera es un Lector de Colores, que permitirá conocer a
un usuario ciego los colores de un objeto al que dirija la
cámara, por ejemplo, una prenda de ropa. La segunda es
un Detector de Movimiento, con el que podrá saber
cuando alguien entra en una habitación, cuando se
apaga o enciende la luz, etcétera.
El resto del documento se organiza de la siguiente
forma: En la sección 2 vamos a hablar de la Mobile
Media API (MMAPI), que es la librería de J2ME que da
acceso a la cámara desde un MIDlet, y del acceso a la
información de las imágenes de la cámara. Tras esto, se
describe la implementación de dos aplicaciones de
ejemplo: el Lector de Colores, al que dedicaremos la
sección 3 y el Detector de Movimiento, que se tratará en
la sección 4. En una segunda parte del proyecto,
realizamos un estudio de un terminal concreto (el Nokia
6600), para determinar su capacidad de ejecutar
aplicaciones de tratamiento de imagen. En la sección 5
hablaremos sobre el acceso a la calidad de las imágenes
disponibles, y en la sección 6, sobre la capacidad de
proceso y la memoria del terminal. Por último, en la
sección 7 usaremos todos los datos obtenidos para
discutir sobre qué aplicaciones pueden implementarse
sobre estos terminales y cuáles no.
2 Mobile Media API (MMAPI) y el acceso a la cámara
La Mobile Media API (MMAPI) es una librería opcional de
J2ME que permite acceder a cualquier tipo de contenido
Actas del II congreso javaHispano
27
multimedia (imágenes, audio, video), tanto local como
remotamente. También es el interfaz J2ME hacia
grabación de audio y video. El hecho de que sea una
librería opcional significa que pueden ofrecerla tanto
terminales MIDP 1.0 como 2.0. Sin embargo, aquellos
terminales que no dispongan de ella, no tendrán ningún
tipo de acceso Java hacia la cámara.
La MMAPI consigue tratar diversos tipos de contenidos
de la misma manera mediante un alto nivel de
abstracción que esconde las operaciones a bajo nivel que
realiza el sistema operativo. La MMAPI ofrece un interfaz
común a la lectura de datos desde cualquier fuente y en
cualquier formato mediante un Identificador Uniforme
de recursos (URI), que definirá el dispositivo y los
parámetros de reproducción. El acceso a la cámara se
realiza con el URI especial de captura “capture://video”.
Con la llamada “System.getProperty(String key);”
podemos saber si nuestro dispositivo permite captura de
video o no, y en qué formatos.
El programador manejará una realización del interfaz
Player, que creará con un Manager, con la orden “Player
player = Manager.createPlayer(String uriString);”. Con
esto, podemos acceder a las imágenes de la cámara
como un flujo de video . Para tomar una fotografía,
usamos el método VideoControl.getSnapshot(String
imageType), que nos devuelve un fotograma de ese flujo
en el tamaño y el formato que especifiquemos en el
parámetro imageType.
Para que nuestra aplicación pueda obtener información
de esta imagen y realizar un procesado sobre ella,
necesitamos acceder a los pixels. MIDP 2.0 ofrece
directamente esta funcionalidad con el método getRGB()
de la clase Image. Esta es la solución utilizada con el
teléfono Nokia 6600 en las aplicaciones que hemos
implementado en este trabajo.
El teléfono Nokia 3650 es MIDP 1.0 y no disponía de este
método. Una posible solución era el método
“DirectGraphics.getPixels”, de la librería NokiaUI. Sin
embargo esta es un API propietaria y no se encuentra en
todos los terminales Java.
La solución adoptada para el Nokia 3650 fue parsear el
fichero PNG. En este caso fue bastante sencillo, puesto
que la información no estaba comprimida, y sólo
tuvimos que eliminar las cabeceras del fichero. En otros
terminales (por ejemplo, en el Nokia 6600) puede no
darse el caso, siendo entonces esta tarea muy
complicada o incluso inabordable por razones de
eficiencia.
3 Lector de Colores
El lector de colores permite a un usuario conocer los
colores del objeto que esté enfocando con la cámara del
móvil. De esta forma un invidente puede separar la ropa
blanca de la de color para hacer la colada, por ejemplo.
La aplicación está dividida en los siguientes bloques:
captura y muestreo de los pixels, clustering, decisión y
por último, comunicación de resultados.
Al inicializar la aplicación, la cámara se pone en
funcionamiento, y cuando el usuario apunta a un objeto
y pulsa el botón de acción, realizamos la captura y
muestreo de la imagen. En este bloque tomamos una
fotografía mediante el método getSnapshot, y
accedemos a los pixels según hemos descrito más arriba.
De toda la imagen, tomamos una muestra adecuada
para el procesado posterior: 50 píxeles de la parte
central de la imagen. La cifra escogida es un compromiso
entre la información de la imagen presente en la muestra
y el tiempo que tardaremos en realizar el procesado.
Sobre estos pixels vamos a ejecutar el algoritmo de
custering llamado K-medias. El clustering o
agrupamiento consiste en dividir un conjunto
heterogéneo de elementos en grupos o clusters, de
modo que los elementos más parecidos estén juntos.
Cada cluster está representado por un centroide, que
viene a ser el promedio del cluster, por la varianza del
centroide, es decir, la dispersión de los elementos en un
cluster, y su número de elementos. El K-medias, es un
algoritmo simple y rápido que trabaja con un número
fijo de clusters, que en nuestra aplicación serán tres. Esto
significa que podremos averiguar hasta tres colores de
una imagen simultáneamente.
Para implementar el algoritmo necesitamos definir la
distancia entre dos colores. Para esto, en primer lugar
convertimos los valores RGB de los píxeles a coordenadas
HSB (Crominancia, Brillo, saturación), que son más
adecuadas para comparar colores. La distancia entre dos
colores que hemos usado responde a la expresión:
d[(h1,s1,b1),(h2, s2,b2)]= α( h 1-h 2)+β( s 1-s 2)+γ( b1-b2)
donde α, β y γ son coeficientes hallados empíricamente
que representan la importancia de cada una de las tres
características a la hora de diferenciar dos colores.
Actas del II congreso javaHispano
28
El K-medias es un algoritmo iterativo que precisa una
condición de parada. En nuestro caso, el algoritmo se
detendrá cuando se cumpla cualquiera de dos
condiciones: La primera es que se alcance un número
máximo de operaciones, para evitar que el algoritmo se
ejecute indefinidamente. La segunda es que la suma de
las varianzas de los clusters deje de disminuir, lo que
significaría que más iteraciones no mejoran la
distribución de los elementos en los clusters.
En el bloque de decisión vamos a determinar cuales son
los colores que contienen los clusters hallados. En primer
lugar, debemos averiguar si dos clusters contienen el
mismo color. Para esto, medimos la distancia entre sus
centroides en relación con la varianza de ambos, y
comparamos con un umbral hallado empíricamente.
También debemos decidir si cada cluster contiene pixels
de un solo color o de varios. Para esto, compararemos
la varianza de los clusters con otro umbral. Con estas
comprobaciones, podemos saber cuántos colores
conforman la imagen, y si son más de tres, que es el
número de clusters, emitiremos un mensaje de error.
Después de esto, asignamos a cada centroide una
categoría de color. Para ello, hemos dividido el espacio
HSB en regiones, cada una con un color asociado. Hecho
esto, basta con ver en qué región cae el centroide de
cada cluster.
Por último, tenemos que comunicar los resultados al
usuario. Cada región tiene asociado un fichero de audio
en que una voz pronuncia el nombre del color. Tras la
etapa de decisión, los colores resultantes son emitidos.
Los más abundantes van primero, de forma que el
usuario sepa el color dominante en la imagen.
4 Detector de Movimiento
Esta aplicación muestrea continuamente el flujo de vídeo
ofrecido por la MMAPI en busca de movimiento y,
cuando éste se produce, la aplicación emite un sonido y
muestra la imagen en que éste se produjo. Esta MIDlet
simplemente compara una imagen con la anterior en un
bucle, y de éste si son distintas, sale del mismo.
La comparación entre dos imágenes es muy simple para
permitir que el bucle sea tan rápido como sea posible, y
que la velocidad esté limitada por la máxima frecuencia a
la que la que podemos llamar al método getSnapshot().
Por esta misma razón, esta velocidad de muestreo puede
no ser constante.
Para detectar el movimiento, simplemente comparamos
el color del píxel central de una imagen con el de la
anterior. Dado existe cierto ruido en la imagen, aún en
dos imágenes representando la misma escena los valores
exactos de los pixels cambiarán ligeramente. Esto
significa que para detectar el movimiento no basta con
ver si los píxeles son iguales. Lo que haremos será medir
la distancia Manhattan entre ellos y compararla con un
umbral que fijaremos empíricamente. Notificaremos que
ha habido movimiento cuando este umbral se supere.
Para evitar falsas alarmas conviene tener un umbral
elevado, sin embargo esto tiene el inconveniente de que,
en el caso de que el objeto responsable del movimiento
sea del mismo color que el fondo, este movimiento
pasará desapercibido. Para reducir este umbral sin
aumentar la probabilidad de falsa alarma, usaremos el
hecho de que el ruido es independiente: Cuando se
produzca un movimiento, lo confirmaremos con los
píxeles vecinos, y sólo lo notificaremos cuando el cambio
se halla producido para todos ellos. De esta forma, el
tiempo de proceso se mantiene reducido para la mayoría
de las iteraciones.
Realizando pruebas con el terminal Nokia 6600,
establecemos que con un umbral por debajo de 80,
sobre una distancia máxima entre pixels de 256·3, se
producen falsas alarmas. En cuanto a la velocidad de
muestreo, es muy reducida. Utilizando el formato BMP,
sólo conseguimos llegar a las 2 imágenes por segundo,
que es baja para aplicaciones con restricciones de tiempo
real en general, pero para este caso puede ser suficiente
si no nos encontramos ante cambios muy bruscos.
5 Pruebas de la cámara
En este apartado vamos a estudiar las imágenes
ofrecidas por la MMAPI en el terminal Nokia 6600, en
términos de formatos, resolución, tamaño, número de
colores, y tiempo de adquisición. Este teléfono, así como
la mayoría de los de su clase, tiene una cámara VGA,
esto es, puede ofrecer fotografías de 640X480 pixels, y
esta es la resolución que podemos conseguir con la
aplicación nativa de la cámara y con programas en
Symbian. Hemos hecho medidas de tiempo y tamaño
por los tres formatos disponibles en el Nokia 6600, PNG,
BMP y JPEG (esta información está disponible en la
variable de sistema video.snapshot.encodings). Por
defecto, el método getSnapshot() devuelve una imagen
PNG de 160X120 pixels, pero se puede especificar el
formato y las dimensiones en pixels deseados. Hemos
Actas del II congreso javaHispano
29
tomado medidas para cada tamaño y formato, y los
resultados se muestran en la Figura 1. También
comparamos la influencia de la complejidad de la
imagen en el tamaño de fichero: Primero, tomamos una
imagen de una pared blanca, y luego otra de una escena
compleja, con gente y diferentes objetos. Los resultados
se pueden ver el la Tabla 1.
Tabla 1: Influencia de la complejidad de la escena en el
tamaño del fichero de imagen.
Tamaño (bytes)
PNG Simple
Compleja
28953
44248
BMP Simple
Compleja
20278
20278
JPEG Simple
Compleja
1223
3469
Cuando el tamaño del fichero es demasiado grande para
el teléfono, lo que depende tanto del formato como de
las dimensiones de la imagen, hemos observado que
pueden ocurrir dos cosas: En ocasiones, la aplicación se
bloquea, y la cámara deja de estar disponible hasta que
reiniciamos el teléfono. Más a menudo, la aplicación
aborta con un mensaje tipo “Application closed Monty
Thread –7”.
Observando la calidad de las imágenes en diferentes
dimensiones, se aprecia que el teléfono siempre utiliza
imágenes del tamaño por defecto (160X120), y después
los reescala si es necesario. Por tanto, la calidad de la
imagen no mejorará.
Para aplicaciones de tiempo real, tales como el Detector
de Movimiento, podemos ver que no se alcanza una
velocidad elevada: En el mejor de los casos, nos
acercamos a dos imágenes por segundo (para imágenes
del tamaño por defecto). Los formatos más rápidos son
JPEG o BMP, con tiempos similares. Uno podría
preguntarse porqué el formato JPEG es el más rápido,
cuando es el que requiere un procesamiento más
complejo. Se da que una cámara típica para este uso
puede ofrecer varios formatos: Imágenes sin comprimir
(YCrCb) o imágenes comprimidas JPEG. De modo que la
máquina virtual sólo necesita tratar imágenes PNG o
BMP, las imágenes JPEG son creadas por hardware. Los
mismos tests han sido realizados para el Nokia 3650 y
alcanza velocidades ligeramente superiores, del orden de
siete imágenes por segundo.
Las imágenes en JPEG son las más pequeñas, y el
tamaño de fichero es poco dependiente de las
dimensiones de la imagen, y más de la complejidad de la
escena. Las imágenes PNG son mayores, pero es el único
formato obligatorio para las especificaciones de la
MMAPI. El formato BMP no usa compresión, por lo que
el tamaño de fichero es proporcional a las dimensiones
de la imagen. Sin embargo, la calidad de imagen es baja,
puesto que usa una paleta de 256 colores para (8 bits
por pixel) para evitar que el tamaño se dispare. Hay que
notar que para el tipo de aplicaciones que estudiamos, el
tamaño de fichero no es tan importante, ya que nosotros
vamos a trabajar sobre los valores RGB de los pixels,
luego en todos los casos necesitaremos un array de
longitud dependiente de las dimensiones de la imagen.
Una vez tengamos este array, podemos liberar la
memoria que ocupe la imagen. Por otro lado podemos
estar interesados en sólo parte de la imagen, así que en
cada caso debe estudiarse qué formato es la mejor
elección.
0
2
4
6
8
10
12
14
16
18
160x120 (default)200x150 320x240 400x300 640x480 800x600
TIM
E (
sec.
) ->
IMAGE DIMENSIONS ->
PNGBMPJPG
0
100
200
300
400
500
600
700
800
160x120 (default)200x150 320x240 400x300 640x480 800x600
FIL
E S
IZE
(K
B)
->
IMAGE DIMENSIONS ->
PNGBMPJPG
Actas del II congreso javaHispano
30
Figura 1: Tiempo de adquisición (arriba) y tamaño de
fichero (abajo) para diferentes tamaños en los formatos
de imagen disponibles.
Algunos de los aspectos que hemos descubierto sobre el
funcionamiento de la MMAPI de Java pueden explicarse
conociendo el acceso a la cámara en lenguaje nativo. En
Symbian, el acceso está basado en imágenes, no en
video. El viewfinder debe implementarlo el programador
en cada aplicación concatenando imágenes
periódicamente. Las imágenes se obtienen usando un
esquema de objetos activos, y en un formato específico
de java, que posteriormente puede convertirse en el
formato deseado. Uno de los parámetros de la petición
de la imagen es la calidad, que puede ser alta o baja.
Una imagen de baja calidad (llamada snapshot), es
QQVGA, es decir, de dimensiones 160X120 pixels, y de
4096 colores (12 bits por pixel). Una imagen de alta
calidad es VGA (640X480) y de 16 millones de colores
(24 bits). Típicamente, las snapshots se usan para formar
el viewfinder y video, y las imágenes VGA para la
fotografía propiamente dicha. Además, el programador
puede elegir entre dos perfiles de iluminación, noche y
día, lo que modifica el brillo de la escena por medio del
balance de blancos.
Con esta información, podemos inferir que el MMAPI
crea el flujo de vídeo por medio de snapshots tomadas
periódicamente, y los pasa a la aplicación. Las imágenes
obtenidas con getSnapshot() son simplemente uno de
estas snapshots, reescalada y con un formato diferente si
es necesario. Por tanto no podremos acceder desde un
MIDlet a la calidad de imagen que la cámara puede
ofrecer.
6 Velocidad de operaciones y memoria
Para implementar aplicaciones de tratamiento de
imagen, nuestro terminal debe ser capaz de ejecutar
algoritmos pesados rápidamente, en ocasiones en
tiempo real, y también necesita almacenar la
información de los píxeles de una o varias imágenes.
Según esto, la velocidad de las operaciones y la memoria
dinámica disponible son características claves para
nuestros objetivos.
6.1 Velocidad de operaciones
Primero mediremos la velocidad de operaciones básicas
del procesador, y podremos ver cuáles son más rápidas y
cuáles deben ser evitadas al programar. Sólo nos
referimos a operaciones de bajo nivel: extracción de una
variable de un array, suma de dos variables,
desplazamientos lógicos, etcétera. Sólo hablaremos de
este tipo de operaciones porque son las que hay
disponibles en J2ME.
Para medir tiempo, J2ME ofrece el método
System.currentTimeMillis(), que tiene una precisión de
milisegundos. Lo que haremos será repetir la operación a
medir en un bucle, restar el tiempo de un bucle vacío, y
dividir por el número de iteraciones. Hemos
implementado un programa que realiza estas medidas y
muestra el resultado por pantalla. Las medidas estarán
distorsionadas por otros procesos concurrentes del
teléfono, como la lectura de eventos de teclado u
operaciones relacionadas con el servicio telefónico. De
hecho, si durante la ejecución del programa presionamos
teclas repetidamente, se aprecia un incremento del
tiempo. No intentaremos evitar estas distorsiones,
simplemente tomaremos varias medidas y calcularemos
la media. Los resultados obtenidos se muestran en la
tabla 2.
Con estos resultados podemos inferir que la división
debe ser evitada en la medida de lo posible, y que no
debemos abusar de la multiplicación, debiendo sustituir
ambas por desplazamientos lógicos cuando sea posible.
Vemos que, sorprendentemente, las comparaciones son
también lentas, incluso más que la multiplicación El
acceso a una variable de un array es más lento que una
suma porque usa indirección.
Hay que decir también que las operaciones deben usar
números enteros. Si es necesario utilizar decimales, lo
adecuado es recurrir a la llamada aritmética de punto
fijo, que permite cálculos decimales usando tipos
enteros.
El teléfono Nokia 6600 utiliza un procesador ARM de 32
bits a 104 MHz, lo que hace un ciclo de reloj de
aproximadamente 10 nanosegundos. Típicamente, una
operación simple de procesador tarda dos ciclos de reloj,
esto es, 20 nanosegundos, lo que nos deja cerca del
tiempo medido para las operaciones más rápidas.
Actas del II congreso javaHispano
31
Esto nos podría hacer pensar que, si las operaciones van
a la velocidad del procesador, entonces un MIDlet
correrá tan deprisa como una aplicación Symbian, lo cual
no parece lógico. La explicación es que la máquina
virtual del Nokia 6600, llamada Monty 1.0 VM, utiliza un
esquema de compilación dinámica. Esto significa que la
máquina virtual tiene, además de un interprete (que
ejecuta bytecodes de Java), un compilador (que convierte
bytecodes en código nativo).
El código nativo es alrededor de un orden de magnitud
más rápido que el interpretado, pero ocupa pero ocupa
más memoria. La Monty VM soluciona esto con un
bloque más llamado Profiler, que identifica, en tiempo
de ejecución y mediante métodos estadísticos, partes del
código que el MIDlet ejecuta a menudo y repetidamente.
Estas partes del código se denominan hotspots. Cuando
el profiler identifica un hotspot, éste se compila de
forma que las siguientes veces que el MIDlet llame a esta
parte del código se pueda ejecutar directamente en
código nativo. El resto del código, accedido menos a
menudo, es ejecutado por el intérprete.
Tabla 2: Duración de un bucle de 10 000 000 iteraciones
y de una sola operación. Promedio de diez medidas.
La compilación dinámica se basa en la suposición de que
los programas ocupan la mayor parte del tiempo
ejecutando una pequeña parte del código. Ésta
propiedad suele cumplirse con el software en general, y
en las pruebas que acabamos de realizar en particular.
También se da que los algoritmos de procesamiento de
imagen son frecuentemente iterativos, con lo que la
suposición se cumplirá también para las aplicaciones
bajo estudio. Esto significa que podemos esperar un
buen comportamiento de nuestros MIDlets respecto a las
aplicaciones nativas.
6.2 Memoria dinámica
A continuación hablaremos sobre la memoria dinámica
de la que disponemos. La memoria volátil que se dedica
al almacenamiento de objetos Java se denomina heap.
Según las especificaciones, el Nokia 6600 dispone de una
heap de 3 Mbytes. Para comprobar esto, utilizamos una
aplicación nativa llamada Fexplorer, que permite navegar
por los archivos del teléfono, así como saber la memoria
libre del dispositivo. Con esta aplicación descubrimos
que la memoria dinámica libre, independientemente de
los datos almacenados en la memoria flash, es de 10
Mbytes. Sin embargo, la heap podría ser menor al ser la
memoria disponible para MIDlets.
Para comprobar esto, y tomar medidas reales,
implementamos una MIDlet que intenta reservar
memoria del tamaño que se le especifique. La
ejecutamos varias veces con tamaños crecientes, y vimos
en qué punto aparecían excepciones del tipo Out of
Memory Error. Si la memoria se reserva nada más iniciar
la aplicación, alcanzábamos los 700 kilobytes. Sin
embargo, si esta memoria se liberaba y se reservaba más
sin salir de la aplicación, y se incrementaba esta cantidad
poco a poco, alcanzábamos varios megabytes, por
encima de la heap indicada en las especificaciones del
teléfono.
Con estos experimentos, podemos concluir, primero, que
la heap que puede alcanzar un MIDlet es de 10
Megabytes, y no de 3. Y segundo, que la memoria
asignada a un MIDlet es dinámica: inicialmente, la
máquina virtual permite una pequeña cantidad, pero
cuando la memoria realmente usada por el MIDlet se
acerca al límite, se le asigna más.
7 Lo que podemos y no podemos hacer
Una primera conclusión es que la primera acción a
realizar al abordar una aplicación de este tipo, es
identificar los recursos de que disponemos desde J2ME,
ya que pueden no ser los mismos que desde una
Operación Duración
bucle (ms)
Una operación
(ns)
Bucle vacío 1 372.0 0.0
Extracción de un
array
2 062.7
69.1
Incremento
(++)
1 573.3 20.1
Suma 1 565.9 19.4
Desplazamiento
lógico
1 562.5
19.1
Multiplicación 1 862.6 49.1
División 12 396.7 1 102.5
Menor o igual 2 375.1 100.3
Menor 2 351.4 97.9
Igual 2 554.8 118.3
Actas del II congreso javaHispano
32
aplicación nativa o que los que permitiría el hardware
del teléfono: que el terminal tenga cámara no significa
que sea accesible desde Java, y que ésta sea VGA no
significa que podamos sacar fotos de 640X480, etcétera.
De acuerdo con los resultados obtenidos, el límite mayor
lo impone la cámara, o más exactamente, las imágenes
que ofrece la MMAPI a los MIDlets. El pequeño tamaño
de las imágenes, combinado con la falta de un
mecanismo de enfoque, que impide tomar fotografías
nítidas de objetos a menos de 20 cm de la cámara, hace
difícil implementar aplicaciones como lectores de
códigos de barras, reconocimiento de texto, etcétera.
Con una aplicación nativa podemos alcanzar una mayor
resolución, aunque el problema del enfoque sigue
presente.
Si nuestra aplicación necesita procesado de tiempo real,
de nuevo J2ME ofrece muy pobres prestaciones,
mientras que Symbian ofrece una velocidad de muestreo
que bastará en la mayoría de los casos.
Si las aplicaciones tienen suficiente calidad y velocidad
con lo ofrecido por la MMAPI, encontrarán el límite
impuesto por la velocidad de operaciones, y también con
la reducida API de matemáticas ofrecida por MIDP.
Respecto al primer punto, según discutimos antes se
puede esperar un comportamiento razonablemente
similar entre aplicaciones Symbian y J2ME. Respecto a las
funciones matemáticas disponibles, aunque en Symbian
disponemos de todas las funciones que podamos
necesitar, así como tipos double para cálculos decimales,
etcétera, el programador debe evitar todo esto si quiere
una aplicación eficiente. Lo correcto será implementar
las funciones que necesite mediante otras técnicas como
tablas de lookup, aritmética de punto fijo, etcétera, que
también son aplicables en J2ME.
Por último, las aplicaciones que hagan uso del color y la
luz de la imagen, como nuestro lector de colores,
encontrarán su límite en el balance de blancos de la
cámara, que no podemos controlar ni conocer su estado.
En Symbian el problema sigue presente, aunque
podemos escoger el perfil de iluminación más adecuado
para la aplicación.
Por supuesto, la mayoría de estas limitaciones serán
superadas por los nuevos terminales que están
apareciendo al escribirse estas líneas, con mejores
cámaras, no sólo en términos de resolución, sino
también con zoom óptico, autoenfoque, flash, etcétera,
y sobre todo , con mejores implementaciones de la
MMAPI que permitan obtener el máximo provecho de las
características de la cámara. Sin embargo, incluso con las
capacidades de los terminales estudiados, pueden
implementarse aplicaciones novedosas y útiles mediante
un diseño cuidadoso y una buena dosis de creatividad.
Referencias
[1] Especificaciones del terminal Nokia 6600. Forum Nokia http://www.nokia.com/nokia/0,8764,33211,00.html.
[2] Especificaciones del terminal Nokia 3650. Forum Nokia http://www.nokia.com/nokia/0,8764,2275,00.html.
[3] Programming Wireless Devices with the Java 2 Platform, Micro Second Edition. Roger Riggs (Editor), Antero Taivalsaari, Jyri Huopaniemi, Mark Patel, James VanPeursem and Aleksi Uotila. Addison-Wesley Pub Co; 2 Edition. June 2003.
[4] Brief Introduction to the Mobile Media API. Forum Nokia, Version 1.0. 2003.
[5] Nokia UI extensions for Nokia MIDP Platform. Forum Nokia, Version 1.1. 2002.
[6] Developing Series 60 applications: a guide for Symbian OS C++ developers. Leigh Edwards and Richard Barker. Addison-Wesley. 2004.
[7] The Project Monty Virtual Machine. Sun Microsystems White Paper. 2002.
[8] What Color Is Your Pair of Shoes? A Review of Two Color Identifiers. Deborah Kendrick. Access World, vol. 5, no. 3, May 2004. http://www.afb.org/aw.
[9] Pattern Recognition. Sergios Theodoridis y Konstantinos Koutroumbas. Academic Press, 1999.
Actas del II congreso javaHispano
33
Actas del II congreso javaHispano
34
Programación de dispositivos Bluetooth a través de Java
Alberto Gimeno Brieba [email protected]
Abstract
En este documento se trata la programación de dispositivos
Bluetooth con Java mediante el API desarrollada por el JCP
y especificada en el JSR-82.
Keywords: Java, Bluetooth, J2ME, JSR-82, móviles.
1 Introducción
Bluetooth es una tecnología de comunicación
inalámbrica, al igual que la tecnología Wi-Fi o los
infrarrojos. A diferencia de la primera, Bluetooth está
diseñada para dispositivos de bajo consumo y para
conexiones de corta distancia (10 metros). A diferencia
de los infrarrojos, Bluetooth es omnidireccional y tiene
un mayor ancho de banda (hasta 11 Mbit/ segundo).
Bluetooth es, pues, una tecnología ideal para la conexión
de dispositivos de bajas prestaciones (móviles, cámaras
de fotos, auriculares manos libres, impresoras,…).
Uno de los mayores ámbitos de utilización de Bluetooth
es sin duda los teléfonos móviles. Cada vez es más
común encontrar terminales móviles con soporte para
Java y Bluetooth y simplemente es un paso natural que
surja la necesidad de programar estos dispositivos a
través de Java. Desde el JCP se ha desarrollado un JSR
que cubre esta necesidad. Se trata del JSR-82 que será
explicado en este documento.
2 El JSR-82
El JSR-82[1] especifica un API de alto nivel para la
programación de dispositivos Bluetooth. Depende de la
configuración CLDC de J2ME, y se divide en dos
paquetes: javax.bluetooth y javax.obex. El primer
paquete provee la funcionalidad para la realización de
búsquedas de dispositivos, búsquedas de servicios y
comunicación mediante flujos de datos (streams) o
arrays de bytes. Por otro lado el paquete javax.obex
permite la comunicación mediante el protocolo OBEX
(OBject Exchange); se trata de un protocolo de alto nivel
muy similar a HTTP.
3 El paquete javax.bluetooth
Primero abordaremos la programación de un cliente y
más tarde veremos cómo programar un servidor.
3.1 Clientes Bluetooth
Un cliente Bluetooth deberá realizar las siguientes
operaciones para comunicarse con un servidor
Bluetooth:
• Búsqueda de dispositivos
• Búsqueda de servicios
• Establecimiento de la conexión
• Comunicación
El punto de partida es la clase LocalDevice que
representa el dispositivo en el que se está ejecutando la
aplicación. Este objeto es un singleton y se obtiene
mediante LocalDevice.getLocalDevice(). Este objeto
permite obtener información sobre el dispositivo: modo
de conectividad, dirección bluetooth y nombre del
dispositivo.
El primer paso que debe realizar un cliente es realizar
una búsqueda de dispositivos. Para ello deberemos
obtener un objeto DiscoveryAgent. Este objeto es único y
se obtiene a través del objeto LocalDevice.
DiscoveryAgent da =
LocalDevice.getLocalDevice().getDiscoveryAgent();
El objeto DiscoveryAgent nos va a permitir realizar y
cancelar búsquedas de dispositivos y de servicios. Y
también nos servirá para obtener listas de dispositivos ya
conocidos. Esto se lleva a cabo llamando al método
retrieveDevices(). A este método se le debe pasar un
argumento de tipo entero que puede ser:
Actas del II congreso javaHispano
35
• DiscoveryAgent.PREKNOWN. Para obtener una
lista de dispositivos encontrados en búsquedas
anteriores.
• DiscoveryAgent.CACHED. Para obtener una lista
de dispositivos “favoritos”.
El método retrieveDevices() devuelve un array de objetos
RemoteDevice. La clase RemoteDevice representa un
dispositivo remoto y tiene métodos similares a
LocalDevice que, recordemos, representa al dispositivo
en el que se ejecuta la aplicación. Así pues, podemos
obtener el nombre del dispositivo mediante
getFriendlyName() y su dirección bluetooth mediante
getBluetoothAddress().
Podríamos omitir la búsqueda de dispositivos y pasar
directamente a la búsqueda de servicios en caso de que
deseásemos conectar con alguno de los dispositivos
pertenecientes a alguna de estas listas. Sin embargo lo
más común será intentar conectar con un dispositivo
encontrado en una búsqueda de dispositivos, debido a
que obviamente lo tendremos a nuestro alcance.
Una búsqueda de dispositivos se inicia llamando al
método startInquiry(). Este método requiere un
argumento de tipo DiscoveryListener. DiscoveryListener
es una interfaz que implementaremos a nuestra
conveniencia y que será usada para que el dispositivo
notifique eventos a la aplicación cada vez que se
descubre un dispositivo, un servicio, o se finaliza una
búsqueda. Estos son los cuatro métodos de la interfaz
DiscoveryListener:
• deviceDiscovered()
• inquiryCompleted()
• servicesDiscovered()
• serviceSearchCompleted()
Los dos primeros métodos son llamados en el proceso
de búsqueda de dispositivos. Los otros dos son llamados
en procesos de búsqueda de servicios.
Cada vez que un dispositivo es encontrado se llama al
método deviceDiscovered() pasando un argumento de
tipo RemoteDevice.
Una vez que la búsqueda de dispositivos ha concluido se
llama al método inquiryCompleted() pasando como
argumento un entero que indica el motivo de la
finalización. Este entero puede valer:
• DiscoveryListener.INQUIRY_COMPLETED si la
búsqueda concluyó con normalidad,
• DiscoveryListener.INQUIRY_TERMINATED si la
búsqueda ha sido cancelada manualmente o
• DiscoveryListener.INQUIRY_ERROR si se produjo
un error en el proceso de búsqueda.
Ya hemos conseguido dar el primer paso para realizar
una conexión cliente. El siguiente paso es realizar una
búsqueda de servicios. Antes de seguir deberemos
comprender ciertos conceptos.
Una aplicación cliente es una aplicación que requiere un
servidor para que le ofrezca un servicio. Este servicio
puede ser: un servicio de impresión, un servicio de
videoconferencia, un servicio de transferencia de
archivos, etc. En una comunicación TCP-IP un cliente se
conecta directamente a un servidor del que conoce el
servicio que ofrece, es decir, conocemos a priori la
localización del servidor y el servicio que nos ofrecerá;
sin embargo un cliente Bluetooth no conoce de
antemano qué dispositivos tiene a su alcance ni cuáles
de ellos pueden ofrecerle el servicio que necesita. De
modo que un cliente Bluetooth necesita primero buscar
los dispositivos que tiene a su alcance y posteriormente
les preguntará si ofrecen el servicio en el que está
interesado. Este último proceso se denomina búsqueda
de servicios y es el siguiente paso que un cliente debe
realizar.
Cada servicio es identificado numéricamente. Es decir, a
cada servicio le asignamos un número y para referirnos
a dicho servicio usaremos su número asociado. Este
identificador se denomina UUID (Universal Unique
IDentifier). Adicionalmente, cada servicio tiene ciertos
atributos que lo describen. Por ejemplo un servicio de
impresión podría describirse por diversos atributos
como: tipo de papel (dinA4, US-letter,…), tipo de tinta
(color, blanco y negro), etc. Los atributos también están
identificados numéricamente, es decir, para referirnos a
un atributo usaremos su número asociado.
Las búsquedas de dispositivos también se realizan
mediante el objeto DiscoveryAgent. Concretamente
usaremos el método searchServices() al que le
tendremos que pasar un objeto DiscoveryListener que
recibirá los eventos de la búsqueda, el dispositivo en el
que realizar la búsqueda (un objeto RemoteDevice que
normalmente obtendremos en la búsqueda de
dispositivos), los servicios en los que estamos
interesados, y los atributos que queremos conocer sobre
Actas del II congreso javaHispano
36
dichos servicios (tipo de papel, tipo de tinta, etc). Por
ejemplo un cliente que esté interesado en un servicio de
impresión, para imprimir un texto probablemente sólo le
interese conocer el tipo de papel, sin embargo si
queremos imprimir una imagen estaremos también
interesados en si soporta o no tinta de color.
Si se encuentra algún servicio se nos notificará a través
del objeto DiscoveryListener mediante el método
servicesDiscovered(). Se nos pasará un array de objetos
ServiceRecord que encapsulan los atributos de servicio
que solicitamos al invocar la búsqueda. Los valores de
estos atributos de servicio son objetos DataElement.
Un objeto DataElement encapsula los tipos de datos en
los que puede ser representado un atributo de servicio.
Estos pueden ser: números enteros de diferente longitud
con o sin signo, cadenas de texto, URLs, booleanos, o
colecciones de DataElements.
Un ServiceRecord es, pues, como una tabla que
relaciona los identificadores de los atributos con sus
valores (objetos DataElement).
Cuando finalice la búsqueda de servicios se nos
notificará mediante una llamada al método
serviceSearchCompleted() de la interfaz
DiscoveryListener. Se nos pasará un argumento de tipo
entero indicando el motivo de la finalización. Este
entero puede valer:
• SERVICE_SEARCH_COMPLETED: la búsqueda ha
finalizado con normalidad.
• SERVICE_SEARCH_TERMINATED: la búsqueda
ha sido cancelada manualmente.
• SERVICE_SEARCH_NO_RECORDS: no existe la
información solicitada.
• SERVICE_SEARCH_ERROR: finalizó por un error.
• SERVICE_SEARCH_DEVICE_NOT_REACHABLE: el
dispositivo no está a nuestro alcance.
Estas constantes son miembros de la interfaz
DiscoveryListener.
Si hemos encontrado algún servicio que nos interesa
pasaremos al siguiente paso: abrir la conexión.
Abrir una conexión Bluetooth se lleva a cabo de la
misma forma que se abre cualquier otro tipo de
conexión en CLDC: a través de la clase
javax.microedition.Connector. Usaremos su método
open() y le pasaremos una URL que contendrá los datos
necesarios para realizar la conexión.
No necesitaremos construir la URL a mano ya que el
objeto ServiceRecord posee un método que nos ahorra
esta tarea: getConnectionURL().
Llegados a este punto debemos saber que tenemos dos
formas diferentes de comunicación: a través de flujos de
datos utilizando el protocolo SPP (Serial Port Profile) , o
bien a través de L2CAP enviando y recibiendo arrays de
bytes. La forma más sencilla es mediante SPP.
Si el servidor utiliza SPP el método Connector.open() nos
devolverá un objeto de tipo
javax.microedition.io.StreamConnection. A través de
este objeto podemos obtener un (Data)InputStream y un
(Data)OutputStream. Por lo tanto ya tenemos un flujo
de lectura y un flujo de escritura por lo que estamso en
condiciones de leer y escribir datos.
En caso de que el servidor utilice L2CAP el método
Connector.open() nos devolverá un objeto del tipo
javax.bluetooth.L2CAPConnection. Con este objeto
leeremos bytes con receive() y escribiremos bytes con
send().
3.2 Servidores Bluetooth
La creación de un servidor Bluetooth es más sencilla que
la programación de un cliente ya que no necesitamos
realizar ningún tipo de búsqueda. Concretamente los
pasos que debe realizar un servidor Bluetooth son los
siguientes:
• Crear una conexión servidora
• Especificar los atributos de servicio
• Abrir las conexiones cliente
Crear la conexión servidora es relativamente simple.
Sencillamente debemos llamar al método
Connector.open() pasándole una URL con una sintaxis
determinada. En caso de querer comunicarnos mediante
SPP la URL comenzará por “btspp://” y en caso de querer
comunicarnos mediante L2CAP la URL comenzará por
“btl2cap://”. A continuación deberemos indicar
“localhost/” como host. Esto determina que no
queremos conectarnos a nadie, sino que queremos ser
servidores. Seguidamente sólo nos queda concatenar a
la URL el identificador del servicio (UUID) que vamos a
ofrecer.
Actas del II congreso javaHispano
37
A continuación llamaremos al método Connector.open()
pasando la URL como argumento. Si la URL comienza
por “btspp://” nos devolverá un objeto del tipo
javax.microedition.StreamConnectionNotifier y en caso
de que la URL comience por “btl2cap://” nos devolverá
un objeto javax.bluetooth.L2CAPConnectionNotifier.
El siguiente paso es especificar los atributos de servicio.
Por ejemplo si vamos a ofrecer un hipotético servicio de
impresión podríamos indicar qué tipo de papel y de tinta
ofrecemos. Los atributos de servicio se almacenan en un
objeto ServiceRecord. Cada conexión servidora tiene un
ServiceRecord asociado que se obtiene a través del
LocalDevice.
Establecer los atributos de servicio es sencillo,
simplemente tenemos que crear objetos DataElement y
añadirlos al ServiceRecord.
Una vez establecidos los atributos de servicio ya estamos
en condiciones de escuchar y procesar las conexiones
cliente. Para ello usaremos el método acceptAndOpen().
En una conexión servidora SPP este método devuelve un
javax.microedition.StreamConnection, y en una conexión
servidora L2CAP devuelve un objeto del tipo
javax.bluetooth.L2CAPConnection. En este punto ya
podemos leer y escribir datos del mismo modo que lo
hace un cliente.
4 El paquete javax.obex
El paquete javax.obex permite manejar el protocolo de
alto nivel OBEX (OBject Exchange). Se trata de un
protocolo muy similar a HTTP. Al igual que este último,
OBEX se basa en mensajes compuestos por cabeceras de
mensaje y opcionalmente de un cuerpo de mensaje.
Adicionalmente los mensajes de respuesta del servidor
poseen un código de respuesta indicando éxito o error.
Al igual que en HTTP, los mensajes de petición del cliente
al servidor en OBEX se clasifican por métodos. Estos son
los métodos que existen.
• CONNECT. Inicia la sesión.
• PUT. Envía un archivo al servidor.
• GET. Solicita un archivo al servidor.
• DELETE. Solicita la eliminación de un archivo.
• SETPATH. El cliente desea cambiar el directorio
actual dentro del sistema de archivos del
servidor.
• DISCONNECT. Usado para finalizar la sesión.
Las cabeceras de un mensaje OBEX son encapsuladas por
un objeto HeaderSet. Existen cabeceras de uso común
como COUNT, NAME, LENGTH,… Sin embargo podremos
crear cabeceras personalizadas.
La clase Operation provee la funcionalidad para leer y
enviar mensajes que no sólo tienen cabeceras sino que
también tienen un cuerpo de mensaje. Esta clase permite
obtener un (Data)InputStream y un (Data)OutputStream
para leer o escribir el cuerpo del mensaje.
Ahora que conocemos las clases básicas pasemos a ver
cómo programar un cliente OBEX.
4.1 Un cliente OBEX
La programación de un cliente OBEX es relativamente
simple. Debemos abrir la conexión, como siempre en
CLDC con el objeto Connector. Deberemos pasarle una
URL que comience por “irdaobex://” y nos devolverá un
objeto de tipo javax.obex.ClientSession. Lo primero que
deberemos hacer será ejecutar el método connect() para
iniciar la sesión.
A partir de aquí ya podemos realizar peticiones al
servidor a través de los métodos put(), delete(), get() y
setPath(). Todos los métodos requieren un objeto
HeaderSet como parámetro. Los métodos put() y get()
adicionalmente devuelven un objeto Operation que
permite escribir o leer el cuerpo del mensaje
respectivamente.
Para cerrar la sesión llamaremos al método disconnect().
4.2 Un servidor OBEX
Crear una conexión servidora OBEX es también muy
simple. Lo primero de todo es crear un SessionNotifier
llamando al método Connector.open(). La URL debe
comenzar por “irdaobex://localhost”. Ahora simplemente
escucharemos las conexiones cliente llamando al método
acceptAndOpen(). Este método requiere un argumento
de tipo ServerRequestHandler. El ServerRequestHandler
es un objeto que deberemos implementar nosotros. Se
implementa de forma muy similar a un servlet: por cada
método del protocolo OBEX tiene un método asociado al
que se le pasan los datos de la petición. Así pues
tenemos los métodos onConnect(), onGet(), onPut(),
onDelete() y onDisconnect(). Todos los métodos tienen
como argumento un objeto HeaderSet que encapsula las
cabeceras de los mensajes, exceptuando los métodos
Actas del II congreso javaHispano
38
onPut() y onGet() que requieren un cuerpo de mensaje y
por ello su argumento es de tipo Operation.
Adicionalmente todos los métodos a excepción de
onDisconnect() deben devolver un entero que será el
código de respuesta indicando el éxito o no de la
petición y su motivo.
5 Implementaciones del JSR-82
Existen dispositivos móviles que soportan Java y tienen
Bluetooth, pero sin embargo no soportan el API JSR-82.
Esto quiere decir que no tenemos posibilidad de acceder
al dispositivo Bluetooth a través de Java. Por ello habrá
que acudir a las especificaciones del fabricante para
cerciorarnos de que las APIs están soportadas.
A pesar de que el JSR-82 se especificó pensando en la
plataforma J2ME. No sólo existen implementaciones y
emuladores para J2ME. Debido a que J2ME es una
versión reducida de J2SE, es perfectamente factible crear
una implementación que pueda ser usada desde J2SE.
De hecho existen implementaciones y emuladores. La
mayoría de estas implementaciones son libres y suelen
soportar dispositivos Bluetooth conectados al puerto
serie. Otras implementaciones son bindings para Java de
las APIs Bluetooth que ofrece el sistema operativo.
Ciertos emuladores y entornos de desarrollo también
implementan estas APIs simulando dispositivos
Bluetooth, es decir, permiten realizar aplicaciones que
usen las APIs JSR-82 sin necesidad de tener físicamente
un dispositivo Bluetooth.
6 Documentación
La documentación sobre las APIs definidas en el JSR-82
es muy escasa y mucho más escasa es en español. Sin
embargo en javaHispano se publicó un tutorial[2] al
respecto en el que se puede encontrar mas información y
enlaces a otros documentos.
Por último añadir que siempre es fundamental tener a
mano la documentación javadoc de las APIs, la cual se
puede descargar desde la página del JSR-82[1] junto con
la especificación.
Agradecimientos
Agradezco su apoyo a la Escuela Universitaira Politécnica
de La Almunia, a Ángel Blesa y a todos mis compañeros
de carrera. Pero sobre todo a todos los miembros de
javaHispano por hacer esto posible.
Referencias
[1] Especificación del JSR-82: Bluetooth desde Java. http://jcp.org/en/jsr/detail?id=82.
[2] Alberto Gimeno Brieba. JSR-82: Bluetooth desde Java. http://www.javahispano.org/tutorials.item.action?id=49.
Actas del II congreso javaHispano
39
Actas del II congreso javaHispano
40
�
�
���� ����������� ������ ����������������� ��������������������� ������ ����������������� ��������������������� ������ ����������������� ��������������������� ������ ����������������� ����������
��������������� ����������������������� ����������������������� ����������������������� �������������
�
�
�����������������������������������������������������������������������������
�����������������
����!��"���#���� ����!��"���#���� ����!��"���#���� ����!��"���#�����
���"������������
�
�
�
�
������������������������������������
$�� ����������� ������� ��� ��� ������"%�� ������ �� ����$�� ����������� ������� ��� ��� ������"%�� ������ �� ����$�� ����������� ������� ��� ��� ������"%�� ������ �� ����$�� ����������� ������� ��� ��� ������"%�� ������ �� ����
��������� �����"����� &��'� ����������� �����"����� &��'� ����������� �����"����� &��'� ����������� �����"����� &��'� ������ ����������� �������'����� ����������� �������'����� ����������� �������'����� ����������� �������'�
����������� �� �������� ���� ���� ���������� ��� ������������������� �� �������� ���� ���� ���������� ��� ������������������� �� �������� ���� ���� ���������� ��� ������������������� �� �������� ���� ���� ���������� ��� ��������
���������� (���� ����� ����������� ����������� ����� ������������� (���� ����� ����������� ����������� ����� ������������� (���� ����� ����������� ����������� ����� ������������� (���� ����� ����������� ����������� ����� ���
���"�������#���� ������������"�������������������)�����������"�������#���� ������������"�������������������)�����������"�������#���� ������������"�������������������)�����������"�������#���� ������������"�������������������)��������
�������������������� �����&�������������� ����������������������������� �����&�������������� ����������������������������� �����&�������������� ����������������������������� �����&�������������� ���������
�������� �����������������*�� � $�� ������*� �������� �� �������������������������*�� � $�� ������*� �������� �� �������������������������*�� � $�� ������*� �������� �� �������������������������*�� � $�� ������*� �������� �� ��������
������� &��� �������� ��� ������ �� �������� ������ #� ���������� &��� �������� ��� ������ �� �������� ������ #� ���������� &��� �������� ��� ������ �� �������� ������ #� ���������� &��� �������� ��� ������ �� �������� ������ #� ���
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
(������������������&������������������������"���������&���(������������������&������������������������"���������&���(������������������&������������������������"���������&���(������������������&������������������������"���������&���
�� ������� ������ �������� ������� ������ �������� ������� ������ �������� ������� ������ ��������� �������'� ����� ������� ����� �������'� ����� ������� ����� �������'� ����� ������� ����� �������'� ����� ������� ��
����������������*�������������������������������*�������������������������������*�������������������������������*�������������������
+���������,������ �����������������*����������'� ���+���������,������ �����������������*����������'� ���+���������,������ �����������������*����������'� ���+���������,������ �����������������*����������'� ���
���������������������������� �������������"���������������������������������� �������������"���������������������������������� �������������"���������������������������������� �������������"������
�������� �� ������ ��������� �*��� ��� ��#��%�� ���������� �� ������ ��������� �*��� ��� ��#��%�� ���������� �� ������ ��������� �*��� ��� ��#��%�� ���������� �� ������ ��������� �*��� ��� ��#��%�� ��
������������ �� �������� ���'� �������������� �� �������� ���'� �������������� �� �������� ���'� �������������� �� �������� ���'� ��� ������� ��� ����� ��� ������� ��� ����� ��� ������� ��� ����� ��� ������� ��� ����� ��
������������� &��� ���� ���� �� �� ��������� �-����� �� ���������������� &��� ���� ���� �� �� ��������� �-����� �� ���������������� &��� ���� ���� �� �� ��������� �-����� �� ���������������� &��� ���� ���� �� �� ��������� �-����� �� ���
�������� ������������������� �������������������'���%�������������� ������������������� �������������������'���%�������������� ������������������� �������������������'���%�������������� ������������������� �������������������'���%������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
�������������������������������������������� ����#������������������������������������������������ ����#������������������������������������������������ ����#������������������������������������������������ ����#����
�������������������������'�����������&�����.���&����������������������'�����������&�����.���&����������������������'�����������&�����.���&����������������������'�����������&�����.���&���������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
#� ������ ��� ����������� ����� �� �������� �� ���������#� ������ ��� ����������� ����� �� �������� �� ���������#� ������ ��� ����������� ����� �� �������� �� ���������#� ������ ��� ����������� ����� �� �������� �� ���������
/)+0$��-�/)+0$��-�/)+0$��-�/)+0$��-�����
1�#2�����1�#2�����1�#2�����1�#2����� ������'� �������'� $�������*� ��
�������'�3���������'���������������"����'�$��-�
�
4�����5�������*���������������������"�����4�����5�������*���������������������"�����4�����5�������*���������������������"�����4�����5�������*���������������������"���������
$��� ��������� �����"����� ��� ������������ ��
�������� ���������������������������������&���
������� ��"�������� ������"�������� ����� ������� #�
�����"��� ��� ��������*� &��� ���������
)���������� "����� ����� ���������� ����� ���
6����� �� ������ �������� �� ���� ������� ��
����������*���������/3����
.���� ��������� ��� ������� �� ���� ����� ������ ���������
����#�� �� ��&��,�� ����������� (���� �.+� ��
��������� ����� ������,��� �� ��������������
������"������������������������"������������"�����
������������� 7��� ����� ����� �� ���� ���������
�����"�����������������������������������������������
&��� ��&������ �� ����� �� � ��"����� ������������
.��� ��������� ����� #� ������� ������*���'�
����������*'� ������� �� �������'� �������� �����
�� ������� #� ���������� �� ����������� .���� &������ ���
�������� ���� ������� ��� ��� ����������� ��������
35�� ��� ���� ��������� /3��� �����'� �-�����
���������������������&��'��������������������
� ����� ��"����'� � ������� ��� �*��� ���������� ��
������������ &������ ������������&��� ��������� ���
�����������
$��� ��������� ������� �� ������������ &��� ���
�����&����������-�������������������-�*������'�
#�� ���� ���� ��� �������� �������� ���������'� ���
����������� �� ������ �� ��� ����� �� ���� ���������
�%������� 7��� ��� ���������*� ��� ��� �-�������
�������� �� �� ����������� �� �������� ��������
�. +��8�����������.�������� ����+��9������������
Actas del II congreso javaHispano
41
��%�� #� �������*� �� �. +�� ��"6� ���� ���������
53:�����������������������������*����������������
.��� ��������'� ����� �������� ������� ��� ������ ��
������� �������� �����'� ��� �������� ����� ���,���
����� ��������� �%��������� ���� ������� �� ������� ��
���������&�����������
�
;����)������������������"%�� �������;����)������������������"%�� �������;����)������������������"%�� �������;����)������������������"%�� �����������
+�� �������� �����"���� ����� ���� ���������� ��
�����������������������������������������������3��
�����"�'� ��#� ��� ������� ����������� &���
���������� ��� ��������*� �� ��� ��&���� ��������
���������������������������"�������
.�������� ��� �����������*� �� ��������� �����"����� #�
��"������� ����"�� ��� �������� �� ������ ���������
�� ������ �� ��� ��������� (���� �� ��� ��#� ���������
������ �� ������ &��� �� ��� ��� �������� �����
���%�� �������� #'� ������ � ����'� ���� ��������� �����
�������� ���������� ����� ����� ������� ���
��������*� �� ������� ����������� .���� ����� &��� #��
����� ����� ����� � ��� ������������ �� ��� �����
�����������������
�9� +���������������������"������#����� ����
��������*� �� ���� ��������*� �� ���
�����������
�9� +� ��&����� �� ��������*� ����� ���
��������*� �� �������� ����������� ��
������������
�9� +��������������������������������������� �����
���������������������������������������
�������������������������
3�"����+�������������������������������������
����'� ���������� &��� ���� ���������� ���������� ���
���������� ��� ������� �� ������� ������� ��
���������'�#� ��������&���������������������
((.<:�'����������� ���������������������������
���������#�������������"�������(��������*������ ����
��&�����������������%�������������������������������
����������#�����������&������������������������
��������*���
=�#� &���� ����� ����� �� ��� ��� ������� ��
������������������������-�������"���������������
����&�������������������������������������������
��������"������������������#������������ �����������
��������&�������������������
�� .���� ���������� ��� ��� ��
����������������� ����� ����� ����
���� �������(��� �(���������������������������
���������*�����&�%����
�� .���� �������� ����� �� ����� (��� +��
��������*� ���%�� ������ ����� �� ���������
2��� �� �� ��� �������'� ����� �� ��� �������� ��
���������������&����������������/3����
�� .��&��� ����������� �����"��� ���
��������*� �� �� ������ ��� ��
����������� ����� ���� ���� ������� (��� +��
��������*� &��� ������� ������� �� ����
����������������������������������������
���������������������������*������
�� .��������������������������"��������������
���������� �������������������������53:��
(��� ����&����� ����� ���������� ��
����������*�#����������*��
�� >� ���&��� �� "������ ��� ��������� ������
�������� ������ �� ��� �������� (��� .������
������"��� ���� �������� �� �������� 6������
�����������������'�������������������������
�*��������������� �����������������������
���� ������
+�����������������&���&�������������������
��������*� &��� ��� �������� ����� ��� ����� �� ���
�������� �����"���� ����������� ��������*� ������
�����������'���"������'���������#�����������������
��������"����������.����
$��� ����������� ��� ����������� �������� ���������
����������� #� ���2���� ������ ��� &��� ������������� (��
��������� ��� &��� ��� ��������*� ���"������ �����
����������'��������������������������� �������
������������*����"�����������������������.�����
�������� �������"���#����������&��,���������������
�������� �������� �� ��� ��������� ������ ��������
�������������������������*����������#���
�����������"����(�����,���� ���������#��������
������� &��� �� ����� �������'� ����� ��#� ������
����������"����������������2�����������������
#�������������������������*��������������������
����������
(�������������������*��������������'����������
�����������,����������������������������.���
Actas del II congreso javaHispano
42
���� ��������������&������ ���"�������������������
�������������������2����#�����2�������������&���
����������������"������(���������������'�"��������
���������������������������
������������ "������ ��� ������� ���
�������������>����&�����#�����������������%�����
�� ���"��������� ��� �-��������� �� ��� ��"�����
����� �������&��������#���%��������������������
��� �-��������� �� ���� ��������� ���������
����������������������������
?����"��������������������������"%��?����"��������������������������"%��?����"��������������������������"%��?����"��������������������������"%��
��������������������������������� ��������������������������������� ��������������������������������� �������������������������������������
��&���������������&��������������������������
������"%�� ������� ����� ������� �������� ��#�
��"�����������������������&��������������
�� (�� ������ �� �������� ����� ��#� ������"���
���� ��� �������'� �������� �� �������� #�
���������������� �������.��������������
������������������������&�������������*���
�� �� ������ �� &��� ��� ������ ������� ���
������������"����&����������������������
���'� ��� ��&���� �������� #� ��� ������ �� ���
����������� ������ ���� ��������� ��#�
�����������������������
�� >� ���� 6�����'� ����� ����� �������� ��� ����
���������'� $��� ��������������� ���������
�� ������� ������� �� ���������� �����
��������������������������������
(����6�����������������������������������������
����������������@.���&�����������A�.�����������
�������,�������� ������������������������"���
�� &������� ��� �������� #� ������� ���� ������
�����&���������������������:�&�����������������
��� ����������� ��"���� ��� ����������� �� �������� (�
����&����� ����� ��� �� ���,�� �� ��� ����� ������ ��#�
��"����������������������������������������&���
��#���%���������������BC1��������������
C�� ��������*�����������������������C�� ��������*�����������������������C�� ��������*�����������������������C�� ��������*���������������������������
3�� ������� ��� ���������*� �� ��� �����������
�������;�4�4�������������
/����"������������D�!����������
���������������"#����������&�������"����"��
�����������)������� ���������������"#�����2�
�-����������������������������'�����������2����
����E����� �������"���� �"���"��"�F����&�����
������G�����#��7��������'���������������"��������
�����������������������������������������������
����������������3����"�������������������������
2����������������#��������������
!�����������������������&������finalize() 2�������
���������#����������������������#��#����� ��������
��������������'������"��������������������#�
����������������H4I��
.�����������&������������������������'����*���
�� ��#� �� ����������� �� ������'� ���� &��� �� ��#�
�"��� ������ �������� �� ���������� ��� ��������
�������=���������������������&��������"�%�����
������������������������������ ��������������
����������2�����&���������������������*H;I���
.���� ������ ��� ��������� ������� ������� ��
����� ���� ����� ������� ��� ������� �� ����-���
����� ������������� +� ������� ����� ������
����������� �� �� ������������ ����� �� �������
����� ������ �������� �� ������� �� �������� ���
�����������(�������������������&��������������
�����&���������&����. +���������������#�����������
<����������� ������� �������� ������ C� �������
�*"����'� ��� ������ �� ��������-��� �. +3� �����
�������'�������*���������6�������������������%�����'�
���%��������� ���&����������������������������
��������������#�&���������������������-������
"����� ��� �%���� �� ��� ������� ��� �������� <����
�����'� �-����� ������������� �����������
3���"����� ����� ������������� ��� "������� ���
���������������*���� �� ��� ��������� ����'� ��������
���������������*����#����6����������� ��������
�� ���������*� ���� ������ �� � �� �������������
@��������������������������������������������������
��������A�.�������������"����5���������%�����
������ &��� ��� ������������ ��� ������ ����
�����������������"�������������������*���'�
����'� ���� �� ��"������ &��� ��"�� &��� �����������
������������������������������������������������
.��� ��� ����� ��� ����"�� �� &��� ��� &������� ���
�������� ���� ����� ����� ���� �������� �� ��� ����
����������������������������&��,����
3�����������������������������������������(����
����*� ;�;�4'� ��� ���� ���� ��� ������*� �� ��
����������� �� �������� .���� &���� ����� ���
Actas del II congreso javaHispano
43
��������%������ �������'� ��� ����'� ���� �������� &���
������� ����������� �� ������� �� ��������� ��
�&����������������&����������"���������������
@����"�����A� ���������&�����������������3��
�������� &��� ��#� JKL� � ������� �� ��������� �������
������ �� ��� ����� �����'� ����%�� ���
�-����������� ��%���� �������� ����������� ��
��������� ��� ��� �����������*� �� ��� ��&����
��������;�;�4��� �����>�����������&������������*�
��������������������������"��������������������
(�����&���������'�������������*���������������������
�������������������������������#�������7�����������
������� �������� ����� ���� ��� �������� #� ��� ���� ��
������ �����'� ������� �� ������ ��������� ������
��������*�����������&���"�������.����������'����
����� �� �� ������� ������*���� �� ��� �"��� ����
���������35����(�����"������&����������������������
����� ��� ������ ��� ��� �������� ����'� ���� &���
��"���������&���������������%���������������"���
������������� ��� �������� �� �������� �� ��
�����������������������������
(�����������������������*����������������������
����� ������� 3�� �����"�� ��"���� �� ����� ���������
������������������"%��:7�4�8:����7������9�&������
����� ����������� ��������� :7�� ��� �� ������
���������������������������. +3�������������
�*���'�#������/3�'�/.<3���+�73��
=����� ������ ��� ����� �� ������ ���*� ���'� �����
@=����� &��� ����� ���������� �� ��������� ����� ���
���"������� ��� ������������ �� ������� �������A�
M��������"�����������
�� ����� ���� �������� ����� ���� �������� ���
����������#��������������������������
�������� ��������� �� ��� ���������� ���
������ �� ��� ������ ��������� (�� ����� ���
����"����� �� ��� ������� �� &��� ��������
��������������������������������������'������
&��� �� ����� ����� ��� ������� �� "�����
����������BC�1��� �����������������������
������� ����� &��� ��� ��������� ��������
��������������������������������#��������
����� ���� ������ ����� ��� ������ ��
����������������
�����������������������������������������������������������
4�(��������:7���������� ���������������������&���������������
�����������������������2������������ �����������"��������"���
�� $��� ��������������� ����������� ����
����������������&�������������������
�������������������� ������������&�����
������ &��� ������#�� ���������� �� ������
��� ����������������� �����������&���
����"�������������������������������������
�� (����#���������&���������"������� �������
����"��� �� ����������� ����������� .��� ��������
�������������������#�"�������#�����������
��������� ����� ������� �� ������ #� ���"��
����� ������ ������� )�� ��� ���"���'� �����
��������������"������*����������������'�
����� ��� ���"������� ��� ���� &��� ��
����������������������
�� =�#�&�����������#������������������-����
&�����������������������������$�������������
����,�����������&������������#�����������
��������� �����-������ ������&��� �*��� ���
������#�����6���������>'�����������'���
�������������������,����
�� 3��&�������������N���"����������������*�
�� ������ ��������� �� ������� ��� ����� N�
��"������� #� �������� ������� &��� ����
���������%��� ��� �������� ��� �������� (� ���
����� �� ���� ������ ������ ��������� ��
������� ��������� #� ������� ���� ���� &��� ���
���������������*���
<���� ���'� ��� ������������ �� ���������� ���
�������� ��� ���� ��� ���� ��� �����"����� &��� �����
������������������#�������������������������%������
���� ���"��������� ������� &��� ����� &��� ��������
���������������"������*��
�����'� ������� ����� �� ������ &��� ��� ��������
����������������������������<:�'�((.<:��#�<����
$��<��������#���������������#������������3�������
����&����� ��"������� &��� ��&������ ���������
���������� ���������� � ����� ������������� ������ +��
��������*� �%����� ����� ��������� &��� ��� ���������
������<��������������������������������������'�
����'� ��� ���� ���� ����� ���� ����������� �����
"��������������������((.<:��#��������������������
<��� ����� ������ ������������� 7��� ����� �����'� ���
���������&��� �*�����#����������� �����������#'�
������ �� ������ &��� �*��� ��#� C1�� �� <���
������������������������������������
Actas del II congreso javaHispano
44
C��3�����*�&�������������C��3�����*�&�������������C��3�����*�&�������������C��3�����*�&�����������������
M����� �� ��������������� ���������� &��� ���� ����
���������� �������� ����� �������� ��� ��������
������*����������������������������� ����� � +�
������� ����� ��������� ��� ������ �� ��������
����������#���������������������������������������
���������������������������������������$��6����&���
��#� &��� ������ ��� &��� ������ �� ��������
&�������� ��������� #� ����� ������� �������� �� ���
��������*�� $��"�� ���� ������� ����� ���������� #�
���������� ����� &������� (�� �������� &��� �����
��"���� ������������ ����� ���������� ���
��������"�'� ����'� ����� &���� �� ��� ������*� ���
���"������� ����� ��� �������� ���������� �����
���������������������#��������������������������
�����������(�����&���������'�������������������
�� ������ &��� ��� ��������� �� �������� �� ���������
�����&���������"���������&��������������*��������
�������"�������������*�������������
K�)����������,���K�)����������,���K�)����������,���K�)����������,�������
(���������&������������*����������������������
�������"�������#����#����#�������������������
����������.���'����"6����������������������
��� ���������� �� ����� ����#� ������� &��'� ��� �������
���%�� ������� �� ��� �������� �� ������ ���������
7��������������������#�������������#��������
���&��'��������%����� �����������������������
���� ����#�� .��� ��� ����'� �������� &��� ����� ���
��������*�������������������������������
$�� ����������*� ���� �� �������� ��#� ����������
)���������� ���� �������� &��� &������ ����������
������������������������������������8����5 9�#���
���������������������"6���"������#��������
����%�������"������������ �������(����������
��� ��#� ��"���� ������� &��� �� ������� �����
��"��������� ��� �� �5 � ������� (���� ��� �� ���������
���&��� ����&������ &��� ����"�� ��� ������ �����
������ ������ �,��� .��� ����� ���� �� �������
���� �������&�����������&�������������#�����#��
&��� ������ ���� ����������� ����������� ��
������"���%��� $�� ������*� &��� ������ ������� ���
��#��������������������'���������������������
���� ��"��������� ������ ��� ���������� �� ��
��&��,�� ������� ��� &��� ����������� ������� �*��� ���
������ ������� ������ &��� ����� ������� ����"��
�������������������������������������������������
����������&������ ��������������������� ����������
����������������������������
3�������"�������������������������������������
��"��������� ��������� )��������������� ���������
�������#��������������������,�����������������
������������(�������������������������� �����*�
���������'������������((.<:����������������
����� �� ���������� .��� ���� ���*� �������������
� ��� ����� ���� �&������ &��� ��&������ �������� ��
����������������������������������������*�����
��"��� �� ����� �� ��� �������� ����������
7������������� ��� ������� ����� ����������
���������� ��"����*'� ��"������*� #� ��"������*�
��"����� (�� ��������� �� ��� ��"������*� ��� &���
������� ���"������*� �-����� &��� ��� ����"�� ��
���������� �������� ��"����'�&���������������&���
�������� �������� (���� ��� ��"����*� #� ���
��"������*���"������������*������������������
$����"����*���������������������������������
��&������ ����� ���������� (�� ��������� ��� &��� ���
��"������*���"���� ������ �����������������
��������������2���������������&��������������
�����������������������������2�����������������*����
�������#��������������������������%�����'��������
����"�����������������"������*���"���'����
�������� ���� ��� ��"����*� ����� ������ ��
��"������*����������������
$��� ���"������ �� �������� ����� ���������
������������������ �����&����������������
���� ���������������&����������������������&���
��&������ ���� �� ��� ��"��� ����� �����
���������� &��� ����� ����� ������� ��� ������
��������� �������� (� ������� ���� ���� ���
����������"��������������������������������
�� ��������� �����'� �� ���� ����� ��� ����������
��������������������*��������
�� .��� ���������������#�����������
�� +�����������"������
�� +���������������������������������
Actas del II congreso javaHispano
45
(���������������� ����������#���������#�����������
B������<O�����������+�-���
(�� �������� �� "������ ���� ���� &��� ���� �������� ��
�������� ��"�� �� ��,�� #� �� "������ ������ ��
�������� �� "������ �� �� �������� �� �������
���������� �� ��� ��������� :���� ������ ���%�� �����
������� ������� ��������� #� ���������� ���� ������� ���
������� �� ��������*� &��� ��� ����� �������� #�
�������������������&�������
(����������������������'�������������������������
�������������������������������������������#������
�����������&����&�����������������������������
������������������ �����������
B��.5�����*�������������B��.5�����*�������������B��.5�����*�������������B��.5�����*�����������������
.������������������������������������������
"�������
4�� 5����������� �� ������*� #� ���� ����*� ��
����������
!���*���"������#����"����������������������������
�������� #� ������ #� ��� ����� ����� ���� ���������
�-����������*����������������
;��5��������������������������������
$����&�������������������'�����'�#���������������'��
����������������"����������"��������������'�
#��������������������������������������������
#� ����� ��� (���� ��� ��%� ���&��� ���������� &��� ���
((.<:�������#������������������.��������������'�
������"����'�������������������������
?��5���������������������#������������
5������������������#�����������#���#��������7������
��� &��� ������ �� �����#� ��������� ����� ��������
��������"��������#��"������
C��:������������������������� �����#�"�������
���2'�����'�������#����� ����"����������������
����������%�������������������
K��:������������������������������������
������� ��� ���� ��� ���������� ����'� �������
���� ����'� ������� ��� 6����� �� ��������� �� ���
���������'� ������� �� ������ �����'� ������� ��'�
��'��E���#�������������������������������������
&��� ������'� ��������� #� ������ ��� ������ �� ��
��������
B�� :���������� ������������ ��� �������� #�
���������������������*��
:������ ��� �������� ������ #� ��� ������� ��%� �����
�#���������������*�
J�(���������������� ����J�(���������������� ����J�(���������������� ����J�(���������������� ��������
5���������������������������������������������
�����������������.5�&��������������-��������+��������
����&������ ����� ����� ��� ������ �� ���� ���������
.������������������������������������������8�5 9�
����*����������������
+�� ���� ��� ��� ������ �� ��� 5������'� ��� �������
������� ����� ������� �� ���� ������� ������ ���� &���
����� �������� ����������� �������� ��� ��������'� ���
������������������������������� ������3*��������
������ ��� ������� �����'� ��"��������'� #� �� ������� ��
������� ����������#�������� �������������������3��
�������� ����� ��� �������� �� ����� ������� ����� �����
&���� ��� ���� ���� �������� (���� ��� ����� ������
������ "������ #� ���"��� ���� ���� �����
��������������� �����������������������������
�����������#��������������������������
N����������*��������-������N����������*��������-������N����������*��������-������N����������*��������-����������
+�� ���� &��� ������� �� ������� ������ �� ��������
�������������������#��������������������������
����-���������(����������������������������������������
&��� ��"�� &��� ����������� �. +3� &��� ��� ���"��'�
��������� ���� �������� �. +�� �� ���������� ��� ���
��������*�����������(������������������������
���"������*�� � ��"���� ������������ &������
����"������������ ��������"�������������. +3���
�����'� ����� �-����� ��� ������ ������ ��
������������������&��������������������������
�������� �� ��� ��������� .��%����� ����� ����� �*����
�� �������� ��������� ����� ��� ������������ ��� ��������
����������������&���������%������������������
������ ��� ����� ���� 7������ ��� �������� &���
���� ���� ��������� #� ����� ����� �� ������ ��"���� #�
Actas del II congreso javaHispano
46
������������� ��� ��� ������� �� �� ����� &���
���"�������������������������������
.���� ��"���� &��� ����� ������� ��"���� ����� &���
������ ���"������ �� �*���� �� �������������
&����������"�������������������������#�����������
�� ��� �.5� �� �. +��� (��� � ����� ������� �������� ���
�����������������"����&�������&������������������
$�� 6���� ��������� ��� &��� ���������� ��������
����������������������������������������������
���������� ���� ������������� �� ��������� ��
����������*� ����� &��� ��� �*���� ��
���������������������������������#��������������
�������������������������.���'�����������'������#�
��"�� &��� ���� ��������� ������� ����� ������ ���
������������������������"�������
+�� ���� ������������ ��� ���������� ��
������������� ������� �. +�'� ��� ���"����� ���
�������� ��� ���� �������� ��� �*���� �� �������� #�
������������'� ����&������ ����� ������� ����� ���
�������� ����� �� ����������� �� �������� ������
�����'�������������������� ���������������������
�������� �������� #� �� �������� ���� ����� �����������
����� ���"��� �� �������� (�� �������� ����� ��������
����� ���� �. +�� ���������� ����� ��"��������'�
���������#������������������������
@>�&���������������������A���������������(����
�����������'������������������������������������
�� ��������� ����� ��&���� /)+� 0� $��-� &��� ���� ���
�����������*������������������������������
����� ������ &��'� ����� ���'� �������� �� ������� ��
���������� +� ��������� ��������� ��� &��� ����
������������ ����� ���,���� ����� �� ������� ��
��������� .��� ���� ��� ����������� ������� �� ���
��������������������������*�&�����������������
����� ��� ���������� .�3�� $���;'� ��� �����*� �������
���������?�.�3����������������
P�(�����������������*P�(�����������������*P�(�����������������*P�(�����������������*����
3���"����� &��� &�������� ������ ��� ��������*�
&������������"������������,����������������35��
����*�������������*������#����� ���������������
&������ ����������� ������ ��� ��"���� �����������
�����������������������������������������������������������
;�.����������������*�������.�3��$�������������
�����00�����������������������"0�
?�$��������������*���.�3������������������
�����00222�����2��E"��������0�
&�������� &��� ��� �������� �� �*���� ��� ��"��
������������� �������������
(�����������&�����������������������
�9� .�����������������������������,��������
�������������������*������
�9� .����"���������������������������������
���������'�&�����������*����#�������
"�����������������
�9� Q��� ����� ��� ������� ;C� ������ ����� ��
�����������,����
.������� ��������������������*��������������������
�����������#��*����+3�7C�8+��������3�� ������������
7���E��9�� (���� ���� ���� �������� �� ����� ��6� ���
����������*���������&�������������������6�
������� R�"��� �������,��S� ��� ���� ��������
R�,���S'�R�����S�#�R���� ���S��&�������������������
������������������.�����������'������������*����
��������%�'� �� �� �������� &��� ��� ��������
��������'����������*���������������.�������
�������� ������� ������� ��������� ���� ���� �����
����������� ��� �6���� �������� ����� ������ �� ��������
�-�����&�������������� �����"�����&�������������
�"������������,����.����������������������*�
�-��������%�������#������������������������������
��� �������� ������� �. +�'� �������� �� ��� .�� #�
��������� ����������� ���� �������� �� ��� ���������
5�����'� ���%�� ���� ������� &��� ��� �������� ���
�����"��������������#�����������������
!�������'� ������� ������ ���� �� ���� ����� #� ����
�������� ��� �*���� ������� ��� +3�7� ����� ������
�����������������#���������&��������������&���
�������&����������������"�������������,������
4L�.�������������������4L�.�������������������4L�.�������������������4L�.�����������������������
(� ������� ��"��'� ������� &��� ��� ��� �����*� �� ���
������������������'����������������������
������ M������ ����������� ������� &���
��������������������������������������#����(����
����������������*���������"�����.����������
&��� �������#��� ��� �������� ((.<:�� ���� ������ ���
���%�� ���"��� ������ ���� ������� �� ��"��� �� ��
����������������
�����������������������������������������������������������
C�������������������������������������������+3�7������371���
�������������35��7���E���
Actas del II congreso javaHispano
47
.��� ����� ���'� ����� &��� ���� ������ ���������
�������������������&������������ ������'�#'� ���
����������#���������������������'�������� ���%�����
��������� �� ��� ��������*� �� ��� ��������� �����'�
���� ������ ��������� ����� �*������ �� ��������
"������*������������&�������������� ���������
��� ��������*� ��#� ����������� 7������
����������� ��� 3��7���E��� 8+3�79� ���� ��������
������������� M������ ��������K� ������ �������
�������������35���������������?/..�8��������������
������ ��� ������� ��� � � �� ������ ���� ��� ��� ������
������� � ��� ������������������� ��� ��� � ����� �
�������� � !"#� �� �������������������������������
$���%������
.���������������'������������������������������
�������� �� ��������� �� /)+� 0� $��-� ������� ��
�*���� �� !+3(B� � #� �� ������� �� ��� �����������
.�3�������
!�������'������������&�����������*������,����
������ ���� ���������� �������� �� ����������
������������ ��� � ��� ������ �������� ��� &��� ���
��%�����������*�����
�"������������"������������"������������"���������������
�� ����� �&������� &��� ����� ��������������� ��� &���
���� (������������ �� ���� #� <����� ���� ����
������������������������������������>��������
���� ����� ����� ������ �� ������%�� ��"6� %�� ���
"��������������
�
�
�
�
<���������<���������<���������<�����������
H4I� ������� ;�4�M��������������3��������������;�;�4�4���"�N�
H;I� ��������������� ��������F��/�����<���4G4L����%�����C'���"���;��
�����������������������������������������������������������
K�.��������������?/..�7<�?4�P4P���;/0?/� �������T��.5������
�����������2��E�"��
B�!+3(���"������!����#���� ���+����3�����#����������������������
��������������������������������������������������������-�
��������&����������������������������������*����
�����00��������������"����0�
�
Actas del II congreso javaHispano
48
JVMTI, creación avanzada de profilers de aplicaciones con la nueva API de Tiger
Daniel Glez-Peña
[email protected] Florentino Fdez-Riverola
Abstract JVMTI (Java Virtual Machine Tool Interface), es la nueva API de J2SE 5.0 orientada principalmente al desarrollo de agentes de depuración y profiling.
La implementación de profilers en las versiones anteriores a J2SE 5.0, se realizaba mediante JVMPI (Java Virtual Machine Profiler Interface), todavía en fase experimental. La migración de proyectos realizados con la API antigua a la nueva, no es trivial en la mayor parte de los casos. Una diferencia importante es que JVMTI requiere casi siempre del uso de BCI (Byte-code Instrumentation), es decir, la modificación del byte-code de las clases cargadas para la generación de callbacks hacia el agente.
JavaTraceIt! es una herramienta de código libre orientada a la depuración y optimización de código Java. Su profiler había sido realizado utilizando JVMPI, por lo que ha sido necesaria su migración a JVMTI debido al fin anunciado de la antigua API.
Keywords: Java Virtual Machine Tool Interface, Java Virtual Machine Profiler Interface, Java Virtual Machine Debug Interface, JavaTraceIt!.
1 Introducción
JVMTI es una nueva interfaz nativa disponible en J2SE 5.0, que se utiliza en herramientas de desarrollo de software y de monitorización. Proporciona un modo común tanto para controlar la ejecución como para inspeccionar el estado de las aplicaciones que se ejecutan en la JVM (Java Virtual Machine). En este sentido, los dos grandes grupos de herramientas que utilizarán JVMTI son los depuradores (control de la ejecución) y los profilers (inspección del estado).
Los depuradores suelen ser parte fundamental de un IDE (Integrated Development Enviroment), siendo de gran ayuda para la detección de errores en el código. Las funciones clásicas de un depurador son la traza o ejecución paso a paso de código, el establecimiento de puntos de ruptura o breakpoints y el seguimiento de los valores de las variables. Los IDE para Java dotados de un depurador han existido desde los comienzos de la plataforma, siendo hoy en día difícil hacer un recuento de todos ellos. Ejemplos de depuradores son, por un lado, soluciones comerciales como VisualCafé, JBuilder y Sun Java Studio, y por otro, soluciones libres como NetBeans, Eclipse, BlueJ, JavaTraceIt!, etc.
Por otro lado, un profiler se puede definir como una utilidad que analiza el rendimiento de un programa con
el objetivo de encontrar posibles cuellos de botella durante su ejecución. Las funcionalidades que proporcionan son más variadas, aunque suelen ser típicas las de análisis de memoria (clases cargadas, número de instancias, bytes ocupados) y análisis de CPU (métodos invocados, su duración y su orden dentro de cada thread). Ejemplos de profilers comerciales son Borland OptimizeIt Profiler, ej-Technologies JProfiler, etc; existiendo además soluciones libres como hprof (incluido en el J2SDK), Eclipse Profiler Plugin, jprof, JavaTraceIt!, etc.
1.1 JVMPI y JVMDI
Si JVMTI es una interfaz nueva y los profilers y depuradores son herramientas ya tradicionales en Java, la respuesta a la pregunta de cómo se desarrollaban hasta ahora la encontramos en JVMPI y JVMDI, dos APIs actualmente sustituidas por JVMTI.
JVMDI (Java Virtual Machine Debug Interface) fue introducida en el J2SDK 1.1 y estaba destinada al desarrollo de depuradores. Formaba parte de JPDA (Java Platform Debugger Architecture), que sigue existiendo en J2SE 5.0, pero con la sustitución de JVMDI por JVMTI. La Figura 1 muestra de manera esquemática este hecho.
Figura 1: JPDA antes y después de J2SE 5.0.
Como se puede observar en la Figura 1, la capa superior de JPDA es JDI (Java Debug Interface), una API 100% Java de alto nivel con la que Sun recomienda que se desarrollen los depuradores. De este modo, los depuradores ya desarrollados con esta API, no se verán en absoluto afectados por la desaparición de JVMDI.
JVMPI (Java Virtual Machine Profiler Interface) fue introducida también en el J2SDK 1.1, pero siempre ha sido tratada con carácter experimental. Esta API fue migrada a la HotSpot JVM, en el J2SDK 1.3, pero nunca llegó a ser tan estable como en la JVM clásica. JVMPI presentaba múltiples inconvenientes: hacía uso de object
Actas del II congreso javaHispano
49
IDs en vez de objetos JNI, lo que añadía dificultad; los formatos de volcado del montón eran complejos; ciertos recolectores de basura no funcionaban. Además, el uso de JVMPI tenía un impacto importante en el rendimiento de la JVM. Esta API ha pasado a estar obsoleta (deprecated) en el J2SE 1.5.0 (o 5.0), con vistas a ser eliminada en la próxima versión 1.6.0.
El problema se plantea en los profilers ya existentes, que usan obligatoriamente JVMPI, puesto que nunca ha existido una arquitectura mayor que pudiese abstraer a los desarrolladores de usar esta API, al contrario de lo que ocurre con JVMDI en los depuradores (ver Figura 1). La dificultad de la migración a la que están obligados los desarrolladores de profilers, dependerá del uso que se hacía de la API JVMPI, pero en la mayor parte de los casos no es un proceso sencillo, debido a los grandes cambios que se presentan en JVMTI.
A continuación se presentan los conceptos básicos de JVMTI (sección 2), los pasos básicos para la construcción de un agente JVMTI (sección 3) y una breve introducción a BCI (sección 4). Finalmente se comentan los detalles más destacables de la migración realizada en JavaTraceIt! para la adopción de JVMTI (sección 5) y se apuntan las conclusiones más relevantes.
2 Conceptos básicos de JVMTI
JVMTI puede ser vista como una interfaz de dos vías. Un cliente de JVMTI, llamado agente es notificado de sucesos de interés a través de eventos. Por otro lado, JVMTI puede controlar y consultar la aplicación a través de funciones, utilizadas en respuesta a los eventos, o de modo independiente a ellos.
Los agentes corren en el mismo proceso y se comunican directamente con la Máquina Virtual que ejecuta la aplicación monitorizada. Esta comunicación es la que se hace a través de JVMTI a través de los eventos y funciones. En general, los agentes son relativamente compactos y, en el caso de los profilers, se dedican a recolectar información durante la vida de la aplicación monitorizada. Los agentes, podrían ser controlados por un proceso a parte que implemente un front-end sin interferir en la ejecución normal de la aplicación monitorizada. La Fig. 2 muestra un esquema de la arquitectura básica de un sistema que usa JVMTI:
Figura 2: Arquitectura general de un sistema con JVMTI.
Los eventos JVMTI a los que se puede atender son muy numerosos. Dependiendo de la finalidad del agente se atenderán unos u otros. Para ello existen funciones que permiten especificar:
• Qué eventos se desean tratar. Por ejemplo, no son muy necesarios eventos orientados a un agente de depuración cuando se está realizando un agente de profiling, como pueden ser los eventos relacionados con los puntos de ruptura o breakpoints.
• Qué funcion se llamará cuando llega el evento. Es necesario establecer un puntero a una función que hará de callback para el evento.
Es preciso destacar que JVMTI no controla los eventos que llegan concurrentemente, por lo que se podría estar en dos funciones de callback a la vez, con la posibilidad de corromper las estructuras de datos que utiliza el agente. Sin embargo, JVMTI proporciona funciones para el manejo de monitores que serán muy útiles para evitar los problemas de exclusión mutua.
La Tabla 1 muestra todos los eventos de JVMTI:
Tabla1: Eventos de JVMTI.
Breakpoint Method Entry Class File Load Hook Method Exit Class Load Monitor Contended
Enter Class Prepare Monitor Contended Exit Compiled Method Load Monitor Wait Compiled Method Unload Monitor Waited Data Dump Request Native Method Bind Dynamic Code Generated Object Free Exception Single Step Exception catch Thread End Field Acess VM Death Field Modification VM Initialiation Frame Pop VM Object Allocation GC Finish VM Start
Actas del II congreso javaHispano
50
GC Start
JVMTI ofrece también un número elevado de funciones, que se podrían clasificar tal y como muestra la Tabla 2.
Tabla 2: Clasificación de las funciones de JVMTI.
Manejo de Memoria
Reserva y liberación de memoria. No se deben usar funciones tipo malloc.
Threads Obtener información de los hilos y controlarlos (suspender, interrumpir, continuar, etc.). Además permite crear hilos para el agente, por ejemplo para implementar un pequeño servidor que escuche en un puerto.
Grupos de threads Obtener información de los grupos de threads
Stack Frame Obtener información de los registros de activación contenidos en la pila de cada hilo.
Montón (Heap) Recorrer el montón llegando a los objetos. Posibilidad de invocar al recolector de basura.
Variables locales Acceder a las variables locales para lectura y/o modificación.
Puntos de ruptura (Breakpoints)
Gestión de los puntos de ruptura. Orientado totalmente a depuración.
Seguimiento de variables (Watches)
Seguimiento de una variable, para detectar cuando se lee o se modifica.
Clases Información completa acerca de las clases, además de la posibilidad de modificarlas (ver sección 4).
Objetos Información básica de un objeto: hash code, tamaño y uso de su monitor.
Atributos (Fields) Información acerca de los atributos (fields) de las clases.
Métodos Información acerca de los métodos de las clases.
Monitores Manejo de monitores para evitar problemas de exclusión mutua, sobre todo en el acceso a la información que recolecta el agente.
Intercepción de funciones JNI
Mapear las funciones JNI a otras que se indiquen. Con ello se pueden capturar las llamadas a dichas funciones realizadas por código nativo de usuario.
Gestión de eventos Establecimiento de qué
eventos se quieren atender así como de qué funciones harán de callback. Además se pueden solicitar eventos pasados, que no se pudieron atender.
Capacidades (Capability)
Obtención de las posibilidades que ofrece la implementación de JVMTI de la JVM usada. Activación y desactivación de esas posibilidades para optimizar el rendimiento.
Temporizadores Utilidades para medir los tiempos de CPU usados por los distintos hilos. La temporización es muy utilizada en profiling.
System Properties Obtener información acerca de la JVM usada.
General Funciones varias.
3 Pasos para la creación de un agente JVMTI
Las posibilidades de JVMTI son mayores a las que se ven en este artículo. Sin embargo, se exponen las tareas más comunes para comenzar el desarrollo de un agente que se adapte a las necesidades de cada caso.
3.1 Inicio del agente
Lo mínimo que debe tener un agente es la función Agent_OnLoad, la cual es invocada cuando se carga la librería. En el momento en que se ejecuta esta función, la JVM no ha ejecutado todavía ningún bytecode, ni ha cargado ninguna clase, ni ha creado ningún objeto. Las tareas habituales que se realizan en esta función son:
• Especificar toda la funcionalidad que va necesitar con JVMTI. Esto se hace a través de funciones específicas que indican si la JVM que se utiliza tiene disponibles las capacidades que se precisan en su implementación propia de JVMTI. Estas funciones son las del tipo *Capabilities. Existen capacidades que pueden ser cambiadas durante la ejecución, aunque esto depende de la JVM.
• Solicitar los eventos de los que se quiere ser notificado. Para ello están las funciones relacionadas con los eventos, como por ejemplo SetNotificationMode. Además se deberá indicar un puntero a una función por cada evento, es decir, especificar las funciones de callback.
• También se suelen iniciar las estructuras de datos que vaya usar el profiler, como tablas hash que mantienen toda la información que irá recogiendo el profiler: clases cargadas, objetos creados, marcas de tiempo de entrada y salida de métodos, etc.
Actas del II congreso javaHispano
51
El siguiente fragmento de código muestra una posible implementación en C++ de la función Agent_OnLoad.
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jvmtiEnv * jvmti; jvmtiCapabilities capabilities; jvmtiEventCallbacks callbacks; /* obtener el entorno JVMTI */ vm->GetEnv( (void **)&jvmti, JVMTI_VERSION); /* exigir capacidades */ jvmti->GetCapabilities( &capabilities); capabilities.can_generate_all_class_hook_events = 1; jvmti->AddCapabilities( &capabilities); /* Establecer los punteros a las funciones callback vmInit y classFileLoadHook son dos funciones que se han definido previamente. */ memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMInit = &vmInit; callbacks.ClassFileLoadHook= &classFileLoadHook; jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); /* Activar los eventos que queremos */ jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); return JNI_OK; }
3.2 Atendiendo a un evento
Una vez inicializado, el trabajo del agente consistirá en atender a los eventos que le lleguen, actualizando sus datos y utilizando funciones, bien cuando llega un evento o bien en otro momento, por ejemplo cuando se le solicita desde otro proceso que coopera con él.
El siguiente ejemplo muestra un fragmento de código para atender al evento VM Object Alloc, que es enviado cuando la JVM crea un objeto que no es de usuario. Para los objetos de usuario se debe usar BCI, comentado en la sección 4.
/* Función de callback para los eventos VM Object Allocation */ static void JNICALL callbackVMObjectAlloc(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jobject object, jclass object_klass, jlong size) { ... char *className; ... if (size > 50) { jvmti->GetClassSignature(jvmti, object_klass, &className, NULL); if (className != NULL) { printf("\nSe ha creado un objeto de la clase %s que ocupa %d\n", className, (jint)size);
} ...
3.3 Compilación y ejecución del agente
Para compilar el agente JVMTI se deben ejecutar los siguientes pasos:
Considerando que el J2SE está en el directorio /jdk1.5.0 y que la plataforma es Linux, nos situaremos en el directorio de donde estén los ficheros fuente y ejecutaremos:
>g++ –c –I/jdk1.5.0/incluye –I/jdk1.5.0/include/linux *.c >g++ –shared –o libagent.so *.o
El agente quedará compilado en libagent.so. Para ejecutar una aplicación Java que utilice el agente se deberán ejecutar los siguientes comandos (se considera que libagent.so está colocado en el mismo directorio que la aplicación Java):
>LD_LIBRARY_PATH=. >java –cp . –agentlib:agent Aplicacion
4 BCI: Bytecode Instrumentation
JVMTI no incluye muchos eventos que podrían considerarse imprescindibles. Ejemplo de ello son los eventos de creación de un nuevo objeto de usuario, o eventos eficientes de entrada y salida de métodos. Sin embargo, JVMTI proporciona el soporte para BCI (Bytecode Instrumentation), es decir, la posibilidad de alterar el bytecode del programa objetivo de optimización. Antes de explicar cómo se puede utilizar esta técnica en JVMTI, se hará una breve introducción a la misma, cuyo uso es cada día más frecuente.
Se pude definir BCI como la técnica destinada a la modificación directa de Bytecode Java. La mayor parte de las aplicaciones que usan BCI son herramientas de monitorización, profiling y de AOP (Aspect Oriented Programming). La intención es poder inyectar instrucciones en el código objeto (.class) del usuario en lugares estratégicos o puntos significativos, como por ejemplo la entrada o salida de un método, para realizar acciones generalmente de aviso.
La Figura 3 muestra cómo se modifica el código de usuario a la entrada y salida de un método para notificar a rutinas de análisis.
Actas del II congreso javaHispano
52
Figura 3: Uso de BCI para detectar llamadas a métodos.
Un ejemplo muy ilustrativo se tiene en AOP que, en líneas muy generales, consiste en interceptar el código de usuario en lugares concretos (join-points), para ejecutar el código arbitrario que implementa el aspecto (advice). Esto se consigue añadiendo el código necesario en la zona de usuario, que provoque una llamada al código que implementa el aspecto. De esta tarea se encarga en general el compilador de AOP.
Existen numerosos productos que usan esta técnica. Por ejemplo, software orientado a la monitorización de sistemas como Wily Technology – Introscope, HP OpenView Transaction Analyzer, la mayor parte de los profilers como OptimizeIt, JProfiler, JProbe y herramientas AOP como AspectJ y AspectWerkz.
Para desarrollar una aplicación que necesite usar BCI, existen también librerías, muchas de libre distribución, como por ejemplo BCEL (Apache Bytecode Engineering Library), BIT (University of Colorado – Bytecode Instrumenting Tool), ej-Technologies – jclasslib, etc. Todas ellas son librerías Java que permiten la manipulación de ficheros .class y su transformación.
Además de las librerías anteriores, se dispone de java_crw_demo (Java Class Read-Write Demo), incluida en J2SE 5.0 como una librería dinámica (.dll o .so). La aparición de JVMTI viene, por tanto, acompañada de esta librería. De hecho, el conocido profiler hprof que se distribuye con el J2SDK, hace uso de ella ya que fue migrado de JVMPI a JVMTI. java_crw_demo ofrece funciones básicas para la manipulación de bytecode, suficiente en muchos casos para la mayoría de las implementaciones. De ella se hablará en el siguiente apartado.
4.1 BCI en JVMTI
Las alteraciones más comunes que se hacen en el bytecode de usuario con BCI son la inclusión de “eventos” al código de un método, como por ejemplo, el añadir al comienzo de un método la llamada a MiProfiler.metodoLlamado(). Debido a que estos cambios
son puramente adiciones, no modifican el estado o comportamiento de la aplicación. El hecho de que la inserción se haga en estándar bytecode, implica que la JVM puede ejecutarse rápidamente. El resultado es que se habrán generado eventos muy eficientes. Esta aproximación proporciona además un control total por parte del agente, ya que la inserción de código podría restringirse a porciones muy concretas del programa (por ejemplo, sólo código de usuario, no de librerías estándar o de sistema). Una vez que se produzca el evento, se puede tratar en código Java o redirigir el evento hacia el agente nativo.
La Figura 4 muestra un ejemplo de inserción de bytecode en código de usuario para ser tratado después por el agente.
Figura 4: Inserción de bytecode en una clase de usuario.
La manipulación de bytecode puede ser llevada a cabo en tres momentos distintos:
• Estáticamente: se trata de modificar los ficheros .class antes del momento de la ejecución, como se suele hacer en AOP. Esta opción no es muy útil JVMTI.
• En el momento de carga. La modificación se produce justo antes de la carga de la clase. Para ello JVMTI proporciona el evento ClassFileLoadHook, que avisa cada vez que una clase está a punto de cargarse, proporcionando la oportunidad de modificarla. Esto ya existía en JVMPI.
• Dinámicamente. La modificación de la clase se realiza una vez cargada la clase, en cualquier momento posterior, incluso habiendo sido ejecutada ya. Para ello, JVMTI proporciona la función RedefineClasses. Esta funcionalidad no existía en JVMPI.
El evento ClassFileLoadHook y la función RedefineClasses es todo lo que proporciona JVMTI para realizar BCI. Es decir, da la oportunidad de realizar modificaciones, pero no realiza la modificación en sí, es responsabilidad del programador del agente JVMTI trabajar a nivel de bytecode con cada clase. Es por ello por lo existe la librería dinámica java_crw_demo mencionada anteriormente, que permite realizar unas modificaciones básicas sobre el código de una clase.
Actas del II congreso javaHispano
53
Las inserciones de bytecode que java_crw_demo realiza en una clase provocan una llamada hacia una clase y método que se le indique. Estas inserciones se pueden hacer hasta en cuatro lugares distintos:
• En el constructor de java.lang.Object: con ello se puede capturar la creación de cualquier objeto, ya que todos pasan obligatoriamente por este método. Es decir, se habrá recuperado un evento disponible en la antigua JVMPI, que ahora no hay: JVMPI_EVENT_OBJECT_ALLOC.
• A la entrada de todos los métodos de la clase indicada.
• A la salida de todos los métodos de la clase indicada.
• Inmediatamente antes de cualquier creación de un array. Con ello se puede capturar el momento de la creación de un objeto array.
5 Migrando JVMPI a JVMTI. Un ejemplo práctico: JavaTraceIt!
En esta sección se muestra un ejemplo real de la migración de un profiler de JVMPI a JVMTI.
JavaTraceIt! implementa un depurador y optimizador de código libre para Java. Fue presentado en el I Congreso JavaHispano de 2003.
JavaTraceIt! consta de dos partes bien diferenciadas. Por un lado implementa un depurador que permite realizar la ejecución paso a paso del código, el establecimiento de puntos de ruptura y la inspección/modificación de las variables en memoria. El depurador ha sido desarrollado con JPDA (ver apartado 1.1). Por otro lado, implementa un profiler con un analizador de memoria que permite visualizar las clases cargadas, el número de instancias de cada clase y los bytes ocupados por los objetos de cada clase.
La Figura 5 muestra una captura de JavaTraceIt! y de su profiler en ejecución.
Figura 5: El profiler de JavaTraceIt!.
El profiler de JavaTraceIt! había sido desarrollado inicialmente con JVMPI, la única alternativa disponible cuando se implementó. La desaparición anunciada de JVMPI a favor de JVMTI ha motivado la migración del profiler a la nueva interfaz. A continuación se explican los pasos más relevantes en el trabajo realizado.
La arquitectura del sistema de profiling de JavaTraceIt! es muy similar a la que se muestra en la Figura 2. JavaTraceIt! presenta un front-end como se puede apreciar en la Figura 5. En el antiguo sistema de profiling, había un agente JVMPI que corría en el mismo proceso que la JVM de la aplicación monitorizada. Su trabajo se dividía en dos partes:
• Capturar los eventos necesarios para elaborar las tablas donde se guardaban las clases cargadas, número de instancias y bytes ocupados.
• Atender las peticiones desde el front-end a través de un protocolo propio. Estas peticiones podían ser la solicitud de las tablas para
Actas del II congreso javaHispano
54
mostrarlas gráficamente y la ejecución del recolector de basura. Para ello, el agente implementaba un pequeño servidor a través de sockets por el que se recibían las órdenes y se devolvían los datos de las tablas.
Debido a la existencia de un protocolo propio que separaba el front-end del agente, no ha sido necesario tocar el front-end, simplemente se exige que el nuevo agente JVMTI implemente el mismo protocolo.
La migración del servidor ha sido muy sencilla. Lo único que se requería era la capacidad de JVMPI de proporcionar un hilo nuevo para que se pudiese ejecutar atendiendo peticiones. JVMTI tiene también esa capacidad con la función RunAgentThread.
El trabajo con los eventos no ha sido tan sencillo como el caso anterior. Los eventos más importantes que atendía el agente con JVMPI eran los siguientes:
• JVMPI_EVENT_CLASS_LOAD (carga de una clase). Cuando se cargaba una clase se creaba una nueva posición para ella en las tablas hash.
• JVMPI_EVENT_OBJECT_ALLOC (creación de un objeto). Cuando se creaba un objeto se consultaba de qué clase era y se incrementaba en uno el número de instancias de esa clase.
• JVMPI_EVENT_OBJECT_FREE (eliminación de un objeto). Cuando se eliminaba el objeto se averiguaba si estaba guardado, ya que uno de los problemas de JVMPI es que no avisaba de todas las creaciones de objetos, por lo que podía avisar de la liberación de objetos de los cuales no había sido informada su creación. Si no se tenía registrado, no se hacía nada, en caso contrario se restaba uno al número de instancias de su clase.
La migración de JVMPI_EVENT_CLASS_LOAD no ha supuesto ningún problema ya que existe su equivalente en JVMTI.
El problema más importante lo presenta sobre todo el evento de JVMPI_EVENT_OBJECT_ALLOC que, como se ha mencionado en la sección 4, no existe equivalente en JVMTI. Para conseguir de nuevo ese evento ha sido necesario incluir BCI, es decir, modificar el Bytecode de las clases. Para ello se utiliza la librería java_crw_demo como se explica en el apartado 4.1.
La idea consiste en modificar la clase java.lang.Object incluyendo al comienzo del constructor una llamada a un método estático de una clase propia: Tracker.ObjectInit(). Por lo tanto, cada objeto creado invocará al construirse a este método. Esta idea es la misma que sigue el profiler hprof cuyo código fuente está disponible. El código de la clase Object se realiza justo cuando se carga. Se utiliza por tanto el evento ClassFileLoadHook, que es el mecanismo que ofrece JVMTI para poder realizar BCI. Una vez que la función Tracker.ObjectInit() se invoca, se transfiere el control a través de JNI al agente.
Para la captura de la creación de arrays, también se inyecta código en todos los métodos de las clases cargadas donde se crea un array. Dicho código introduce una llamada a Tracker.NewArray().
El siguiente fragmento de código corresponde a la clase Tracker:
public class Tracker { /* Para contar el numero de instancias se necesita capturar la creacion de objetos y de arrays.*/ /* Al principio de java.jang.Object.<init>(), se inyecta una llamada a Tracker.ObjectInit().*/ private static native void nativeObjectInit(Object thr, Object obj); public static void ObjectInit(Object obj) { … nativeObjectInit(Thread.currentThread(), obj); … } /* Inmediatamente despues del bytecode newarray, se inyecta una llamada a Tracker.NewArray(). */ private static native void nativeNewArray(Object thr, Object obj); public static void NewArray(Object obj) { … nativeNewArray(Thread.currentThread(), obj); … }
Como se puede observar en el fragmento de código anterior, existen métodos nativos a los cuales se invoca desde las funciones ObjectInit y NewArray. Estos métodos están también implementados y registrados en el agente, es decir, cuando se llaman el control lo toma el agente para que pueda actualizar sus tablas.
Por último, el evento JVMPI_EVENT_OBJECT_FREE, proporciona uno similar en JVMTI. La diferencia que existe es que en JVMTI el evento no llega para todos los objetos, sino que solamente para aquellos objetos “marcados” (tagged). JVMTI permite establecer una marca a cualquier objeto (un jlong) a través una función. Con esto se evita que llegue este evento sobre un objeto del que el agente no tiene información de su existencia, problema que presentaba JVMPI.
6 Conclusiones
En la actualidad la demanda de profilers Java está en aumento. La enorme carga transaccional que pueden llegar a tener las aplicaciones J2EE puede generar problemas en producción. Es por ello que las herramientas de monitorización, como son los profilers, pueden ser de gran ayuda en esas situaciones. Con JVMTI, la plataforma Java ya dispone de una interfaz estándar para desarrollar este tipo de aplicaciones, que
Actas del II congreso javaHispano
55
permitirá a los desarrolladores no sólo comerciales el poder crear sus propias utilidades para la monitorización y optimización de las aplicaciones Java, de un modo sencillo y eficiente.
La transformación que ha sufrido la antigua API JVMPI hacia JVMTI ha traído consigo ventajas ya mencionadas en el presente trabajo, aunque obligue al uso de BCI en la mayor parte de los casos. Sin embargo esta evolución está justificada ya que esta técnica permite la creación de profilers muy eficientes, restricción básica en este tipo de herramientas.
Referencias
[1] Sun Microsystems. JVM Tool Interface v.1.0. http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html.
[2] Sun Microsystems. The Java Virtual Machine Profiler Interface (JVMPI). http://java.sun.com/j2se/1.4.2/docs/guide/jvmpi/jvmpi.html
[3] C.K. Prasad, Rajesh Ramchandani, Gopinath Rao, y Kim Levesque. Creating a Debugging and Profiling Agent with JVMTI. http://java.sun.com/developer/technicalArticles/Programming/jvmti/
[4] Kelly O’Hair. The JVMPI Transition to JVMTI. http://java.sun.com/developer/technicalArticles/Programming/jvmpitransition/
[5] Sun Microsystems. Java Platform Debugger Architecture (JPDA). http://java.sun.com/products/jpda/index.jsp.
[6] Joseph Coha y David Seidman. Bytecode Instrumentation – Making it simple. JavaOne 2004. http://www.hp.com/products1/unix/java/pdfs/bytecode.pdf
[7] Han Bok Lee. BIT: Bytecode instrumenting tool. University of Washington, 1996. http://www-plan.cs.colorado.edu/hanlee/pubs/master.pdf
[8] Apache Software Foundation. BCEL: Bytecode Engineering Library. http://jakarta.apache.org/bcel/index.html.
[9] Ej-Technologies - jclasslib. http://www.ej-technologies.com/products/jclasslib/overview.html
[10] D. Glez-Peña, F. Fdez-Riverola. JavaTraceIt: Depurador y optimizador de aplicaciones Java. I Congreso javaHispano, Madrid, España, Octubre 2003. http://congreso.javahispano.org/files/doc/j2se/JavaTraceIt_depurador_y_optimizador.pdf
Actas del II congreso javaHispano
56
JNIEasy, Aspect Oriented Programming a la ayuda de Java como ciudadano de primera clase en el desktop
Jose María Arranz Santamaría [email protected]
Abstract
Las necesidades en el desktop son habitualmente mucho
más complejas y diversas que en el servidor, las
aplicaciones normalmente necesitan acceder directamente a
recursos hardware o servicios del sistema operativo no
necesariamente cubiertos por el Java estándar, con los que
se consigue una mayor integración con el sistema y una
mayor familiaridad para el usuario. Este problema ha sido
resuelto por Java programando con lenguajes nativos
(C/C++) usando el complejo JNI y necesitando un
compilador o un IDE. La realidad muestra que buena parte
de los desarrolladores de Java no conocen C/C++.
JNIEasy[1] es una herramienta que permite la interacción
con librerías nativas (DLLs) para acceder directamente
desde Java a recursos del S.O. o a hardware especializado
sin necesidad de programar con JNI, C o C++. Los
programas desarrollados con JNIEasy serán 100% Java,
pudiendo llamar desde Java a métodos nativos contenidos
en DLLs, exponer automáticamente métodos de clases
normales a llamadas desde código nativo (callbacks), definir
“estructuras nativas” como simples clases, y gestionar la
“memoria nativa” de forma automática con el garbage
collector. Las soluciones existentes hasta ahora no han
conseguido una solución tan transparente y céntrica en
Java, gracias a la Aspect Oriented Programming y el
bytecode enhancement.
Keywords: Java, JNI, AOP, bytecode enhancement, C, C++, DLL, transparencia
1 El dominio casi absoluto de las tecnologías Windows en el desktop
De todos es sabido la enorme penetración que tiene Java
en el mundo del software, Java es sin duda una de las
tres plataformas relevantes de desarrollo software
actualmente junto con .Net y LAMP
(Linux+Apache+MySQL+Perl/PHP).
Pero no nos engañemos, Java al igual que la plataforma
LAMP tiene su relevancia fundamentalmente en
aplicaciones basadas en Web y especialmente en gestión
de bases de datos. Este papel muy relevante en el Web
se debe a varios factores entre otros:
1) La portabilidad de Java entre sistemas
En un mundo, el del servidor, en donde Linux y otras
plataformas Unix han tenido un gran papel al ofrecer
tradicionalmente potencia y robustez. Y siguen
teniéndolo gracias en parte a Java y a la explosión de
servicios y comercio electrónico basados en Web que nos
trajo la burbuja tecnológica. El papel de JDBC ha sido
fundamental permitiendo una conectividad con bases de
datos más estandarizada y portable que las soluciones
basadas en ODBC, y OleDB.
2) La portabilidad de las tecnologías Web y la casi
independencia tecnológica entre el cliente y el
servidor
Por ejemplo, nada impide que Java en un servidor no
Windows produzca HTML transmitido por HTTP
consumido en Windows por el Internet Explorer.
3) La calidad, fiabilidad, seguridad, robustez y
capacidad de gestión de la complejidad que ofrecía
Java como plataforma frente a soluciones más
“amateur” como ASP o PHP, o frente a la
inseguridad y complejidad que ofrece C/C++ poco
apto para el mundo esencialmente “concurrente”
que exige el Web.
4) La “familiaridad” de Java con las tecnologías
subyacentes a Internet (http, applets, RMI etc)
5) La aparición de una pléyade de frameworks
orientados fundamentalmente a simplificar el
desarrollo Web y la manipulación de bases de datos,
desde los estandarizados (J2EE=JSP+EJB, JSF, JDO)
con versiones libres y comerciales, hasta los
Actas del II congreso javaHispano
57
populares de código abierto como Struts, Hibernate,
Spring etc.
Sin embargo todo cambia cuando lo vemos desde el
punto de vista de los sistemas orientados a ser usados
como puesto de trabajo individual es decir el llamado
desktop o escritorio.
Varios hechos:
1) Windows está instalado en más del 90% de los
computadores del mundo [2]
2) La gran mayoría de aplicaciones Windows
orientadas al desktop están desarrolladas con
tecnologías Microsoft (Visual C++, Visual Basic)
sean aplicaciones de Microsoft o no.
3) Windows está fuertemente componentizado,
normalmente a través de dichos componentes
accedemos a los recursos del hardware o a los
servicios del sistema operativo.
4) La API primaria con la que Windows se “expone” es
C (Platform SDK), las demás son capas sobre ésta
(incluido el COM). El SDK C es gratuito.
5) Los drivers propietarios que acceden a los recursos
del hardware exponen prácticamente siempre una
interfaz C o COM.
Java ha hecho un gran esfuerzo por ofrecer una “capa”
Java para cualquier necesidad permitiendo una
integración portable con el S.O., pero la experiencia dice
que no siempre es posible “esperar” que Sun (o en
general el ecosistema Java) lo haga, pues obviamente
Microsoft siempre irá delante en sus propias tecnologías.
Esfuerzos como el JDIC[3] o el JMF[4] son encomiables,
pero en el caso del JMF, un componente tan básico,
todavía no tiene lugar en el JRE y su licencia es todavía
demasiado restrictiva.
De todas formas existen infinidad de casos no típicos,
normalmente acceso a hardware propietario, en donde
es difícil esperar una solución estándar, ni siquiera del
propio fabricante, el “háztelo tu mismo si puedes” es la
regla.
2 WORA, realidad y utopía
El WORA (Write Once Run Anywhere) es la utopía
deseada por Sun (y competidores en el mismo “lado”)
para poder vender más hardware y arrebatar un trozo
de pastel al mundo Microsoft/Intel (aunque ahora el reto
también está contra Linux/Intel).
El WORA es un hecho en el lado del servidor, pero está
todavía lejos de conseguirse en el desktop. El terreno del
desktop está lleno de “piedrecitas” en donde una
solución relativamente sencilla en C, C++, Visual Basic ¡y
ahora en .Net!, puede ser un verdadero “pain in the ass”
en Java, cuando ha de accederse directamente al sistema
operativo o al hardware, y no siempre estamos hablando
de necesidades de alto rendimiento que justifiquen JNI.
En este sentido Java está hoy día en una encrucijada: o
conquista un aceptable territorio en el desktop o puede
perder definitivamente esa batalla (como la perdió
Borland con C++ y Delphi), en la medida en que .Net es
un competidor directo de Java, obviamente se integra
muy bien con Windows, no es un juguete para amateurs
como en cierto modo era Visual Basic, ni tan complejo e
inseguro como era el Visual C++.
En esta guerra hay muchos factores, ciertamente se
necesita mucho más WORA pero quizás también una
visión más pragmática y menos exigente a corto plazo:
más “Java for Windows only”, huyendo de la “palabra
maldita” que hace inclinar la balanza por el universo
Microsoft: JNI
3 JNI amigo y enemigo
La programación en lenguajes nativos es más compleja
que con Java y menos robusta, JNI es relativamente
complejo como mezcla de dos mundos (Java y nativo)
pues supone programar “en Java” pero con C/C++.
Cuando se realiza una aplicación Java con JNI siempre
existe la lucha entre si más Java o más C/C++, el
modelar dos veces, la conversión de datos etc. De hecho
no es raro que el resultado sea una aplicación basada en
código nativo fundamentalmente y una fina capa Java.
El problema no es el JNI en sí mismo, JNI tiene el
importantísimo papel de no permitir que Java sea una
plataforma cerrada. En algunas aplicaciones en donde se
necesita una comunicación íntima con el hardware con
alto rendimiento es insustituible, pero es sin duda una
de las grandes piedras que se encuentra Java en el
desktop: cuando es la única alternativa para problemas
que no resuelve Java (es decir, que otros todavía no han
resuelto con JNI).
Ejemplo ilustrativo: Sourceforge.net, el inmenso
repositorio de aplicaciones de código abierto, Java goza
Actas del II congreso javaHispano
58
de muy buena salud (ver Tabla 1, muchos de los
proyectos es sabido que combinan C y C++ y el propio
Java por lo que no son “sumables”).
Tabla 1: Lenguajes en Sourceforge
Lenguaje Proyectos Java 13274
C 13807 C++ 14117
Visual Basic 1977
C# 1976
Sin embargo filtremos la categoría: Multimedia / Vídeo y
con combinaciones de lenguajes entre Java, C y C++
(Fig. 1).
Figura 1: Sourceforge, lenguajes en proyectos de vídeo
(noviembre de 2004)
Tabla 2
Lenguaje Proyectos Java sin C o C++ 10
Java con C o C++ 13
C o C++ sin Java 87
De acuerdo con la Tabla 2 podemos concluir que Java no
es muy relevante en áreas como la manipulación de
vídeo, tarea típica de desktop, y lo normal es que venga
acompañado de programación en lenguajes nativos (JNI
con gran probabilidad).
4 Aspect Oriented Programming y el bytecode enhancement
Aunque no son filosofías y tecnologías nuevas, es en este
momento cuando están teniendo un gran auge, en
concreto la AOP, pero hablar de AOP en Java casi no es
posible sin el bytecode enhancement, los dos
frameworks más relevantes: AspectWerkz y AspectJ
están basados en esta técnica (existen otras como los
proxies dinámicos, usados en Spring, pero el alcance de
posibilidades es menor), ambos vienen a ser la expresión
formal y bien estructurada de los conceptos de la AOP, y
de las posibilidades que permite el bytecode
enhancement.
El bytecode enhancement es AOP en su sentido más
primitivo, consiste en la introducción de código nuevo
Java, precompilado, directamente en las clases a nivel de
bytecode, así como la modificación de código ya
existente. A través de esta técnica se consigue el objetivo
de la AOP de resolver de forma elegante (ortogonal) los
llamados crosscutting concerns, en síntesis, tareas o
problemas que no pueden expresarse de forma directa a
través de una clase base o derivada o por agregación, y
que su resolución por las técnicas habituales supone la
diseminación de pedazos de código similares por
multitud de puntos del programa de forma fuertemente
intrusiva.
A través de bytecode enhancement podemos acometer
en síntesis los objetivos de la AOP:
1) Añadir nuevos métodos y atributos a una clase
preexistente
2) Introducir llamadas a los nuevos métodos
(advices) en ciertos puntos del código original
(pointcuts) dentro del conjunto de puntos de
unión posibles (joinpoints)
El conjunto de nuevas funciones (advices), atributos y
poincuts orientados a realizar una cierta tarea es lo que
se denomina en AOP aspecto.
En JNIEasy no se utiliza ningún framework AOP concreto,
sino que se utiliza directamente bytecode enhancement,
la razón hay que buscarla en la gran libertad de
manipulación que permite respecto a usar un framework
concreto, pues todo framework al mismo tiempo que
supone una formalización elegante de un servicio,
supone al mismo tiempo la introducción de limitaciones,
la analogía hay que encontrarla por ejemplo entre
programar en C o en ensamblador. De todas formas no
está descartado introducir en el futuro un framework,
seguramente AspectWerkz, más flexible que AspectJ.
La razón de usar bytecode enhancement es una absoluta
necesidad, sin el mismo no sería posible conseguir el
nivel de transparencia que ofrece JNIEasy, la alternativa
es usar las técnicas claramente intrusivas “y poco Java”
que han acompañado hasta ahora las aproximaciones
clásicas al problema.
Actas del II congreso javaHispano
59
5 JNIEasy hacia una solución Java sin JNI
5.1 Estrategias de diseño
La finalidad de JNIEasy es muy simple: sustituir a JNI, es
decir, ser un nuevo Java Native Interface pero basado
totalmente en Java desde el punto de vista del
programador. De esta manera conseguimos eliminar la
problemática mezcla de C/C++ y Java.
Hay dos aproximaciones o estrategias al problema:
1) Crear un modelo Java que represente los tipos de
datos, las instancias y la forma de manipular la
memoria de un programa en C
2) Vincular la forma “normal” de manipulación y tipos
de datos de Java a la manipulación nativa
JNIEasy utiliza la segunda estrategia y el resultado es una
mayor transparencia y familiaridad.
Analicemos la primera opción con varios ejemplos
tomados de otro producto con similar objetivo [5]:
Int value = new Int(); // int Pointer pValue=new Pointer(value); // int* En este ejemplo se hace una correspondencia directa
entre el tipo de datos nativo C int con un objeto Java,
la instanciación de un objeto Int vendría a ser como la
declaración C de una variable (int value; por
ejemplo). A continuación se crea un objeto Pointer que
albergará la dirección de memoria que apunta al dato
entero value. Como vemos se identifica un objeto Java
por cada dato nativo correspondiente que se quiere
poner en memoria.
Veamos una llamada a función:
// int sprintf(const char*,…); C Function sprintf = new Library("msvcrt") .getFunction("sprintf"); AnsiString result = new AnsiString(); sprintf.invoke(null,result, new AnsiString("Hello, %s!"), new AnsiString("World")); System.out.println(result.getValue()); //Output: Hello, World! Como se puede observar las cadenas que se pasan como
parámetros se envuelven en objetos especiales
AnsiString y el retorno también cadena es pasado
como parámetro.
Sea la definición de una estructura:
// C: struct TestStruct { short a; int b;}; private static class TestStruct
extends Structure { public Int16 a = new Int16(); public Int32 b = new Int32(); public TestStruct() { init(new Parameter[] {_int16,_int32});} } Aparte de lo intrusivo que supone la necesidad de
derivar de la clase Structure, tampoco nos ofrece una
natural simetría ni con una clase Java con dos atributos
short e int ni con la propia estructura C original.
Veamos la definición de una callback: una callback en C
es una función diseñada para ser llamada normalmente
desde dentro de otra función en donde la dirección de
memoria de la callback ha sido dada como argumento:
/* C callback: int callback(int); */ private static class IncIntCallback
extends Callback { private Int _arg = new Int(); private Int _result = new Int(); public IncIntCallback() { init(new Parameter[] {_arg},_result);} public void callback() { _result.setValue(_arg.getValue()+1);} } Como se puede comprobar los parámetros “formales” de
la función están expresados como atributos de la
callback, expresada como una clase Java. Dichos
atributos recibirán los valores de la llamada cuando se
produzca y el resultado se pondrá en un atributo
específico.
La consecuencia de éste enfoque es código Java que no
parece Java” ni siquiera programación C.
El desarrollo de JNIEasy tuvo en cuenta este problema
desde el principio, de ahí que uno de los principales
principios rectores fuera no tanto expresar en Java el
modelo nativo C sino más bien lo contrario corresponder
el modelo de trabajo normal de Java con el modelo
nativo. Para ello JNIEasy emplea un arsenal de técnicas
desde la AOP via bytecode enhancement, código C y
C++ con JNI, ensamblador e incluso generación
dinámica de código máquina.
Punto de vista de JNIEasy:
String cadena = new String("Texto");
Actas del II congreso javaHispano
60
Representa en JNIEasy desde el punto de vista nativo a
una cadena de caracteres (char) acabada en un nulo
('\0') instanciada en memoria a la que apunta un
puntero (de tipo const char*) que equivale a la
referencia String cadena en Java. Aunque el objetivo
no se consigue 100% el alcance conseguido es
probablemente el más alto de las herramientas
disponibles.
Lo demostraremos a continuación con ejemplos que nos
servirán para descubrir la “naturalidad” de uso y las
capacidades de JNIEasy.
5.2 Llamadas a funciones
Consideremos la siguiente función de la API Win32:
HWND FindWindow( LPCTSTR lpClassName, // class name LPCTSTR lpWindowName ); // window name Su finalidad es devolver el handler de la ventana cuya
clase y nombre (título) vienen dados por dos cadenas, la
clase es opcional y puede ser NULL.
JNIEasy necesita conocer cómo es dicha función para
poder convertir la llamada Java en llamada nativa:
JNIEasy.getInstance().load(); DynamicLibrary user32DLL = JNIEasy.getInstance().getDLLManager().get(
"User32.dll"); CMethod method = user32DLL.addCMethod("FindWindowA", int.class, // tipo de retorno new Class[] { String.class, // nombre clase String.class }, // nombre ventana CallConv.STD_CALL ); // method representa al método C en la DLL int hwnd = method.callInt(new Object[] {null, "DDE Server Window"} ); Notar que utilizamos directamente un objeto String
como argumento al igual que el null análogo al NULL
en C (y no un Pointer() cuyo contenido sea un nulo
según la anterior estrategia). El dato devuelto es un int
lo que nos simplifica la programación y evita la
necesidad de objetos especiales, de hecho cuando es
necesario manejar int como objeto (como argumento
por ejemplo) JNIEasy usa el tipo básico Integer (lo
mismo puede decirse para los demás tipos básicos long,
byte, char etc), por eso es también válido llamar como:
Integer hwndObj = (Integer)method.call(new Object[] {null,"DDE Server Window"} ); En donde el método “call” es agnóstico en el tipo de
retorno (debe ser Object).
Sólo es necesario obtener una sola vez el objeto que
representa el método, a cambio en todas las llamadas los
parámetros son supervisados comprobando que sean del
tipo adecuado, lo cual da una robustez mayor que la que
ofrece la misma llamada en lenguaje nativo.
Es recomendable y más cómodo poner todas las
funciones de una DLL en una clase al efecto:
public class User32 { public static DynamicLibrary dll
= JNIEasy.getInstance(). getDLLManager().get("User32.dll"); public static GenericMethod[] methodList = new GenericMethod[…]; static { methodList[0] = dll.addCMethod("FindWindowA",... ); ... } public static int findWindow(String className,String windowName) { CMethod method = (CMethod)methodList[0]; return method.callInt(new Object[]
{ className, windowName} ); } . . . Conscientes de que esta es una tarea tediosa, JNIEasy
dispone de un generador de código Java a partir de una
sucinta descripción de los métodos en XML. Ejemplo de
fragmento de declaración en XML:
<method returnType="int" name="findWindow" nativeName="FindWindowA" type="C" conv="STD_CALL" > <param type="String" name="className" /> <param type="String" name="windowName" /> </method>
5.3 Polimorfismo en funciones
Cualquier programador en C conoce que aunque no es
válido el polimorfismo de funciones en C, el lenguaje es
lo suficientemente débil en su tipado para permitir un
cierto grado de polimorfismo.
Actas del II congreso javaHispano
61
Por ejemplo, en el caso de un puntero a cadena de
caracteres, const char*, aunque nuestra
correspondencia “natural” en Java es un objeto String
o StringBuffer, la cabecera de la función C admite
realmente como parámetro un entero, la dirección de
memoria de la cadena.
JNIEasy evita la simplificación de identificar nombre de
función nativa – función Java , usando la más compleja
idea de “signatura” de función tal y como se emplea en
C++ o Java para el polimorfismo de funciones: dos
funciones son diferentes si cambia el nombre o el tipo de
alguno de los parámetros.
Por tanto sería válido añadir un nuevo método:
CMethod method = user32DLL.addCMethod( "FindWindowA",int.class, new Class[] { int.class, // nombre clase int.class }, // nombre ventana CallConv.STD_CALL ); Pudiendo usar enteros (direcciones de memoria) para
indicar las cadenas.
Igualmente es posible obtener referencias a los métodos
registrados con el mismo nombre:
List methods = user32DLL.findMethods("FindWindowA"); u obtener el método exacto indicando la signatura a
través de un objeto Signature :
CMethodSignature sig = SignatureUtil.newCMethodSignature(int.class ,new Class[]{String.class,String.class}); method = user32DLL.findCMethod(
"FindWindowA", sig);
5.4 Unicode
El uso del Unicode está cada vez más generalizado y
soportado por las herramientas software: los lenguajes
modernos como Java lo soportan nativamente, C y C++
incorporan caracteres Unicode (wchar_t de 16 bits) y la
API Win32 tiene todas las funciones que usan cadenas
con dos prototipos: ANSI (terminadas en A) y Unicode
(terminadas en W).
JNIEasy es por defecto ANSI pero puede usarse Unicode
como codificación por defecto o explícita.
Voluntariamente podemos indicar que un parámetro (o
retorno) es ANSI o Unicode cambiando el tipo de datos
del parámetro (o retorno). Para ello se dispone de las
interfases StringAnsi y StringUnicode:
CMethod method = jniEasyDLL.addCMethod( "FindWindowW",int.class, new Class[] { StringUnicode.class, // class name StringUnicode.class }, // window name CallConv.STD_CALL); int hwnd = method.callInt(new Object[] {null, "DDE Server Window"} ); Aunque este método “espera” objetos StringUnicode
como parámetros, JNIEasy sabe que los objetos String
son compatibles, el resultado será una llamada a la
versión Unicode de FindWindow usando cadenas en
Unicode.
5.5 Callbacks
Es posible programar callbacks en Java, funciones en
Java que serán llamadas directamente desde código
nativo como si fueran funciones nativas.
Veamos un ejemplo: Win32 dispone de una función
EnumWindows que enumera a través de una callback
todas las ventanas presentes en el desktop.
BOOL EnumWindows( WNDENUMPROC lpEnumFunc, /* callback */ LPARAM lParam); /* app-defined value */ El tipo WNDENUMPROC se define a su vez como un tipo
puntero a función cuyo prototipo es el siguiente:
BOOL CALLBACK EnumWindowsProc( HWND hwnd, // handle to parent window LPARAM lParam ); // app-defined value Una callback se define con JNIEasy como una clase que
deriva de CCallbackImpl (en el caso de C), que
implementa el método: Object onCall(Object[] params), dicho método es el que será llamado cuando
la callback sea invocada desde código nativo.
public class EnumWindowsProc extends CCallbackImpl { private static final CMethodSignature SIGNATURE =
SignatureUtil.newCMethodSignature( int.class,new Class[]
Actas del II congreso javaHispano
62
{int.class,int.class}, CallConv.STD_CALL );
public EnumWindowsProc() { super(SIGNATURE); } public Object onCall(Object[] params) { int hwnd =
((Integer)params[0]).intValue(); int lParam =
((Integer)params[1]).intValue(); System.out.println("handle:" + hwnd +
" param:" + lParam); return new Integer(1); } } Invocaríamos EnumWindows por ejemplo:
method = user32DLL.addCMethod( "EnumWindows",int.class, new Class[] { EnumWindowsProc.class, int.class }, CallConv.STD_CALL ); method.callInt(new Object[]{ new EnumWindowsProc(),new Integer(10)}); Salida por pantalla:
handle:65784 param:10 handle:328538 param:10 ... Mientras el objeto callback esté en memoria es como si
la función “se creara” desde el punto de vista nativo,
cuando se pierde vía garbage collector pasa a ser
inaccesible (como si se destruyera código máquina)
liberando automáticamente sus recursos. El número de
callbacks en memoria es ilimitado. Detrás de las callbacks
de JNIEasy está una compleja mezcla de JNI, Java y
código máquina generado dinámicamente desde Java.
Existe la posibilidad de que la definición de la función
que define una callback sea autónoma de JNIEasy, para
ello se usa Java Reflection, en donde un objeto callback
interno de JNIEasy “representa” la función “normal” de
Java. Sea por ejemplo:
public class EnumWindowsProc2 { public static int enumWindows(
int hwnd, int lParam) { System.out.println("handle:" + hwnd +
" param:" + lParam); return 1; } } java.lang.reflect.Method reflMethod = EnumWindowsProc2.class.getDeclaredMethod(
"enumWindows", new Class[]{int.class,int.class} ); CCallback callback = CallbackUtil.newCCallback(reflMethod);
method.callInt(new Object[]{callback, new Integer(10)});
5.6 Estructuras
La definición de estructuras es donde JNIEasy se
distancia claramente de las soluciones existentes, y en
donde entra a fondo la AOP y el bytecode enhancement.
Con ambas técnicas conseguimos “extender” una clase
normal Java para que represente a una estructura nativa,
introduciendo de forma ortogonal, el “aspecto” de la
gestión de los atributos para que representen a la
imagen de una estructura C.
Ejemplo, sea la estructura Win32 C:
typedef struct tagPAINTSTRUCT { HDC hdc; // HDC = int en Win32 BOOL fErase; // BOOL = int en Win32 RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT; En Java:
public class PaintStruct { int hdc; int fErase; Rect rcPaint = new Rect(); // Es embebido int fRestore; int fIncUpdate; byte[] rgbReserved =
new byte[32]; // Es embebido } Rect sería una simple clase Java con cuatro enteros
como atributos. En un archivo XML se indicaría además
que el atributo rcPaint y rgbReserved son
embebidos (no RECT* o BYTE*). Finalmente la
sentencia:
PaintStruct ps = new PaintStruct(); supone la reserva en “memoria nativa” de una estructura
C equivalente, la referencia ps puede usarse en aquellas
funciones donde se pida un PAINTSTRUCT*. Si se
modifica desde Java hdc, por ejemplo, se modifica
Actas del II congreso javaHispano
63
también en la memoria nativa, un programa C/C++
puede modificar ¡en cualquier momento! (no sólo
dentro de una llamada iniciada desde Java) el atributo
hdc , percibiendo ese cambio en Java cuando se acceda
al mismo. La interacción transparente memoria Java-
memoria nativa es una de las características más
avanzadas de JNIEasy.
Por supuesto el ciclo de vida de la estructura vista desde
C es el mismo que el objeto Java, por lo que el garbage
collector se convierte en el “delete” o “free()” de las
aplicaciones basadas en JNIEasy evitando los consabidos
memory leaks de los que han estado plagadas las
aplicaciones C/C++.
Conseguimos en resumen una programación “nativa”
con Java, con filosofía Java y con mayor robustez y
seguridad que usando un lenguaje nativo.
5.7 Exportación de funciones
Una de las características más interesantes es la
capacidad de acceder directamente al método de una
callback desde código nativo sin JNI y sin conocer la
dirección de memoria previamente. Se mimetiza así la
exportación de un método de una DLL pero ¡desde Java!.
Sea el código Java:
public class CallbackTest { public static long suma(int a,int b) { return a + b; } } reflMethod = CallbackTest.class.getDeclaredMethod( "suma",new Class[]{int.class,int.class}); JNIEasyLibrary dll = JNIEasy.getInstance().getJNIEasyLib(); dll.exportMethod(reflMethod,
CallConv.STD_CALL);
Sea el siguiente código C/C++:
HMODULE dllHandle = ::LoadLibrary("JNIEasy.dll"); void* (__stdcall *findCBAddress)(
const char*); findCBAddress = (void* (__stdcall *)( const char*))::GetProcAddress(dllHandle, "_findExportedMethodAddress@4"); const char* sig =
"CallbackTest.suma(int,int)";
// signatura de la callback Java __int64 (__stdcall *suma)(int,int); suma = (__int64 (__stdcall *)(int,int)) findCBAddress(sig); __int64 res = suma(1,2); llama desde código nativo al método Java exportado
como si fuera un método C “normal”.
5.8 C++
JNIEasy también aporta herramientas para acceder a
métodos C++ en DLLs, clases Java emulando clases
C++ cuyos métodos y atributos “normales” pueden ser
visto como métodos y atributos “C++” etc.
6 ¿Hacia un desktop “muy” Java?
El tiempo lo dirá, JNIEasy es un paso más en esa
dirección.
Referencias
[1] JNIEasy. http://www.jnieasy.com
[2] Microsoft domina el mercado de sistemas operativos, pese al éxito de Linux. Dealer World, 09/10/2003 http://www.idg.es/dealer/actualidad.asp?id=32481
[3] JDesktop Integration Components. https://jdic.dev.java.net/
[4] Java Media Framework. http://java.sun.com/products/java-media/jmf/
[5] JNIWrapper. http://www.jniwrapper.com
Actas del II congreso javaHispano
64
Guasaj. Un framework de programación basado en componentes
Benito Mateo, UrkoGolf Enparantza, Nº 1, 3 A
C.P. 20.160 Lasarte (Gipuzkoa) [email protected]
Blesa Jarque, ÁngelPlaza del Olmo, Nº 5, 2º Piso
C.P.44770 Escucha (Teruel) [email protected]
Lop lis, José JavierIsabel la Católica, Nº 16-18, Piso
5 C,Esc. Izq.
C.P. 50009 Zaragoza [email protected]
Abstract
En el ciclo de vida de un proyecto, independientemente del
proceso de desarrollo que se utilice (RUP, FDD, XP, etc…) en
un determinado momento se debe pasar del análisis de los
requisitos al diseño e implementación del mismo.
Los patrones de diseño hacen colaborar un conjunto de
clases e instancias de las mismas, para solucionar un
problema determinado permitiendo obtener una solución con
buenas características de acoplamiento, cohesión,
granularidad, rendimiento, adaptabilidad, evolución, etc. , pero
con los patrones de diseño sólo no basta, no dicen
directamente que modelo superior crear para hacer colaborar
N paquetes de casos de uso, y que permanezcan
desacoplados, independientes, reutilizables, etc..
En la búsqueda de este modelo superior surge el componente
de software como unidad independiente, cooperativa,
desacoplable, etc.
Lo que pretendemos con este proyecto es proponer una
metodología de organización de código en torno a
componentes con una estructura basada en patrones de
diseño bien conocidos(MVC, Observer, ViewDispatcher,
Factory, Command, Facade, VO, Service Activator , Service
Locator, …) que nos lleve del análisis de los casos de uso
(historias, requisitos funcionales, casos de aplicación, etc…)
a la implementación de la solución en código de una manera
metódica, repetible y con resultados reutilizables en
diferentes proyectos..
Keywords: framework, patrón, componente, metodología.
1 Motivación. Origen de guasaj.
El origen desde el cual guasaj fue concebido fue crear un
entorno de trabajo en el cual se defina un procedimiento
para plasmar el análisis y diseño de la solución de un
problema en un conjunto de componentes reutilizables.
Con ello se pretende aumentar la productividad a través de
dos cuestiones:
• El establecimiento de un procedimiento claro de
implementación de las funciones descubiertas en el
análisis y el diseño.
• La reutilización automática del trabajo realizado en el
paso anterior.
En primer lugar se pretende crear un esquema de clases
sencillo para la implementación de componentes, con un
ciclo de vida, en la llamada a sus servicios, lógico y que
cumpla las premisas de los patrones de diseño
sobradamente conocidos [1] (MVC, command, service
locator, factory, etc…). A través del estudio de la filosofía de
creación de aplicaciones basadas en componentes pasar a
una implementación de la misma, sencilla, pero potente y
flexible, creando una asociación rápida y semiautomática
entre el modelo conceptual de una aplicación y su
correspondiente implementación.
Con este proceso se pretende eliminar, dentro de lo posible,
el problema de tener que “reinventar la rueda” en cada
proceso de análisis y diseño de un determinado problema,
muchas veces supeditado a la imaginación e inspiración en
el diseño final del arquitecto/diseñador/programador.
Otro de los factores o puntos de especial interés radica en
el hecho de establecer una definición, implementación,
manejo, capacidades, y ciclo de vida de los componentes,
de tal manera que todo el equipo de desarrollo emplee el
mismo idioma.
Actas del II congreso javaHispano
65
En definitiva, ir un paso más allá de los patrones de diseño
en el establecimiento de un lenguaje común para la
nomenclatura de las funciones, comportamiento y
arquitectura de un sistema.
2 Problemas detectados en el desarrollo deprogramas en la actualidad
En la labor de desarrollo de proyectos software se
presentan una serie de problemas con demasiada
frecuencia, como son:
• Dificultad en el paso de análisis, al diseño y
codificación. La identificación y agrupación de las
funcionalidades del sistema en paquetes y su posterior
paso a codificación no está suficientemente
sistematizado.
• Poca o ninguna aplicación de patrones de diseño para
resolución de problemas genéricos en los proyectos
cotidianos, debido en parte a la dificultad para abstraer
la problemática concreta de un gran proyecto, a las
soluciones a un nivel más micro que aportan los
patrones de diseño. Se hace complicado pasar de
pequeñas implementaciones de soluciones en una serie
de clases y objetos aplicando unos determinados
patrones, a la implementación de una serie de paquetes
o componentes que desarrollen una parte concreta,
independiente, reutilizable, extensible y configurable de
la aplicación de un tamaño considerablemente mayor.
• No existe una separación clara entre la vista de la
aplicación y el modelo, sucumbiendo en la mayoría de
las veces ante la dificultad de disociar las distintas
porciones de código que participan en una parte de la
solución, interface gráfico, eventos del GUI, validación
de datos, acceso a datos persistentes , etc …
• Dificultad de programación para el interface en lugar de
la implementación.
• Mala gestión de la creación – instanciacion de objetos
tanto para ejecutar la lógica del sistema, como para
representar el estado del mismo en un momento dado.
• No se define una separación clara entre al estructura de
datos que almacenan el estado del sistema en cada
momento de ejecución del mismo, y las diferentes
operaciones que se pueden realizar sobre dichos datos.
No existe, en general, un procedimiento sistemático para
codificar la funcionalidad que se requieren (casos de uso,
historias, requisitos arquitectónicos, características
funcionales y no funcionales, etc), de tal manera que
mantengamos desde el principio un separación entre vista y
modelo, una gestión clara de los datos del proceso, un
manejo de excepciones efectivo, y una posibilidad de
reutilización practica y rápida, desde el principio, de la
codificación, de tal manera que la exportación de
funcionalidades desde un proyecto a otro sea automática.
3 Del análisis a la programación. Pasos ytrabas intermedias
En cualquiera de las metodologías o procesos de desarrollo
de software, salvando sus peculiaridades, se sigue un
proceso similar al que se describe a continuación para la
consecución de la solución a un proyecto:
• Estudio de los requisitos.
• Análisis de la solución. Modelo conceptual.
Descubrimiento de las entidades del problema.
Agrupación en paquetes conceptuales.
• Diseño de la solución. Patrones. Establecimiento de la
arquitectura. Diseño de pruebas. Cumplimiento de
requisitos no funcionales, etc…
• Codificación. Llegado este momento en determinadas
metodologías podemos estar en un estado más o menos
avanzado en la identificación de clases y métodos a
implementar, pero en general se dista bastante de tener
una visión clara y lo más importante, acertada y con
posibilidades de ser la definitiva. Esto deriva en una
lógica de negocio más o menos dispersa en métodos de
clases cuyas responsabilidades no están claramente
definidas. Esto puede llevar a gravísimos problemas de
acoplamiento entre partes funcionales del sistema,
difíciles de independizar a posteriori, siendo muy difícil
cuando el modulo en cuestión tiene ya un tamaño
considerable y nos percatamos de las limitaciones
impuestas, pensar en una refactorización del diseño que
no nos provoque más de un dolor de cabeza y desde
luego una merma en nuestra productividad.
3.1 Solución. Aplicación de patrones
Los patrones nos aportan buenas prácticas de código, en la
pequeña escala, para salvar de una manera óptima y muy
probada circunstancias que se dan en la programación
cotidiana.
El uso de patrones hace que las porciones de código
resultantes sean robustas, flexibles, extensibles, adaptables,
optimizadas y fiables, de tal manera que el conjunto de la
aplicación también herede dichas cualidades. Esto es lo que
se pretende pero en el camino para lograrlo se tiene una
Actas del II congreso javaHispano
66
serie de imponderables, que hacen que las decisiones de
diseño no sean triviales.
En el momento de diseñar la solución para una aplicación el
arquitecto/diseñador debe tener una concepción de las
entidades y la colaboración entre las mismas que le harán
desempeñar la solución requerida, así como una capacidad
de abstracción para descubrir entre estas entidades la
problemática que encierran y que parte de ésta, está
contemplada como patrón sobradamente conocido.
3.2 Problemática de los patrones en lacodificación
El uso de patrones de diseño en la solución de una
aplicación facilita el paso del diseño a la implementación por
estar estos ya codificados en multitud de ocasiones. Aún
así, hay un nivel de abstracción intermedio en las soluciones
genéricas que aportan los patrones, y los problemas de
modelado, diseño, empaquetado, y reutilización de código
que se necesitan en los proyectos con una tamaño
considerable, sobre todo en sistemas en los que se
desarrollan una serie de aplicaciones (suite) para dar una
serie de servicios integrales a una empresa.
La solución puede pasar por la elaboración de un
framework que encapsule las buenas prácticas de los
patrones sobradamente conocidos y haga que los
programadores sean capaces de representar el modelo
lógico de la solución del problema, expuesto en el análisis,
con un buen diseño, una buena arquitectura y un código
ampliamente reutilizable sin tener que implementar
físicamente los patrones, sino que sea el framework el que
maquille para el desarrollador final el uso de los mismos.
Dicho de otra manera, el hecho de programar bajo este
framework garantiza la herencia de la mayoría de los
patrones de diseño más utilizados, sin prohibir en absoluto
la implementación de alguno de los mismos para cubrir una
determinada necesidad.
4 Condiciones deseadas en la solución
Un framework que pretendiese implementar una solución
para la problemática anteriormente descrita deberá poseer
una serie de características como:
• Guiarse por un patrón de comportamiento como el MVC
para la separación de vista, modelo y controlador.
• El código resultante debe quedar en forma de
componente reutilizables, y cumplir con el paradigma y
filosofía de construcción de aplicaciones basadas en
componentes tal y como se detalla más en profundidad
en [2].
• El cliente de un componente estará desacoplado del
mismo, ni siquiera a través de una interface. La
resolución del componente y del método a ejecutar se
realizará en tiempo de ejecución
• Los componentes que participan en una aplicación, la
descripción de los mismos en cuanto a comportamiento
y aspectos (acceso, relaciones, vista, excepciones, log)
debe tener un soporte de configuración exterior al
código (archivo xml de configuración).
• Los componentes deben permitir la ejecución
secuencial de dos métodos de distintos componentes, o
desde uno de estos a otro. El hecho de que un método
de un componente sea llamado desde otro puede
provocar en determinadas circunstancias que la
respuesta de éste sea de un tipo o de otra
• Un componente tiene una lógica de negocio
representado por un BO (Bussiness Object) con los
métodos operativos, y además un componente trabaja
con un conjunto de datos en forma de VO (Value
Object).
• El estado en memoria de un componente en un
determinado momento de ejecución (por ejemplo, un
servidor de Chat, con una serie habitaciones y unos
usuarios en las mismas, en un determinado momento)
se debe reflejar en este BO. Pero no una instancia del
BO por cada elemento a reflejar. Por ejemplo, el
componente “Habitación” en una aplicación de chat,
estaría formado por un BO con las operaciones y una
lista de posibles habitaciones.
• De esta manera un componente será gestor y almacén
de los datos que puede manejar. Los componentes son
elementos de peso de tal manera que en el sistema no
tendremos múltiples instancias de un componente[1],
sino que será el componente el que maneje los datos
necesarios y trabaje con las operaciones para construir
y manejar el estado del sistema en un momento dado.
• Cuando en una vista de un componente se lleve a cabo
un cambio, dicho cambio deberá ser reflejado de alguna
manera en el estado de la lógica del componente, en su
BO, es decir, un cambio en una de las vistas se ve
reflejado en su homónimo del modelo. Por ejemplo,
cuando en un componente gestor de barra de
herramientas seleccionamos un elemento y desde otro
componente, queremos acceder al elemento
seleccionado. No podemos desde una vista de un
componente acceder directamente a la vista de otro,
Actas del II congreso javaHispano
67
debemos pasar por el modelo y solicitar algún atributo
que nos de la información que requerimos.
5 Características de los componentesguasaj
Un componente guasaj es un conjunto de clases diseñadas
para llevar a cabo una serie de funciones relacionadas con
un problema del modelo de negocio del sistema o
desarrollar una función de carácter más amplio dentro de la
arquitectura, estando este acompañado de un fichero de
descripción de las características y posibilidades de
ejecución de dicho componente. Ejemplo, roles de usuario,
pre y post ejecuciones, controlador de excepciones,
métodos virtuales…
Un componente guasaj es autocontenido en su ciclo de vida,
llevando a cabo una separación entre lógica de negocio y
datos, así como vista y modelo. En un principio la
plataforma objetivo de guasaj es la J2SE aunque su filosofía
es extrapolable tanto a J2EE como J2ME. Todo
componente aporta un mecanismo de llamada a sus
servicios, un método de gestión y acceso a sus datos, así
como un control de sus vistas, manejo de excepciones y
control de acceso.
Dentro de los tipos de componentes que podemos
desarrollar se pueden encontrar 2 tipos principales con sus
peculiaridades y variantes:
Componente entidad: Son componentes que intentan
representar el modelo lógico de negocio propio de un
proyecto asumiendo una serie de características de la
solución final. Estos componentes desarrollan una función,
tienen una responsabilidad y unos mecanismos o reglas de
interacción con otros componentes de entidad o de servicio.
Un ejemplo de componente representativo de una entidad
del modelo de negocio podría ser los componentes cliente,
factura, proveedor, empresa, albarán, etc... por ejemplo, en
una aplicación de gestión. En un sistema de tratamiento de
señales provenientes de un sistema de instrumentación
electrónica, estos pudieran ser, equipo, señal, punto de
medida, etc. En general los candidatos a este tipo de
componentes pueden ser los provenientes del análisis
conceptual del dominio del problema.
Componentes genéricos de servicio: Componentes que
permanecen completamente independientes de las
peculiaridades de un determinado proyecto siendo
totalmente reutilizables en otro ámbito de ejecución e
incluso plataforma, si están diseñados para ello. Por
ejemplo, un componente para comunicaciones TCP (cliente
y servidor), un componente para selección y gestión del
idioma para una aplicación, un componente manejador del
estilo y fuentes de una aplicación, un componente
calendario, un componente calculadora, etc…
De esta manera en la elaboración de una solución
completa para un problema, partiremos del modelo
tradicional de resolución de problemas software, de la
siguiente manera:
• Se plantea una serie de requisitos o funcionalidades a
desarrollar.
• Se lleva a cabo un análisis funcional y se determina un
modelo conceptual en el que se descubren entidades,
relaciones entre estas y atributos de las mismas.
• Se realiza una agrupación en paquetes guiándose por
una determinada premisa pudiendo esta ser,
agrupación por función, agrupación por complejidad,
agrupación por arquitectura, agrupación por
departamento lógico del cliente, etc, etc.. Suele ser en
este momento donde empiezan a surgir los problemas
separación de responsabilidades entre las entidades del
sistema. Aquí evidentemente no hay magia que valga,
la decisión final de agrupación, separación de
responsabilidades de cara a un diseño u otro es propia
del arquitecto /diseñador, siendo muchas veces
equivalentes soluciones con topologías de entidades
diferentes.
Una cuestión que puede y debería ayudar sobremanera en
este paso del proceso de desarrollo, sería el hecho de saber
exactamente el procedimiento de codificación que
seguiremos para llegar a implementar esta entidad, e
igualmente los servicios a los que tendremos acceso sólo
por el hecho de implementar la entidad de acorde a este
procedimiento / framework de programación.
La idea radica en crear una equivalencia entre cada uno de
los términos utilizados en el análisis funcional de la
aplicación y la codificación de los mismos. Saber medir que
implicaciones tendrá el hecho de utilizar palabras como
paquete, clase, entidad, atributo, relación, caso de uso,
servicio, etc.… en la codificación del sistema.
6 Introduciéndonos en guasaj
De esta manera y según lo expuesto fundamentos de los
cuales parte el desarrollo de guasaj son:
• Separación en modelo, vista y controlador.
Actas del II congreso javaHispano
68
• Independencia de componentes.
• Variación del comportamiento de un componente según
una definición exterior, sin modificar el código.
• Estandarizar la nomenclatura y tipo de clases al crear
un componente reutilizable.
• Crear un elemento amigable tanto para el analista como
para el programador del sistema, pasando por el
arquitecto y el diseñador.
6.1 Localización de componentes pornombre.
Para tener acceso a los servicios de un componente
debemos primero localizar una instancia de dicho
componente. Para ello, pasaremos un nombre único con el
que denominaremos unívocamente a un componente dentro
de la aplicación y estará referido en el fichero de descripción
guasaj y del componente.
Una vez localizado un componente y apuntado por una
interfaz común para todos podremos llamar a los servicios
que implementa.
6.2 Ejecución de métodos de loscomponentes dinámicamente.
Resolución de los métodos a ejecutar según la firma del
método, nombre y paso de parámetros, pudiendo derivar en
diferentes comportamientos en la ejecución y la respuesta
según los parámetros pasados.
6.3 Posibilidades de redirección a la vista.
Cuando se llama a un método de un componente, se quiere
llevar a cabo una funcionalidad y generalmente obtener un
resultado de la misma, acompañada de una visualización de
los resultados. Otras veces, se quiere ejecutar la lógica de
modelo pero no se quiere visualizar el resultado de la misma
a nivel de interface gráfico, sino que sólo se quiere manejar
los datos del resultado de la operación. De esta manera se
puede elegir en el momento de la llamada a un método de
un componente si queremos ejecutar la parte de vista
vinculada a ese método o no. Igualmente, podemos indicar
en el momento de la llamada qué método y de qué
componente queremos que se haga cargo de manejar la
respuesta en forma de vista de ese método. Y aún hay más,
podemos decirle en que instancia de dicha vista queremos
que se ejecute el método de respuesta, en el caso de que
manejemos diversas instancias de vista de la misma clase.
Con todo lo anteriormente expuesto, tenemos tres
funciones relacionadas con la vista:
• Primero, indicar si queremos manejar vista o no.
• Segundo, indicar que método de que componente
queremos que maneje la respuesta en forma de vista.
• Tercero y último, si para ese componente queremos
instanciar una nueva vista o manejar una que pasamos
en la llamada al método. Tres en uno.
Así, teniendo la ventaja de una separación entre
componentes, podemos acceder a través de las funciones
del contenedor (éste las resuelve en tiempo de ejecución,
no de desarrollo), a otros componentes para pasarles
(notificarles) resultados de operaciones de otros
componentes. Esto corresponde con la teoría de inversión
de control [3], por la cual es el contenedor el que llama a
nuestras clases para realizar ciertas funcionalidades en un
momento dado.
6.4 Posibilidad de llamar a métodos nobloqueantes.
Sin tener que esperar a que termine la ejecución del método
llamado. Esta utilidad resulta interesante cuando queremos
tener un mecanismo automático de ejecución en segundo
plano de una operación y recibir la respuesta de esta
cuando finalice la misma, sin que tengamos por ello
bloqueado el flujo principal del programa, esperando
retornar de la llamada efectuada. Un ejemplo concreto de
esta funcionalidad seria la localización remota de un servicio
o componente que puede llevar unos segundos. Es posible
que no podamos seguir la ejecución de algo concreto sin
este componente o servicio pero eso no impide que
podamos ejecutar otras opciones del programa y ser
avisados cuando se haya localizado el componente o
cuando haya vencido el plazo de timeout.
6.5 Concentración del manejo deexcepciones.
A través de un HandlerException por componente.
Mecanismo por el cual las excepciones producidas son
dirigidas a un método de una clase que se ocupa de su
gestión y tratamiento. También existe la posibilidad de
definir si el trato de excepciones se procesa en el
componente original o si se redirecciona a otro manejador
en el proyecto destino (ya que dependiendo de cómo y
dónde se ejecute un componente puede que no queramos
ni deseemos su tratamiento original o incluso necesitemos
capturar las excepciones producidas por un componente y
tratarlas a nuestro modo).
Actas del II congreso javaHispano
69
6.6 Control de acceso a los métodos ycomponentes por rol de usuario.
A través del fichero de descripción del componente se
puede indicar que roles de usuario tendrán acceso a cada
método. Lo único que se debe realizar es activar en el
contenedor guasaj el rol de usuario activo. En tiempo de
ejecución comparará si el rol activo coincide con alguno de
los que tiene permiso de ejecución. El proceso común será
aprovechar el momento en el que pidamos validación al
usuario para recoger de este el rol de usuario y activarlo en
guasaj. Para este usuario cuando ejecute el resto de
operaciones su nivel de acceso vendrá determinado por
dicho rol.
6.7 Métodos virtuales.
Son métodos que no está implementados en el componente
original, sino que estarán redireccionados a otro método de
otro componente que será el que realmente se ejecute,
recibiendo los parámetros originales. Esta función es
necesaria para permitir que las llamadas a métodos
realizadas desde el “interior” del componente puedan ser
capturadas y manejadas por otro componente. Por ejemplo,
tenemos un componente que gestiona conexiones TCP,
atiende a los clientes conectados, y recibe peticiones de los
mismos. Este componente es reutilizable en multitud de
proyectos, pero no sabemos en tiempo de implementación
del componente que método de que clase tenemos que
llamar cuando recibamos una trama, ya que,
evidentemente, dependerá de lo que queramos hacer con la
misma en el proyecto que nos ocupe. Para ello podemos
definir un método virtual en este componente, receiveData,
que será invocado por cada hilo gestor de un cliente
conectado, para indicar que nos ha llegado una trama de
datos. En el fichero de descripción de componente
indicaremos que éste método es virtual y qué método de
qué componente queremos que se ejecute cada vez que se
llame a dicho método virtual.
6.8 Preejecución, postejecución eintercepción de métodos.
En la descripción de un método, dentro del archivo de
configuración del componente, podemos especificarle un
método que se ejecutará antes que este. Este último recibirá
los parámetros del método original para su ejecución. En el
caso de queramos una postejecución, podemos definir un
método que se ejecutará con posterioridad a la ejecución
del método original. En este caso este método recibirá tanto
los parámetros del método original como la respuesta
producida por este. Y por último, en el caso de la
intercepción, el método interceptor recibe los parámetros del
original, puede modificar los mismos, y llamará al método
original con éstos. Un ejemplo de preejecución, puede ser
incluir un sistema de logs a un determinado método. En el
caso de la postejecución, un posible ejemplo sería la
elaboración de un informe al término de la ejecución de un
método, necesitando para ello, los parámetros de entrada,
respuesta del método original y los posibles cambios que
haya podido originar dicha llamada, como escritura en base
de datos, cambios de estado, alteración de ficheros o
configuraciones, etc…
Y por último, un ejemplo de intercepción sería el hecho de
dotar de un sistema de cifrado a un mensaje pasado entre
componentes. Podríamos capturar los datos de la llamada
original, cifrarlos o descifrarlos y continuar con la llamada en
el componente destino que se ejecutaría ya con los datos
cifrados o descifrados.
Los métodos, pre, post e interceptor son ejemplos claros de
componentes que dotan una funcionalidad añadida no
contemplada anteriormente en el sistema.
7 Otras consideraciones en loscomponentes guasaj
Los componentes guasaj están concebidos desde un
principio con una vocación de reutilización de los mismos en
diferentes ámbitos y soluciones informáticas, sin por ello
acarrear un malus de productividad para el usuario final de
los mismos como desarrollador de aplicaciones guasaj. Al
contrario al pensar directamente en la reutilización del
trabajo realizado se tienen en cuenta desde las fases más
tempranas de codificación unas características de
arquitectura y diseño muy positivas. La clave de todo este
proceso resulta en no crear una abstracción vacía de
elementos software de difícil implementación sino todo lo
contrario, dotar de un framework que nos haga una gran
parte del trabajo de nomenclatura de nuestras clases,
separación de responsabilidades, posibilidades de relación
entre componentes y acceso a los servicios de los mismos
desde cualquier parte de la aplicación.
8 Conclusiones
En la ejecución de un proyecto software, el paso del análisis
del problema al diseño y codificación presenta una serie de
dificultades para asignar las diferentes responsabilidades
entre las unidades de código que vamos creando. Dentro de
las unidades de código queremos mantener una separación
entre la lógica de negocio y la vista. Gran parte del código
que generamos para la solución de un determinado
problema deberíamos poder reutilizarlo en parte o por
completo, con alguna pequeña variación de configuración
Actas del II congreso javaHispano
70
en otros proyectos, dentro del mismo ámbito o no. El
componente guasaj nos permite crear estructuras de código
que desarrollen un conjunto de funciones reutilizables en
otros proyectos. Los patrones de diseño por si mismos no
establecen un lenguaje suficiente para la nomenclatura de
la funcionalidad, comportamiento y arquitectura de un
sistema. Debemos ir hacia una entidad de mayor calibre
que cubra esta necesidad.
Resulta fundamental definir un conjunto, framework -
procedimiento, que automatice el proceso de codificación
del sistema, sin depender en exceso de la inspiración
particular, para llegar a lograr un diseño arquitectónico
correcto, repetible y reusable, todo ello en aras de una
mayor productividad y calidad software.
Agradecimientos
Queremos agradecer a la EUPLA la posibilidad de contribuir
a este congreso con el desarrollo de este trabajo.
Igualmente a ARCO ELECTRÓNICA S.A, las facilidades
para la realización del mismo, y la asistencia a este
congreso. También un agradecimiento especial para Sergio
Gil García por contribuir en los inicios de esta idea.
Y en especial a la comunidad javaHispano por permitirnos
participar en este congreso.
Referencias
[1] Erich Gamma, Richard Helm, Ralph Johnson and JohnVlissides. Design Patterns. Element of Reusable Object-Oriented Software, Addison-Wesley, 1995.
[2] Claudia Patricia García Zamora y Samuel Garrido.Programación basada en componentes. CINVESTAV MéxicoD.F 4 - Noviembre -2003.
[3] Mike Spille, Inversion of controlhttp://www.pyrasun.com/mike/mt/archives/2004/11/06/15.46.14/index.html
Actas del II congreso javaHispano
71
Actas del II congreso javaHispano
72
Extensión del patrón Observador para la integración de eventos decomponentes heterogéneos.
Luis Rodero MerinoUniv Rey Juan Carlos, [email protected]
Miguel A. Ortuño PérezUniv Rey Juan Carlos, DIET
Luis López FernándezUniv Rey Juan Carlos, [email protected]
Abstract
En este artículo presentamos una extensión al ya conocido
patrón Observador. Esta extensión está orientada a
facilitar la monitorización de eventos provenientes de
distintos componentes de un sistema. Este artículo también
contiene un ejemplo de como aplicar este patrón dentro
del contenedor PicoContainer.
Palabras clave: Observador, observable, patrón,contenedor, componente.
1. Introducción
El patrón Observador es uno de los más ampliamente
utilizados en la programación de sistemas, aunque a
veces incluso el programador no sabe que lo está
usando. Hay dos roles básicos en este patrón: el
componente observable, que lanza eventos para
notificar cambios en su estado, y el observador que
espera y recibe esos eventos.
El API estándar de Java [3] contiene la interfaz
java.util.Observer y la clase abstracta
java.util.Observable, que pueden ser usadas como base
para la implementación de este patrón. Aunque como
veremos más adelante, esta solución es muy limitada
cuando se necesita manejar notificaciones desde
distintos componentes dentro del mismo sistema, cada
uno capaz de generar sus propios eventos.
Este artículo muestra una extensión del patrón
Observador que simplifica el manejo de distintas fuentes
de eventos dentro de un sistema. El artículo comienza
con una definición más completa y profunda del patrón
Observador, y describe sus limitaciones, mostrando las
dificultades que pueden surgir al usarlo. Después se
explica la extensión que hemos desarrollado,
comentando sus ventajas y cómo aplicarla. Finalmente
mostramos un posible uso de esta extensión aplicándolo
dentro del contenedor de componentes PicoContainer.
2 El patrón Observador
El patrón Observador es uno de los 23 patrones
descritos en [2]. Este patrón “Defines a relationship
between a group of objects such that whenever one
object is updated all others are notified automatically”.
El objeto observado no necesita saber nada acerca de los
observadores. Son los observadores quienes deben
registrarse como 'oyentes' para poder recibir eventos.
Esto permite el desarrollo de aplicaciones con
componentes poco acoplados. el bajo acoplamiento se
considera una ventaja ya que simplifica la posterior
reutilización de componentes.
Los oyentes reciben notificación de todos los eventos
generados en los objetos observados. Pueden registrarse
en cualquier momento durante el ciclo de vida del
componente.
Este patrón debe aplicarse cuando:
• Cambios en algunos de los objetos del sistemarequieren cambios en otros objetos del mismo grupo.
• El número de oyentes puede variar durante el ciclo devida del objeto.
• El bajo acoplamiento es un requerimiento básico deldiseño..
Este patrón implica dos roles distintos, como se muestra
en la Figura 1.
El patrón es bastante sencillo, y puede encontrarse como
parte de otros patrones más complejos tales como el
patrón Modelo Vista Controlador, donde el controlador
es un observador que recibe eventos de la interfaz, y la
vista recibe eventos referidos a cambios en el modelo.
Actas del II congreso javaHispano
73
3 Desventajas del patrón Observador
Debido a su sencillez, el patrón Observador tiene
algunas desventajas:
• Todos los oyentes reciben todos los eventos lanzados, sindistinción. Esto es ineficiente, y los observadoresdeberían ser capaces de registrarse sólo para aquelloseventos en los que estén interesados.
• A menudo los sistemas tienen varios componentes queson generadores de eventos, y varios componentes queescuchan por esos eventos. Las relaciones entre todosestos componentes pueden ser difíciles de maneja. Verpor ejemplo la Figura 2
Gamma et al. [2] ya sugerían utilizar un mediador
(patrón Mediador [2]), llamado Gestor de Cambios. El
mediador mantiene las relaciones entre oyentes y
componentes observados (Figura 3).
El Gestor de Cambios puede ser un gestor sencillo, que
sólo reenvíe eventos desde cualquier objeto observable a
todos los observadores. Pero también puede ser más
complejo, guardando información acerca de qué eventos
interesan a qué observadores. En ese caso, el Gestor
reenviaría a cada oyente sólo los eventos por los que
está interesado.
Esta solución, aunque más potente, también presenta
problemas:
• Los oyentes aún necesitan saber los componentes quedeben observar, aunque es posible que sólo sepan loseventos que quieren recibir. Si el oyente es uncomponente externo al sistema, entonces es posible queno sepa cuales son esos componentes.
• En algunos casos, puede que no todos los observadoresdeban ser informados acerca de todos los eventos queocurren en el sistema, incluso aunque estén interesadosen ellos, por razones de seguridad o rendimiento.
• Si los observadores tardan un tiempo apreciable enprocesar eventos, el rendimiento del sistema puede verseafectado. Este hecho se agrava si los componentes sonexternos al sistema.
• Si se generan muchos eventos, el tiempo para crearlos yreenviarlos puede ser no despreciable. Esta circunstanciase agrava cuando se utiliza un estrategia push para latransmisión de la información referida al evento. Estosignifica que el evento transporta toda esta información,por ejemplo el nuevo estado del componente observado.Para solucionar este problema debería ser posible fijarlos eventos que deben ser creados y los que deben serignorados.
• La implementación de políticas referidas al manejo deeventos no es una tarea trivial, ya que puede implicar eltrabajar con componentes y oyentes repartidos a travésde todo el sistema.
• Java no implementa herencia múltiple. Por lo tanto,muchas clases no serán capaces de heredar dejava.util.Observable ya que ya tienen otra clase padre.
Para concluir, hay otro problema que aparece en un
ámbito bastante más concreto. Si se usa un contenedor
de componentes para hospedar las partes del sistema es
bastante probable que algunos componentes deseen ser
avisados acerca de eventos relacionados con el ciclo de
vida de otros eventos. Esto es, cuando son creados,
iniciados, parados... Sin embargo, no es posible asegurar
que todos los componentes mandarán los eventos
apropiados (puede que sean componentes 'importados'
al sistema y desarrollados por otros). Además, puede
discutirse si es el componente y no el contenedor quien
debe generar estos eventos.
4 Administrador de Eventos, Anunciantes
Los problemas descritos en la sección anterior fueron
detectados durante el desarrollo de un sistema peer to
Figura 1 Patrón Observador
Figura 2 Varios componentes observados yobservadores
Actas del II congreso javaHispano
74
peer. Un requisito importante es que debíamos ser
capaces de monitorizar y registrar todo lo que ocurriera
en el nodo (eventos referidos a conexiones,
búsquedas...). Nos enfrentamos al problema de
monitorizar todos estos eventos, y concluimos que lo
mejor era utilizar un sistema de gestión de eventos
centralizado. Más adelante añadimos nuevas
capacidades al nodo, por ejemplo la posibilidad de fijar
filtros de eventos.
Para todo ello, hemos desarrollado una extensión del
patrón Observador, introduciendo Anunciantes y un
nuevo Administrador de Eventos que sustituye al Gestor
de Cambios.
4.1 Administrador de Eventos
El Administrador de Eventos sigue la misma idea que el
Gestor de Cambios explicado en la sección 3. Se sitúa
entre oyentes y componentes observables, y mantiene
las relaciones entre estos. Pero también añade algunas
capacidades nuevas:
• Un observador puede registrarse para recibir cualquierevento, sin especificar el componente que lo genera.Esto es, la administración se centra en eventos y no encomponentes.
• Pueden especificarse filtros de eventos. Si algún eventono pasa esos filtros, no es reenviado a los observadores.
• El Administrador de Eventos contiene una cola deeventos que puede ser activada al inicio. Los eventos sonalmacenados en la cola, y un hilo dedicado se encarga dereenviar los eventos de la cola a los oyentes.
4.2 Anunciantes
Un anunciante es un objeto asociado al componente
observable. El componente llamará su Anunciante
siempre que un nuevo evento deba ser notificado. El
Anunciante comprueba primero que el evento puede ser
reenviado, es decir, pasa los filtros (usa una llamada al
Administrador de Eventos). Si es así, la instancia del
evento correspondiente es creada y enviada al
Administrador de Eventos. Este notificará el evento a los
observadores
Este mecanismo tiene las siguientes ventajas:
• Los componentes no necesitan heredar de la clasejava.util.Observable.
• Los eventos que no pasen el filtro no son instanciados,mejorando por lo tanto el rendimiento..
• La administración de políticas está centralizada en unpunto.
En la Figura 4 vemos como los anunciantes se sitúan
entre el Administrador de Eventos y los componentes
4.3 Administración de eventoscentralizada
El uso de un Administrador de Eventos y un Anunciante
permite usar una implementación centralizada de la
gestión de eventos. Los programadores pueden
centrarse en la lógica de los componentes, ya que la
creación y reenvío de eventos son manejados por el
Anunciante correspondiente. Además, la administración
de eventos es facilitada ya que las tareas relacionadas
con los eventos se realizan en un único punto. Así, la
especificación de políticas referidas a notificaciones
(filtros, permisos...) puede hacerse llamando a los
métodos adecuados del Administrador de Eventos. Sin
este, la implementación de políticas es una tarea
complicada que implica trabajar con todos los
componentes observables.
Un Administrador de Eventos, o un componente similar,
podría estar presente dentro de un contenedor de
componentes. Así, el manejo centralizado de eventos
sería un servicio más proporcionado por dicho
contenedor. El contenedor a su vez podría también crear
notificaciones referidas a instantes del ciclo de vida de
los componentes.
Figura 3 Gestor de Cambios
Actas del II congreso javaHispano
75
5 Administrador de Eventos en elcontenedor PicoContainer
En sistemas complejos, con un número grande de
componentes, es fácil encontrar muchas fuentes
posibles de eventos, así como observadores. Como se
explicó en la sección anterior, el manejo de las relaciones
entre observados y observadores y la implementación de
políticas referidas al reenvío de eventos son tareas
difíciles.
Sin embargo, utilizar eventos para la comunicación entre
componentes es una buena opción cuando se
desarrollan componentes con bajo acoplamiento.
Además, nuestra experiencia demuestra que es mucho
mejor y más factible utilizar eventos cuando se necesita
monitorizar el sistema desde un componente externo.
En los últimos años nuevos contenedores han aparecido
dentro del mundo Java. Aunque diferentes en
complejidad y capacidades, comparten el mismo
objetivo básico de facilitar el desarrollo de sistemas
mediante componentes. Las tareas básicas que suelen
realizar son:
• Instanciación y ensamblaje de componentes. El patrónInversión de Control es usado normalmente para estatarea. Las dependencias entre componentes sonautomáticamente resueltas por el contenedor.
• Mantenimiento del ciclo de vida de los componentes.
• Proporcionar servicios tales como logging,configuración...
• Servicios avanzados tales como pools de threads, fuentesde datos...
Por ejemplo, Avalon [1] es un plataforma para la
programación de componentes y contenedores. Uno de
sus proyectos hijo es el contenedor Merlin, que
proporciona varios servicios como los mencionados
antes. Otros contenedores tales como Spring [4], ,
NanoContainer [5]... están también disponibles para la
programación con componentes.
Sin embargo, no hemos podido encontrar ningún
contenedor que proporcione un servicio de manejo de
eventos como el descrito en la sección 4.3.
Por ello, hemos tomado el contenedor PicoContainer [6]
e intentado añadirle capacidades para la gestión de
eventos. Las razones por las que hemos elegido este
contenedor son básicamente dos:
• Es código de fuente abierta, que puede ser leído ymodificado.
• Es un contenedor sencillo. Sólo resuelve dependenciasentre componentes, y no proporciona ningún servicioextra. Esta simplicidad facilitó la comprensión y posteriormodificación de su código
5.1 Cambios realizados en PicoContainer
Hay dos cambios básicos que hemos realizado sobre el
contenedor.
Primero ,un Administrador de Eventos ha sido añadido.
Otras entidades puede acceder al mismo para fijar
políticas de filtrado, para registrarse como observador,
etc.
Figura 5 Diagrama de clases del Administrador deEventos y de los Anunciantes
Figura 4 Administrador de Eventos, Anunciantes
Actas del II congreso javaHispano
76
Segundo, a todos los componentes se les asigna un
Anunciante que envía eventos referidos al ciclo de vida
del componente. Esto es, notifica cuando el componente
es iniciado, parado. Este Anunciante no es llamado por
el componente, sino por el mismo contenedor, que es el
que controla el ciclo de vida. Así el componente no
necesita heredar de ninguna clase, ni implementar
ninguna interfaz o llamar a ciertos métodos. No es
necesario cambiar el componente en absoluto, todo es
hecho automáticamente por el contenedor. Sin embargo
es posible que el componente quiera proporcionar su
propio Anunciante al contenedor, en el caso que tenga
su propia implementación. Por supuesto, cualquier
componente puede tener varios Anunciantes.
Para conseguir esto, hemos extendido la clase
DefaultPicoContainer para crear la nueva clase
PicoEventAbleContainer. Esta nueva clase mantiene las
relaciones entre componentes y Anunciantes. Los
Anunciantes manejados por este contenedor deben
implementar la interfaz PicoComponentAdviser, tal y
como se define en la Figura 6.
Cuando se instancia cualquier componente, si el
componente implementa la interfaz PicoAdviserOwner
significa que posee su propio Anunciante capaz de
notificar eventos acerca de su ciclo de vida.. Así, el
contenedor utilizará este anunciante para notificaciones.
Si no, el contenedor crea una instancia del Anunciante
por defecto PicoDefaultAdviser (ver Figura 7) y lo asigna
al componente.
Después de cada paso en el ciclo de vida del
componente el contenedor utiliza el Anunciante de ese
componente y llama al método correspondiente, que
creará un evento y lo mandará al Administrador de
Eventos para que sea reenviado a los oyentes.
Referencias[1] Apache Avalon framework. http://avalon.apache.org
[2] E Gamma, R. Helm, R. Johnson, J. Vissides. Design PatternsAddison-Wesley, 1995. ISBN: 0201633612
[3] JavaTM 2 Platform, Standard Edition, v 1.4.2, APISpecification http://java.sun.com/j2se/1.4.2/docs/api/
[4] Java/J2EE Spring frameworkhttp://www.springframework.org
[5] NanoContainer http://nanocontainer.codehaus.org
[6] PicoContainer http://picontainer.codehaus.org.
[7] Trygve Reenskaug, The original MVC.http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html.
package jhii;
public interface PicoComponentAdviser extends Adviser {
public void componentStarted();
public void componentStopped();
public void componentDisposed();
}
Figura 6 PicoComponentAdviser interface
package jhii;
public class PicoDefaultAdviser extends AdviserDefaultImplimplements PicoComponentAdviser {
public void componentStarted(){
if(component != null)
advise(new ComponentStartedEvent(component));
else
advise(new ComponentStartedEvent(this));
}
public void componentStopped(){
if(component != null)
advise(new ComponentStoppedEvent(component));
else
advise(new ComponentStoppedEvent(this));
}
public void componentDisposed(){
if(component != null)
advise(new ComponentDisposedEvent(component));
else
advise(new ComponentDisposedEvent(this));
}
}
Figura 7 PicoDefaultAdviser interface
Actas del II congreso javaHispano
77
Actas del II congreso javaHispano
78
Seguridad no intrusiva con Acegi Security System for Spring
Carlos Sánchez González Softgal
Plgno. POCOMACO, parcela I, nave 19, 15190 A Coruña - España
Abstract
Uno de los aspectos que toda aplicación debe considerar es
la seguridad, entendiendo como tal la necesidad de saber
que el usuario es quien dice ser (autenticación), y permitirle
acceso sólo a aquellos recursos necesarios (autorización).
Acegi Security System for Spring proporciona la
funcionalidad necesaria para adoptar mecanismos de
seguridad en aplicaciones Java utilizando características de
programación orientada a aspectos, de forma transparente
para el desarrollador, sin necesidad de desarrollar código,
utilizando para ello el soporte prestado por el framework
Spring, pero siendo posible utilizarlo en aplicaciones no
desarrolladas con Spring. En este artículo se detallarán las
funcionalidades que ofrece y una visión detallada sobre la
arquitectura del sistema, así como un simple ejemplo que
demostrará la sencillez con la que se puede adoptar su uso.
Keywords: Acegi, Spring Framework, seguridad, autenticación, autorización, java.
1 Introducción
Aplicar una política de seguridad a una aplicación es un
aspecto que afecta a prácticamente la totalidad de las
aplicaciones empresariales, y si no se adopta desde una
perspectiva correcta puede llegar a ser una carga que
afectará y lastrará el desarrollo del sistema.
Si bien existe el estándar JAAS (Java Authorization and
Authentication Service) que pretende cubrir tanto
autenticación como autorización, su adopción dista
mucho de ser sencilla y portable, debido a que el soporte
proporcionado por los contenedores de aplicaciones
dista mucho de ser adecuado, existen incompatibilidades
entre distintas implementaciones y cada contenedor
requiere una configuración distinta, normalmente con
adición de librerías. Por otro lado la funcionalidad
proporcionada por Acegi es mucho mayor, y además
permite la integración con JAAS, utilizándolo en la fase
de autenticación.
Acegi Security System [1] es un framework creado por
Ben Alex e íntimamente ligado al proyecto Spring [2], si
bien no requiere su utilización en nuestra aplicación, que
facilita la tarea de adoptar medidas de seguridad en
aplicaciones Java, sean aplicaciones standalone o
aplicaciones web. Y lo mejor de todo es que es open
source, sin coste de licencias y con la seguridad añadida
que proporciona el respaldo de un enorme y creciente
grupo de usuarios que lo están utilizando, y con un
manual de referencia con más de 50 páginas que no
tiene nada que envidiar a la documentación de un
producto comercial.
La arquitectura de Acegi está fuertemente basada en
interfaces y en patrones de diseño, proporcionando las
implementaciones más comúnmente utilizadas y
numerosos puntos de extensión donde nuevas
funcionalidades pueden ser añadidas. Esta arquitectura
puede hacer un poco difícil seguir el flujo de ejecución al
principio, pero una vez comprendida la idea global se
acepta como el precio necesario para poder disfrutar de
un framework con una gran potencia.
Como ejemplo se mostrará la configuración realizada en
el proyecto ONess [3] para la protección de peticiones
http en una aplicación web, además de mencionar uno
de los completos ejemplos que se distribuyen con el
proyecto.
1 Autenticación
Antes de poder tomar decisiones sobre si un usuario
puede acceder o no a un recurso, el usuario debe
identificarse para comprobar su identidad. Para ello
Actas del II congreso javaHispano
79
existe el interfaz Authentication, desde el que se puede
acceder a tres objetos:
• principal, típicamente un nombre de usuario.
• credentials, las credenciales del usuario que
prueban que es quien dice ser, normalmente su
contraseña, aunque podría ser otro tipo de
información como certificados electrónicos.
• authorities, un lista de los roles que posee el
usuario o grupos a los que pertenece.
Cuando el usuario se autentica se crea un objeto
Authentication, con los dos primeros objetos, principal y
credenciales. En el caso de autenticación mediante
nombre de usuario y contraseña se creará un objeto
UsernamePasswordAuthenticationToken.
Acegi proporciona las clases necesarias para que esta
autenticación se realice mediante usuario y contraseña,
utilizando un adaptador para enlazar con la
autenticación proporcionada por un contenedor de
aplicaciones como son catalina, jboss, resin o jetty, o
utilizando el servicio de Single Sign On que proporciona
el proyecto CAS de la universidad de Yale [4].
Una vez creado este objeto Authentication se pasa al
AuthenticationManager, que a partir del principal y las
credenciales determina si éstas concuerdan con las
esperadas, añadiéndole al objeto Authentication las
authorities correspondientes en caso afirmativo o
lanzando una excepción de tipo AuthenticationException
en caso contrario.
Acegi proporciona una implementación del gestor de
autenticación AuthenticationManager que debería ser
suficiente para la mayoría de los casos, el
ProviderManager. Esta clase tan sólo delega la
autenticación en una lista de proveedores configurable,
cada uno de los cuales implementa el interfaz
AuthenticationProvider. Entre las implementaciones de
proveedores suministradas con el proyecto se
encuentran las necesarias para realizar la autenticación
contra varios servidores de aplicaciones (catalina, jboss,
resin y jetty), contra un fichero de configuración JAAS,
contra CAS (la solución Single Sign On de la universidad
de Yale [4]), y contra un objeto de acceso a datos
usando DaoAuthenticationProvider, que es el
comúnmente usado puesto que es el que permite
acceder a la información almacenada en una base de
datos.
El proveedor DaoAuthenticationProvider merece una
mención especial. Esta implementación delega a su vez
en un objeto de tipo AuthenticationDao, un interfaz que
define un objeto de acceso a datos con un único método
loadUserByUsername que permite obtener la
información de un usuario a partir de su nombre de
usuario. Acegi proporciona dos implementaciones de
este interfaz, InMemoryDaoImpl, en la que la
información de los usuarios se guarda en memoria, útil
para la realización de pruebas, y JdbcDaoImpl, que
accede a una base de datos a través de JDBC. Realizar
implementaciones de este interfaz es sumamente sencillo
y en el proyecto ONess [3] se encuentra disponible una
implementación que utiliza el mapeador objeto-
relacional Hibernate.
Entre otras características que también se proporcionan
de forma transparente, tan sólo estableciendo unos
parámetros de configuración, son soporte para cifrado
de contraseñas (SHA y MD5), caché de la información de
autenticación y redirección automática de peticiones
http a canales seguros https para aquellas urls que
deseemos.
Para el caso de aplicaciones web existen tres formas de
que un usuario se autentique:
• Utilizando autenticación de tipo BASIC, definida
en el RFC 1945, el usuario introduce su usuario
y contraseña en una simple ventana emergente
del navegador. Es necesario en el caso de que se
quieran añadir características de seguridad a
servicios web.
• Autenticándose mediante un formulario web, es
la forma más habitual ya que permite integrar el
formulario de login en la aplicación web.
• Utilizando el servicio de autenticación central
CAS de la Universidad de Yale [4], en caso de
que se requieran características de Single Sign
On, de forma que el usuario sólo tiene que
autenticarse una vez para todos los servicios
que puedan proporcionarse en el ámbito de una
empresa, incluso en distintos servidores y
desarrollados con distintos lenguajes de
programación.
Cada una de las formas anteriores requiere de la
configuración del filtro correspondiente en el descriptor
de aplicación web, BasicProcessingFilter,
AuthenticationProcessingFilter o CasProcessingFilter
respectivamente. La forma de configurarlos es definir los
Actas del II congreso javaHispano
80
filtros como del tipo FilterToBeanProxy, delegando,
según un parámetro de inicialización, en uno de los
filtros anteriores definidos en el contexto de aplicación
de Spring, lugar donde pueden ser más fácilmente
configurados.
Los clientes llamados “ricos” o aplicaciones standalone
también están soportados, utilizando un gestor de
autenticación remoto cuya implementación utiliza un
servicio web en el lado del servidor, utilizando
RemoteAuthenticationManager y
RemoteAuthenticationProvider.
2 Autorización
Una vez el usuario está autenticado entra en juego la
parte del sistema encargada de la autorización, con el fin
de permitir que el usuario acceda sólo a aquellos
recursos a los que tiene permiso. Para ello Acegi
intercepta las llamadas a los objetos, utilizando proxies
dinámicos u orientación a aspectos basada en AspectJ, o
las peticiones http, utilizando filtros, y actúa en
consecuencia. Así permite restringir tanto llamadas a
métodos de determinadas clases o instancias, así como
acceso a urls.
Cuando se intercepta una petición a un recurso
protegido se comienza una cadena de eventos que
finalizará permitiendo el acceso al recurso o lanzando
una excepción AccessDeniedException. La cadena
comienza en un objeto de tipo AccessDecisionManager,
que a partir del objeto Authentication y de los
parámetros de configuración decide si la llamada debe
proseguir. Acegi proporciona tres implementaciones de
AccessDecisionManager que se basan en el concepto de
una votación, pero diferenciando las reglas de decisión:
• UnanimousBased: permite el acceso si no hay
votos negativos
• AffirmativeBased: permite el acceso si un voto
es afirmativo
• ConsensusBased: permite el acceso si el número
de votos positivos es mayor o igual que el de
negativos
Al igual que en la autenticación el ProviderManager
delegaba en una lista de AuthenticationProviders, en el
caso de la autorización el AccessDecisionManager delega
la facultad de emitir votos en objetos de tipo
AccessDecisionVoter. Se proporcionan dos
implementaciones de éste último interfaz:
• RoleVoter, que comprueba que el usuario
presente un determinado rol, comprobando si
se encuentra entre sus authorities.
• BasicAclEntryVoter, que a su vez delega en una
jerarquía de objetos que permite comprobar si
el usuario supera las reglas establecidas como
listas de control de acceso.
El primer caso es el más común, proporcionando una
autenticación basada en grupos o roles, donde se
permite el acceso si el usuario pertenece a alguno de los
configurados como requeridos. En el segundo caso se
permite restringir el acceso a objetos a nivel de instancia,
caso que será discutido más adelante.
En ambos casos el sistema que intercepta las llamadas
debe ser configurado. En el caso de las aplicaciones web
se hará mediante la configuración de un filtro en el
fichero web.xml.
Los posibles recursos que se pueden proteger son
• urls, mediante un filtro en el descriptor de
aplicación web, FilterSecurityInterceptor.
• métodos de objetos definidos en el contexto de
aplicación de Spring, utilizando
MethodSecurityInterceptor.
• cualquier PointCut definible en AspectJ,
mediante AspectJSecurityInterceptor.
2 Autorización a nivel de instancia mediante listas de control de acceso
Existen casos en los que la protección de las llamadas a
métodos no es suficiente, necesitando protegerse de
distinta forma distintas instancias de una clase. Como
ejemplo se puede pensar en un sistema de ficheros, en
los que cada archivo tiene distintos permisos, según si el
usuario que accede a ellos es el dueño del archivo,
pertenece al grupo del dueño o no cumple ninguna de
las opciones anteriores.
Acegi proporciona en sus últimas versiones el soporte
necesario para implementar seguridad basada en listas
de control de acceso. Las clases clave que se deben
conocer son
• BasicAclEntryVoter obtiene las ACLs del objeto
llamado y vota sobre si se debe permitir el
acceso a él o no.
Actas del II congreso javaHispano
81
• BasicAclAfterInvocationProvider permite
denegar el acceso a un objeto después de que
el método se haya invocado, útil cuando no se
puede saber a priori.
• BasicAclAfterInvocationollectionFilteringProvider
, similar al anterior, elimina los objetos a los que
el acceso no ha sido permitido en aquellos
métodos que devuelven colecciones.
Para más detalles sobre ACLs se recomienda consultar el
completo manual de referencia de Acegi.
2 Ejemplo
Como ejemplo se utilizará el proyecto ONess [3],
subproyectos user-model y user-webapp. En este
proyecto se ha configurado una aplicación web para
proteger sus recursos en peticiones http. Se han omitido
partes no relevantes para este ejemplo, pero que pueden
ser consultadas en la página web y en el repositorio de
código fuente del proyecto, entre otros la configuración
necesaria para utilizar autenticación basada en HTTP
BASIC o CAS o las clases necesarias para ejecutarlo.
Acegi utiliza el contexto de aplicación de Spring para
definir la configuración necesaria. Para aquellas personas
que no están familiarizados con Spring decir que tan
sólo es necesario crear un fichero /WEB-
INF/applicationContext.xml con el contenido que se
muestra en la Fig. 1 y añadir a /WEB-INF/web.xml el
contenido de la Fig. 2. En este segundo fichero se
configura un listener que procesa el primero
automáticamente cada vez que el contenedor de
aplicaciones inicia la aplicación web, y unos filtros que
procesan todas las peticiones que llegan.
La autenticación se realiza a través de un objeto de
acceso a datos DAO implementado con el mapeador
objeto-relacional Hibernate, authenticationDao, para
acceder a la información de usuarios almacenada en una
base de datos. Como alternativa para realizar pruebas
también se incluye comentada la definición de un DAO
de tipo InMemoryDaoImpl. Como proveedor de
autenticación se utiliza por tanto
DaoAuthenticationProvider, configurándose con una
caché de usuarios basada en EHCache. Por comodidad
no se ha activado el cifrado de contraseñas, acción que
puede realizarse con tan sólo descomentar la línea
indicada. El gestor de autenticación ProviderManager
tan sólo tendrá como proveedor el anteriormente
mencionado, ya que el único repositorio de usuarios será
la base de datos.
En la aplicación web es necesario añadir dos filtros,
Acegi Security System for Spring Http Session Integration
Filter, que hace que la información de autenticación esté
disponible para sucesivas peticiones del usuario al
guardarla en la sesión, y Acegi Authentication Processing
Filter para procesar el formulario de login de un usuario.
El primer filtro no requiere configuración, mientras que
el segundo la delega en el contexto de aplicación de
Spring, definiendo un bean
authenticationProcessingFilter, donde se referencia el
gestor de autenticación anteriormente configurado y la
página a la que ir en caso de error en el login, entre
otros, y authenticationProcessingFilterEntryPoint, donde
se configura la página donde se encuentra el formulario
de login.
En cuanto a autorización, las decisiones se tomarán
basándose en los roles del usuario utilizando RoleVoter,
y puesto que tan sólo existe ese AccessDecisionVoter no
influirá el gestor de decisiones, optando por un
AffirmativeBased.
En la aplicación web se configurará un filtro Acegi HTTP
Request Security Filter para restringir el acceso a
determinadas urls. Este filtro al igual que los anteriores
se configura mediante el contexto de aplicación de
Spring, donde se define un FilterSecurityInterceptor,
filterInvocationInterceptor, que define los roles
necesarios para acceder a las urls utilizando comodines,
y se define también securityEnforcementFilter, donde se
enlazan el interceptor, para el caso en el que el usuario
ya está autenticado, y el punto de entrada, para el caso
contrario.
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- AUTENTICACION --> <!-- Data access object which stores authentication information -->
Actas del II congreso javaHispano
82
<!-- Hibernate implementation --> <bean id="authenticationDao" class="net.sf.oness.user.model.dao.UserHibernateDao"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <!-- Implementacion util para pruebas <bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl"> <property name="userMap"> <value> marissa=koala,ROLE_USER,ROLE_ADMIN dianne=emu,ROLE_USER scott=wombat,ROLE_USER peter=opal,disabled,ROLE_USER </value> </property> </bean> --> <bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="authenticationDao"><ref local="authenticationDao"/></property> <property name="userCache"><ref local="userCache"/></property> <!-- Descomentar para activar cifrado de contraseñas <property name="passwordEncoder"><ref local="passwordEncoder"/></property> --> </bean> <bean id="passwordEncoder" class="net.sf.acegisecurity.providers.encoding.Md5PasswordEncoder"/> <bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"> <property name="minutesToIdle"><value>5</value></property> </bean> <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref local="daoAuthenticationProvider"/> </list> </property> </bean> <!-- Filtro web --> <bean id="authenticationProcessingFilter" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager"> <ref bean="authenticationManager"/> </property> <property name="authenticationFailureUrl"> <value><![CDATA[/show.do?page=.login&login_error=1]]></value> </property> <property name="defaultTargetUrl"><value>/</value></property> <property name="filterProcessesUrl"><value>/security_check</value></property> </bean> <bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl"> <value><![CDATA[/show.do?page=.login]]></value> </property> <property name="forceHttps"><value>false</value></property>
Actas del II congreso javaHispano
83
</bean> <!-- AUTORIZACION --> <bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/> <bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions"><value>false</value></property> <property name="decisionVoters"> <list> <ref local="roleVoter"/> </list> </property> </bean> <!-- Filtro web --> <bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"> <property name="filterSecurityInterceptor"> <ref bean="filterInvocationInterceptor"/> </property> <property name="authenticationEntryPoint"> <ref local="authenticationProcessingFilterEntryPoint"/> </property> </bean> <bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager"/> </property> <property name="accessDecisionManager"> <ref bean="accessDecisionManager"/> </property> <property name="objectDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /secure/**=ROLE_ADMIN /**/*create*=ROLE_USER,ROLE_ADMIN /**/*edit*=ROLE_USER,ROLE_ADMIN /**/*update*=ROLE_USER,ROLE_ADMIN /**/*delete*=ROLE_USER,ROLE_ADMIN </value> </property> </bean> </beans> Figura 1. Spring application context
<!-- carga el contexto de aplicación de /WEB-INF/applicationContext.xml --> <listener> <listener-class>net.sf.oness.common.webapp.controller.listener.SpringContextLoaderListener</listener-class> </listener> <filter> <filter> <filter-name>Acegi Security System for Spring Http Session Integration Filter</filter-name> <filter-class>net.sf.acegisecurity.ui.HttpSessionIntegrationFilter</filter-class> </filter> <filter-name>Acegi Authentication Processing Filter</filter-name> <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
Actas del II congreso javaHispano
84
<init-param> <param-name>targetClass</param-name> <param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value> </init-param> </filter> <filter> <filter-name>Acegi HTTP Request Security Filter</filter-name> <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class> <init-param> <param-name>targetClass</param-name> <param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>Acegi Security System for Spring Http Session Integration Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Acegi Authentication Processing Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Acegi HTTP Request Security Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> Figura 2. web.xml
Otro completísimo ejemplo “contacts” puede
encontrarse en la distribución de Acegi Security,
incluyendo autorización basada en listas de control de
acceso, y acceso remoto desde aplicaciones standalone.
8 Conclusiones
Acegi es uno de los mejores frameworks de seguridad
existentes en Java, potente y flexible a la vez que sencillo
de configurar, sin necesitad de modificar código ya
existente y portable entre distintos contenedores de
aplicaciones sin necesidad de cambios. Su integración
con Spring hace que sea el recomendado para añadir
funcionalidades de seguridad a las aplicaciones que
utilizan ese magnífico framework, cuyo número crece día
a día, si bien puede ser utilizado en cualquier tipo de
aplicación sin ningún problema.
En este artículo se dado una visión global del framework
y se ha mostrado con un ejemplo cómo se pueden
proteger las urls de una aplicación web sin necesidad de
modificar ni una línea de código. Para mayor
información se recomienda la lectura del manual de
referencia, realmente completo.
Agradecimientos
A Ben Alex, principal desarrollador del proyecto Acegi,
por su colaboración.
Referencias
[1] Acegi Security System for Spring http://acegisecurity.sourceforge.net.
[2] Spring Framework http://www.springframework.org.
[3] ONess http://oness.sourceforge.net
[4] CAS (Central Authentication Service). Universidad de Yale, http://www.yale.edu/tp/auth/.
[5] Weblog del autor http://www.jroller.com/page/carlossg
Actas del II congreso javaHispano
85
Actas del II congreso javaHispano
86
Estándares libre y Java: ¿Es el JCP un organismo que crea estándares libres?
Abraham Otero Quintana Dep. de Electrónica y Computación. Edificio Monte da Condesa.
Santiago de Compostela, 15768. España [email protected]
Abstract
La plataforma Java ha sido y es blanco de las críticas de
numerosos activistas de la comunidad del Software
Libre (SL) por “no ser Software Libre”. Sin embargo
Java, a diferencia de otras alternativas, no es un
software, sino un conjunto de especificaciones que
definen una serie de tecnologías. Por ello carece de
sentido juzgar si la plataforma Java es o no libre en base
a la licencia de algunas de las implementaciones de un
subconjunto de las especificaciones, principal base de
las críticas de la comunidad del SL. Lo que deberíamos
plantearnos, dada su condición de especificaciones, es si
Java puede considerarse o no un estándar libre.
En este trabajo analizaremos el proceso de creación de
las especificaciones de la plataforma Java para
determinar si Java es o no libre. Emplearemos en este
análisis tanto los criterios de la comunidad del SL,
creados bajo la dirección de Bruce Perens, como los
criterios de la comunidad académica. También se
discutirá la problemática relacionada con la licencia de
las implementaciones de referencia de la plataforma y
se propondrá una solución que busca un equilibrio entre
los intereses de la comunidad del SL y de Sun
Microsystems.
Keywords: Libertad de Java, estándares libres, Java Community Process, Software Libre.
1 Introducción
Determinar la libertad de la plataforma Java no es
cuestión de analizar las licencias bajo las que se
distribuye el conjunto de herramientas de desarrollo
(JDK), o el entorno de ejecución (JRE) de Sun
Microsystems, principal fuente de críticas de la
comunidad del SL hacia Java. La plataforma Java no
es un software concreto, sino un conjunto de
especificaciones que definen todas y cada una de las
tecnologías que componen la plataforma. Un análisis
acerca de la libertad de la plataforma no puede
basarse en la licencia de una, o varias, de las
implementaciones de un subconjunto de estas
especificaciones, que es en lo que habitualmente la
comunidad del SL basa sus críticas, sino que debe
analizar el proceso de creación y mantenimiento de
las especificaciones que definen la plataforma Java.
Esta tarea, creación y mantenimiento de las
especificaciones, es responsabilidad de un organismo,
el Java Community Process (JCP) [1], cuya
organización y funcionamiento básico se perfilará en
este trabajo. A continuación se estudiará si las
especificaciones creadas por el JCP pueden o no
considerarse estándares libres. Para ello se tendrán en
cuenta tanto los criterios de la comunidad del SL,
creados bajo la dirección de Bruce Perens, como los
criterios de la comunidad académica, definidos por
Ken Krechmer. Irónicamente Java cumple con creces
los primeros, pero falla en los segundos.
Finalmente, y dado que la licencia de las
implementaciones de referencia (IR) parece ser tan
importante para el moviendo del SL, se discute este
problema. Se mostrará que el miedo a la
fragmentación con el que Sun se excusa para no
liberar su código es infundado. Sun defiende que las
ideas del movimiento del SL, acceder y modificar el
código fuente del software sin restricciones, entran
en conflicto con el principio básico de la plataforma
Java: “escríbelo una vez y ejecútalo en cualquier sitio”
(adaptación de “Write Once Run Anywhere”, WORA).
Se mostrará que en la práctica no hay evidencias de
que esta fragmentación pudiera producirse y se
propondrá una fórmula mediante la cual Sun podría
seguir manteniendo el control de Java a nivel
Actas del II congreso javaHispano
87
empresarial y cobrando royalties por estas tecnologías
(dos ventajas relevantes de Sun sobre sus
competidores empresariales), a la vez que libera el
código de las IR.
2 ¿Qué es la plataforma Java?
Hill Venners en su libro Inside the Virtual Machine [2]
afirma que Java está formado por cuatro piezas
diferentes: una especificación de un lenguaje de
programación; una especificación de un formato
binario, los bytecodes; una especificación de una
máquina virtual, encargada de interpretar los
bytecodes; y un conjunto de librerías estándar. Sin
duda estos cuatro elementos definen el lenguaje de
programación Java.
Sin embargo, si Java ha alcanzado tanto éxito y
difusión no es sólo gracias al lenguaje, sino también
al resto de la plataforma, que integra múltiples
tecnologías en su seno: tecnologías para el desarrollo
de aplicaciones Web (Servlets, JSP, portlets, etc.),
aplicaciones empresariales (EJB, JTA, JCA, JMS, etc.),
aplicaciones para telefonía móvil (CLCD, CDC, OpenGL
ES, etc.), tarjetas inteligentes (JavaCard), y un
inmenso sinfín de tecnologías que hicieron a Java
único hasta hace tan sólo un par de años, cuando
apareció .NET.
El fin de este trabajo no es sólo analizar la libertad del
lenguaje de programación, y/o del JDK distribuido por
Sun, sino de toda la plataforma en su conjunto. Por
ello, para nuestros objetivos, es más correcto
considerar que la plataforma Java está compuesta por
un conjunto de especificaciones, que definen todas y
cada una de las partes de la plataforma, y una serie
de implementaciones de estas especificaciones. Sin
duda, por ser la base sobre la cual se edifica el resto
de la plataforma, las especificaciones del lenguaje,
bytecode, máquina virtual, y de las librerías estándar
juegan un papel protagonista, pero no son las únicas.
3 El Java Community Process
El Java Community Process [1] es el organismo que
dirige, mediante la creación de nuevas
especificaciones y el mantenimiento de las ya
existentes, la evolución plataforma Java. El JCP define
su propio funcionamiento y las normas por las que se
rige, normas que han ido evolucionando desde su
creación en Diciembre de 1998. La versión actual del
JCP, y la que se describe en este documento, es la 2.6
[3].
El JCP es administrado por el Program Management
Office (PMO), organismo formado por asalariados de
Sun Microsystems. Dos Comités Ejecutivos (CE) se
encargan de aprobar las modificaciones y extensiones
de la plataforma; uno encargado de las
especificaciones relacionadas con J2EE y J2SE
(ediciones empresarial y estándar, respectivamente,
de la plataforma), y el otro de las relacionadas con
J2ME (edición para pequeños dispositivos: teléfonos
móviles, tarjetas inteligentes…). Cada uno de estos
comités está compuesto por 16 miembros con
derecho a voto, 5 elegidos mediante votación entre
los miembros del JCP, votación en la que participan
todos los miembros del JCP. 10 son propuestos por el
PMO, siguiendo los criterios de “comunidad
balanceada” y “representación regional” [3]. Estos
miembros han de ser ratificados mediante votación
pública. El miembro restante es un representante de
Sun. En cada comité hay un segundo representante
de Sun, que ejerce la labor de presidente, pero que
no posee derecho a voto.
Convertirse en miembro del JCP es gratis para
empresas licenciatarias de Sun (empresas que
implementan especificaciones de la plataforma con
ánimo de lucro y pagan por ello a Sun), y para
personas que a título individual deseen formar parte
del JCP. Las empresas no licenciatarias han de pagar
5000 $ por año. En el caso de organizaciones sin
ánimo de lucro e instituciones académicas es un
comité formado por un miembro de Sun, un miembro
del mundo del SL o del mundo académico,
Actas del II congreso javaHispano
88
Figura 1 Diversas etapas por las que pasa un Java Specification Request.
y un miembro elegido democráticamente, quien decide
si pueden formar parte del JCP gratis, o han de pagar
2000 $.
Las incorporaciones de nuevas tecnologías a la
plataforma Java se realizan a través del JCP, mediante un
“Java Specification Request” [4] (JSR). Para crear un JSR
se ha de explicar en un documento la necesidad de la
nueva tecnología, o de modificación de una tecnología
ya existente, y cómo afectará este cambio al resto de la
plataforma. Aunque no es necesario, también es
recomendable proponer un grupo de expertos que se
encargará de desarrollar el JSR, en caso de ser aceptado.
Cualquiera, sea o no miembro del JCP, puede proponer
nuevos JSR. Uno de los dos CE del JCP, según el nuevo
JSR afecte a J2ME o a J2SE/J2EE, analiza la propuesta y
decide si acepta o no la creación del JSR. Esta decisión se
realiza mediante una votación pública, donde el nuevo
JSR debe obtener mayoría simple.
A continuación el grupo de expertos, normalmente
liderados por quien propuso el nuevo JSR, trabaja sobre
la especificación. Deberán completar el documento que
define la nueva tecnología o modificación de una ya
existente, una implementación que prueba que la
tecnología es factible, denominada Implementación de
Referencia (IR), y un Test de Compatibilidad (TC), una
batería de pruebas que permite comprobar si una
implementación cumple o no con la especificación.
Tanto la IR como el TC son desarrollados bajo la licencia
que el líder del grupo de expertos decida, pudiendo
optar por cualquier licencia libre.
En una primera fase el grupo de expertos se centra en la
definición la tecnología. Primero debe crear un borrador
de la especificación, “Early Draft”, que se publica en
Internet y es accesible a todo el mundo; cualquier
individuo con o sin vinculación con el JCP puede
estudiar, opinar y enviar realimentación sobre este
borrador al grupo de expertos. En base a la
realimentación de la comunidad el grupo de expertos
realiza modificaciones, completa la especificación y
nuevamente hace pública una versión más madura de la
especificación, el “Public Draft”. Nuevamente cualquiera
puede enviar realimentación a cerca de este documento,
y el grupo de expertos puede realizar cambios en base a
la realimentación.
Cuando se llega a un acuerdo en la especificación ésta
ha de ser ratificada por el CE correspondiente, mediante
votación pública en la que necesita una mayoría simple
de los votos emitidos. Si es ratificada pasa a una tercera
fase, donde es toda la comunidad la que revisa y
comenta la especificación, mientras que el líder del
grupo de expertos se encarga del desarrollo de la IR y el
TC. Una vez que el grupo de expertos ha terminado su
trabajo éste, nuevamente, ha de ser aprobado por el CE
correspondiente mediante una votación pública.
Finalmente se entra en una fase cíclica de
mantenimiento de la tecnología, fase también abierta al
público (ver Fig. 1).
Hay una serie de JSR en los que Sun tiene privilegios
especiales y que se rigen por unas normas ligeramente
diferentes: los “Umbrella Java Specification Request”
(UJSR) [3]. Estos son los JSR que definen las tres
ediciones de la plataforma: J2SE, J2EE, J2ME y los
diversos perfiles de J2ME. Estos JSR “paraguas”
protegen, entre otros, la especificación de la máquina
virtual, el lenguaje, y el bytecode. Los cambios en estas
especificaciones pueden afectar a la compatibilidad hacia
atrás, o a la portabilidad, de las aplicaciones, por ello
Sun justifica este trato especial. Cualquier nueva
Actas del II congreso javaHispano
89
funcionalidad o tecnología que quiera incorporarse a
cualquiera de las tres ediciones de la plataforma ha de
hacerlo a través de un UJSR.
Para que un UJSR sea aprobado debe obtener al menos 5
votos positivos del CE correspondiente, y de los votos
emitidos al menos dos tercios han de ser de aprobación.
Además el representante de Sun puede vetar la nueva
especificación, lo que se traduce en que Sun tiene la
última palabra en lo que se refiere a la aprobación de los
UJSR, y por lo tanto a cambios sobre J2SE, J2EE y J2ME.
No todos los JSR se rigen por las normas aquí descritas;
como ya se ha comentado el propio JCP es un JSR que
evoluciona y por lo tanto sus normas cambian. En un
principio no era tan abierto, no permitiendo, por
ejemplo, la implementación de los TC e IR bajo licencias
libres. Sun también tenía derechos de veto sobre más
JSR, y las implementaciones de las especificaciones
estaban obligadas a respetar las posibles patentes que
hubiese en las especificaciones, obligación que ya no
existe en la actualidad. Algunos JSR todavía se guían por
versiones anteriores del JCP, siendo el líder del grupo de
expertos el que puede actualizar, si lo considera
conveniente, la versión del JCP. Es de esperar que según
estos JSR saquen nuevas versiones de mantenimiento se
vayan pasando a la última versión del JCP, más abierta y
más respetuosa con el mundo del SL.
Finalmente, cualquiera puede implementar las
especificaciones, y distribuir su implementación bajo la
licencia que considere oportuno. No está obligado a
pasar el TC, pero si desea hacerlo (esto es imprescindible
si se desea emplear la marca registrada “Java” y términos
relacionados), y ha realizado la implementación con
ánimo de lucro, ha de pagar a Sun por pasar el TC. Si la
implementación ha sido realizada por una organización
sin ánimo lucro, como suele ser el caso de los desarrollos
libres, el mismo comité de tres personas que decide si las
organizaciones sin ánimo de lucro pagan por pertenecer
al JCP, decide si ha de pagar o no por pasar el TC. No
pasar el TC no implica que una determinada
implementación no cumpla la especificación, sin
embargo es una garantía para sus usuarios de que
efectivamente la cumple.
3.1 Estándares vs especificaciones
El nombre que recibe la documentación que define las
tecnologías que forman la plataforma Java es
“especificaciones”; sin embargo cuando se habla de
libertad, tanto dentro del mundo del SL como en el
académico, se emplea el término “estándares”. Cabe
preguntarse qué diferencias hay entre una especificación
y un estándar; y si podemos juzgar a las especificaciones
de la plataforma Java con los criterios de los estándares
libres. He aquí una definición de cada uno de estos
términos:
• Un estándar es una tecnología, formato o método
desarrollado y adoptado a través de un proceso
abierto de consenso, bajo la guía de cuerpos
nacionales (ANSI, BSI, ect.) o internacionales (ISO,
IEEE, etc.) de estándares.
• Una especificación es un conjunto de documentos
desarrollados por un grupo dentro de la industria,
pero sin guías o procedimientos formales que
aseguren que el trabajo está abierto a cualquier otra
parte interesada, o abierto para su revisión y
comentario durante el desarrollo (IETF, WC3, OMG,
etc.).
En algunas definiciones, como la aquí recogida, se exige
que un estándar esté aprobado por un organismo
reconocido oficialmente, un SDO (Standard Definition
Organization), mientras que en otras no se especifica el
tipo de organismo que debe reconocerlo. En otras
ocasiones sólo se le requiere que “esté creado a través
de un procedimiento abierto que garantice que se
escucharán toda las partes interesadas y se llegará a un
consenso”.
Las especificaciones del JCP se definen entre más de 700
organizaciones e individuos, entre la cuales se hallan
todas las grandes empresas de informática menos
Microsof; están abiertas a todo el que las quiera
examinar; cualquiera puede participar gratuitamente en
el JCP, y por lo tanto en la definición y evolución de la
plataforma; e incluso sin pertenecer al JCP se puede
contribuir al proceso de definición de las
especificaciones. No parece justo caracterizar de
“especificación” (al menos en el sentido de cierta
bibliografía), a algo que es consensuado entre tantas
partes, con garantías de acceso, transparencia, y con
posibilidad de participación sin discriminación en su
construcción. Por otro lado en la bibliografía sobre
estándares libres se trata a las especificaciones del JCP
como “estándares” [5].
Evitaremos entrar más en discusiones lingüísticas y, dado
el carácter consensuado y abierto de las especificaciones
del JCP las trataremos como “estándares”, aún cuando
Actas del II congreso javaHispano
90
no estén reconocidas por organismos de estándares
oficiales. El motivo principal para ello es la lentitud de la
evolución de los estándares en los organismos oficiales.
En su día Sun consideró llevar a ISO las especificaciones
del JCP, pero lo descartó porque ISO ralentizaría
demasiado la evolución de la plataforma: más de un
estándar definido dentro de ISO u organismos similares
se ha quedado obsoleto antes de llegar a ver la luz.
4 Importancia de los estándares libres
La interoperabilidad, capacidad de un sistema para
trabajar con otro, es una de las características más
deseables para cualquier aplicación informática. De poco
nos valdría un cliente de correo que no siga el protocolo
SMTP, un procesador de textos cuyos documentos sólo
se pudiesen visualizar y editar en la máquina donde se
crearon, o un servidor web que no respeta el protocolo
http. En el pasado era posible concebir un sistema
informático que no interoperaba con ningún otro
sistema; sin embargo en la era de Internet, donde
proliferan múltiples aplicaciones distribuidas que son
accedidas por múltiples dispositivos de diversa
naturaleza (PCs, PDA, terminales móviles de última
generación, mainframes…) no hay lugar para este tipo
de islas.
El software libre por sí sólo no genera interoperabilidad;
los estándares son la base para conseguirla. De aquí la
enorme importancia de contar con estándares libres,
para invitar que estándares propietarios coarten la
libertad del software, aún cuando su código fuente sea
libre. Los estándares propietarios, o el no uso de
estándares, es un mecanismo para entorpecer e incluso
anular el desarrollo de soluciones de terceros (en especial
las libres). Así por ejemplo los desarrolladores de
OpenOffice se ven obligados a hacer reingeniería inversa
para acceder a los formatos OLE de Microsoft, y se
exponen a ser demandados por sus actividades. De un
modo similar los desarrolladores de clientes de
mensajería libres que interactúan con la red MSN cada
vez que surge una nueva versión del protocolo MSNpX
deben reventarlo y actualizar los clientes; y Microsoft
podría cerrar la red de MSN, impidiendo que clientes de
terceros, libres y no libres, se conecten a su red.
Un estándar libre debe poder implementarse libremente.
Para ello su contenido ha de ser público y accesible,
eliminando las barreras de tipo técnico en la
implementación. Tampoco deben existir barreras de tipo
económico o legal (patentes y copyright) que impidan su
implementación; esto último es casi imprescindible para
el mundo del SL, que no puede permitirse pagar royalties
por usar el estándar, ni afrontar largos y costos juicios.
Un estándar libre respeta la libertad de elección:
podemos tomar hoy una decisión y mañana otra
diferente: si desarrollamos una web con ASP .NET nos
ataremos a un servidor web y a un sistema operativo. Si
empleamos JSP o php podremos cambiar en cualquier
momento de servidor web, ya que hay múltiples
vendedores e implementaciones libres que los soportan,
y/o de sistema operativo.
Incluso las más “pequeñas” estandarizaciones libres han
permitido enormes avances. Por ejemplo, fueron los
estándares libres como TCP/IP, FTP, HTML, SMTP, etc. los
que permitieron construir el único “caso de éxito”
auténtico en lo referente interoperabilidad: Internet. A
principios de los años 90 en las redes empresariales
coexistían un conjunto de protocolos propietarios para la
comunicación en red entre las máquinas de distintos
fabricantes (IBM, Apple, Microsoft, VAX, ect.) entre los
cuales era difícil interoperar. En este contexto Internet se
abre a las empresas y trae consigo TCP/IP, un protocolo
libre para la comunicación en red, y un conjunto de
protocolos libres para interoperar entre equipos (FTP,
Telnet, ect.). No le llevó mucho tiempo imponerse a los
otros protocolos propietarios siendo clave en su triunfo
la apertura del estándar, y el no tener que pagar ningún
tipo de royalties para emplearlo.
Un estándar libre fomenta la aparición de múltiples
implementaciones de características heterogéneas, libres
y comerciales. Esto beneficia al usuario final, ya que se
evita el “vendor lock-in”, quedar atrapado por un
proveedor del que dependo por haber basado mi
solución en su producto.
Tras estos argumentos cabe hacerse una pregunta:
¿Hasta que punto es “libre” un software cuyo código
fuente es libre pero que implementa un estándar
propietario? Podremos modificar su código fuente, pero
lo hacemos siempre dentro de la jaula (formato o
protocolo) que define el estándar propietario, que no
controlamos y al que estamos obligados a ceñirnos.
Podría darse el caso de que el dueño del estándar
propietario ponga trabas, o impida, el desarrollo del
producto libre. Un software “libre”, en el sentido más
general de la palabra, debe basarse en estándares libres.
Actas del II congreso javaHispano
91
4.1 ¿Qué es un estándar libre?
Dentro de la comunidad del SL no hay una definición
globalmente aceptada sobre qué es un estándar libre.
Así, por ejemplo, ni Free Software Foundation (FSF) [6],
ni Open Source Iniciative (OSI) [7] proporcionan criterios
bien definidos para determinar si un estándar es o no
libre. El mayor esfuerzo en la definición de estos
criterios, dentro del mundo del Software Libre, está
liderado por Bruce Perens y desarrollado en el entorno
de Debian. Perens es uno de los principales líderes del
movimiento del SL, en el cual milita desde 1987;
cofundador de Open Source Iniciative es el autor del
manifiesto “Open Source Definition”. Actualmente hay
un borrador con el trabajo realizado hasta la fecha en
[8].
Otro trabajo interesante es el de Ken Krechmer, “The
Principles of Open Standards” [9]. Krechemer, a
diferencia de Pernes, no es un activista del Software
Libre. Su trabajo está orientado a organizaciones
sensiblemente diferentes del JCP, como ISO o ECMA, que
también estandarizan objetos materiales y tangibles, y
no sólo software, algo que se puede replicar y trasladar
de un punto a otro del planeta a coste prácticamente
cero. Por ello no todas las ideas que hay tras los criterios
de Krechemer poseen sentido en el mundo de lo
intangible. Sin embargo hay algunas ideas que sí
podemos tomar prestadas, ideas que no están presentes
entre las de Perens y que, desde el punto de vista de
autor, son de gran importancia en la definición de qué es
un estándar libre; dicho de otro modo: son carencias del
borrador de Perens.
Al margen de estos trabajos existen varios portales
dedicados a los estándares libres, pero ninguno de ellos
define de un modo concreto y preciso qué es un
estándar libre, y se hallan muy lejos de tener un peso
equivalente a que FSF u OSI tienen sobre el SL.
OpenStandards.org [10] es una web creada a principios
del 2000 con un propósito no muy claro y que en la
actualidad está abandonada, sin que nunca haya tenido
actividad real. OpenStandards.net [11] es un escaparate
de noticias de otros sitios web y un almacén de enlaces
que carece de contenidos propios. Mención a parte
merece Free Standards Group [12], dedicado a crear
estándares para el mundo Linux. Poseen certificaciones
de gran reconocimiento, como LSD (Linux Standard
Base), OpenI18n, OpenPrinting, etc. Sin embargo sus
interesantes objetivos, crear estándares para Linux, no
coinciden con los que se persiguen en este trabajo.
Los criterios sobre los que trabaja Perens constituyen el
intento más serio y consensuado de la comunidad del SL
para definir qué es un estándar libre, por ello los
emplearemos como base del análisis que realizaremos.
No obstante en este estudio añadiremos algunas ideas
del trabajo de Krechmer a los criterios de Perens, por
considerar que éstas los complementan.
5 ¿Está la plataforma Java compuesta por estándares libres?
En este apartado analizaremos el proceso de creación y
mantenimiento de los JSR así como las condiciones bajo
las cuales se puede acceder a ellos e implementarlos,
para determinar si Java puede o no considerarse un
estándar libre.
5.1 Criterios de Perens
El borrador creado por Pernees [8] en colaboración con
el entorno de Debian define una serie de requerimientos
que debe cumplir un estándar para considerase un
“estándar libre” (open standard). Además identifica una
serie de buenas prácticas, a modo de recomendaciones,
para los estándares libres, buenas prácticas que no se
exigen para poder considerarse como libre. Analizaremos
punto por punto cada uno de los requerimientos
descritos en este documento. En las siguientes
subsecciones el contenido en cursiva es un resumen, que
no una traducción, del contenido de cada uno de los
puntos del documento de Perens.
5.1.1 Disponibilidad:
Un estándar libre debe estar disponible para leer e
implementar por cualquier individuo u organización. Se
recomienda como buena práctica que la documentación
y la IR del estándar estén disponibles en Internet. No se
hace ninguna referencia a la licencia del código fuente
de la IR.
La documentación de cualquier JSR está disponible en
Internet, y cualquiera puede descargársela e
implementarlo. En cuanto a la IR de referencia de un JSR,
y su test de compatibilidad, son siempre accesibles de un
modo gratuito para un individuo u organización sin
ánimo de lucro.
Actas del II congreso javaHispano
92
5.1.2 Maximizar la elección del usuario final
Los estándares abiertos deben generar un mercado
competitivo permitiendo un amplio rango de
implementaciones, con precios que varíen “desde muy
altos a nulos”.
Evitar el “vendor lock-in” es uno de los principios básicos
sobre los que se ha construido la plataforma, así como
una de sus principales ventajas frente a otras tecnologías
propietarias. En la práctica, efectivamente podemos
encontrar costosas implementaciones empresariales de
las especificaciones (Ej.: BEA WebLogic, IBM WebSphere
~100.000 $ por CPU) e implementaciones libres
completamente gratuitas (Ej.: JBoss, JOnAS, Gerónimo).
5.1.3 Sin Royalties:
Debe ser posible implementar una especificación sin
pagar royalties. Para garantizar que esto será siempre así
(evitar posibles patentes submarinas) cualquier patente
incluida en el estándar debe poderse implementar de un
modo totalmente libre de royalties. Perens acepta que la
certificación del estándar pueda requerir pagar, y
recomienda (aunque no exige) proporcionar un camino
para la auto-certificación.
Todas las patentes involucradas en un JSR deben
“garantizar una licencia perpetua, no excluyente,
mundial, sobre la cual nunca se podrá exigir ningún
pago (fully paid-up), libre de royalties e irrevocable” [13]
a quien las implemente.
La certificación de compatibilidad requiere pagar cuotas
a Sun, pero los TC son accesibles gratuitamente para
individuos y organizaciones sin ánimo de lucro [13], por
lo que es posible auto-certificarse. Además existe un
camino para la certificación gratuita de
implementaciones libres y sin ánimo de lucro: el servidor
de aplicaciones J2EE JOnAS se certificó completamente
gratis, y Geronimo lo hará en breve en las mismas
condiciones.
El código de la IR (y del TC) debe estar disponible bajo
condiciones RAND (Reasonable And Non Discriminatory)
[10], o más permisibles, para basar en ella otras
implementaciones. Como se discute en [5] las
condiciones RAND en la práctica pueden no ser
razonables y sí suelen ser discriminatorias; sin embargo
que el código esté disponible bajo condiciones no más
restrictivas que RAND es mejor que ningún tipo garantía.
5.1.4 No discriminación:
No se puede favorecer a ninguna implementación sobre
otras: la certificación ha de ser justa y sólo basarse en
motivos tecnológicos.
En la actualidad sólo se tienen en cuenta motivos
tecnológicos para pasar los TC. Esto no fue así hasta
hace unos meses, cuando entró en vigor JCP 2.6; hasta
entonces una implementación libre de un UJSR no podía
certificarse por el tipo de licencia de su código fuente.
5.1.5 Extensión o subconjunto:
Los estándares libres se pueden extender u ofrecer en
subconjunto, aunque en este caso puede negarse la
certificación.
Es posible implementar subconjuntos, superconjuntos, o
modificaciones de las especificaciones del JCP; por
ejemplo en [14] se recogen múltiples variaciones del
lenguaje Java, bytecode y/o JVM, normalmente
desarrolladas con fines científicos. En general no es
posible certificar subconjuntos ni modificaciones de las
especificaciones, aunque sí superconjuntos de ciertas
especificaciones. Así, por ejemplo, los distintos
vendedores de servidores de aplicaciones J2EE suelen
extender la especificación con características propias
para diferenciar sus productos de los de la competencia.
5.1.6 Defensa contra prácticas predatorias
Este punto es una concesión más que un requerimiento.
Los estándares libres pueden protegerse contra
modificaciones por parte de terceras partes para evitar
prácticas “embrace-and-extend”. Este tipo de prácticas
las suele llevar a cabo el vendedor predominante de un
estándar, el cual crea una extensión del estándar no
compatible con el original e impide mediante patentes y
copyright que los demás vendedores implementen estas
extensiones. Esto provoca que las implementaciones de
los demás vendedores no sean compatibles con las del
vendedor predominante y permiten a éste último crear
un monopolio sobre el estándar.
Dentro de los JSR, o del JCP, no se identifican defensas
contra ese tipo de prácticas. No obstante la certificación
de las implementaciones, así como la propiedad de Sun
de la marca “Java” y términos relacionados, que sólo se
pueden emplear tras certificarse, defienden a la
plataforma de estas prácticas. Esto es lo que sucedió en
1998 con la JVM de Microsoft [15], que no seguía la
Actas del II congreso javaHispano
93
especificación de J2SE, por lo que Sun llevó a la
compañía de Redmon a los tribunales.
5.2 ¿Se cumplen los criterios de Perens?
El draft de Perens es el intento más serio y consensuado
que la comunidad del SL ha realizado para definir qué es
un estándar libre. Como se ha mostrado, las
especificaciones de la plataforma Java cumplen todos los
requerimientos de este borrador, todas sus buenas
prácticas y en varias ocasiones incluso van más allá, por
ejemplo, ofreciendo una certificación completa gratuita
a implementaciones sin ánimo de lucro.
En base a los criterios de Perens la plataforma Java, un
conjunto de especificaciones que definen una serie de
tecnologías, es libre. Esto no quiere decir que todas las
implementaciones de cada tecnología se distribuyan bajo
una licencia libre, pero la licencia de las
implementaciones nada tiene que ver con la libertad del
estándar; de hecho Perens nunca la menciona en sus
criterios.
Estos requerimientos no son suficientes, desde el punto
de vista del autor, para considerar un estándar libre.
Podrían definir un estándar “accesible”: puedo acceder a
él e implementarlo libremente, pero esto no es
suficiente: la única libertad que se garantiza es la de
seguir un camino (el marcado por el estándar) que
podría haber sido definido por una sola parte interesada
(empresa, organización o individuo), y no por todas las
partes interesadas. Por ello, para completar este estudio,
añadiremos dos criterios del documento de Krechmer a
los definidos por Perens.
5.3 Criterios de Krechmer
Krechmer, a diferencia de Perens, es un investigador del
laboratorio de Palo Alto, en California, USA. Su trabajo
ganó un premio en la competición “World Standards
Day” de 1998 y fue publicado en la revista Standards
Engineering. Este trabajo aborda la problemática de los
estándares en general, no sólo informáticos, y se escribió
pensando en organizaciones muy diferentes del JCP,
como ISO o ECMA, que también estandarizan objetos
materiales, y no sólo software. Por ello no todas las ideas
que hay tras los criterios académicos poseen sentido en
el mundo de lo intangible, y algunos requieren cierta
adaptación.
No obstante, en líneas generales, los criterios de
Krechemer incluyen a los de Perens y añaden más
requerimientos. Veamos cuales son los requerimientos
añadidos.
5.3.1 Apertura:
Todas las partes interesadas pueden participar en el
desarrollo del estándar.
Cualquier individuo puede formar parte del JCP, ya bien
sea a título personal o como representante de una
empresa u organización. En el caso de las organizaciones
sin ánimo de lucro e individuos esta participación es
completamente gratuita. Por otro lado, incluso sin
participar en el JCP se puede participar en la definición
de un único JSR, e incluso sin estar directamente
involucrado en el JCP, o en uno de sus JSR, cualquiera
puede acceder a la documentación de todos los JSR
mientras se están desarrollando, y enviar realimentación
al grupo de expertos.
5.1.6 Consenso y proceso de decisión justo:
En el proceso de definición del estándar todos los
intereses son discutidos y se llega a un acuerdo sin
dominación. Una votación puede ser empleada para
encontrar una solución.
Todos los intereses y contribuciones de las distintas
partes son considerados y discutidos en el JCP, y la
votación, de los CE, es la que finalmente decide qué se
incorpora y qué no se incorpora a las especificaciones
que forman la plataforma.
Sin embargo siempre hay un miembro de Sun
Microsystems en cada CE, que tiene derecho de veto
sobre todos los UJSR, y de los restantes 15 miembros 10
son propuestos por Sun, aunque deben de pasar una
ratificación pública. El PMO, aunque es un mero órgano
administrativo, está compuesto por asalariados de Sun, y
siempre hay un miembro de Sun en el comité de tres
personas que decide si una organización sin ánimo de
lucro puede o no pertenecer gratis al JCP o pasar el TC
sin pagar. Este criterio no se cumple por los privilegios
de uno de los participantes en el proceso de definición
de las especificaciones: Sun Microsystems
La solución a esta falla podría pasar por la gestión del
JPC por un organismo independiente, estilo Apache
Software Foundation. Esto evitaría que la plataforma se
mueva en una dirección que no es la más conveniente
para todas las partes interesadas, por causa de los
Actas del II congreso javaHispano
94
intereses particulares de una empresa. Todos los
miembros de los CE deberían elegirse de un modo
democrático y debería desaparecer el derecho de veto.
6 Implementaciones de Referencia y Software Libre
Resulta evidente que la comunidad del SL se beneficiaría
de poseer un código libre en el que basar sus
implementaciones de las especificaciones, y es deseable
que este código fuese el de las propias IR. Por un lado
éstas son la primera implementación que se desarrolla, lo
que permitiría a la comunidad del SL disponer del código
base rápidamente. Por otro, si las todas las IR fuesen
libres serían testadas por multitud de desarrolladores, lo
que redundaría en una mejora de su portabilidad y su
calidad, y por consiguiente en la portabilidad y calidad
de las demás implementaciones: Jason Hunter afirma
que desde que Tomcat es la IR de los motores de Servlets
se han eliminado muchos bugs de portabilidad en los
motores comerciales.
La licencia de las IR desarrolladas por Sun son la principal
fuente de críticas a Java por parte del mundo del SL. Sin
embargo, como hemos mostrado, Java no es un
software, sino un conjunto de especificaciones. Alguna
de las implementaciones de estas especificaciones son
libres, otras no, pero esto no guarda relación con la
libertad de las especificaciones. Decir que Java no es libre
porque una de las implementaciones de una
especificación no licencia su código fuente mediante una
licencia libre es tan incoherente como decir que el
protocolo http no es libre porque Internet Explorer e
Internet Information Server no son libres.
La IR más polémica es sin duda la de J2SE. Efectivamente
la implementación de Sun no es libre, ni la de IBM,
BlackDown, Bea, etc. Sin embargo Kaffe [16], GCJ [17],
GNU Classpath [18], Japhar [19], Jikes [20], etc. sí son
libres, si bien es cierto que ninguna de ellas tiene en la
actualidad una funcionalidad suficiente para resultar
atractiva a los desarrolladores. En este sentido es la
propia comunidad del SL quien, por la causa que fuere,
está fallando a la hora de proveer una alternativa libre a
este estándar, del mismo modo que esta comunidad ha
creado alternativas libres para otros estándares (Mozilla
y Apache web server en el caso de http, por ejemplo).
En general el SL goza de un excelente estado de salud en
la plataforma Java: a pesar de ser un lenguaje de
programación relativamente reciente es el tercer
lenguaje de programación en Sourceforge, estando muy
cerca de de los primeros, C y C++; y existen una gran
cantidad de implementaciones libres de especificaciones
Java, suficientes para construir una compleja y completa
solución empresarial [21].
Cabe preguntarse porqué siendo la comunidad Java una
de las más prolíficas desarrollando SL las
implementaciones libres del entorno base van tan lentas.
La respuesta posiblemente sea que esta prolífera
comunidad, dado que dispone de varias
implementaciones gratuitas del entorno base que
poseen una calidad contrastada, prefiere centrarse en
desarrollar aplicaciones de más alto nivel, como librerías,
aplicaciones de escritorio, servidores de aplicaciones, etc.
que tienen un beneficio comparativo mucho mayor para
la sociedad. No obstante esta situación podría cambiar
en breve, debido a la intención del gobierno de Brasil de
desarrollar una implementación libre de J2SE [21], que
esperan tener lista para finales del 2005. De cumplirse
este objetivo el año 2005 podría ser el año en que la
comunidad del SL deje a un lado sus reparos y abrace
definitivamente a la plataforma Java.
La IR del J2SE, así como otras IR desarrolladas por Sun,
se distribuyen bajo licencia Sun Community Source
License (SCSL) [22]. Esta licencia permite ver el código
fuente del desarrollo, e incluso modificarlo, pero sólo
bajo ciertas condiciones, las cuales, en esencia, fuerzan a
que el producto modificado siga cumpliendo las
especificaciones de la máquina virtual, bytecode y
lenguaje Java. Su objetivo es evitar que la plataforma se
fragmente por causa de implementaciones del
compilador o máquina virtual no compatibles con las
especificaciones. Sun afirma que esta licencia “toma las
ventajas de los modelos de código abierto y propietarios,
y elimina sus inconvenientes” [22], ya que permite
acceder al código fuente libremente, con todos los
beneficios que para la comunidad y para la propia
plataforma conlleva, siempre que el acceso no atente
contra el principio básico de WORA.
Sin embargo, una vez que un individuo ha accedido al
código licenciado SCSL esta licencia le impide participar
en proyectos de código abierto, ya que el código que ha
visto es propiedad intelectual de Sun y no puede
emplear en proyectos libres lo que en él ha aprendido.
Además, si desea cobrar a sus usuarios por el trabajo que
ha realizado está obligado a pagar cuotas a Sun.
Evidentemente una licencia así dista mucho de poder
considerarse libre.
Actas del II congreso javaHispano
95
¿Es necesaria una licencia de este tipo para algunas IR,
para proteger a la plataforma Java de la fragmentación?
Es una pregunta difícil de responder. Sun afirma que sí;
sin ella algunas empresas podrían aprovecharse de su
posición privilegiada en el mercado para imponer una
versión de la máquina virtual devaluada, o no
compatible con las demás. Este fue el caso de Microsoft,
quien en su día desarrolló una versión de la máquina
virtual y herramientas de desarrollo que permitían
acceder a ciertos servicios de Windows, lo que daba
lugar a crear aplicaciones que sólo funcionaban en este
sistema operativo [15]. Si la plataforma Java hubiese sido
completamente abierta nada hubiese podido detener a
Microsoft. Hoy en día la plataforma estaría fragmentada;
más de la mitad de los desarrolladores Java desarrollan
bajo Windows, y probablemente buena parte de ellos
emplearían las herramientas de Microsoft. Según Sun
SCSL vela para que esto no suceda.
Sin embargo Microsoft, u otra gran empresa, y la
comunidad de SL tienen recursos suficientes para
implementar la máquina virtual desde cero, sin
necesidad de basarse en ningún código. Es posible partir
de las especificaciones del lenguaje y máquina virtual
[24, 25] e implementar una variante de Java sin basarse
en ningún código, con lo que esta licencia pierde
bastante sentido.
Por otro lado no hay ningún indicio de fragmentación
dentro de la comunidad del SL: las implementaciones
libres de J2SE (y de cualquier otro JSR) siempre tratan de
adherirse a las especificaciones.
Sun a menudo alega que las múltiples distribuciones de
Linux prueban la comunidad del SL no es capaz de
reconocer que hay cosas que si se bifurcan pierden su
valor. Sin embargo parece que Sun se olvida del
tremendo esfuerzo que esta comunidad realiza a través
de Free Standards Group [12] para lograr que todas las
distribuciones de Linux, y otros sistema operativos *nix,
sigan una serie de estándares que garanticen la
compatibilidad entre distribuciones; y es que la
comunidad del SL sabe reconocer y respetar ciertas
cosas qué no se deben fragmentar.
En la práctica no hay indicios de que liberar el código
fuente de las IR suponga un riesgo de fragmentación
para la plataforma. Desde el punto de vista del autor lo
que realmente detiene a Sun es un problema de
naturaleza muy diferente: Sun, como empresa, necesita
Java, y necesita controlar Java a nivel empresarial. Las
plataformas Intel y compatibles, junto con Linux, están
comiendo el terreno de los servidores SPARC y Solaris.
Sun con Solaris10, un sistema operativo de código libre
que sale al mercado con una agresiva campaña de
precios que intenta competir con los servidores Linux de
RedHat, junto con la venta de hardware de bajo precio
basado en la arquitectura x86, va a tratar de cambiar
esta tendencia; pero el éxito de su nueva estrategia no
está garantizado.
En respuesta a esto Sun, una compañía que
tradicionalmente vivía del hardware, está apostando
fuertemente por los negocios basados en el software y
los servicios. El centro del negocio del software en Sun es
Java: recientemente hemos visto como Sun ha cambiado
el nombre de varios de sus productos para incluir el
nombre Java en ellos: Java Entreprise System, Java Studio
Creator, Sun Java System Application Server, Java
Desktop System. En el último la palabra Java se incluyó
en el nombre sólo como marketing, ya que Java Desktop
System es un S.O. de escritorio basado en Linux que
poco tiene que ver con Java. Menos aún tienen que ver
las Sun Java Workstation, unas estaciones de trabajo con
procesadores Opteron que no incluyen ningún tipo de
optimización para Java.
Por otro lado, Sun actualmente obtiene una cantidad de
ingresos considerable por las licencias de las tecnologías
Java; y su control sobre Java puede ayudarle a
posicionarse en un mercado emergente que a medio
plazo alcanzará un notable volumen: J2ME. Esto sin
olvidar el prestigio que le da a la compañía haber creado
y controlar la que actualmente es la principal tecnología
para crear aplicaciones de servidor y para dispositivos
móviles.
Siendo razonable, las empresas no son ONGs, no es
lógico que Sun desperdicie su posición ventajosa,
respecto a sus competidores y no explote una tecnología
en la que ha invertido tanto. A primera vista parece que
el mundo del SL busca un control sobre Java que
probablemente Sun no se pueda permitir ceder en este
momento sin dañar gravemente sus intereses.
Desde el punto de vista del autor existe un punto de
encuentro entre Sun y la comunidad del SL, que puede
permitir al primero seguir controlando Java a nivel
empresarial, y al segundo disponer del código fuente de
las IR. Este punto de encuentro es licenciar el código
SCSL mediante una doble licencia [26]. Un software que
se distribuye mediante una doble licencia se libera bajo
dos licencias, una libre con un “copyleft” [27] muy
fuerte (GPL o similar) y una propietaria. La libre satisfaría
Actas del II congreso javaHispano
96
todas las demandas del mundo del SL, sin embargo no
sería aceptable, en general, para las empresas: éstas
verían su código infectado por el efecto “virus” de la
licencia con copyleft y tendrían que liberar su propio
desarrollo bajo una licencia libre. Dado que muy pocas
empresas estarán dispuestas a ello se verán obligadas a
optar por la licencia propietaria, y de este modo Sun
seguiría obteniendo beneficios y controlando Java a nivel
empresarial.
Al mismo tiempo Sun gana el apoyo de la comunidad del
SL y garantiza que toda distribución de Linux incluya
una JVM. A su vez la comunidad del SL obtiene un
valioso código en el cual basar sus desarrollos; dejaría de
tener reticencias sobre si algún día Sun empieza a cobrar
por Java o cambia sus condiciones de distribución; y
tendría garantías de que Java no se vería afectado por
una hipotética opa hostil, o cualquier otra adversidad,
que pueda padecer Sun y hacer que la propiedad
intelectual relacionada con la plataforma Java cambie de
dueño.
6.1 Unas palabras a favor de Sun
Sun a lo largo de los años ha mostrado, tanto
verbalmente como con sus acciones, que sus privilegios
sobre la plataforma Java son para hacer frente a ataques,
como sucedió en el caso de Microsoft, y no poner trabas
al desarrollo de SL en la plataforma Java. Durante mucho
tiempo Apache violó las normas del JCP, pudiendo haber
sido demandada por Sun. Lejos de ocurrir esto Apache
mantenía una excelente relación con Sun, quien le
apoyaba y financiaba sus desarrollos libres, y todos los
problemas se zanjaron modificando el JCP para legalizar
las actividades (desarrollo de una IR y TC bajo licencias
libres) que Apache llevaba tiempo realizando, pero que
de ningún modo suponían un riesgo para la
fragmentación de la plataforma.
Si bien Sun posee privilegios en el JCP al autor no le
consta ninguna evidencia de que los haya empleado con
otro fin que no fuese proteger la compatibilidad y
portabilidad de la plataforma.
También es evidente que Sun empuja cada vez más la
plataforma Java hacia la liberación: cada nueva versión
del JCP “libera” un poco más la plataforma. El JCP ha
ido evolucionando desde un estado en el que no era
posible realizar IR bajo licencias libres, los JSR podían
contener patentes y Sun poseía notables privilegios,
hasta una tolerancia total con el mundo del SL, al cual
incluso se incentiva dando opción a certificarse
gratuitamente, un rechazo frontal a cualquier patente no
licenciada Royalty Free en los JSR, y una auto-limitación
de los privilegios de Sun.
7 Conclusiones
La plataforma Java, un conjunto de especificaciones que
definen una serie de tecnologías, según los criterios
recogidos en el borrador de Perens, principal esfuerzo
del mundo del SL para definir qué es un estándar libre,
es libre. En este análisis las licencias de las IR, u otras
implementaciones, carecen de relevancia; de hecho
nunca se mencionan estas licencias en el documento de
Perens. Extendiendo estos criterios del modo que al
autor le ha parecido adecuado, se identifica un
problema: los privilegios que Sun posee sobre el JCP. A
pesar de ello no hay constancia de que Sun los haya
empleado con otro fin distinto de velar por la
plataforma.
Liberar el código SCSL bajo una doble licencia (SCSL y
GPL, por ejemplo) beneficiaría tanto al mundo del SL
como a Sun. Haría que Java fuese mejor acogido y
tuviese más apoyos dentro de la comunidad del SL, con
los consecuentes beneficios para la plataforma, y por
extensión para Sun; habría más desarrollos libres en y
para Java; y el código de las IR estaría más chequeado, lo
que incrementaría la portabilidad y fiabilidad de las IR y
de todas las implementaciones que se basan en ellas.
Por su parte Sun seguiría controlando y obteniendo
beneficios de Java a nivel empresarial, ya que la mayor
parte de las empresas no optarían por la licencia libre
por su efecto vírico. Sun podría ganar el apoyo del
mundo del SL sin perder su posición privilegiada en el
mercado Java.
Agradecimientos
Deseo expresar mi agradecimiento a Alvaro-Sánchez
Mariscal, Alberto Molperceres y Martín Pérez. Sin los
trabajos que vosotros realizasteis antes que el mío, sin
las discusiones que hemos mantenido y sin la
realimentación que he recibido de vosotros este trabajo
probablemente nunca habría visto la luz.
Actas del II congreso javaHispano
97
Referencias
[1] Java Community Process, http://jcp.org.
[2] Bill Venners. Inside the Java Virtual Machine. McGraw-Hill Osborne Media, 2000.
[3] JCP 2.6, http://jcp.org/en/jsr/detail?id=215.
[4] Java Specification Request, http://jcp.org/en/jsr/overview.
[5] Robin Cover. Patents and Standars, http://xml.coverpages.org/patents.html.
[6] Free Software Foundation. http://www.fsf.org.
[7] Open Software Iniciative. http://www.opensource. org.
[8] Bruce Perens et al. Open Standards Principles and Practice. http://perens.com/OpenStandards/Definition.html.
[9] Ken Krechmer. The Principles of Open Standars, http://www.ses-standards.org/library/krechmer.pdf.
[10] OpenStandards.org. http://www.openstandards.org/.
[11] OpenStandards.net. http://www.openstandards.net/.
[12] Free Standards Group. http://freestandards.org/.
[13] Java Specification Participation Agreement, http://jcp.org/aboutJava/communityprocess/JSPA2.pdf.
[14] Múltiples variaciones del lenguaje Java, bytecode y JVM. http://www.robert-tolksdorf.de/vmlanguages.html.
[15] Declaración de James Gosling en el juicio contra Microsoft por su implementación fraudulenta de la JVM. http://java.sun.com/lawsuit/82198gosling.html.
[16] Kaffe, http://www.kaffe.org.
[17] GCJ, http://gcc.gnu.org/java/.
[18] Classpath, http://www.gnu.org/software/classpath/.
[19] Japhar, http://www.japhar.org.
[20] Jikes, http://www-124.ibm.com/developerworks/oss/jikesrvm/
[21] Alberto Molpeceres y Martín Pérez. Arquitectura empresarial y software libre, J2EE. http://www.javahispano.org/articles.article.action?id=70.
[22] Noticia de javaHispano. http://www.javahispano.org/news.item.action?id=673049882
[23] Sun Community Source License (SCSL), http://www.Sun.com/software/communitysource.
[24] Tim Lindholm y Frank Yellin. The JavaM Virtual Machine Specification. Addison Wesley. 1999.
[25] J. Gosling, B. Joy, G. Steele, G. Bracha. The Java Language Specification. Addison Wesley.
[26] Mikko Valimaki, Dual Licensing in Open Source Software Industry. http://www.soberit.hut.fi/~msvalima/dual_licensing.pdf.
[27] What is copyleft? http://www.gnu.org/copyleft/copyleft.html.
Actas del II congreso javaHispano
98
Caso de uso: Empleo de tecnologías J2EE para el desarrollo de una plataforma
Rafael Pedraza Carmona
[email protected] Alberto Planas Domínguez
SEIRC/CESEAND [email protected]
Antonio Navarro González [email protected]
Benjamín de la Fuente Ranea
Jose David Fernández Rodríguez
Abstract
En esta presentación se pretende explicar la experiencia
que ha supuesto el desarrollo de una plataforma para la
gestión de ofertas y demandas tecnológicas, desarrollada
para dos organismos dependientes de la Consejería de
Innovación, Ciencia y Empresa de la Junta de Andalucía. El
objetivo principal de esta plataforma es servir de nexo de
unión entre aquellas empresas, grupos de investigación u
otros organismos que ofrecen algún producto con marcado
carácter innovador y aquellas otras que pueden ser
consumidores de estas tecnologías.
Las características más reseñables de este proyecto son el
uso en exclusiva de tecnologías de código abierto, el
empleo de las especificaciones J2EE para el módulo de
servidor (basado en JBoss, Tomcat, Axis, Lucene,...), el
innovador sistema de seguridad que facilita el acceso
segmentado a la información, un workflow para el control
de distintos grupos de trabajo y el desarrollo de una
interface de cliente Java basado en la plataforma Eclipse 3.
La conjunción de todas estas tecnologías, con el lenguaje
Java como hilo conductor, puede suponer un importante
punto de referencia para otros desarrolladores que
pretendan alcanzar un alto nivel tecnológico, basándose
para ello en la potencia y seguridad que aportan los
desarrollos de código abierto.
Keywords: J2EE, JBoss, JavaCC, Eclipse, Lucene, Hylafax, Software Libre, Seguridad.
1 Salto tecnológico con software libre
Properly Software cuenta en la actualidad cuenta con
una plantilla de diez personas, de las que la mitad
pertenecen al Departamento de Desarrollo. Los primeros
productos se realizaron exclusivamente con tecnología
de Microsoft (Visual Basic for Applications); si bien al
principio nos beneficiamos de la facilidad y simplicidad
de esta tecnología, pronto nos vimos en un callejón sin
salida: acorralados por unas necesidades y
especificaciones crecientes en complejidad y al mismo
tiempo limitados por unas herramientas que no nos
proporcionaban soluciones cuando pretendíamos
obtener algún resultado más allá de los casos de uso
más simples. Problemas de escalabilidad y la
obsolescencia de la tecnología empleada no nos deja
otra alternativa que pegar el salto tecnológico.
A la hora de tomar la decisión de hacia donde mover
nuestro marco de desarrollo los factores determinantes
para elegir J2EE y software libre, fueron razones
económicas (soluciones de Oracle, IBM y BEA son
excesivamente caras en un principio) y tecnológicas
(posibilidad de acceder al código y modificarlo, creciente
madurez de la tecnología, masa crítica de
desarrolladores, Java como lenguaje común para
aplicaciones de servidor y de cliente, presencia de J2EE
en la industria frente a otras soluciones).
Esta apuesta de futuro tenía que como consecuencia el
destinar recursos en tiempo y económicos a
investigación, partida para la que hasta ese momento no
había existido presupuesto.
2 Nuestro primer cliente: descripción del proyecto
Gracias a nuestro trabajo de investigación durante más
de un año, tenemos la opción de presentar una oferta al
Instituto de Fomento de Andalucía para el desarrollo de
una aplicación para la gestión de entidades y
transferencia de tecnología, así como herramientas que
Actas del II congreso javaHispano
99
permitan obtener conclusiones estadísticas de los datos
cruzados.
Este proyecto consta de cuatro apartados principales:
• Gestión de entidades. Consideramos entidades las
empresas, los centros tecnológicos, grupos de
investigación y organismos. De cada entidad se
guarda información sobre su localización, una
descripción codificada de su actividad, personas de
contacto, proyectos en los que participan y derechos
de propiedad.
• Gestión de documentos de información
tecnológica. Aquí se almacenan y clasifican
documentos en formato XML validados por sus
respectivos esquemas XML y se transforman a HTML
mediante trasnformadores XLST.
• Herramienta de distribución de información. El
objetivo de ésta es la entrega selectiva de aquellos
documentos tecnológicos a aquellas entidades que lo
requieran o que puedan serles de interés. Los medios
de envío son el correo electrónico y el fax.
• Gestión de expedientes de patentes y marcas.
Este apartado está relacionado con las entidades y
almacena la información relativa a las diversas
actuaciones que se llevan a cabo con las mismas en
relación al patentado o registro de tecnologías.
Del análisis de requerimientos de la plataforma a
desarrollar determinamos las siguientes necesidades
principales:
• Seguridad: Además de los servicios de seguridad
proporcionados por el sistema operativo (firewall,
cifrado de comunicaciones por SSL) y de la base de
datos, nuestra plataforma aporta la posibilidad de
definir políticas de particionamiento basadas en los
datos (seguridad semántica).
• Variedad de clientes: Serán consumidores de los
servicios de la plataforma clientes web y clientes ricos
conectados por Internet con múltiples escenarios de
velocidad de conexión, proxies, firewalls y routers.
• Extensibilidad: Se prevé la incorporación de nueva
funcionalidad al sistema conforme se produzca la
implantación de los servicios existentes.
• Escalabilidad: Se plantea la necesidad de atender
solicitudes de clientes en un número creciente y al
mismo tiempo se espera un incremento considerable
de la información manejada.
3 Tecnologías de servidor
La tecnología J2EE abarca desde JDBC hasta JSP y los
EJBs. Toda esta cantidad de tecnologías está muy bien
documentada en la literatura técnica. Nosotros hemos
decidido usar EJBs, SOAP, JAAS, JNDI, JMX y Mbean.
Usamos el contenedor de aplicaciones libre JBoss 3.2.x.
Hemos tratado de seguir el estandar a traves de sus
BluePrints, pero en los escenarios donde JBoss o AXIS
nos proporcionaba una alternativa más limpia o
eficiente, no hemos dudado en hacer uso de ellas. Estos
casos pueden resumirse en:
• Consultas dinámicas. El cliente debe poder lanzar
consultas atendiendo a múltiples criterios de
búsqueda. Los mecanismos que proporciona J2EE
(findBy y select) no son lo suficientemente flexible.
JBoss propone una alernativa: DinamycQL. Podemos
construir la sentencia SQL que deseemos a partir de
los datos suministrados por el cliente.
• Modelo de Seguridad. El modelo basado en roles
no permite expresar restricciones basadas en datos.
Poder impedir el acceso de un usuario a las entidades
de una provincia determinada no es expresable con
una política de roles (ni declarativa ni programada).
Diseñamos un evaluador de expresiones (usando
gramáticas JJTree en JavaCC) que son invocados a
nivel de Beans por los SecurityProxy de JBoss. Estos
autorizan (o no) al usuario a retirar este dato.
• SOAP por HTTPS. Modificando WSDL4Java y
parametrizando los stubs generados por esta
herramienta de AXIS, logramos que el protocolo
XML-RCP SOAP viaje por un canal cifrado.
• Extensión del modelo de paso de parámetros de
configuración. La especificación EJB facilita un
medio de proporcionar valores de configuración
publicados en el directorio JNDI a las beans, sin
embargo este mecanismo requiere de un redeploy de
la aplicación ante un cambio de estos valores. Para
resolver esta problemática, además de otras
asociadas al tipo de datos que podemos gestionar,
hemos desarrollado un mecanismo mediante una
MBean que lee los valores de configuración desde un
fichero XML.
Actas del II congreso javaHispano
100
3.1 Seguridad
3.1.1 Requerimientos
Un requisito de la aplicación que estamos desarrollando
es la de disponer de un mecanismo que permita
controlar las acciones que un usuario puede realizar
sobre los datos del sistema. Necesitamos restringir el
acceso de un usuario a un conjunto de campos de un
bean de entidad y necesitamos limitar el conjunto de
beans de entidad accesibles por ese usuario.
La primera restricción acotará las acciones que se pueden
realizar en un campo determinado p.ej: el usuario U
puede tener acceso de lectura a un campo pero no lo
puede modificar. Este tipo de limitaciones casan
perfectamente con el concepto de seguridad descrita en
la especificación J2EE [1]. En este modelo el usuario tiene
o no tiene derecho de llamada sobre un método de un
bean atendiendo a lo declarado en el fichero descriptor
del deploy.
Por contra, limitar el conjunto de beans de entidad sobre
los que un usuario debe tener conocimiento no puede
ser expresado por medio de la seguridad declarativa de
J2EE. El estándar no nos proporciona ninguna forma de
indicar que un usuario concreto, al solicitar la lista de
entidades de nuestro sistema mediante la llamada al
método getEntityList() localizado en un SLSB, esta nos
devuelva solo aquellas entidades que pertenezcan a una
provincia determinada. Es decir, no tenemos la
capacidad de expresar el dominio de datos de los
usuarios.
En la Tabla 1 podemos ver un resumen de las acciones
que debemos controlar y el receptor de dicha acción.
Tabla 1: Acciones básicas de un usuario
Acción Receptor
Acceso (S) Entidad
Lectura (R) Dato miembro
Escritura (W) Dato miembro
Consulta (X) Dato miembro
Las operaciones R,W y X se realizan sobre campos de una
entidad. Podemos leer el contenido de un campo,
modificarlo o lanzar una consulta con este campo como
criterio de búsqueda. La operación S determina qué
entidades pertenecen al dominio de datos de un usuario.
Podemos transformar estas acciones a predicados de la
forma expuesta en la Tabla 2. De esta manera un usuario
U puede leer el contenido de un campo F de una entidad
E si S(U,E) ^ R(U, F).
El predicado S, para poder ser evaluado sobre E necesita
de un conjunto de cláusulas o expresiones de restricción
de U sobre E. Es decir, tenemos que indicar el conjunto
de condiciones que una vez evaluadas nos digan si E
pertenece al dominio de este usuario.
Tabla 2: Predicados
Acción Descripción
S(U, E) Entidad E en dominio
R(U, F) Permiso de lectura de F
W(U, F) Permiso de modificación de F
X(U, F) Consultar por campo F
Repasados los requerimientos de nuestro sistema de
seguridad vemos que necesitamos de:
1. Una gramática para expresar restricciones de
dominio.
2. Un evaluador de restricciones.
3. Una descripción de los metadatos donde podemos
poner restricciones semánticas y permisos.
4. Un lugar estratégico en la arquitectura de nuestro
servidor donde determinar los permisos de usuario y
rechazar o aceptar la solicitudes del mismo.
5. Un modelo de datos donde alojar metadatos,
restricciones y usuarios.
Figura 1: Compilación de las restricciones.
Actas del II congreso javaHispano
101
3.1.2 Restricciones y Gramáticas
Para poder expresar las restricciones de dominio de
manera adecuada hemos diseñado una gramática
sencilla que sea fácil de evaluar y de convertir a clausulas
WHERE en EJBQL por razones de optimización de las
consultas.
Hemos usado la herramienta JJTree de JavaCC [2] para
generar árboles AST evaluables mediante un recorrido en
postorden del mismo. Realmente el procedimiento de
evaluación se ha optimizado traduciendo el árbol AST a
una lista evaluable por medio de una pila (Fig. 1).
La gramática propuesta permite expresar restricciones
del tipo:
#contato.apellidos LIKE “Delgado%”
#facturacion > #empleados * 1000
Tenemos variables que vienen prefijadas por el símbolo
#, operadores, constantes, expresiones regulares, fechas,
booleanos, números y listas. Es decir, es lo
suficientemente completa para expresar un conjunto
importante de restricciones.
Para simplificar el uso por parte del administrador
encargado de definir estas restricciones de manera
dinámica, hemos incorporado la variable sin nombre (#)
para indicar 'el campo actual'. De esta manera la
expresión (# > 10) AND (# < 100) tiene un significado
diferente si se aplica al campo 'número de empleados' o
'edad'.
Con JJTree definimos los tokens que deberá encontrar el
analizador lexicográfico, la gramática que el parser
descendiente recursivo de JavaCC reconocerá, y las reglas
de creación del árbol AST. Especificar una gramática en
JavaCC es sencillo siempre que mantengamos en mente
algunas reglas sencillas como la de ir definiendo las
reglas de producción en orden inverso a la precedencia
de los operadores. Es decir, debemos indicar primero las
reglas de producción que tienen operadores de más baja
precedencia.
Recorrido el árbol en postorden (subárbol izquierdo -
subárbol derecho – raíz), y puesto los nodos del árbol de
manera lineal, la tarea del evaluador queda simplificada.
3.1.3 Modelo de datos
Disponemos ya de una herramienta para evaluar
restricciones. Para realizar su tarea el evaluador necesita
de los valores de las variables de su expresión. Estos
valores deben encontrarse en los beans de entidad sobre
los cuales estamos imponiendo restricciones. Estas a su
vez dependen del usuario que realiza la acción.
Vemos pues que necesitamos formalizar y almacenar las
relaciones y los datos (y metadatos) del modelo de
seguridad a través de un modelo de datos. Usaremos
diagrama Entidad Relación de la Figura 2 para guiarnos.
Figura 2: Diagrama ER de la seguridad.
Figura 3: Arquitectura general del sistema.
Hay restricciones sobre una entidad y sobre campos de la
entidad. Necesitamos recopilar en el sistema toda la
información que tengamos sobre los objetos susceptibles
de ser controlados. Hemos denominado a dichos objetos
del sistema, PDO (Persistent Data Object, Objetos de
Datos Persistentes). Cada PDO tiene un conjunto de
campos. Así por ejemplo, el PDO de un Contacto tiene
los campos Nombre, Dirección y Teléfono sobre los que
podremos poner restricciones y permisos.
Estos metadatos pueden generarse automáticamente
usando herramientas como XDoclet [3] o bien
introducidos en el sistema de manera manual. Por
desgracia generan una evidente redundancia, puesto
Actas del II congreso javaHispano
102
que los datos securizables son los mismos que tenemos
en los Beans de Entidad (CMP y BMP), que a su vez
tienen una contrapartida en el modelo de datos global
de la aplicación.
Necesitamos almacenar los datos de los usuarios (login,
nombre, contraseña...) para poder activar el mecanismo
de autentificación y autorización de J2EE. Cada usuario
pertenece a un grupo de usuarios y este a su vez dispone
de varios roles dentro del sistema.
Almacenamos los datos correspondiente a los permisos
de lectura, escritura y consulta de los campos y las
restricciones que determinan el dominio de datos de un
usuario.
Los permisos se relacionan con un grupo de usuarios y
los campos de los PDOs. Por tanto para cada
grupo/campo indicamos los permisos R,W,X.
Las restricciones de dominio asocian el grupo de usuario
con el conjunto de restricciones sobre un PDO en
concreto. Ahora tenemos una estructura donde
recuperar la lista de restricciones que tenemos que
evaluar para saber si un usuario del sistema tiene o no
tiene permiso de acceder a un Bean de Entidad en
concreto.
3.1.4 JBoss y los SecurityProxy
Llegados a este punto disponemos de un evaluador de
restricciones y de un sistema que nos devuelve la lista de
restricciones a evaluar cuando un usuario quiere acceder
a una entidad. La cuestión ahora es en qué lugar de la
arquitectura de nuestro sistema sería más conveniente
realizar estas evaluaciones para aceptar o denegar la
acción.
La Figura 3 es una sobresimplificación de la arquitectura
del sistema. Vemos tres oportunidades donde colocar
nuestro mecanismo de seguridad.
Si lo colocamos en la capa más externa de todas, la capa
SOAP, por cada petición de un usuario, el sistema de
seguridad deberá atravesar toda la arquitectura para
recuperar los permisos, las restricciones y los datos
necesarios para evaluar dichas restricciones. Es evidente
que es poco óptimo y complejo. Además estamos
incorporando lógica en la capa de comunicaciones.
Incorporar la seguridad semántica dentro de la Session
Façade es factible. Estamos completamente dentro del
servidor, dentro del segmento de nuestra aplicación
donde recaen las decisiones de negocio. El problema
está en que la seguridad quedaría difuminada y
repartida en cada uno de los métodos de esta capa. La
dificultad en el mantenimiento e implementación sería
desaconsejable. Si bien tenemos a nuestra disposición
patrones de diseño como los Decorators y los Proxy que
pueden ayudarnos, quizás hay una alternativa mejor.
Si lográramos poner la seguridad en la capa de
persistencia, donde encontramos lo BMPs y CMPs de
nuestro sistema, la seguridad sería óptima. Ningún
acceso a los datos (excepto claro está, accediendo
directamente a la base de datos) podría saltarse las
comprobaciones de seguridad, y la situaríamos al mismo
nivel que la seguridad nativa de J2EE, mano a mano y
complementándose.
Para lograr este nivel de integración necesitaremos echar
mano de los interceptores. Aquí AOP nos puede ayudar,
pero decidimos, puesto que usamos JBoss 3.2.x, acceder
a las facilidades en la arquitectura de interceptores que
nos brinda este contenedor J2EE de código libre.
Los SecurityProxy [4] son unos interceptores a nivel de
beans que permiten separar la seguridad del resto de los
procedimientos. Este tipo de proxy es llamado antes de
cada invocación a los métodos del bean, es por tanto
una gran oportunidad para abortar esta llamada en caso
de que no se cumplan las restricciones de seguridad de
nuestro sistema. Hay dos tipos de SecurityProxy, uno que
tiene dos métodos (uno que se ejecuta para cada
llamada a los métodos Home (create, findBy y selects) y
otro para los métodos del objeto (postCreate, set, get y
remove)) y otro tiene el mismo interfaz que el CMP o
BMP que estamos interceptando.
Este modelo tiene la venta de poder navegar por las
relaciones de manera natural. Podemos establecer una
restricción sobre un elemento relacionado con el que
entramos tratando. Así puedo decir, por ejemplo, que un
usuario del sistema no puede acceder a las empresas que
tenga al menos un cliente de nombre Raul. Restricciones
así demuestran la potencia del planteamiento del
modelo, si bien presenta un problema: demasiadas
intercepciones pueden penalizar el rendimiento.
3.1.5 Optimizando el modelo
Si disponemos de un conjunto de restricciones
expresadas en un lenguaje similar al EJBQL, al menos en
lo que respecta a la cláusula WHERE ¿por qué no
construir una expresión que aplicada a cualquier
Actas del II congreso javaHispano
103
consulta del sistema nos traiga el subconjunto de datos
accesibles por el usuario?
De esta manera limitamos el númer de excepciones
asegurando siempre la corrección del modelo. Aun sin
esta optimización el sistema impide desde su base el
acceso a los datos no autorizados.
Esta optimización hay que entenderla como tal: es la
única separación del modelo de seguridad, ya que hasta
ahora todo el asunto estaba localizado en los
interceptores.
4 Tecnologías auxiliares
Además de las tecnologías que nos proporciona J2EE,
hemos necesitado usar otras herramientas para cumplir
con las especificaciones de la plataforma desarrollada.
Esta tarea la hemos llevado a cabo mediante el
mecanismo de integración por Mbeans proporcionado
por JBoss. Los casos en los que ha sido necesaria esta
integración son:
4.1 Lucene
El modelo de datos de Lucene gira alrededor del
concepto de documento: un conjunto estático de
campos de texto (accesibles por nombre), que constituye
la unidad de indexación. En la compilación del índice se
le añaden documentos y en la búsqueda por palabras se
obtienen los documentos que satisfacen las condiciones
de las consultas.
Bajo esta funcionalidad de alto nivel hay un sofisticado
sistema cuyo conocimiento resulta fundamental cuando
se requiere (como en nuestro caso) modificar el sistema
en cierto grado para adaptarlo a nuestras necesidades.
En este sentido, ha resultado fundamental la posibilidad
de consultar el código fuente para poder comprender
Lucene de un modo más completo.
Para el sistema de notificación de modificaciones en los
documentos indexados se dispone de un acceso a la
capa de persistencia usando el patrón de diseño Session
façade. Aprovechamos la característica de proxys de
JBoss para interceptar las llamadas de actualización a los
beans. Este enfoque sugiere una aproximación a la
programación orientada a aspectos.
4.2 Hylafax
Hylafax es un servidor para el envío y recepción de faxes,
de código libre y amplio uso. Acepta peticiones de
transmisión vía sockets mediante un protocolo derivado
de FTP. Para facilitar la comunicación de nuestra
aplicación Java con Hylafax existen librerías también de
código libre. Nuestra interacción con el servidor Hylafax
se reduce a dos puntos muy concretos: el envío de faxes
y la determinación de si han sido enviados
correctamente.
Utilizamos HTML2PS para la transformación de nuestros
documentos HTML (generados a partir de los
documentos de información tecnológica mediante
transformadores XSLT) en documentos PostScript.
Para la comprobación del estado de los envíos usamos
una técnica de polling a intervalos regulares de tiempo.
5 Tecnologías de cliente
Inicialmente consideramos dos alternativas como
framework para la creación de nuestra aplicación de
escritorio. Una era NetBeans Platform, que permite la
construcción de aplicaciones Java con componentes
Swing. Descartamos esta solución debido a cuestiones
de rendimiento, consumo de memoria y aspecto general.
La otra alternativa, y la elegida, era Eclipse. Se basa en el
uso de componentes nativos del sistema operativo
cuando estos están disponibles (SWT) y un modelo muy
sencillo de datos para los componentes JFace.
5.1 Eclipse Rich Client Platform
Con RCP disponemos de todo un framework para la
escritura de aplicaciones de escritorio. Con el término
Rich Client Platform, la comunidad Eclipse se refiere al
mínimo conjunto de plugins que son necesarios para
construir una aplicación con un interfaz de usuario. Este
conjunto mínimo de plugins se reduce a tan solo dos:
org.eclipse.ui y org.eclipse.core runtime, sin embargo
podemos usar el resto del API ofrecida por Eclipse.
5.2 SWT (Standard Widget Toolkit)
SWT es una librería para crear interfaces de usuario en
Java. Se caracteriza por su integración con los
componentes gráficos del sistema operativo sobre el que
corre, lo que supone una apariencia unificada con el
resto de programas de esa plataforma y una velocidad
de ejecución nativa. Como contrapartida, se plantean
Actas del II congreso javaHispano
104
problemas a la hora de hacer portable el código, aunque
ha sido solucionado bastante elegantemente. La manera
de acceder a los elementos gráficos del sistema
operativo se resuelve usando JNI (Java Native Interface),
accediendo a una serie de métodos escritos en C, que
son los que realmente acceden a los recursos del sistema
operativo. Por tanto, es necesario tener en cuenta dos
aspectos: la elección de las funciones que se proveerán
en la primera capa implementada en C, de manera que
hagan una abstracción genérica de las diferentes
plataformas (Windows, Motif, MacOs, etc…). Esta capa
debe ser tan delgada como sea posible para asegurar
eficiencia y que todo el código del manejo de widgets
esté implementado en Java. El segundo es aportar una
implementación diferente para cada plataforma,
generando una librería dinámica para cada una de ellas,
la cual será importada en tiempo de ejecución por la
librería de SWT mediante System.loadLibrary().
5.3 JFace
JFace es una librería para simplificar las tareas comunes
de la programación de interfaces de usuario. Está escrita
sobre SWT, pero no lo sustituye.
Las utilidades que suministra están clasificadas como
sigue:
• Viewers: manejan las pesadas tareas de rellenar,
ordenar, filtrar y actualizar los widgets.
• Acciones y Contribuciones: incorporan una
semántica para definir acciones de usuario.
• Imágenes y fuentes: patrones de manejo de
recursos de interfaces de usuario.
• Diálogos y wizards: estructuras para definir
interacciones complejas con el usuario.
6 Conclusiones
6.1 Consideraciones económicas
Debido al tamaño de nuestra empresa y a los limitados
recursos económicos disponibles para tareas de
investigación, el uso de software libre ha supuesto un
hito definitivo en nuestro salto tecnológico. Es de
resaltar que partíamos de una filosofía de programación
procedural, dependientes por completo de una solución
cerrada, sin posibilidades de crecimiento, desarrollada y
soportada por una única empresa. El software libre nos
ha permitido ser capaces de ofrecer un tipo de
soluciones al nivel de empresas de mucha más
embergadura y con un presupuesto muchísimo mayor.
Si hubiésemos pretendido implementar todo nuestro
desarrollo con software propietario, el coste hubiese
superado con creces nuestras posibilidades si sumamos
sistema operativo, gestor de bases de datos, servidor de
aplicaciones, servidor de fax, entorno de desarrollo, etc.
Por el contrario, siempre hemos encontrado una
alternativa libre con unas prestaciones en ocasiones
superiores a las ofrecidas por la alternativa propietaria.
6.2 Consideraciones técnicas
Disponer del código fuente de las herramientas usadas
ha sido, en determinados casos, decisivo para el éxito de
nuestro proyecto. En ocasiones en las que no
comprendíamos una determinada tecnología, la
inmersión en el código fuente nos ha facilitado la
información que no encontrábamos en la
documentación, además de corregir errores (tratamiento
de las conexiones HTTPS en Axis) y extender su
funcionalidad (modelo de documentos de Lucene, uso
de certificados digitales de la SDK de IBM en JBoss).
En nuestro desarrollo hemos empleado herramientas que
nos permiten asegurar características avanzadas en
rendimiento, seguridad, escalabilidad y fiabilidad.
Ejemplos son el SGBD PostgreSQL 7.4, el servidor de
aplicaciones J2EE certificado en su versión 4.0 JBoss, el
sistema operativo GNU/Linux o el contenedor de
aplicaciones web Tomcat 5.0.
6.3 Coste del aprendizaje
La desventaja del software libre radica en que,
generalmente, la documentación existente no suele ser
muy abundante o estar muy actualizada. Esto es debido
principalmente a que los esfuerzos de la comunidad de
desarrolladores se centran en la evolución del propio
código y no en facilitar la transmisión de esa
información. Como ejemplo de muy mala
documentación nombraremos el servidor Hylafax y el API
Java para su manejo, que prácticamente carecía de ella y
nos obligó a estudiar su código fuente para descubrir su
modo de funcionamiento. Por el contrario, el entorno de
desarrollo Eclipse tiene una muy buena y abundante
documentación.
El coste de aprendizaje, por tanto, es elevado pero se ve
compensado por el gran ahorro de costes en licencias.
Actas del II congreso javaHispano
105
Referencias
[1] Sun Microsystems, Inc. J2EE Patfrom Specification. http://java.sun.com/j2ee/j2ee-1_4-fr-spec.pdf
[2] Java Compiler Compiler. https://javacc.dev.java.net/
[3] XDoclet. http://xdoclet.sourceforge.net
[4] Customized EJB security in JBoss. JavaWorld 15/02/2002 http://www.javaworld.com/javaworld/jw-02-2002/jw-0215-ejbsecurity.html
Actas del II congreso javaHispano
106
Integración Continua utilizando herramientas Open Source
Jesús Pérez Sánchez www.agile-spain.com / Germinus
Abstract
En un entorno en el que el sector de servicios se ha vuelto
mucho más complejo, ser eficientes se convierte en una
exigencia para poder sobrevivir en este mercado. La
experiencia en el desarrollo de software a medida nos dice
que es habitual emplear mucho tiempo en integrar el
trabajo realizado por todo el equipo de desarrollo y, sobre
todo, en llevar este desarrollo del entorno de desarrollo a
producción.
La integración continua es un proceso que permite
comprobar continuamente que todos los cambios que lleva
cada uno de los desarrolladores no producen problemas de
integración con el código del resto del equipo. Los entornos
de integración continua construyen el software desde el
repositorio de fuentes y lo despliegan en un entorno de
integración sobre el que realizar pruebas unitarias o de
aceptación.
Implantar procesos de este tipo conlleva una inversión en
tiempo que será recuperada conforme avance el proyecto.
No obstante, esta inversión es cada vez más reducida
gracias a la disponibilidad de herramientas Open Source
que nos ofrecen soluciones de Integración Continua cada
vez más sencillas de implantar. Herramientas como
CruiseControl combinadas con Ant, Maven, Junit o DBUnit,
nos ofrecen la posibilidad de implantar un proceso de
Integración Continua pudiendo utilizar en este proceso
otras técnicas como la gestión de la configuración o
generación de informes de forma automática.
Palabras clave: Metodologías Ágiles, XP, Integración Continua, Herramientas Open Source, Agile-Spain,
1 Metodologías ágiles
Las metodologías ágiles han aparecido dentro del marco
de la Ingeniería del Software como una respuesta ante
los problemas de las metodologías tradicionales.
Problemas de adaptación a los entornos actuales de
proyectos, que se caracterizan por requisitos variables,
plazos breves así como presupuestos y recursos
ajustados. Las metodologías ágiles introducen cambios
importantes sobre los modelos existentes de ingeniería
del software, apoyándose en la experiencia de prácticas
aplicadas con éxito, que en muchos casos no resultan
novedosas.
La integración continua es una de las prácticas que
propone XP (eXtreme Programming []), una de las
metodologías ágiles más conocidas. Se trata de una
práctica que organiza el trabajo de integración a lo largo
de todo el proyecto. Implantar esta práctica supone un
cambio más dramático de lo que en principio parece,
dado que implica una nueva forma de entender el
desarrollo.
La integración continua es también un buen ejemplo de
una regla básica de un programador pragmático []:
“Intentar automatizar todo el trabajo repetitivo que
realiza”.
2 Integración Continua
¿Cuántas veces hemos oído a un desarrollador decir que
el código que ha desarrollado “funciona correctamente
en su máquina”? Ésta es la respuesta estándar cuando
un problema de integración ha sucedido.
Integración continua consiste en disponer de un proceso
automatizado que permita la construcción de nuestro
software desde las fuentes, que despliegue nuestro
software en un entorno similar al entorno final y que
lleve a cabo el conjunto de pruebas que validan su
correcto funcionamiento. Si nuestro sistema pasa
correctamente las pruebas podemos completar el
proceso desplegando nuestro software sobre un entorno
Actas del II congreso javaHispano
107
donde pueda estar disponible. Este proceso debe poder
realizarse muchas veces al día.
El concepto que hay detrás de integración continua
(Continuous Integration o CI) es que se debe integrar el
desarrollo de una forma incremental y continua. Esto
permite encontrar problemas de integración y resolver
este tipo de problemas durante el desarrollo.
Integración continua es el término que se utiliza para dar
nombre a esta práctica dentro de la metodología de XP.
No obstante es una práctica que no es nueva y que es
aplicada en todo tipo de entornos de desarrollo de
software.
Para conseguir automatizar este proceso de
construcción, pruebas y despliegue de manera que se
realice diariamente muchas veces es necesario también:
• Almacenar las fuentes en un único lugar del que
pueda obtenerse la última versión del proyecto
(y versiones anteriores).
• Automatizar el proceso de construcción de
manera que se pueda, con un único comando,
construir todo el sistema a partir de las fuentes
• Automatizar las pruebas de forma que sea
posible y de forma automática, saber si todo
está bien o hay algún problema.
Introducir integración continua en un proyecto no suele
resultar sencillo. En muchos casos es realmente
complicado automatizar el conjunto de tareas repetitivas
que debemos realizar para conseguir una versión de
nuestro software. Es muy habitual que no existan
pruebas automatizadas asociadas al proyecto, lo que
implicará un esfuerzo considerable conseguir dotar a
nuestro software de este conjunto de pruebas. A pesar
de todo, el mayor esfuerzo que podíamos encontrar, el
desarrollo de la plataforma de integración, no es
necesario realizarlo en la actualidad gracias a la aparición
de plataformas abiertas de integración continua.
3 Beneficios de la Integración Continua
Introducir integración continua supone generalmente
para una organización, un cambio sustancial en su forma
de desarrollar software. Es una práctica que requiere
introducir cierta disciplina dentro del grupo de desarrollo
de software, que se verá compensada generalmente con
una mejora de la eficiencia. Probablemente sea una de
las prácticas más complicadas a nivel técnico de aplicar
de las que nos proponen XP.
Estos beneficios son los siguientes:
• Minimiza las sesiones de búsqueda de fallos a la
hora de integrar el código. Fallos realmente
complicados de encontrar dado que suelen ser
efectos colaterales de código que ha sido
desarrollado de manera independiente.
• Permite identificar fallos en el entorno de
producción en etapas tempranas. Esto permite
ser eficiente en los pasos a producción y evitar
los periodos de integración finales en los
entornos de producción.
• Minimización del tiempo de realimentación con
el cliente, al minimizar el paso de desarrollo a
un entorno de integración.
• Eficiencia del equipo de desarrollo: no es
necesario todo el conjunto de pruebas en el
entorno local, minimiza el tiempo de paso a
producción.
• Aumenta la confianza en el cdigo subido al
control de versiones.
4 Plataforma Open-Source
Implantar Integración Continua es una tarea de una
cierta complejidad técnica. Automatizar todo el proceso
que hemos descrito anteriormente sin apoyarnos en
ninguna herramienta resultaría bastante costoso.
En la actualidad no es necesario desarrollar
herramientas propias que permitan esta automatización
desde cero, dado que existen ya una serie de frameworks
que ofrecen soporte para esta práctica. Podemos
encontrar herramientas comerciales y herramientas
Open-Source.
El movimiento de Open-Source se ha consolidado en la
actualidad como un punto de referencia para cualquier
desarrollo. El extenso conjunto de herramientas, la
calidad y el espectacular avance que se puede observar
en muchos de los proyectos ha hecho que, hoy en día,
muchos de estos desarrollos se hayan convertido en
estándares (Struts, Ant, etc..). Resulta una ventaja
competitiva la utilización de estas soluciones en lugar de
tratar de desarrollar soluciones similares pero
propietarias. Es complicado que soluciones propietarias
puedan competir en funcionalidad con las soluciones
Actas del II congreso javaHispano
108
libres cuyo coste de desarrollo es cero. Unas soluciones
cuya funcionalidad esta en continua evolución y que nos
permitirán evolucionar tecnológicamente con ellas.
Para Integración continua podemos encontrar diferentes
soluciones tanto Comerciales como Open-Source:
• CruiseControl (Open-Source)
• AntHill(Comercial/Open-Source)
• DamageControl
El más extendido de estos frameworks de integración es
Cruise-Control que ha sido desarrollado por una de las
empresas más importantes dentro del movimiento ágil,
ThoughtWorks, por lo que supone probablemente la
mejor forma de introducirse en esta práctica.
4.1 CruiseControl
Cruisecontrol es un framework que nos va a permitir
implementar dentro de nuestro proyecto un proceso de
integración continua. Este framework se apoya en otras
herramientas que se describen a continuación
4.1.1 Control de Versiones
Para poder implantar una herramienta de integración
continua la primera condición es tener un repositorio
con todas las fuentes del proyecto. Existen diferentes
repositorios de control de versiones como CVS,
Subversión, ClearCase, VisualSourceSafe, etc. Cada uno
de estos repositorios tiene características diferentes que
tendremos que evaluar cuando decidamos implantarlo
en nuestro proyecto. Uno de los mas implantados y que
es utilizado en la mayoría de los proyecto de Open
Source es CVS. No obstante y en los últimos tiempos,
parece que está siendo desplazada por Subversión que
ofrece unas funcionalidades para trabajar con diferentes
ramas de trabajo mucho mas potentes.
Utilizar Control de Versiones es una práctica se hace
especialmente imprescindible cuando se trabaja en
entornos colaborativos y en los que se desarrolla de una
forma evolutiva para que el software pueda estar
disponible desde las primeras versiones. Estas dos
razones han hecho de estos sistemas el corazón de los
proyecto de Software Libre.
El control de versiones contiene toda la historia de los
cambios que se han ido produciendo en nuestro código.
Éste nos permitirá replicar cualquier momento de
nuestro desarrollo y por tanto generar cualquier de
nuestro software que hayamos generado hace tiempo
siempre para construir el software únicamente nos
hayamos apoyado en las fuentes que existían el control
de versiones.
Introducir el control de versiones en un equipo es una
práctica que supone un cambio sustancial en la forma de
trabajar de un equipo. A partir de ahora nuestro trabajo
se compartirá con nuestros compañeros diariamente, lo
que nos impondrá ciertas buenas practicas que
permitirán que todos podamos trabajar eficientemente.
Será importante ser cuidadoso con el código que
introducimos en el control de versiones para evitar que
nuestros compañeros al actualizarse tengan problemas
para seguir desarrollando.
La experiencia al final nos hace ver que lo necesitaremos
incluso cuando realicemos desarrollos individuales y en
los que tengamos una única entrega, dado que es fácil
descubrir que es una herramienta muy potente para
desarrollar (permite poder identificar los cambios que se
hicieron en las fuentes desde una fecha determinada,
evita la proliferación de copias de seguridad de nuestro
código, identifica claramente cuál es la ultima versión,
etc…).
4.1.2 Pruebas Automatizadas
El siguiente paso para poder llegar a implantar
Integración Continua es el desarrollo de pruebas
automatizadas.
Implantar esta práctica es uno de los retos más difíciles
que nos encontraremos en la implantación de la
integración continua.
Desarrollar un conjunto de pruebas completas de
nuestro sistema es una tarea cuya complejidad
dependerá del entorno de ejecución de nuestra
aplicación y de las interacciones con sistemas externos.
Un conjunto de pruebas completas de nuestro sistema
implicaría tener diferentes conjuntos de pruebas:
• Pruebas Unitarias
• Pruebas de Rendimiento
• Pruebas de Integración con sistemas externos
• Pruebas de Diseño (Accesibilidad)
• Pruebas Funcionales
En el modelo tradicional de desarrollo este tipo de
pruebas se realizaban una vez finalizado el desarrollo. No
Actas del II congreso javaHispano
109
obstante este enfoque no suele funcionar en la mayoría
de los proyectos de desarrollo y es realmente poco
efectivo.
Desarrollar pruebas automáticas nos permite comprobar
en cualquier momento y ejecutando la ‘suite’ de pruebas
si nuestro sistema cumple con la funcionalidad
implementada hasta el momento. Esta facilidad es
tremendamente útil durante el desarrollo, puesto que
nos permitirá desarrollar cambios con seguridad, sin
tener que pagar el esfuerzo de volver a probar todo.
Dejar las pruebas para el final del desarrollo nos privará
de esta ventaja.
La herramienta más extendida y la que cuenta con una
comunidad más activa para el desarrollo de pruebas es
JUnit. JUnit nos ofrece un framework sobre el que
desarrollar las pruebas de nuestro sistema de una forma
homogénea, que nos permitirá que nuestro conjunto de
pruebas crezca de una forma ordenada. Este framework
se acompaña de un conjunto de utilidades que permitirá
presentar los resultados en diferentes formatos (XML,
HTML). Una de las características más importantes de
esta plataforma es que nos ofrece la posibilidad de
ejecutar todo el conjunto de pruebas ejecutando un
único comando y que se integra perfectamente con
lenguajes de scripting como es Ant.
Esta combinación es muy potente dado que podremos
incorporar de manera automática las pruebas de nuestro
software al proceso en el que se construye una nueva
versión. De esta manera podremos asegurar, cuando
creemos una nueva versión de nuestro software o
cuando lo despleguemos sobre un entorno, que el
código pasó previamente las pruebas que certifican el
funcionamiento correcto de todas las funcionalidades.
Sobre JUnit se han desarrollado muchas extensiones
realmente útiles que ofrecen librerías para facilitar cierto
tipo de pruebas. Especialmente interesante son las
librerias de:
• JUnitPerf: Introducir pruebas de rendimiento
durante el desarrollo es muy sencillo y muy
valioso, especialmente en aquellas clases criticas
como las de acceso a Basede Datos, las que
realicen operaciones pesadas (algoritmos,
parseo de XML, integración con otros sistemas
externos, etc..)
• Cactus: Para permitir pruebas realizadas
directamente sobre el contenedor. Pruebas
unitarias sobre el entorno final
• StrutsTestCase: Extensión para hacer pruebas
unitarias sobre actions de struts
• DBUnit: Suite para pruebas muy dependientes
de los datos almacenados en Bases de Datos
• HttpUnit, JUnit, CanooWebTest: Pruebas
directamente contra la aplicación web
Realizar pruebas puede ser complicado pero siempre es
posible. Incluso en entornos muy específicos en los que
no encontremos suites que simulen el comportamiento
de los elementos externos, es posible desarrollarnos
mediante Mock-Objects clase que simulen estos
comportamientos. En muchos casos este tipo de pruebas
nos servirán no solamente para comprobar que nuestras
clases funcionan correctamente, si no para acelerar
nuestro desarrollo, al no tener la necesidad de realizar
las pruebas sobre esos entornos.
4.1.3 Automatización (Scripting)
Una vez llegados a este punto nos encontramos en
situación de poder comenzar a pensar en automatizar
todas las tareas que realizamos, para conseguir construir
nuestro sistema a partir del código que hemos
desarrollado y lograr que sea desplegado y este
disponible para poder ser utilizado.
Una de las claves de la eficiencia en el desarrollo es el
nivel de automatización que hemos conseguido
implantar en todas las tareas relacionadas con el
desarrollo.
Pero aún siendo la mejora de la eficiencia una excelente
razón para decidir comenzar a automatizar los procesos
de nuestro desarrollo hay otra razón que muchas veces
queda oculta y que es en mi opinión mucho más
importante: la capacidad de poder repetir los procesos
de una manera exacta. El desarrollo de software es una
disciplina con un alto grado de incertidumbre. Al gran
número de variables que afectan a un desarrollo
(Sistemas Operativos, Base de Datos, Integración con
sistemas externos, Servidores de aplicaciones) en muchas
ocasiones añadimos nosotros todavía más incertidumbre
al introducir procesos manuales al proceso de
construcción del software.
Conseguir que nuestro proceso de despliegue sea
idéntico en todas las ocasiones nos asegurará poder
replicar o repetir cualquier proceso previo que hubiera
sido exitoso.
Actas del II congreso javaHispano
110
Pero automatizar un proceso puede ser una tarea que
conlleve mucho esfuerzo. Entonces la pregunta es ¿Qué
debemos automatizar?. En nuestra opinión cualquier
proceso manual que vayamos a realizar en más de una
ocasión.
Para automatizar todos estos procesos Java cuenta con
una herramienta ANT que nos permite llevar a cabo la
mayoría de las tareas que realizamos de forma manual
con una consola. Tareas como copiar ficheros, compilar
nuestras clases, hacer FTP a otras máquinas con
versiones de nuestro software, ejecutar nuestras
pruebas, pueden ser automatizadas con ANT.
ANT es un framework para realizar scripts a partir de una
serie de tareas predefinidas. Este framework es muy
extensible y permite introducir nuevas tareas si fueran
necesario. Aunque la extensa contribución de la
comunidad de ANT hace que sea complicado encontrar
tareas que no estén implementadas dentro de ANT.
El problema con Ant reside en que las tareas que ofrece
son de muy bajo nivel y deberemos invertir en tiempo en
automatizar nuestras tareas mas comunes de desarrollo
Para resolver este problema ha surgido Maven que nos
ofrece una posible implementación de todas estas tareas
de alto nivel. (preguntar a Dani)
4.1.3 Configuración de CruiseControl
Hay diferentes formas de abordar un proyecto de
desarrollo. Nos encontramos desde proyectos en los que
todo el equipo desarrolla en misma máquina, hasta
proyectos en los que cada desarrollador tiene asignado
su ordenador personal y una máquina adicional similar al
entorno de desarrollo. También nos encontramos
empresas que tiene un servicio de control de versiones
general para todos los proyectos y otras en las que cada
proyecto es responsable de su repositorio de control de
versiones.
Para detallar el proceso de integración continua y la
configuración de CruiseControl supondremos un entorno
en el que cada usuario desarrollar en su entorno local,
que existe una maquina dedicada al control de versiones
y que existen dos entornos comunes integración y
producción.
Cada desarrollador dispondrá de un entorno local en el
cual realizará la mayor parte del trabajo de desarrollo. En
este entorno dispondrá de un entorno simulado del
entorno real a escala reducida. Dependiendo de los
sistemas involucrados con el software que se desarrolla
en algunos casos se compartirá alguno de estos sistemas
en lugar de instalarlos en el entorno local. También es
posible solucionar este problema utilizando clases que
en lugar de utilizar los servicios remotos, simulen este
comportamiento.
Cada desarrollador subirá periódicamente sus nuevos
desarrollos al entorno de control de versiones e irá
actualizándose con las modificaciones que vayan
realizando otros miembros del equipo.
El entorno de integración continua (en nuestro caso
CruiseControl) suele estar instalado en el entorno de
integración. Esto permite que podamos desplegar de
una manera sencilla el software sobre el entorno de
integración una vez que ha pasado las pruebas.
Para instalar CruiseControl es necesario tener en cuenta
los siguientes directorios
En estos directorios se instalar
• CruiseControl: En este directorio instalaremos la
herramienta de integración continua.
• Tomcat: Tenemos la opción de publicar los
resultados mediante una aplicación web. Esto
Actas del II congreso javaHispano
111
nos obligará a instalar un contenedor de
servlets como Tomcat
• Fuentes del Proyecto: Este directorio lo
utilizaremos para que CruiseControl tenga su
copia del código del control de versiones.
CruiseControl no tiene una arquitectura muy sofisticada,
consta de un proceso que cada cierto tiempo
comprobará una serie de condiciones para lanzar una
serie de acciones.
La otra parte de la arquitectura es la herramienta de
publicación de la actividad de este proceso. Esta
herramienta esta desarrollada en Java y esta
empaquetada en un war. Instalando un Tomcat y
desplegando esta aplicación configurada correctamente
tendremos disponibles via web la información de la
actividad del proceso principal de CruiseControl
Toda la configuración de CruiseControl esta centralizada
en un único fichero. En este fichero podremos configurar
todas las propiedades del proceso de integración
continua. Las más importantes son:
• ModificationSet: En este propiedad se especifica
a CruiseControl cuales son las condiciones que
se tiene que producir para lanzar un proceso de
construcción, despliegue y pruebas del
Software. Generalmente lo asociaremos a
cambios en nuestro control de Versiones
• Schedule: En esta propiedad especificaremos
cada cuanto tiempo el control de versiones
deberá realizar un build como máximo si no se
produce alguna condición que lo lance. En esta
propiedad se detalla exactamente el script de
Ant y el objetivo que deberá lanzar
CruiseControl que es el que automatiza todo el
proceso de construcción, despliegue y pruebas.
• Publisher: En esta propiedad especificamos
como notificaremos de los resultados de prueba
al equipo de desarrollo.
5 Experiencia Real
La experiencia real aplicando integración continua
demuestra que aplicar esta práctica supone una pequeña
revolución en la forma de desarrollar. Implica un cambio
de filosofía para el que es necesario un periodo de
adaptación.
La Integración continua es una práctica de la que todo el
mundo ha oído hablar pero que muy pocos equipos de
desarrollo aplican (incluyendo aquellos que utilizan
metodologías ágiles como XP)
¿A que se debe que no este nada extendida?.
5.1 Problemas implantando Integración Continua
El principal problema que nos hemos encontrado al
aplicarla es que en la mayoría de los casos supone la
introducción de otras prácticas en las que se apoya.
• Control de Versiones: Necesitamos tener control
de versiones de nuestro código, de manera que
podamos recuperar cualquier versión del
proyecto. Esta es una practica muy extendida y
no suele suponer un problema.
• Pruebas Automáticas: La integración continua
pierde gran parte de su valor si no existen.
Implantar esta práctica una vez comenzado el
proyecto es muy costoso y supone una parada
del desarrollo. Incluir pruebas automatizadas al
proyecto y programar desarrollando pruebas es
uno de los cambios de filosofía necesario si
queremos implantar Integración Continua
• Construcción y despliegue automatizados: Esta
es una de las tareas que tendremos que realizar
si no existen los scripts que permitan llevar a
cabo este proceso sin intervención manual.
5.2 Buenas Prácticas
La integración continua impone un desarrollo en equipo
más disciplinado. En muchos proyectos no se es
consciente de que se trabaja en equipo hasta que llega el
momento de la integración final.
La integración continua hace que nuestro desarrollo sea
incremental. En cada momento nuestro sistema ofrecerá
una serie de funcionalidades que serán las que prueben
nuestro conjunto de pruebas automáticas. Los nuevos
desarrollos añadirán nuevas funcionalidades evitando
que las anteriores dejen de funcionar. Cuando el
conjunto de funcionalidades de nuestro sistema ofrezca
la posibilidad de tener un software coherente con nuevas
posibilidades, entonces en ese caso deberemos crear una
nueva versión.
Actas del II congreso javaHispano
112
Para que este desarrollo incremental funcione
correctamente la experiencia nos dice que es necesario
seguir las siguientes prácticas:
• Desarrollar pruebas a medida que se desarrollan
funcionalidades: Cada nueva funcionalidad que
se integre en el control de Versiones deberá
tener su conjunto de pruebas. Cuando se
modifique alguna funcionalidad también será
necesario modificar su conjunto de pruebas.
Utilizar desarrollo dirigido por pruebas TDD
facilita esta tarea.
• Política de Semáforos: Una vez que utilicemos
integración continua el sistema nos informara
del estado del software De esta manera
recibiremos continuamente notificaciones
cuando el código que tengamos en nuestro
repositorio de versiones no pase las pruebas. En
ese caso tendremos el semáforo en rojo para
subir nuevo código al CVS. Esto nos permitirá
no introducir nuevas clases antes de identificar
cuales han sido las clases responsables de este
problema. Si el control de versiones nos ha
enviado un mail avisándonos del éxito de
nuestras pruebas, entonces en ese momento el
semáforo estar verde para subir nuevo código
estará en Verde.
• Tiempo máximo de solución de errores en el
control de Versiones: En la medida de lo posible
tenemos que minimizar el tiempo en que el
desarrollo que hay en el control de versiones no
pasa las pruebas. En aquellos casos en los que
sea difícil arreglar un problema que hemos
detectado una vez subido el código al control
de versiones la mejor opción es volver al código
de la versión anterior.
• Minimizar el tiempo entre integraciones: Uno
de los objetivos principales de la integración
continua es realizar continuamente
integraciones, cuanto mas a menudo se realicen
mejor funcionara el proceso. Por este motivo
hay que intentar que este tiempo sea el mínimo
posible. Por este motivo tenemos que conseguir
mantener nuestro proceso automático lo mas
rápido posible, tratando de reducir el tiempo
que consumen las pruebas y el tiempo que
consume el despliegue.
• Coordinar aquellos cambios o
reestructuraciones que influyan en gran parte
del código: La integración continua implica un
elevado grado de coordinación. Estas
coordinaciones son especialmente importantes
en aquellas modificaciones que vayan a afectar
a muchos módulos. En estos casos muchas
veces es más eficiente hacer este tipo de
cambios desde situaciones estables, para
intentar evitar efectos colaterales. Es posible
que estos cambios sean realizados por cada
programador en los módulos en los que esta
trabajando.
• Responsable del entorno de integración
continua: Nuestra experiencia aplicando
Integración Continua es que es muy
conveniente tener un responsable encargado de
que este entorno funcione correctamente y de
que el equipo esta siguiendo las reglas
anteriores. Esta persona será la responsable de
arreglar aquellos errores que surgen sin que se
haya realizado ningún cambio por el entorno en
el que se ejecutan los test.
• Los mails que genera el Control de Versiones no
son Spam: Nuestra experiencia nos dice que hay
ciertos periodos en los que se produce cierta
inestabilidad en el entorno de control de
versiones. En esos momentos es cuando al
recibir muchos emails de sistema de integración
continua nos sentimos tentado de tratar este
como Spam (redirigiendolo a la carpeta de
borrar o a otra carpeta que no consultaremos).
Estas situaciones tenemos que tratar de
evitarlas tratando de solucionar el problema de
la inestabilidad lo antes posible e
interrumpiendo temporalmente el envió de
correos. Los correos nuestro sistema de
integración continua debemos tratar de
atenderlo lo antes posible para conseguir todos
lo beneficios de implantar esta práctica.
• Conseguir que nuestras pruebas, prueben el
sistema y sean fáciles de mantener Es
importante que nuestras pruebas nos ayuden a
conocer que nuestro sistema funciona. En
muchos casos desarrollamos conjuntos de
pruebas que nos dan muy poca información
sobre las funcionalidades de nuestro sistema y
con un gran coste de mantenimiento. No
Actas del II congreso javaHispano
113
conseguir este propósito nos hará plantearnos
en ocasiones si merece la pena en esta situación
seguir manteniendo nuestro entorno de
integración continua.
7 Conclusiones: El poder de la Automatización.
Es realmente sorprendente que equipos que
desarrollan programas para automatizar el trabajo
en otros ámbitos, en muchas ocasiones no utilicen
esta capacidad para automatizar su propio trabajo
en lo posible. Detrás de los equipos más eficientes
de desarrollo encontraremos generalmente un alto
grado de automatización de su trabajo.
La integración continua es una práctica cuya
implementación implica automatizar una de las
tareas más pesadas y complejas de predecir: el
proceso de integración del desarrollo en un entorno
similar al final. Para implantar esta práctica el
movimiento de Software libre nos proporciona
herramientas muy potentes (CruiseControl, Ant,
CVS, Junit, etc) que facilitarán este camino.
La Integración continua nos permitirá detectar de
manera temprana posibles problemas de integración
que puede tener soluciones muy complejas a
posteriori. Alcanzaremos un grado de confianza alto
sobre nuestro desarrollo al validarlo continuamente,
con el conjunto de pruebas, en un entorno similar al
final.
La integración continua minimizará el tiempo de
puesta en producción acortando el ciclo desde que
nuestro cliente nos pide un desarrollo nuevo o un
cambio hasta que puede tenerlo en producción. Esto
nos hará ser mucho mas ágiles con nuestro cliente,
mostrándole en cada momento como evoluciona el
desarrollo para que, junto con él, podamos llegar a
la solución que mas valor le aporte.
Referencias
[1] Kent Beck, Extreme Programming Explained: Embrace Change, Addison-Wesley, 1999.
[2] Martin Fowler y Matthew Foemmel. Título del Articulo: Continuous integration, ThoughtWorkshttp://www.martinfowler.com/articles/continuous Integration.html
[3] Lasse Koskela. . Driving on CruiseControl., JavaRach Journal, September
2004http://www.javaranch.com/journal/200409/DrivingOnCruiseControl_Part1.htm
[4] CVS. https://www.cvshome.org/
[5] Subversion. http://subversion.tigris.org/
[6] Ant. http://ant.apache.org/
[7] Maven http://maven.apache.org/
[8] DBUnit . http://dbunit.sourceforge.net/
[9] Easy Mock Objects http://www.easymock.org/ l
Actas del II congreso javaHispano
114
Top Related