JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer para uso con JBoss Enterprise Application Platform 5 Edición 5.1.0 Mark Newton Aleš Justin JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer para uso con JBoss Enterprise Application Platform 5 Edición 5.1.0 Mark Newto n Red Hat mark.newto n@jbo ss.o rg Aleš Justin Red Hat ajustin@redhat.co m Edited by Misty Stanley-Jo nes Red Hat misty@redhat.co m Legal Notice Copyright © 2011 Red Hat, Inc. T his document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed. Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law. Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries. Linux ® is the registered trademark of Linus T orvalds in the United States and other countries. Java ® is a registered trademark of Oracle and/or its affiliates. XFS ® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries. MySQL ® is a registered trademark of MySQL AB in the United States, the European Union and other countries. Node.js ® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project. T he OpenStack ® Word Mark and OpenStack Logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community. All other trademarks are the property of their respective owners. Abstract Este manual está dirigido a aquellos desarrolladores Java que desean utilizar el microcontenedor JBoss para implementar entornos Java modulares y personalizados para sus aplicaciones. Table of Contents Table of Contents .Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . 1. Convenciones del Documento 4 1.1. Convenciones tipográficas 4 1.2. Convenciones del documento 5 1.3. Notas y Advertencias 6 2. Cómo obtener ayuda y hacer sus comentarios 6 2.1. ¿Necesita ayuda? 6 2.2. ¡Necesitamos sus comentarios! 7 . . . . . . .I.. Introducción Parte . . . . . . . . . . . . . .al . . tutorial . . . . . . . . sobre . . . . . . .el . . Microcontainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8. . . . . . . . . . .Capítulo . . . . . . . . .1. . . Prerequisitos . . . . . . . . . . . . . . .para . . . . .el . . uso . . . . .de . . .este . . . . .manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . . 1.1. Instale Maven 9 1.2. Configuración especial de Maven para los ejemplos del microcontenedor 12 1.3. Descarga de los ejemplos 13 .Capítulo . . . . . . . . .2. . . Introducción . . . . . . . . . . . . . .al. .microcontenedor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 ............ 2.1. Funcionalidades 14 2.2. Definiciones 14 2.3. Instalación 15 .Capítulo . . . . . . . . .3.. .Construcción . . . . . . . . . . . . . . de . . . servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 ............ 3.1. Introducción al ejemplo de recusos humanos 16 3.2. Compilación del proyecto de ejemplo HRManager 17 3.3. Creación de POJOs 17 3.3.1. Descriptores de implementación XML 17 3.4. Conexión de POJOs 17 3.4.1. Consideraciones especiales 18 3.5. T rabajar con servicios 18 3.5.1. Configuración de un servicio 19 3.5.2. Probar un servicio 19 3.5.3. Empacar un servicio 21 .Capítulo . . . . . . . . .4. .. Uso . . . . .de . . .los . . . servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23 ........... 4.1. Bootstrap del microcontenedor 26 4.2. Implementación del servicio 27 4.3. Acceso directo 28 4.4. Acceso indirecto 30 4.5. Carga de clase dinámica 31 4.5.1. Problemas con cargadores de clase creados con los descriptores de implementación 36 .Capítulo . . . . . . . . .5. . . Agregar . . . . . . . . .comportamiento . . . . . . . . . . . . . . . . . con . . . . .AOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37 ........... 5.1. Creación de un aspecto 37 5.2. Configuración del microcontenedor para AOP 39 5.3. Aplicación de un aspecto 41 5.4. Callbacks del ciclo de vida 43 5.5. Agregar búsquedas de servicios por medio de JNDI 45 . . . . . . .II.. .Conceptos Parte . . . . . . . . . . . .avanzados . . . . . . . . . . . con . . . . .el . . microcontenedor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. .7. . . . . . . . . . .Capítulo . . . . . . . . .6. . . Modelos . . . . . . . . . de . . . componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. .8. . . . . . . . . . 6.1. Interacciones permitidas con los modelos de componentes 48 6.2. Un Bean sin dependencias 48 6.3. Uso del microcontenedor con Spring 48 1 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer 6.3. Uso del microcontenedor con Spring 6.4. Uso de Guice con el microcontenedor 6.5. MBeans de legado y mezcla de diferentes modelos de componentes 6.6. Exponer POJOs como MBeans 48 49 52 53 .Capítulo . . . . . . . . .7. . . Inyección . . . . . . . . . . avanzada . . . . . . . . . . .de . . . dependencias . . . . . . . . . . . . . . . y. .ldC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 ............ 7.1. Fábrica de valores 56 7.2. Callbacks 58 7.3. Modo de acceso del Bean 60 7.4. Alias Bean 61 7.5. Soporte para anotaciones XML (o metadatos) 61 7.6. Autowire 64 7.7. Fábrica de beans 64 7.8. Constructor de metadatos Bean 67 7.9. ClassLoader personalizado 68 7.10. Modo controlador 69 7.11. Ciclo 70 7.12. Oferta y demanda 71 7.13. Instalaciones 71 7.14. Imitación perezosa 72 7.15. Ciclo de vida 73 .Capítulo . . . . . . . . .8. . . El . . .sistema . . . . . . . . virtual . . . . . . . de . . . .archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 ............ 8.1. API pública VFS 77 8.2. Arquitectura VFS 86 8.3. Implementaciones existentes 86 8.4. Ganchos de extensión 87 8.5. Funcionalidades 87 .Capítulo . . . . . . . . .9. . . La . . . capa . . . . . .ClassLoading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 ............ 9.1. ClassLoader 89 9.2. ClassLoading 96 9.3. Carga de clase VFS 101 .Capítulo . . . . . . . . .10. . . . Marco . . . . . . . de . . . trabajo . . . . . . . . de . . . .la. .implementación . . . . . . . . . . . . . . . . .virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 ............. 10.1. Manejo agnóstico de tipos de implementación 103 10.2. Separación del reconocimiento de la estructura de la lógica del ciclo de vida de la implementación 103 10.3. Control de flujo natural en forma de anexos 106 10.4. Detalles de la implementación y del cliente, usuario y uso del servidor 107 10.5. Máquina de estado único 108 10.6. Escaneo de clases en busca de anotaciones 108 . . . . . . . . . .de Historial . . .revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 ............. 2 Table of Contents 3 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Prefacio 1. Convenciones del Documento Este manual utiliza varias convenciones para resaltar algunas palabras y frases y llamar la atención sobre ciertas partes específicas de información. En ediciones PDF y de papel, este manual utiliza tipos de letra procedentes de Liberation Fonts. Liberation Fonts también se utilizan en ediciones de HT ML si están instalados en su sistema. Si no, se muestran tipografías alternativas pero equivalentes. Nota: Red Hat Enterprise Linux 5 y siguientes incluyen Liberation Fonts predeterminadas. 1.1. Convenciones tipográficas Se utilizan cuatro convenciones tipográficas para llamar la atención sobre palabras o frases específicas. Dichas convenciones y las circunstancias en que se aplican son las siguientes: Negrita m onoespaciado Utilizado para resaltar la entrada del sistema, incluyendo los comandos de shell, nombres de archivos y rutas. T ambién sirve para resaltar teclas y combinaciones de teclas. Por ejemplo: Para ver el contenido del archivo m y_next_bestselling_novel en su directorio actual de trabajo, escriba el comando cat m y_next_bestselling_novel en el intérprete de comandos de shell y pulse Enter para ejecutar el comando. El ejemplo anterior incluye un nombre de archivo, un comando de shell y una tecla . T odo se presenta en negrita-monoespaciado y distinguible gracias al contexto. Las combinaciones de teclas se pueden distinguir de las individuales con el signo más que conecta cada partee de la combinación de tecla. Por ejemplo: Pulse Enter para ejecutar el comando. Pulse Ctrl+Alt+F2 para pasar a una terminal virtual. El primer ejemplo resalta una tecla particular a pulsar. El segundo ejemplo, resalta una combinación de teclas: un set de tres teclas pulsadas simultáneamente. Si se discute el código fuente, los nombres de las clase, los métodos, las funciones, los nombres de variables y valores de retorno mencionados dentro de un párrafo serán presentados en Negritam onoespaciado. Por ejemplo: Las clases de archivo relacionadas incluyen filenam e para sistema de archivos, file para archivos y dir para directorios. Cada clase tiene su propio conjunto asociado de permisos. Negrita proporcional Esta denota palabras o frases encontradas en un sistema, incluyendo nombres de aplicación, texto de cuadro de diálogo, botones etiquetados, etiquetas de cajilla de verificación y botón de radio; títulos de menú y títulos del submenú. Por ejemplo: Seleccione Sistema → Preferencias → Ratón desde la barra del menú principal para lanzar Preferencias de ratón. En la pestaña de Botones, seleccione la cajilla de ratón 4 Prefacio de m ano izquierda y luego haga clic en Cerrar para cambiar el botón principal del ratón de la izquierda a la derecha (adecuando el ratón para la mano izquierda). Para insertar un carácter especial en un archivo gedit, seleccione Aplicaciones → Accesorios → Mapa de caracteres de la barra del menú. Luego, seleccione Búsqueda → Buscar… de la barra del menú de Mapa de caracteres, escriba el nombre del carácter en el campo de Búsqueda y haga clic en Siguiente. El carácter que buscó será resaltado en la T abla de caracteres. Haga doble clic en ese carácter resaltado para colocarlo en el campo de T exto a copiar y luego haga clic en el botón Copiar. Ahora regrese al documento y elija Modificar → Pegar de la barra de menú de gedit. El texto anterior incluye nombres de aplicación; nombres y elementos del menú de todo el sistema; nombres de menú de aplicaciones específicas y botones y texto hallados dentro de una interfaz gráfica de usuario, todos presentados en negrita proporcional y distinguibles por contexto. Itálicas-negrita monoespaciado o Itálicas-negrita proporcional Ya sea negrita monoespaciado o negrita proporcional, la adición de itálicas indica texto reemplazable o variable. Las itálicas denotan texto que usted no escribe literalmente o texto mostrado que cambia dependiendo de la circunstancia. Por ejemplo: Para conectar a una máquina remota utilizando ssh, teclee ssh nombre de usuario@ dominio.nombre en un intérprete de comandos de shell. Si la máquina remota es exam ple.com y su nombre de usuario en esa máquina es john, teclee ssh john@ exam ple.com . El comando m ount -o rem ount file-system remonta el sistema de archivo llamado. Por ejemplo, para volver a montar el sistema de archivo /hom e, el comando es m ount -o rem ount /hom e. Para ver la versión de un paquete actualmente instalado, utilice el comando rpm -q paquete. Éste entregará el resultado siguiente: paquete-versión-lanzamiento. Observe que las palabras resaltadas en itálicas — nombre de usuario, dominio.nombre, sistema de archivo, paquete, versión y lanzamiento. Cada palabra es un marcador de posición, ya sea de texto a ingresar cuando se ejecuta un comando o para un texto ejecutado por el sistema. Aparte del uso estándar para presentar el título de un trabajo, las itálicas denotan el primer uso de un término nuevo e importante. Por ejemplo: Publican es un sistema de publicación de DocBook. 1.2. Convenciones del documento Los mensajes de salida de la terminal o fragmentos de código fuente se distinguen visualmente del texto circundante. Los mensajes de salida enviados a una terminal se muestran en rom ano m onoespaciado y se presentan así: books books_tests Desktop Desktop1 documentation downloads drafts images mss notes photos scripts stuff svgs svn Los listados de código fuente también se muestran en rom ano m onoespaciado, pero se presentan y resaltan de la siguiente manera: 5 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer package org.jboss.book.jca.ex1; import javax.naming.InitialContext; public class ExClient { public static void main(String args[]) throws Exception { InitialContext iniCtx = new InitialContext(); Object ref = iniCtx.lookup("EchoBean"); EchoHome home = (EchoHome) ref; Echo echo = home.create(); System.out.println("Created Echo"); System.out.println("Echo.echo('Hello') = " + echo.echo("Hello")); } } 1.3. Notas y Advertencias Finalmente, utilizamos tres estilos visuales para llamar la atención sobre la información que de otro modo se podría pasar por alto. Nota Una nota es una sugerencia, atajo o enfoque alternativo para una tarea determinada. Ignorar una nota no debería tener consecuencias negativas, pero podría perderse de algunos trucos que pueden facilitarle las cosas. Importante Los cuadros con el título de importante dan detalles de cosas que se pueden pasar por alto fácilmente: cambios de configuración únicamente aplicables a la sesión actual, o servicios que necesitan reiniciarse antes de que se aplique una actualización. Ignorar estos cuadros no ocasionará pérdida de datos, pero puede causar enfado y frustración. Aviso Las advertencias no deben ignorarse. Ignorarlas muy probablemente ocasionará pérdida de datos. 2. Cómo obtener ayuda y hacer sus comentarios 2.1. ¿Necesita ayuda? Si encuentra dificultades con alguno de los procedimientos descritos en este documento, visite el Portal del cliente de Red Hat en http://access.redhat.com. A través del portal del cliente, usted podrá: 6 Prefacio buscar o navegar a través de la base de artículos de soporte técnico sobre productos de Red Hat. enviar un caso de soporte a Servicios de Soporte Global de Red Hat (GSS) acceder a otra documentación del producto. Red Hat alberga una lista grande de correos electrónicos para discutir sobre software de Red Hat y tecnología. Encontrará un listado de las listas de correo disponibles al público en https://www.redhat.com/mailman/listinfo. Haga clic en el nombre de la lista a la que quiera suscribirse o para acceder a los archivos de listados. 2.2. ¡Necesitamos sus comentarios! Si encuentra algun error o si se le ocurre una manera de mejorar este manual, nos encantaría escuchar sus sugerencias. Complete un reporte en Bugzilla frente al producto JBoss Enterprise Application Platform 5 y el componente doc-JBoss_Microcontainer_User_Guide. El siguiente enlace le llevará a un reporte de error ya completado para este producto: http://bugzilla.redhat.com/. Llene la siguiente plantilla en el campo de Description de Bugzilla. Sea tan especifico como le sea posible al describir el problema, esto ayudará a asegurarnos de que lo podemos solucionar rápidamente. URL del documento: Número de la sección y nombre: Describa el problema: Sugerencias para mejorar: Información adicional: Asegúrese de darnos su nombre para poder darle todo el crédito por reportar el problema. 7 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Parte I. Introducción al tutorial sobre el Microcontainer 8 Capítulo 1. Prerequisitos para el uso de este manual Capítulo 1. Prerequisitos para el uso de este manual Para poder utilizar los ejemplos en este manual es necesario instalar y configurar el software de soporte y debe descargar el código para los ejemplos. 1.1. Instale Maven Los ejemplos utilizados en este proyecto requieren Maven v2.2.0 o posteriores. Descargue Maven directamente de la página de inicio de Apache Maven e instale y configure su sistema tal como se describe en Procedimiento 1.1, “Instale Maven”. Procedimiento 1.1. Instale Maven 1. Verifique que tiene instalado Java Developer Kit 1.6 o posteriores. Este también es un requerimiento para la plataforma empresarial. Asegúrese de que tiene instalado Java en su sistema y configure la variable de entorno JAVA_HOME en su ~/.bash_profile para Linux o en las propiedades del sistema para Windows. Para mayor información con relación a la configuración de las variables de entorno, consulte el paso Paso 4 en este procedimiento. 2. Descargue Maven Nota Este paso y en el futuro se asume que ha guardado Maven en la ubicación sugerida en su sistema operativo. Maven, como cualquier otra aplicación Java se puede instalar en cualquier lugar razonable en su sistema. Visite http://maven.apache.org/download.html. Haga clic en el enlace de fichero zip compilado, por ejemplo apache-m aven-2.2.1-bin.zip Seleccione un espejo de descarga de la lista. Para usuarios de Linux Guarde el fichero zip en su directorio hom e. Para usuarios de Windows Guarde el fichero zip en su directorio C:\Docum ents and Settings\user_name. 3. Instale Maven Para usuarios de Linux Extraiga el archivo zip en su directorio hom e. Si seleccionó el fichero zip en el paso 2 y no vuelve a nombrar el directorio, el directorio extraído se llama apache-m aven-version. Para usuarios de Windows Extraiga el fichero zip en C:\Program Files\Apache Software Foundation. Si seleccionó el fichero zip en el paso 2 y no vuelve a nombrar el directorio, el directorio extraído se llama apachem aven-version. 4. Configure las variables del entorno 9 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Para usuarios de Linux Agregue las siguientes líneas a su ~/.bash_profile. Asegúrese de cambiar el [username] a su nombre de usuario real y verifique que el directorio Maven es de hecho el nombre del directorio. El número de la versión puede ser diferente del que se lista a continuación. export M2_HOME=/home/[username]/apache-maven-2.2.1 export M2=$M2_HOME/bin export PATH=$M2:$PATH Al incluir M2 al comienzo de su ruta, la versión Maven que acabó de instalar será la versión predeterminada utilizada. Puede que también quierar establecer la ruta de su variable de entorno JAVA_HOME con la ubicación del JDK en su sistema. Para usuarios de Windows Agregue las variables de entorno M2_HOME, M2 y JAVA_HOME. a. Oprima Start+Pause|Break. Se presenta la ventana de propiedades del sistema. b. Haga clic en la pestaña Advanced y luego haga clic en el botón Environm ent Variables. c. Bajo System Variables, seleccione Path. d. Haga clic en Edit y agregue las dos rutas Maven usando un punto y coma para separar cada entrada. No se requieren comillas alrededor de las rutas. Agregue la variable M2_HOME y establezca la ruta como C:\Program Files\Apache Software Foundation\apache-m aven-2.2.1. Agregue la variable M2 y configure el valor como %M2_HOME%\bin. e. En la misma ventana, cree la variable de entorno JAVA_HOME: Agregue la variable %JAVA_HOME% y establezca el valor con la ubicación de su JDK. Por ejemplo C:\Program Files\Java\jdk1.6.0_02. f. En la misma ventana actualice o cree la variable de entorno de la ruta: Agregue la variable %M2% para permitir que se ejecute Maven desde la línea de comandos. Agregue la variable %JAVA_HOME%\bin para establecer la ruta con la instalación correcta de Java. g. Haga clic en OK hasta que la ventana System Properties se cierre. 5. Implemente los cambios en .bash_profile Solo para los usuarios de Linux Para actualizar los cambios realizados al .bash_profile en la sesión de la terminal actual proporcione su .bash_profile. [localhost]$ source ~/.bash_profile 6. Update gnome-terminal profile Solo para los usuarios de Linux Actualice el perfil de la terminal para asegurarse de que las iteraciones posteriores de la terminal gnome (o la terminal Konsole) lean las nuevas variables de entorno. 10 Capítulo 1. Prerequisitos para el uso de este manual a. Haga clic en Edit → Profiles b. Seleccione Default y luego haga clic en el botón Edit. c. En la ventana Editing Profile, haga clic en la pestaña T itle and Com m and. d. Seleccione la opción Run com m and as login shell. e. Cierre todas las terminales que tenga abiertas. 7. Verifique los cambios en las variables de entorno y en la instalación de Maven Para usuarios de Linux Para verificar que los cambios se han implementado correctamente, abra una terminal y ejecute los siguientes comandos: Ejecute echo $M2_HOME, el cual debe retornar el siguiente resultado. [localhost]$ echo $M2_HOME /home/username/apache-maven-2.2.1 Ejecute echo $M2, el cual debe retornar el siguiente resultado. [localhost]$ echo $M2 /home/username/apache-maven-2.2.1/bin Ejecute echo $PAT H y verifique que el directorio Maven /bin está incluído. [localhost]$ echo $PATH /home/username/apache-maven-2.2.1/bin Ejecute which m vn, el cual debe presentar la ruta al Maven ejecutable. [localhost]$ which mvn ~/apache-maven-2.2.1/bin/mvn Ejecute m vn -version, la cual debe presentar la versión de Maven, la versión Java relacionada y la información relacionada con el sistema operativo. [localhost]$ $ mvn -version Apache Maven 2.2.1 (r801777; 2009-08-07 05:16:01+1000) Java version: 1.6.0_0 Java home: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre Default locale: en_US, platform encoding: UTF-8 OS name: "Linux" version: "2.6.30.9-96.fc11.i586" arch: "i386" Family: "unix" Para usuarios de Windows Para verificar que los cambios se han implementado correctamente, abra una terminal y ejecute el siguiente comando: En una línea de comandos ejecute m vn -version C:\> mvn -version Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700) Java version: 1.6.0_17 Java home: C:\Sun\SDK\jdk\jre Default locale: en_US, platform encoding: Cp1252 OS name: "windows xp" version: "5.1" arch: "x86" Family: "windows" Ha configurado de manera exitosa Maven para utilizarlo con los ejemplos en este manual. 11 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer 1.2. Configuración especial de Maven para los ejemplos del microcontenedor Maven es un sistema modular de construcción que llama las dependencias cuando se necesitan. Los ejemplos en este manual asumen que ha incluído el bloque de XML en Ejemplo 1.1, “Archivo settings.xm l de ejemplo” en su ~/.m 2/settings.xm l (Linux) o C:\Docum ents and Settings\username\.m 2\settings.xm l (Windows). Si el archivo no existe entonces créelo primero. Ejemplo 1.1. Archivo settings.xm l de ejemplo <settings> <profiles> <profile> <id>jboss.repository</id> <activation> <property> <name>!jboss.repository.off</name> </property> </activation> <repositories> <repository> <id>snapshots.jboss.org</id> <url>http://snapshots.jboss.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>repository.jboss.org</id> <url>http://repository.jboss.org/maven2</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>repository.jboss.org</id> <url>http://repository.jboss.org/maven2</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>snapshots.jboss.org</id> <url>http://snapshots.jboss.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> </settings> 12 Capítulo 1. Prerequisitos para el uso de este manual 1.3. Descarga de los ejemplos Los ejemplos en este manual le muestran cómo crear un proyecto maven que dependa del JBoss Microcontainer utilizando Maven. Puede descargarlos de images/examples.zip . Esta ubicación cambiará pero la hemos incluído para su conveniencia. Después de descargar el archivo Z IP que contiene los ejemplos, extraiga su contenido en un lugar conveniente y mire los ejemplos para familiarizarse con su estructura. 13 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Capítulo 2. Introducción al microcontenedor El microcontenedor JBoss es un rediseño del JBoss JMX Microkernel para soportar la implementación POJO directa y el uso autónomo fuera del servidor de aplicaciones JBoss. El microcontenedor está diseñado para llenar las necesidades especificas de los desarrolladores Java que querían utilizar técnicas de programación orientadas a objetos para implementar software de manera rápida. Además permite implementar software en un amplio rango de dispositivos desde plataformas de computación móviles, entornos de computación de grande escala y lo que se encuentre entre estos dos. 2.1. Funcionalidades T odas las funcionalidades del microkernel JMX Implementación POJO directa (no hay necesidad de estándar/XMBean o MBeanProxy) Inyección directa de dependencias de estilo IOC Administración mejoarada del ciclo de vida Control adicional sobre las dependencias Integración transparente AOP Sistema virtual de archivos Marco de trabajo de implementación virtual Carga de clase OSGi 2.2. Definiciones Este manual usa algunos términos que puede que no le sean familiares. Algunos de ellos se definen en Lista de definición del mMicrocontenedor. Lista de definición del mMicrocontenedor Microkernel JMX JBoss JMX Microkernel es un entorno Java modular. Es diferente de los entornos estándar como J2EE ya que el desarrollador puede escoger exactamente los componentes que son parte del entorno y dejar por fuera el resto. POJO Un POJO (del inglés Plain Old Java Object) es un objeto Java modular y reutilizable. El nombre se utiliza para enfatizar que un objeto dado es un objeto Java normal, no es un objeto especial y en particular no es un JavaBean empresarial. El término lo utilizó por primera vez Martin Fowler, Rebecca Parsons y Josh MacKenzie in September 2000 en una charla en la cual estaban resaltando los muchos beneficios de codificar la lógica empresarial en objetos java normales en lugar de utilizar beans de entidad. Bean Java Un bean Java es un componente software re-utilizable que se puede manipular visualmente en una herramienta de construcción. Un bean Java es un pedazo de código independiente. No requiere herencias de ninguna clase o interfaz base en particular. Aunque los beans Java se crean principalmente en IDEs gráficos también se pueden desarrollar en simples editores de texto. 14 Capítulo 2. Introducción al microcontenedor AOP Aspect-Oriented Programming (AOP) es un paradigma de programación en el cual las funciones secundarias o de soporte se aislan de la lógica empresarial del programa principal. Es un sub-grupo de la programación orientada a objetos. 2.3. Instalación El microcontenedor es una parte integral de la plataforma empresarial. En el manual de configuración y administración encontrará mayor información sobre la instalación y configuración de la plataforma empresarial. 15 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Capítulo 3. Construcción de servicios Los servicios son pedazos de código que realizan tareas que múltiples clientes necesitan. Poara nuestro propósito pondremos algunas restricciones adicionales en la definición de un servicio. Los servicios deben tener nombres únicos, a los cuales se puede hacer referencia o los clinetes pueden llamar. El interior de un servicio debe ser invisible y sin importancia para los clientes. Este es el concepto "caja negra"de la programación orientada a objetos (OOP del inglés object-oriented programming). En OOP, cada objeto es independente y ningún otro objeto necesita saber cómo hace su trabajo. En el contexto del microcontenedor, los servicios se construyen desde POJOs. Un POJO es casi un servicio como tal pero no se puede acceder por un nombre único y debe ser creado por el cliente que lo necesita. Aunque un POJO se debe crear en tiempo de ejecución por parte del cliente, no es necesario implementarlo por medio de una clase separada con el fin de proporcionar una interfaz bien definida. Con tal de que no se borren los métodos y campos y el acceso a ellos no sea restringido, no hay necesidad de recompilar los clientes para utilizar un POJO recién creado. Nota El iImplementar una interfaz solo es necesario con el fin de permitir que un cliente escoja entre implementaciones opcionales. Si el cliente se compila frente a una interfaz, se pueden proporcionar muchas implementaciones diferentes de la interfaz sin tener que recompilar el cliente. La interfaz se asegura de que las firmas de método no cambien. El resto de este manual consiste de la creación del servicio de recursos humanos utilizando el microcontenedor para capturar y modularizar la lógica empresarial de la aplicación. Después de que el microcontenedor esté instalado, el código de ejemplo se encuentra en exam ples/User_Guide/gettingStarted/hum anResourcesService. 3.1. Introducción al ejemplo de recusos humanos Al familiarizarse con la estructura del directorio de los archivos en el ejemplo note que usa la estructura del directorio estándar Maven. Los archivos fuente Java se encuentran en los paquetes debajo del directorio exam ples/User_Guide/gettingStarted/hum anResourcesService/src/m ain/java/org/j boss/exam ple/service después de extraer el archivo Z IP. Cada una de estas clases representa un POJO simple que no implementa ninguna interfaz especial. La clase más importante es HRManager, la cual representa el punto de entrada del servicio proporcionando todos los métodos públicos que los clientes llamarán. Métodos que la clase HRManager proporciona addEm ployee(Employee employee) rem oveEm ployee(Employee employee) getEm ployee(String firstName, String lastName) getEm ployees() getSalary(Employee employee) setSalary(Employee employee, Integer newSalary) 16 Capítulo 3. Construcción de servicios isHiringFreeze() setHiringFreeze(boolean hiringFreeze) getSalaryStrategy() setSalaryStrategy(SalaryStrategy strategy) El servicio de recursos humanos está compuesto de unas pocas clases, las cuales mantienen una lista de empleados y sus detalles (direcciones y salarios, en este caso). Al utilizar la interfaz SalaryStrategy es posible configurar el HRManager de manera que hayan disponibles diferentes implementaciones de la estrategia de salario para poner límites mínimos y máximos en los salarios para diferentes roles de empleados. 3.2. Compilación del proyecto de ejemplo HRManager Para compilar el código fuente, escriba m vn com pile desde el directorio hum anResourcesService/. Esto crea un nuevo directorio llamado target/classes, el cual contiene las clases compiladas. Para limpiar el proyecto y borrar el directorio destino emita el comando m vn clean. 3.3. Creación de POJOs antes de poder utilizar un POJO, necesita crearlo. Necesita un mecanismo de nombrado que le permita registrar una referencia a la instancia POJO con un nombre. Los clientes necesitan este nombre para utilizar el POJO. El microcontenedor proporciona dicho mecanismo: un controlador. Un controlador le permite implementar sus servicios basados en POJO en un entorno en tiempo de ejecución. 3.3.1. Descriptores de implementación XML Después de compilar las clases, use un descriptor de implementación XML para crear instancias de ellas. El descriptor contiene una lista de beans representandos instancias individuales. Cada bean tiene un nombre único de manera que los clientes lo pueden llamar en tiempo de ejecución. El siguiente descriptor implementa una instancia del HRManager: <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"/> </deployment> Este XML crea una instancia de la clase HRManager y la registra con el nombre HRService. Este archivo se le pasa a un programa de implementación XML asociado con el microcontenedor en tiempo de ejecución, el cual realiza la implementación real e instancia los beans. 3.4. Conexión de POJOs Las instancias individuales POJO solo pueden proporcionar comportamientos relativamente simples. El verdadero poder de los POJOs viene de conectarlos entre ellos para realizar tareas complejas. ¿Cómo puede conectar POJOs para seleccionar diferentes implementaciones de estrategia de salarios? El siguiente descriptor de implementación XML hace justamente eso: 17 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> </bean> <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"/> </deployment> Este XML crea una instancia de la implementación de la estrategia de salario incluyendo un elemento <bean> adicional. Esta vez, se selecciona AgeBasedSalaryStrategy. Luego el código inyecta una referencia a este bean en la instancia de HRManager creada utilizando el bean HRService. La inyección es posible ya que la clase HRManager contiene un método setSalaryStrategy(SalaryStrategy strategy). Mientras tanto detrás de bastidores el microcontenedor JBoss llama a este método en la instancia HRManager recién creada y le pasa una referencia a la instancia AgeBasedSalaryStrategy. El descriptor de implementación XML causa la misma secuencia de eventos como si hubiera escrito el siguiente código: HRManager hrService = new HRManager(); AgeBasedSalaryStrategy ageBasedSalary = new AgeBasedSalaryStrategy(); hrService.setSalaryStrategy(ageBasedSalary); Además de realizar la inyección por medio de los métodos setter de las propiedades, el microcontenedor JBoss también puede realizar la inyección por medio de parámetros del constructor si es necesario. Para obtener mayores detalles consulte el capítulo sobre 'Inyección' en la parte II 'Desarrollo POJO.' 3.4.1. Consideraciones especiales Aunque es posible el crear instancias de clases utilizando el elemento <bean> en el descriptor de implementación, no siempre es la mejor manera. Por ejemplo, el crear instancias de las clases Em ployee y Address es innecesario ya que el cliente crea estas en respuesta a la entrada del usuario. T odavía son parte del servicio pero no se referencian en el descriptor de implementación. Realice comentarios en su código Puede definir múltiples beans dentro de un descriptor de implementación en tanto cada uno tenga un nombre único, el cual se utiliza para realizar inyecciones como se mostró anteriormente. Sin embargo, todos los beans no necesariamente representan servicios. Aunque un servicio se puede implementar utilizando un solo bean, usualmente se utilizan múltiples beans. Un bean usualmente representa el punto de entrada del servicio y contiene los métodos públicos que los clientes llaman. En este ejemplo, el punto de entrada es el bean HRService. El descriptor de implementación XML no indica si un bean representa un servicio o si un bean es el punto de entrada del servicio. Es una buena idea el utilizar comentariosy un esquema de nombrado obvio para diferenciar los beans de servicio de los beans que no son de servicio. 3.5. Trabajar con servicios Después de crear POJOs y conectarlos para formar servicios, necesita configurar los servicios, 18 Capítulo 3. Construcción de servicios probarlos y empacarlos. 3.5.1. Configuración de un servicio Los servicios se pueden configurar de dos maneras: Inyección de referencias entre instancias POJO Inyección de valores en propiedades POJO En este ejemplo se utiliza el segundo método. El siguiente descriptor de implementación configura la instancia de HRManager de la siguiente manera: Se implementa una congelación en la contratación La AgeBasedSalaryStrategy implementa un nuevo valor de salario mínimo y máximo. El inyectar referencias enter instancias POJO es una manera de configurar un servicio; sin embargo, también podemos inyectar valores en propiedades POJO. El siguiente descriptor de implementación muestra cómo podemos configurar la instancia HRManager para que realice una congelación en la contratación y para que la AgeBasedSalaryStrategy tenga un valor de salario mínimo y máximo: <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <property name="hiringFreeze">false</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> </bean> <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> </deployment> Las clases deben tener métodos setter públicos para las propiedades relevantes de manera que los valores se puedan inyectar. Por ejemplo, la clase HRManager tiene un método setHiringFreeze(boolean hiringFreeze) y la clase AgeBasedSalaryStrategy tiene los métodos setMinSalary(int m inSalary) y setMaxSalary(int m axSalary). Los valores en el descriptor de implementación se convierten de cadenas a los tipos relevantes (boolean, int, etc) por medio de PropertyEditors JavaBean. Muchos PropertyEditors se brindan por defecto para los tipos estándar, pero puede crear los propios si es necesario. Consulte el capítulo de propiedades en la parte II 'Desarrollo POJO' para obtener mayores detalles. 3.5.2. Probar un servicio Después de crear sus POJOs y de conectarlos para formar servicios, necesita probarlos. JBoss Microcontainer le permite la prueba de unidades de POJOs individuales así como de servicios por medio del uso de una clase MicrocontainerT est. La clase org.jboss.test.kernel.junit.MicrocontainerT est hereda de junit.fram ework.T estCase, configurando cada prueba realizando bootstrap en JBoss 19 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Microcontainer y agregando un BasicXMLDeployer. Luego busca la ruta de clase para un descriptor de implementación XML con el mismo nombre que la clase de prueba terminado en .xm l y que se encuentra en el nombre del paquete de la clase que representa la estructura del directorio. Cualquier bean que se encuentre en este archivo se implementan y luego se pueden acceder utilizando un método conveniente llamado getBean(String nam e). Puede encontrar ejemplos de estos descriptores de implementación en Ejemplo 3.1, “Listado del directorio src/test/resources”. Ejemplo 3.1. Listado del directorio src/test/resources ├── log4j.properties └── org └── jboss └── example └── service ├── HRManagerAgeBasedTestCase.xml ├── HRManagerLocationBasedTestCase.xml ├── HRManagerTestCase.xml └── util ├── AgeBasedSalaryTestCase.xml └── LocationBasedSalaryTestCase.xml El código de prueba se encuentra en el directorio the src/test/java : Ejemplo 3.2. Listado del directorio src/test/java └── org └── jboss └── example └── service ├── HRManagerAgeBasedTestCase.java ├── HRManagerLocationBasedTestCase.java ├── HRManagerTestCase.java ├── HRManagerTest.java ├── HRManagerTestSuite.java └── util ├── AgeBasedSalaryTestCase.java ├── LocationBasedSalaryTestCase.java └── SalaryStrategyTestSuite.java La clase HRManagerT est extiende MicrocontainerT est con el fin de configurar un número de empleados a utilizar como base para la prueba. Ejemplos individuales luego crean sub-clases en HRManagerT est para realizar el trabajo en sí. T ambién se includyen un par de clases T estSuite que se utilizan para agrupar ejemplos individuales por comodidad. Para ejecutar las pruebas escriba m vn test desde el directorio hum anResourcesService/. Debe ver alguna salida de registro DEBUG, el cual muestra a JBoss Microcontainer inicando e implementando los desde el archivo XML relevante antes de ejecutar cada prueba. Al final de la prueba se borra la implementación de los beans y se apaga el microcontenedor. 20 Capítulo 3. Construcción de servicios Nota Algunas de las pruebas tal como HRManagerT estCase, AgeBasedSalaryT estCase y LocationBasedSalaryT estCase prueban POJOs individuales. Otras pruebas tal como HRManagerAgeBasedT estCase y HRManagerLocationBasedT estCase prueban servicios enteros. De cualquier manera, las pruebas se ejecutan de la misma manera. El utilizar la clase MicrocontainerT est facilita el configurar y conducir pruebas completas de cualquier parte de su código. Las clases Address y Em ployee no se anidan aquí. El escribir pruebas para ellas queda de su parte. 3.5.3. Empacar un servicio Después de probar su servicio es hora de empacarlo de manera que otros puedan utilizarlo. La manera más simple de hacer esto es crear una JAR que contenga todas las clases. Puede escoger el incluir el descriptor de implementación si hay una manera predeterminada sensible de configurar el servicio, pero es opcional. Procedimiento 3.1. Empacar un servicio 1. Ponga el descriptor de implementación en el directorio MET A-INF (opcional) Si decide incluir el descriptor de implementación por convención se debe llamar jbossbeans.xm l y se debe poner en un directorio MET A-INF. Esta es la distribución predeterminada para la plataforma empresarial así que el programa de implementación JAR reconoce esta distribución y realiza la implementación de manera automática. El descriptor de implementación no se incluye en el ejemplo de recursos humanos ya que el servicio se configura modificando el descriptor directamente como un archivo a separado. 2. Generación de la JAR Para generar una JAR que contenga todas las clases compiladas introduzca m vn package desde el directorio hum anResourcesService/. 3. Hacer la JAR disponible para otros proyectos Maven Para hacer la JAR disponible para los otros proyectos Maven introduzca m vn install con el fin de copiarlo a su repositorio Maven local. La distribución final de la JAR se puede ver en Ejemplo 3.3, “Listado de los directorios org/jboss/exam ple/service y MET A-INF”. 21 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 3.3. Listado de los directorios org/jboss/exam ple/service y MET A-INF `-- org `-- jboss `-- example `-- service |-- Address.java |-- Employee.java |-- HRManager.java `-- util |-- AgeBasedSalaryStrategy.java |-- LocationBasedSalaryStrategy.java `-- SalaryStrategy.java `--META-INF `-- MANIFEST.MF `-- maven `-- org.jboss.micrcontainer.examples `-- humanResourceService Nota Maven crea automáticamente el directorio MET A-INF/m aven y no estará presente si está utilizando uns sistema de construcción diferente. 22 Capítulo 4. Uso de los servicios Capítulo 4. Uso de los servicios El capítulo anterior le mostró cómo crear, configurar, probar y empacar un servicio. El siguiente paso es crear un cliente, el cual realizará el trabajo utilizando el servicio. El cliente en este ejemplo usa un Text User Interface (TUI) para aceptar entradas del usuario y presentar los resultados. Esto reduce el tamaño y complejidad del código ejemplo. T odos los archivos necesarios se encuentran en el directorio exam ples/User_Guide/gettingstarted/com m andLineClient, el cual sigue la estructura del directorio estándar Maven, como se puede ver en Ejemplo 4.1, “Listar el directorio exam ples/User_Guide/gettingstarted/com m andLineClient”. Ejemplo 4 .1. Listar el directorio exam ples/User_Guide/gettingstarted/com m andLineClient ├── ├── │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── pom.xml src ├── main │ ├── assembly │ │ ├── aop.xml │ │ ├── classloader.xml │ │ ├── common.xml │ │ └── pojo.xml │ ├── config │ │ ├── aop-beans.xml │ │ ├── classloader-beans.xml │ │ ├── pojo-beans.xml │ │ └── run.sh │ ├── java │ │ └── org │ │ └── jboss │ │ └── example │ │ └── client │ │ ├── Client.java │ │ ├── ConsoleInput.java │ │ ├── EmbeddedBootstrap.java │ │ └── UserInterface.java │ └── resources │ └── log4j.properties └── test ├── java │ └── org │ └── jboss │ └── example │ └── client │ ├── ClientTestCase.java │ ├── ClientTestSuite.java │ └── MockUserInterface.java └── resources └── jboss-beans.xml target └── classes └── log4j.properties El cliente consiste de las tres clases y una interfaz que se encuentra en el directorio 23 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer org/jboss/exam ple/client. UserInterface describe los métodos que el cliente llama en tiempo de ejecución para solicitar lla entrada del usuario. ConsoleInput es una implementación de UserInterface que crea un T UI que el usuario utiliza para interactuar con el cliente. La ventaja de este diseño es que puede crear fácilmente una implementación Swing de UserInterface en una fecha posterior y puede reemplazar el T UI con un GUI. T ambién puede simular el proceso de ingreso de datos con un script. Luego puede chequear el comportamiento del cliente automáticamente usando ejemplos JUnit convencionales que se encuentran en Ejemplo 3.2, “Listado del directorio src/test/java”. Para que la construcción funcione primero debe construir e instalar auditAspect.jar desde el directorio exam ples/User_Guide/gettingStarted/auditAspect usando el m vn install com m and. Se crean un número de distribuciones diferentes de clientes, incluyendo una basada en AOP, la cual depende de que auditAspect.jar se encuentre disponible en el repositorio local Maven. Si anteriormente escribió m vn install desde el directorio exam ples/User_Guide/gettingStarted entonces hum anResourcesService.jar y auditAspect.jar ya se han construído y empacado junto con el cliente así que este paso no será necesario. Para compilar el código fuente, se realizan todos los pasos que se encuentran en Procedimiento 4.1, “Compilación del código fuente” al emitir el comando m vn package desde el directorio com m andLineClient. Procedimiento 4 .1. Compilación del código fuente 1. Ejecute las pruebas de las unidades. 2. construya una JAR cliente. 3. Ensamble una distribución que contenga todos los archivos necesarios. Después de compilar y empacar el cliente, la estructura del directorio en el diretcorio com m andLineClient/target incluye los subdirectorios descritos en Ejemplo 4.2, “Los subdirectorios del directorio com m andLineClient/target”. Ejemplo 4 .2. Los subdirectorios del directorio com m andLineClient/target client-pojo utilizado para llamar al servicio sin AOP. client-cl utilizado para demostrar las funcionalidades de carga de clase. client-aop Agregar soporte AOP. Consulte Capítulo 5, Agregar comportamiento con AOP para obtener mayores detalles. Cada sub-directorio representa una distribución diferente con todos los scripts shell, JARs y descriptores de implementación XML que se necesitan para ejecutar el cliente en diferentes configuraciones. El resto de este capítulo usea la distribución client-pojo que se encuentra en el 24 Capítulo 4. Uso de los servicios sub-directorio client-pojo, el cual se lista en Ejemplo 4.3, “Listado del directorio client-pojo”. Ejemplo 4 .3. Listado del directorio client-pojo |-|-|-| | | | | | | | | | | | `-- client-1.0.0.jar jboss-beans.xml lib |-- concurrent-1.3.4.jar |-- humanResourcesService-1.0.0.jar |-- jboss-common-core-2.0.4.GA.jar |-- jboss-common-core-2.2.1.GA.jar |-- jboss-common-logging-log4j-2.0.4.GA.jar |-- jboss-common-logging-spi-2.0.4.GA.jar |-- jboss-container-2.0.0.Beta6.jar |-- jboss-dependency-2.0.0.Beta6.jar |-- jboss-kernel-2.0.0.Beta6.jar |-- jbossxb-2.0.0.CR4.jar |-- log4j-1.2.14.jar `-- xercesImpl-2.7.1.jar run.sh Para ejecutar el cliente, cámbiese al directorio client-pojo y escriba ./run.sh. Aparecerá el T he Ejemplo 4.4, “Pantalla del menú de HRManager”. Ejemplo 4 .4 . Pantalla del menú de HRManager Menú: d) Implementar el servicio de recursos humanos u) Borrar la implementación del servicio de recursos humanos a) l) r) g) s) t) Agregar empleado Listar los empleados Borrar empleado Ver un salario Establecer un salario Alternar la congelación de la contratación m) Ver el menú p) Imprimir el estatus del servicio q) Salir > Para seleccionar una opción, introduzca la letra que se muestra en el lado izquierdo y oprima RET URN. Por ejemplo para ver lsa opciones del menú introduzca m y luego presione RET URN. El introducir más de una letra o el introducir una opción inválida genera un mensaje de error. 25 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Importante El script run.sh establece el entorno de ejecución agregando todas las JARs que se encuentran en el directorio lib/ a la ruta de clase usando la propiedad del sistema java.ext.dirs. T ambién agrega el directorio actual y la client-1.0.0.jar usando la eiqueta -cp de manera que el descriptor de implementación jboss-beans.xm l se encuentre en tiempo de ejecución junto con la clase org.jboss.exam ple.client.Client, la cual se llama para iniciar la aplicación. 4.1. Bootstrap del microcontenedor Antes de utilizar el cliente para implementar y llamar a su servicio, mire detalladamente lo que pasó durante la construcción: public Client(final boolean useBus) throws Exception { this.useBus = useBus; ClassLoader cl = Thread.currentThread().getContextClassLoader(); url = cl.getResource("jboss-beans.xml"); // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); bus = kernel.getBus(); } Primero que todo se creó una URL que representa el descriptor de implementación jboss-beans.xm l. Esto se necesita luego de manera que el programa de implementación XML pueda implementar y borrar la implementación de los beans declarados en el archivo. El método getResource() del cargador de clase de la aplicación se utiliza ya que se incluye el archivo jboss-beans.xm l en la ruta de clase. esto es opcional; el nombre y ubicación del descriptor de implementación no son importantes en tanto la URL sea válida y se pueda llegar a ella. Luego se crea una instancia de JBoss Microcontainer junto con un programa de implementación XML. Este proceso se llama bootstrapping y se proporciona una clase llamada BasicBootstrap como parte del microcontenedor para tener en cuenta la configuración programática. Para agregar un programa de implementación XML, extienda BasicBootstrap para crear una clase Em beddedBootstrap y sobrescriba el método protegido bootstrap() así: 26 Capítulo 4. Uso de los servicios public class EmbeddedBootstrap extends BasicBootstrap { protected BasicXMLDeployer deployer; public EmbeddedBootstrap() throws Exception { super(); } public void bootstrap() throws Throwable { super.bootstrap(); deployer = new BasicXMLDeployer(getKernel()); Runtime.getRuntime().addShutdownHook(new Shutdown()); } public void deploy(URL url) { ... deployer.deploy(url); ... } public void undeploy(URL url) { ... deployer.undeploy(url); ... } protected class Shutdown extends Thread { public void run() { log.info("Shutting down"); deployer.shutdown(); } } } El gancho shutdown se asegura de que cuando la MVJ termina, se borra la implementación de todos los beans en el orden correcto. Los métodos públicos deploy/undeploy delegan al BasicXMLDeployer de manera que los beans declarados en jboss-beans.xm l se puedan implementar y borrar. Finalmente las referencias al controlador del microcontenedor y el bus se reestablecen así que puede buscar las referencias de beans por su nombre y puede accederlas directamente o indirectamente cuando los necesite. 4.2. Implementación del servicio Después de crear el cliente puede implementar el servicio de recursos humanos. Esto se logra introduciendo la opción d del T UI. La salida indica que el BasicXMLDeployer ha analizado sintácticamente el archivo jboss-beans.xm l usando la URL y ha instanciado los beans que se encuentran adentro. 27 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Nota El microcontenedor puede instanciar los beans ya que sus clases están disponibles en la ruta de clase de extensión dentro del archivo lib/hum anResourcesService.jar. T ambién puede poner estas clases en una estructura de directorio expandido y agregarla a la ruta de clase de la aplicación, pero el empacarlos en una JAR usualmente es más conveniente. El descriptor de implementación es completamente separado del archivo hum anResourcesService.jar. Esto permite modificarlo para propósitos de pruebas. El archivo jboss-beans.xm l en el ejemplo contiene algunos fragmentos de XML comentados, lo cual muestra algunas de las configuraciones posibles. <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <!-- <property name="hiringFreeze">true</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> --> </bean> <!-- <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> <bean name="LocationBasedSalary" class="org.jboss.example.service.util.LocationBasedSalaryStrategy"> <property name="minSalary">2000</property> <property name="maxSalary">90000</property> </bean> --> </deployment> Importante Dependiendo de la manera en que acceda el servicio en tiempo de ejecución es posible que necesite apagar la aplicación y re-iniciarla para volver a implementar el servicio y ver sus cambios. Esto reduce la flexibilidad de la aplicación, pero incrementa el rendimiento en tiempo de ejecución. Opcionalmente puede simplemente volver a implementar el servicio mientras que la aplicación esté ejecutando. Esto incrementa la flexibilidad pero disminuye el rendimiento en tiempo de ejecución. Mantenga estas opciones en consideración al diseñar sus aplicaciones. 4.3. Acceso directo Si no se le pasan parámetros al script run.sh cuando se inicia el cliente entonces se busca una 28 Capítulo 4. Uso de los servicios referencia al bean HRService usando el controlador del microcontenedor después de que se ha implementado el servicio: private HRManager manager; ... private final static String HRSERVICE = "HRService"; ... void deploy() { bootstrap.deploy(url); if (!useBus && manager == null) { ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); } } } En lugar del buscar inmediatamente una referencia a la instancia del bean, el ejemplo primero busca una referencia a un ControllerContext, luego obtiene una referencia a la instancia del bean del contexto utilizando el método getT arget(). El bean puede existir dentro del microcontenedor en cualquiera de los estados listados en Estados de un Bean dentro del microcontenedor. Estados de un Bean dentro del microcontenedor NO_INST ALADO DESCRIT O INST ANCIADO CONFIGURADO INST ALADO Para mantener el registro de en qué estado se encuentra el bean, envuélvalo en otro objeto llamado un context, el cual describe el estado actual. El nombre del contexto es el mismo que el nombre del bean. Una vez que un contexto alcanza el estado INST ALADO entonces el bean que representa se considera implementado. Después de crear una referencia a la instancia del bean que representa el punto de entrada del servicio, puede llamar a los métodos en este para realizar tareas: @SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) ... else return manager.getEmployees(); } El cliente está accediendo el servicio directamente ya que está utilizando una referencia a la instancia 29 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer del bean real. El rendimiento es bueno ya que cada llamada de método va directamente al bean. Sin embargo, ¿Qué pasa si desea re-configurar el servicio y volver a implementarlo mientras la aplicación está ejecutando? La re-configuración se logra realizando cambios al descriptor de implementación XML y guardando el archivo. Con el fin de volver a implementar el servicio, se debe borrar la implementación de la instancia actual. Al borrar la implementación el controlador del microcontenedor libera su referencia a la instancia del bean, junto con los beans dependientes. Posteriormente estos beans se harán disponibles para el servicio de recolección de basura ya que la aplicación ya no los necesita más. El volver a implementar el servicio crea nuevas instancias del bean representando la nueva configuración. Cualquier búsqueda posterior de parte de los clientes recuperará las referencias a estas nuevas instancias y podrán volver a acceder el servicio re-configurado. El problema es que la referencia a la instancia del bean que representa nuestro punto de entrada del servicio va al caché cuando implementa el servicio por primera vez. El borrar la implementación del servicio no tienen ningún efecto ya que la instancia del bean todavía se puede acceder usando la referencia en caché y no irá a la basura hasta que el cliente la libere. De la misma manera, el implementar el servicio de nuevo no generará otra búsqueda ya que el cliente ya tiene una referencia en caché. Por lo tanto continuará utilizando la instancia del bean representando la configuración del servicio inicial. Puebe probar este comportamiento escribiendo u seguido de RET URN para borrar la implementación del servicio actual. Debe poder acceder el servicio todavía desde el cliente aunque haya 'borrado' la implementación. Luego, realice algunos cambios a la configuración utilizando el archivo jbossbeans.xm l, guarde el archivo y vuelva a implementarlo usando la opción d. Puede imprimir el estatus del servicio utilizando la opción p, la cual muestra que el cliente todavía está accediendo la instancia inicial del servicio que se implementó. Aviso Incluso si modifica el cliente para que busque una nueva referencia cada vez que el servicio se vuelve a implementar, es posible que los nuevos desarrolladores entreguen por error copias de esta referencia a otros objetos. Si todas estas referencias no se limpian al volver a realizar la implementación se puede presentar el mismo problema de caché. Para volver a implementar el servicio reconfigurado de manera confiable, apague la aplicación completamente utilizando la opción 'q' y reinícela usando el script run.sh. Para servicios empresariales tal como transacciones, mensajería y persistencia este es un comportamiento completamente aceptable ya que generalmente se utilizan. No se pueden volver a implementar en tiempo de ejecución y también se benefician del alto rendimiento dado por el uso del acceso directo. Si su servicio cae en esta categoria, considere el utilizar el acceso directo por medio del controlador del microcontenedor. 4.4. Acceso indirecto El script run.sh se puede llamar con un parámetro opcional bus, el cual hace que las llamadas al servicio de recursos humanos utilicen el bus del microcontenedor. En lugar de utilizar una referencia directa a la instancia del bean que se obtuvo del controlador microcontenedor, el nuevo comportamiento es llamar a un método invoke() en el bus, pasando el nombre del bean, el nombre del método, los argumentos del método y los tipos de método. El bus usa esta información para llamar al bean de parte del cliente. 30 Capítulo 4. Uso de los servicios private final static String HRSERVICE = "HRService"; ... @SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) return (Set<Employee>) invoke(HRSERVICE, "getEmployees", new Object[] {}, new String[] {}); else return manager.getEmployees(); } private Object invoke(String serviceName, String methodName, Object[] args, String[] types) { Object result = null; try { result = bus.invoke(serviceName, methodName, args, types); } catch (Throwable t) { t.printStackTrace(); } return result; } El bus busca la referencia a la instancia del bean nombrado y llama al método seleccionado usando la reflexión. El cliente nunca tiene una referencia directa a la instancia del bean así que se dice que accede al servicio indirectamente. Ya que el bus no pone en el caché la referencia puede realizar de manera segura los cambios a la configuración del servicio y se puede volver a implementar en tiempo de ejecución. Las llamadas posteriores por parte del cliente utilizarán la nueva referencia tal como se espera. El cliente y el servicio han sido desvinculados. Nota Este comportamiento se puede probar implementado el servicio y utilizando la opción p para imprimir el estatus. Borre la implementación del servicio utilizando la opción u y observe que es inaccesible. Luego realice algunos cambios al archivo jboss-beans.xm l, guarde los cambios e implemente de nuevo usando la opción d. Imprima el estatus de nuevo usando la opción p. El cliente está accediendo la nueva configuración del servicio. Ya que el bus usa la refleción para llamar instancias del bean, es un poco más lento que el acceso directo. El beneficio del enfoque es que solo el bus tiene referencias a las instancias del bean. Cuando un servicio se vuelve a implementar, todas las referencias existentes se pueden limpiar y reemplazar con las nuevas. De esta manera, se puede volver a implementar de manera segura un servicio en tiempo de ejecución. Los servicios que no se utilizan con tanta frecuencia o que son especificos para ciertas aplicaciones son buenos candidatos para acceso indirect usando el bus del microcontenedor. Con frecuencia la reducción en el rendimiento es superior a la flexibilidad que proporciona. 4.5. Carga de clase dinámica Hasta ahora ha utilizado los cargadores de clase de aplicación y extensión para cargar todas las clases en la aplicación. La ruta de clase de la aplicación se configura por medio del script run.sh utilizando la etiqueta -cp para incluir el directorio actual y la client-1.0.0.jar como se puede ver aquí: 31 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1 Por comodidad las JARs en el directorio lib se agregaron a la extensión de la ruta de clase del cargador de clase usando la propiedad del sistema java.ext.dirs, en lugar de listar la ruta completa para cada una de las JARs después de la etiqueta -cp. Ya que la extensión classloader es padre de la aplicación classloader, las clases del cliente pueden encontrar todas las clases del microcontenedor y las clases del servicio de recursos humanos en tiempo de ejecución. Nota Con las versiones de Java 6 y posteriores puede utilizar un comodín para incluir todas las JARs en un directorio con la etiqueta -cp: java -cp `pwd`/lib/* :.:client-1.0.0.jar org.jboss.exam ple.client.Client $1 Aquí todas las clases en la aplicació se agregarán a la ruta de clase del cargador de clase de la aplicación y extensión de la ruta de clase del cargador de clase retendrá su valor predeterminado. ¿Qué pasa si necesita implementar un servicio adicional en tiempo de ejecución? Si el nuevo servicio está empacado en un archivo JAR debe ser visible para un cargador de clase antes de que cualquiera de sus clases puedan ser cargadas. Ya que ya configuró la ruta de clase para el cargador de clase de la aplicación (y el cargador de clase de extensión) en el arranque, no es fácil el agregar la URL de la JAR. La misma situación aplica si las clases del servicio se encuentran en una estructura de directorio. A menos de que el directorio a nivel superior se encuentre en el directorio actual (el cual está en la ruta de clase de la aplicación) entonces el cargador de clase de la aplicación no encoentrará las clases. Si desea volver a implementar un servicio existente cambiando algunas de sus clases, necesita trabajar teniendo en cuenta las restricciones de seguridad, las cuales le impiden a un cargador de clase existente el volver a cargar las clases. La meta es crear un nuevo cargador de clases que conozca la ubicación de las clases del nuevo servicio o que pueda cargar nuevas versiones de las clases de un servicio ya existente con el fin de implementar los beans del servicio. JBoss Microcontainer usa el elemento <classloader> en el descriptor de implementación para lograr esto. La distribución client-cl contiene el archivo listado en el Ejemplo 4.5, “Listado del directorio com m andLineClient/target/client-cl” . 32 Capítulo 4. Uso de los servicios Ejemplo 4 .5. Listado del directorio com m andLineClient/target/client-cl |-- client-1.0.0.jar |-- jboss-beans.xml |-- lib | |-- concurrent-1.3.4.jar | |-- jboss-common-core-2.0.4.GA.jar | |-- jboss-common-core-2.2.1.GA.jar | |-- jboss-common-logging-log4j-2.0.4.GA.jar | |-- jboss-common-logging-spi-2.0.4.GA.jar | |-- jboss-container-2.0.0.Beta6.jar | |-- jboss-dependency-2.0.0.Beta6.jar | |-- jboss-kernel-2.0.0.Beta6.jar | |-- jbossxb-2.0.0.CR4.jar | |-- log4j-1.2.14.jar | `-- xercesImpl-2.7.1.jar |-- otherLib | `-- humanResourcesService-1.0.0.jar |`-- run.sh Se ha movido el archivo hum anResourcesService.jar a un nuevo sub-directorio llamado otherLib. Ya no está disponible para los cargadores de clase de extensión o de la aplicación, coya ruta de clase se configura en el script run.sh: java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1 Para solucionar esto cree un nuevo cargador de clase durante la implementación del servicio, cárguela en las clases del servicio y cree instancias de los beans. Para ver cómo se logra esto vea el contenido del archivo jboss-beans.xm l: 33 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="URL" class="java.net.URL"> <constructor> <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_Gui de/gettingStarted/commandLineClient/target/clientcl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter> </constructor> </bean> <bean name="customCL" class="java.net.URLClassLoader"> <constructor> <parameter> <array> <inject bean="URL"/> </array> </parameter> </constructor> </bean> <bean name="HRService" class="org.jboss.example.service.HRManager"> <classloader><inject bean="customCL"/></classloader> <!-- <property name="hiringFreeze">true</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> --> </bean> <!-- <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> <bean name="LocationBasedSalary" class="org.jboss.example.service.util.LocationBasedSalaryStrategy"> <property name="minSalary">2000</property> <property name="maxSalary">90000</property> </bean> --> </deployment> 1. Primero cree una instancia de java.net.URL llamada URL, usando la inyección de parámetros en el constructor para especificar la ubicaci´on del archivo hum anResourcesService.jar en el sistema local de archivos. 2. Luego, cree una instancia de un URLClassLoader inyectando el bean URL en el constructor como único elemento en el array. 3. Incluya un elemento <classloader> en su definición de bean HRService e inyecte el bean custom CL. Esto especifica que la clase HRManager necesita ser cargada por el cargador de clase customCL. 34 Capítulo 4. Uso de los servicios Necesita una manera de decidir cuál cargador de clase utilizar para los otros beans en la implementación. T odos los beans en la implementación utilizan el cargador de clase del contexto del hilo actual. En este caso el hilo que maneja la implementación es el hilo principal de la aplicación, el cual tiene su cargador de clase de contexto establecido para el cargador de clase de la aplicación durnate el arranque. Si desea puede especificar un cargador de clase diferente para toda la implementación utilizando un elemento <classloader> como se puede ver en Ejemplo 4.6, “Especificar un cargador de clase diferente”. Ejemplo 4 .6. Especificar un cargador de clase diferente <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <classloader><inject bean="customCL"/></classloader> <bean name="URL" class="java.net.URL"> <constructor> <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_G uide/gettingStarted/commandLineClient/target/clientcl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter> </constructor> </bean> <bean name="customCL" class="java.net.URLClassLoader"> <constructor> <parameter> <array> <inject bean="URL"/> </array> </parameter> </constructor> </bean> ... </deployment> Esto sería necesario para permitir la reconfiguración del service al borrar los comentarios de los beans AgeBasedSalary o LocationBasedSalary. Los cargadores de clase especificados a nivel del bean sobreescriben el cargador de clase a nivel de la implementación. Para sobreescribir el cargador de clase a nivel de implementación y utilizar el cargador de clase predeterminado para un bean, use el valor <null/> así: 35 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer <bean name="HRService" class="org.jboss.example.service.HRManager"> <classloader><null/></classloader> </bean> 4.5.1. Problemas con cargadores de clase creados con los descriptores de implementación Si crea un nuevo cargador de clase para su servicio utilizando el descriptor de implementación es posible que no pueda acceder las clases cargadas por este desde el cargador de clase de la aplicación. En el ejemplo HRManager, el cliente ya no puede poner en caché una referencia directa a la instancia del bean al utilizar el controlador del microcontenedor. Para ver este comportamiento inicie el cliente usando el comando run.sh y luego trate de implementar el servicio. Se presenta una excepción java.lang.NoClassDefFoundError y la aplicación termina. En este escenario debe usar el bus para acceder el servicio indirectamente y proporcionar acceso a cualquier clase compartida por el cliente en la ruta de clase de la aplicación. En este ejemplo, las clases afectadas son Address, Em ployee y SalaryStrategy. 36 Capítulo 5. Agregar comportamiento con AOP Capítulo 5. Agregar comportamiento con AOP La programación orientada a objetos (OOP del inglés Object Oriented Programming) tiene muchas técnicas útiles para el desarrollo de software incluyendo la encapsulación, herencia y polimorfismo. Sin embargo, no soluciona el problema de la lógica de direccionamiento que con frecuencia se repite en muchas clases diferentes. Ejemplos de esto incluye los registros, seguridad y lógica transaccional, la cual es tradicionalmente codificada a fuego en cada clase. Este tipo de lógica se llama un asunto de corte transversal. La programación orientada a aspectos (AOP del ingles Aspect Oriented Programming funciona para permitir el aplicar asuntos de corte transversal a las clases después de que se han compilado. Esto mantiene el código fuente libre de lógica, lo cual no es una parte central del propósito principal de la clase y optimiza el mantenimiento. El método depende de la implementación AOP. Usualmente si una clase implementa una interfaz, cada llamada de método a una instancia de la clase primero pasa por un proxy. Este proxy implementa la misma interfaz, agregando el comportamiento requerido. Opcionalmente, si no se utiliza una interfaz entonces el código byte java de la clase compilada se modifica: los métodos originales se re-nombran y se reemplazan con métodos que implementan la lógica de corte transversal. Luego estos nuevos métodos llaman a los métodos originales después de haber ejecutado la lógica de corte transversal. Otro método para lograr el mismo resultado es modificar el código byte para crear una subclase de la clase original que sobreescribe sus métodos. Los métodos sobreescritos luego ejecutan la lógica de corte transversal antes de llamar los métodos correspondiente de la super clase. JBoss AOP es un marco de trabajo para AOP. Utilizándolo puede crear asuntos de corte transversal utilizando métodos y clases java convencionales. En terminologia de AOP cada asunto está representado por un aspecto que usted implementa utilizando un POJO simple. El comportamiento lo proporciona los métodos dentro del aspecto llamado consejos. Estos consejos siguen ciertas reglas para su parámetro y retornan tiposy cualquier excepción que presenten. Dentro de este marco de trabajo puede utilizar nociones convencionales orientadas a objetos tal como la herencia, encapsulación y composición para hacer que sus asuntos de corte transversal sean fáciles de mantener. Los aspectos se aplican al código utilizando un lenguaje de expresiones que le permite especificar los constructores, los métodos e incluso los campos de destino. Puede cambiar rápidamente el comportamiento de múltiples clases modificando el archivo de configuración. Este capítulo contiene ejemplos, los cuales demuestran cómo utilizar JBoss AOP junto con el microcontenedor para crear y aplicar un aspecto de auditoría al servicio de recursos humanos. El código de auditoría se puede poner dentro de la clase HRManager, pero llenaría la clase con código que no es relevante para su propósito principal, expandiéndola y haciendo más dificil el mantenerla. El diseño del aspecto también proporciona modularidad, facilitando el auditar otras clases en el futuro, si el ámbito del proyecto cambia. AOP también se puede utilizar para aplicar comportamiento adicional durante la fase de implementación. Este ejemplo creará y enlazará un proxy a una instancia bean en un servicio básico JNDI, permitiendo accederlo utilizando una búsqueda JNDI en lugar del controlador del microcontenedor. 5.1. Creación de un aspecto El directorio exam ples/User_Guide/gettingStarted/auditAspect contiene todos los archivos necesarios para crear el aspecto. pom .xm l src/m ain/java/org/jboss/exam ple/aspect/AuditAspect.java 37 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 5.1. POJO de ejemplo public class AuditAspect { private String logDir; private BufferedWriter out; public AuditAspect() { logDir = System.getProperty("user.dir") + "/log"; File directory = new File(logDir); if (!directory.exists()) { directory.mkdir(); } } public Object audit(ConstructorInvocation inv) throws Throwable { SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy-kkmmss"); Calendar now = Calendar.getInstance(); String filename = "auditLog-" + formatter.format(now.getTime()); File auditLog = new File(logDir + "/" + filename); auditLog.createNewFile(); out = new BufferedWriter(new FileWriter(auditLog)); return inv.invokeNext(); } public Object audit(MethodInvocation inv) throws Throwable { String name = inv.getMethod().getName(); Object[] args = inv.getArguments(); Object retVal = inv.invokeNext(); StringBuffer buffer = new StringBuffer(); for (int i=0; i < args.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append(args[i].toString()); } if (out != null) { out.write("Method: " + name); if (buffer.length() > 0) { out.write(" Args: " + buffer.toString()); } if (retVal != null) { out.write(" Return: " + retVal.toString()); } out.write("\n"); out.flush(); } return retVal; } } 38 Capítulo 5. Agregar comportamiento con AOP Procedimiento 5.1. Creación del POJO 1. El constructor chequea a ver si hay un directorio log en el directorio actual de trabajo y lo crea si no lo encuentra. 2. Luego se define un aviso. Se llama a este aviso cuando se llama al constructor de la clase de destino. Esto crea un nuevo archivo de registro dentro del directorio log para registrar la llamadas de métodos realizadas en diferentes instancias de la clase destino en archivos separados. 3. Finalmente se define otro aviso. Este aviso aplica a cada llamada de método realizada en la clase destino.El nombre del método y los argumentos se almacenan junto con el valor de retorno. Esta información se utiliza para construir un registro de auditoría y escribirlo en el archivo de registro actual. Cada aviso llama a inv.invokeNext(), el cual encadena los avisos si se ha aplicado más de un asunto de corte transversal o para llamar el constructor/método destino. Nota Cada aviso se implementa utilizando un método que toma un objeto de invocación como parámetro, presenta T hrowable y retorna Object. En el momento del diseño no se sabe a qué constructores o métodos se aplicarán estos avisos así que haga los tipos tan genéricos como sea posible. Para compilar la clase y crear un archivo auditAspect.jar que lo puedan utilizar otros ejemplos, escriba m vn install desde el directorio auditAspect. 5.2. Configuración del microcontenedor para AOP Antes de aplicar el aspecto de auditoría al servicio de recursos humanos, se debe agregar un número de JARs a la ruta de clase de la extensión. Se encuentran en el sub-directorio lib de la distribución client-aop que se encuentra en el directorio exam ples/User_Guide/gettingStarted/com m andLineClient/target/client-aop.dir: 39 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 5.2. Listado del directorio examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-`-|-`-`-- client-1.0.0.jar jboss-beans.xml lib auditAspect-1.0.0.jar concurrent-1.3.4.jar humanResourcesService-1.0.0.jar javassist-3.6.0.GA.jar jboss-aop-2.0.0.beta1.jar jboss-aop-mc-int-2.0.0.Beta6.jar jboss-common-core-2.0.4.GA.jar jboss-common-core-2.2.1.GA.jar jboss-common-logging-log4j-2.0.4.GA.jar jboss-common-logging-spi-2.0.4.GA.jar jboss-container-2.0.0.Beta6.jar jboss-dependency-2.0.0.Beta6.jar jboss-kernel-2.0.0.Beta6.jar jbossxb-2.0.0.CR4.jar log4j-1.2.14.jar trove-2.1.1.jar xercesImpl-2.7.1.jar log auditLog-18062010-122537 run.sh Primero, se necesita lib/auditAspect-1.0.0.jar para crear una instancia del aspecto en tiempo de ejecución con el fin de ejecutar la lógica. Luego el archivo jar para JBoss AOP (jboss-aop.jar) junto con sus dependencias javassist y trove, agrega la funcionalidad AOP. Finalmente, se necesita la jar jboss-aop-mc-int ya que contiene una definición de esquema XML que le permite definir aspectos dentro de un descriptor de implementación XML. T ambién contiene código de integración para crear dependencias entre beans normales y beans de aspecto dentro del microcontenedor, permitiéndole agregar comportamiento durante las fases de implementación y de borrado de la implementación. Ya que está utilizando Maven2 para montar la distribución cliente-aop, debe agregar estos archivos JAR declarando las dependencias apropiadas en su archivo pom .xm l y creando un descriptor de ensamblaje válido. Puede ver un ejemplo de pom .xm l en Ejemplo 5.3, “Extracto de ejemplo pom .xm l para AOP”. Para realizar su construcción utilizando Ant, el procedimiento será diferente. 40 Capítulo 5. Agregar comportamiento con AOP Ejemplo 5.3. Extracto de ejemplo pom .xm l para AOP <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-oap</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>javassist</artifactId> <version>3.6.0.GA</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>trove</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-aop-mc-int</artifactId> <version>2.0.0.Beta6</version> </dependency> 5.3. Aplicación de un aspecto Ahora que tiene una distribución válida que contiene todo lo que necesitad, puede configurar jbossbeans.xm l para aplicar el aspecto de auditoría. Se encuentra en exam ples/User_Guide/gettingStarted/com m andLineClient/target/client-aop.dir. <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="AuditAspect" class="org.jboss.example.aspect.AuditAspect" method="audit" pointcut="execution(public org.jboss.example.service.HRManager->new(..)) OR execution(public * org.jboss.example.service.HRManager->*(..))"> </aop:aspect> ... </deployment> 41 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Procedimiento 5.2. Explicación del código para aplicar un aspecto 1. Antes de que pueda aplicar su aspecto a cualquier clase necesita crear una instancia de org.jboss.aop.AspectManager usando un elemento <bean>. Aquí se utiliza un método de fábrica en lugar de llamar un constructor convencional ya que solo es necesario una instancia del AspectManager en la MVJ en tiempo de ejecución. 2. Luego se crea una instancia de nuestro aspecto llamada AuditAspect, utilizando el elemento <aop:aspect>. Este se ve similar al elemento <bean> ya que tiene los atributos name y class que se utilizan de la misma manera. Sin embargo, también tiene los atributos method y pointcut que puede utilizar para aplicar o enlazar un consejo dentro del aspecto para los constructores y métodos dentro de otras clases. Estos atributos enlazan el consejo de auditoría a todos los constructores y métodos públicos dentro de la clase HRManager. Sólo es necesario especificar el método audit ya que ha sido sobrecargado dentro de la clase AuditAspect con diferentes parámetros. JBoss AOP sabe cuál seleccionar en tiempo de ejecución dependiendo de si se está realizando una invocación de método o constructor. Esta configuración adicional es todo lo que se necesita para aplicar el aspecto de auditoría en tiempo de ejecución agregando un comportamiento de auditoría al servicio Human Resources. Puede probar esto ejecutando el cliente utilizando el script run.sh. Se crea un directorio log al iniciar junto con el directorio lib cuando el microcontenedor crea el bean AuditAspect. Cada implementación del servicio de recursos humanos hace que aparezca un nuevo archivo de registros dentro del directorio log. El archivo de registro contiene un registro de las llamadas realizadas del cliente al servicio. Se llama algo como auditLog-28112007-163902 y contiene salidas similares a Ejemplo 5.4, “Ejemplo de la salida del registro AOP”. Ejemplo 5.4 . Ejemplo de la salida del registro AOP Method: getEmployees Return: [] Method: addEmployee Args: (Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860) Return: true Method: getSalary Args: (Santa Claus, null - Birth date unknown) Return: 10000 Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)] Method: isHiringFreeze Return: false Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)] Method: getSalaryStrategy Para borrar el comportamiento de auditoría, comente los fragmentos relevantes de XML en el descriptor de implementación y re-inicie la aplicación. Aviso El orden de la implementación sí importa. Especificamente cada aspecto debe ser declarado antes de los beans a los que aplica, así que el microcontenedor los implementa en ese orden. Esto se debe a que es posible que el microcontenedor tenga que alterar el código byte de la clase bean para agregar la lógica cross-cutting, antes de crear una instancia y almacena una referencia a esta en el controlador. Si ya se ha creado una instancia normal del bean entonces no es posible. 42 Capítulo 5. Agregar comportamiento con AOP 5.4. Callbacks del ciclo de vida Además de aplicar aspectos a los beans que instanciamos utilizando el microcontenedor también podemos agregar comportamiento durante el proceso de implementación y de borrado de la implementación. T al como lo mencinamos en Sección 4.3, “Acceso directo”, un bean atravieza varios estados diferentes cuando se implementa. Estos incluyen: NOT _INST ALLED el descriptor de implementación que contiene el bean ha sido analizado sintácticamente junto con cualquier anotación en el bean mismo. DESCRIBED cualquier dependencia que AOP ha creado ha sido agregada al bean y se han procesado las anotaciones personalizadas. INST ANT IAT ED se ha creado una instancia del bean. CONFIGURED se han inyectado las propiedades en el bean junto con cualquier otra referencia a los otros beans. CREAT E se ha llamado al método create, si está definido en el bean. ST ART se ha llamado al método start, si está definido en el bean. INST ALLED cualquier acción de instalación personalizada que se definió en el descriptor de implementación se ha ejecutado y está listo para acceder al bean. Importante Los estados CREAT E y ST ART están incluídos por razones de legado. Esto le permite a los servicios que estaban implementados como MBeans en versiones anteriores de la plataforma empresarial el funcionar correctamente cuando se implementan como beans en la plataforma empresarial 5.1. Si no define ningún método crear/inicar correspondientes en su bean, pasará derecho por estos estados. Estos estados representan el ciclo de vida del bean. Puede definir un número de callbacks para que se apliquen en cualquier momento utilizando un grupo adicional de elementos <aop>. <aop:lifecycle-describe> aplicado al entrar/salir del estado DESCRIBED 43 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer <aop:lifecycle-instantiate> aplicado al entrar/salir del estado INST ANT IAT ED <aop:lifecycle-configure> aplicado al entrar/salir del estado CONFIGURED <aop:lifecycle-create> aplicado al entrar/salir del estado CREAT E <aop:lifecycle-start> aplicado al entrar/salir del estado ST ART <aop:lifecycle-install> aplicado al entrar/salir del estado INST ALLED Así como los elementos <bean> y <aop:aspect>, los elementos <aop:lifecycle-> contienen atributos name y class. El microcontenedor usa estos atributos para crear una instancia de la clase callback, nombrándola de manera que se pueda utilizar cuando los beans entren o salgan del estado relevante durante la implementación y durante el borrado de la implementación. Puede especificar cuáles beans son afectados por el callback utilizando el atributo clases como se puede ver en Ejemplo 5.5, “Uso del atributo classes”. Ejemplo 5.5. Uso del atributo classes <aop:lifecycle-install xmlns:aop="urn:jboss:aop-beans:1.0" name="InstallAdvice" class="org.jboss.test.microcontainer.support.LifecycleCallback" classes="@org.jboss.test.microcontainer.support.Install"> </aop:lifecycle-install> Este código especifica que la lógica adicional en la clase lifecycleCallback se aplica a cualquier clase de bean que esté anotada con @org.jboss.test.microcontainer.support.Install antes de que entren y luego de que han dejado el estado INST ALLED. Para que la clase callback funcione, debe contener los métodos install y uninstall que toman ControllerContext como parámetro, como se puede ver en Ejemplo 5.6, “Métodos de instalación y desinstalación”. 44 Capítulo 5. Agregar comportamiento con AOP Ejemplo 5.6. Métodos de instalación y desinstalación import org.jboss.dependency.spi.ControllerContext; public class LifecycleCallback { public void install(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being installed"; } public void uninstall(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being uninstalled"; } } El método install se llama durante la implementación del bean y se llama al método uninstall durante el borrado de la implementación. Nota Aunque se está agregando comportamiento al proceso de implementación y de borrado de la implementación utilizando callbacks, AOP de ehcho no se utiliza aquí. La funcionalidad pointcut expression de JBoss AOP se utiliza para determinar a cuáles clases bean se aplican los comportamientos. 5.5. Agregar búsquedas de servicios por medio de JNDI Hasta ahora ha utilizado el microcontenedor para buscar referencias a instancias del bean que representan servicios. Esto no es ideal ya que requiere una referencia al kernel del microcontenedor antes de poder acceder al controlador. Esto se puede ver en Ejemplo 5.7, “Búsqueda de referencias a los Beans”. 45 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 5.7. Búsqueda de referencias a los Beans private private private private private HRManager manager; EmbeddedBootstrap bootstrap; Kernel kernel; KernelController controller; final static String HRSERVICE = "HRService"; ... // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); ... ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); } El entregar referencias del kernel a todos los clientes que buscan un servicio es un riesgo de seguridad ya que proporciona un amplio acceso a la configuración del microcontenedor. Para mayor seguridad aplique el patrón ServiceLocator y utilice una clase para realizar búsquedas de parte de los clientes. Incluso puede pasar las referencias del bean junto con sus nombres al ServiceLocator en el momento de la implementación utilizando un callback del ciclo de vida. En ese escenario, el ServiceLocator puede buscarlos sin llegar a saber del microcontenedor. El borrado de la implementación posteriormente borraría las referencias del bean del ServiceLocator para evitar más búsquedas. No sería dificil es escribir su propia implementación ServiceLocator. En integrar una ya existente tal como JBoss Naming Service (JBoss NS) es incluso más rápido y tiene el beneficio adicional de cumplir con la especificación de JNDI - Java Naming and Directory Interface. JNDI habilita a los clientes para acceder diferentes y posiblemente múltiples servicios de nombrado utilizando una API común. Procedimiento 5.3. Escritura de su propia implementación ServiceLocator 1. Primero cree una instancia de JBoss NS usando el microcontenedor. 2. Luego, agregue un callback del ciclo de vida para realizar el enlace y des-enlace de las referencias bean durante la implementación y durante el borrado de la implementación. 3. Marque las clases bean a las que desea enlazar sus referencias, utilizando anotaciones. 4. Ahora puede ubicar los beans en tiempo de ejecución utilizando la expresión como se mostró anteriormente. 46 Parte II. Conceptos avanzados con el microcontenedor Parte II. Conceptos avanzados con el microcontenedor Esta sección aborda conceptos avanzados y muestra algunas funcionalidades interesantes del microcontenedor. Se asume que los ejemplos de código en el resto del manual son ejemplos incompletos y es responsabilidad del programador el extrapolarlos y extenderlos como sea necesario. 47 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Capítulo 6. Modelos de componentes El JBoss Microcontainer funciona dentro de varios modelos de componentes POJO populares. Los componentes son programas de software re-utilizables que puede desarrollar y ensamblar fácilmente para crear aplicaciones sofisticadas. Una de las metas clave del microcontenedor es la integración efectiva con estos modelos de componentes. Algunos modelos de componentes populares que se pueden utilizar con el microcontenedor son JMX, Spring y Guice. 6.1. Interacciones permitidas con los modelos de componentes Antes de discutir la interacción como algunos de los modelos de componentes populares, es importante el comprender qué tipos de interacciones se permiten. JMX MBeans son un ejemplo de un modelo de componentes. Sus interacciones incluyen la ejecución de operaciones MBean, referencia a atributos, el configurar atributos y la declaración explícita de dependencias entre las MBeans nombradas. Las interacciones y comportamientos predeterminados en el microcontenedor son lo que normalmente obtiene de cualquier otro contenedor Inversión de Control (IoC del inglés >Inversion of Control) y es similar a la funcionalidad que los MBeans proporcionan, incluyendo simples invocaciones de método para operaciones, setters/getters para los atributos y dependencias explícitas. 6.2. Un Bean sin dependencias Ejemplo 6.1, “Descriptor de implementación para un POJO simple” muestra un descriptor de implementación para un POJO simple sin dependencias. Este es el punto inicial para integrar el microcontenedor con Spring o Guice. Ejemplo 6.1. Descriptor de implementación para un POJO simple <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="PlainPojo" class="org.jboss.demos.models.plain.Pojo"/> <beanfactory name="PojoFactory" class="org.jboss.demos.models.plain.Pojo"> <property name="factoryClass">org.jboss.demos.models.plain.PojoFactory</property> </beanfactory> </deployment> 6.3. Uso del microcontenedor con Spring 48 Capítulo 6. Modelos de componentes Ejemplo 6.2. Descriptor con soporte para Spring <beans xmlns="urn:jboss:spring-beans:2.0"> <!-- Adding @Spring annotation handler --> <bean id="SpringAnnotationPlugin" class="org.jboss.spring.annotations.SpringBeanAnnotationPlugin" /> <bean id="SpringPojo" class="org.jboss.demos.models.spring.Pojo"/> </beans> El espacio de nombre del archivo es diferente del archivo del bean del microcontenedor. El espacio de nombre urn:jboss:spring-beans:2.0 apunta a su versión del puerto del esquema Spring, el cual describe el estilo Spring de su bean. El microcontenedor implementa los beans a manera opuesta de la noción de fábrica de beans de Spring. Ejemplo 6.3. Uso de Spring con el microcontenedor public class Pojo extends AbstractPojo implements BeanNameAware { private String beanName; public void setBeanName(String name) { beanName = name; } public String getBeanName() { return beanName; } public void start() { if ("SpringPojo".equals(getBeanName()) == false) throw new IllegalArgumentException("Name doesn't match: " + getBeanName()); } } Aunque el bean SpringPojo tiene una dependencia en la biblioteca de Spring causada por la implementación de la interfaz BeanNameAware, su único propósito es exponer e imitar algo del comportamiento del callback de Spring. 49 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer El enfoque de Guice es el acomplamiento de tipos. Los beans Guice se generan y se configuran usando módulos. Ejemplo 6.4 . Descriptor de implementación para la integración Guice en el microcontenedor <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="GuicePlugin" class="org.jboss.guice.spi.GuiceKernelRegistryEntryPlugin"> <constructor> <parameter> <array elementClass="com.google.inject.Module"> <bean class="org.jboss.demos.models.guice.PojoModule"/> </array> </parameter> </constructor> </bean> </deployment> Dos partes importantes de observar en este archivo son PojoModule y GuiceKernelRegistryEntryPlugin. PojoModule configura sus beans tal como en Ejemplo 6.5, “Configuración de Beans para Guice”. GuiceKernelRegistryEntryPlugin proporciona integración con el microcontenedor tal como se puede ver en Ejemplo 6.6, “Integración mock con el microcontenedor”. 50 Capítulo 6. Modelos de componentes Ejemplo 6.5. Configuración de Beans para Guice public class PojoModule extends AbstractModule { private Controller controller; @Constructor public PojoModule(@Inject( bean = KernelConstants.KERNEL_CONTROLLER_NAME) Controller controller) { this.controller = controller; } protected void configure() { bind(Controller.class).toInstance(controller); bind(IPojo.class).to(Pojo.class).in(Scopes.SINGLETON); bind(IPojo.class).annotatedWith(FromMC.class). toProvider(GuiceIntegration.fromMicrocontainer(IPojo.class, "PlainPojo")); } } 51 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 6.6. Integración mock con el microcontenedor public class GuiceKernelRegistryEntryPlugin implements KernelRegistryPlugin { private Injector injector; public GuiceKernelRegistryEntryPlugin(Module... modules) { injector = Guice.createInjector(modules); } public void destroy() { injector = null; } public KernelRegistryEntry getEntry(Object name) { KernelRegistryEntry entry = null; try { if (name instanceof Class<?>) { Class<?> clazz = (Class<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(clazz)); } else if (name instanceof Key) { Key<?> key = (Key<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(key)); } } catch (Exception ignored) { } return entry; } } Nota Se crea un Injector desde la clase Modules luego realiza una búsqueda en este para ver si hay beans que coincidan. Consulte Sección 6.5, “MBeans de legado y mezcla de diferentes modelos de componentes” para encontrar mayor información sobre la declaración y uso de MBeans de legado. 6.5. MBeans de legado y mezcla de diferentes modelos de componentes El ejemplo más simple de mezclar diferentes modelos de contenido se puede ver en Ejemplo 6.7, 52 Capítulo 6. Modelos de componentes “Inyección de un POJO en un MBean”. Ejemplo 6.7. Inyección de un POJO en un MBean <server> <mbean code="org.jboss.demos.models.mbeans.Pojo" name="jboss.demos:service=pojo"> <attribute name="OtherPojo"><inject bean="PlainPojo"/></attribute> </mbean> </server> Para implementar MBeans por medio del microcontenedor debe escribir un manejador nuevo por completo para el modelo de componentes. Consulte system -jm x-beans.xm l para obtener mayores detalles. El código de este archivo se encuentra en el código fuente del servidor de aplicaciones JBoss: sub-proyecto system-jmx. 6.6. Exponer POJOs como MBeans Ejemplo 6.8. Exponer un POJO existenete como un MBean <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AnnotatedJMXPojo" class="org.jboss.demos.models.jmx.AtJmxPojo"/> <bean name="XmlJMXPojo" class="org.jboss.demos.models.mbeans.Pojo"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(exposedInterface=org.jb oss.demos.models.mbeans.PojoMBean.class, registerDirectly=true)</annotation> </bean> <bean name="ExposedPojo" class="org.jboss.demos.models.jmx.Pojo"/> <bean name="AnnotatedExposePojo" class="org.jboss.demos.models.jmx.ExposePojo"> <constructor> <parameter><inject bean="ExposedPojo"/></parameter> </constructor> </bean> </deployment> Este descriptor presenta un POJO existente como un MBean y lo registra en un servidor MBean. Para exponer un POJO como un MBean finalícelo con una anotación @JMX asumiendo que ha importado org.jboss.aop.m icrocontainer.aspects.jm x.JMX. El bean se puede exponer directamente o en su propiedad. 53 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 6.9. Presentación de un POJO como un MBean usando su propiedad <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="XMLLoginConfig" class="org.jboss.demos.models.old.XMLLoginConfig"/> <bean name="SecurityConfig" class="org.jboss.demos.models.old.SecurityConfig"> <property name="defaultLoginConfig"><inject bean="XMLLoginConfig"/></property> </bean> <bean name="SecurityChecker" class="org.jboss.demos.models.old.Checker"> <property name="loginConfig"><inject bean="jboss.security:service=XMLLoginConfig"/></property> <property name="securityConfig"><inject bean="jboss.security:service=SecurityConfig"/></property> </bean> </deployment> Puede utilizar cualquiera de los tipos de búsqueda de inyección ya sea buscando un POJO simple u obteniendo un MBean del servidor MBean. Una de las opciones de inyección es utilizar la inyección de tipos, algunas veces llamada autowiring y se puede ver en Ejemplo 6.10, “Autowiring”. Ejemplo 6.10. Autowiring <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="FromGuice" class="org.jboss.demos.models.plain.FromGuice"> <constructor><parameter><inject bean="PlainPojo"/></parameter></constructor> <property name="guicePojo"><inject/></property> </bean> <bean name="AllPojos" class="org.jboss.demos.models.plain.AllPojos"> <property name="directMBean"><inject bean="jboss.demos:service=pojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> </bean> </deployment> El bean FromGuice inyecta el bean Guice por medio de la correspondencia de tipos, en donde PlainPojo se inyecta con una inyección de nombres común. Ahora puede probar a ver si el enlace 54 Capítulo 6. Modelos de componentes Guice funciona como se esperaba como se puede ver en Ejemplo 6.11, “Prueba de la funcionalidad de Guice”. Ejemplo 6.11. Prueba de la funcionalidad de Guice public class FromGuice { private IPojo plainPojo; private org.jboss.demos.models.guice.Pojo guicePojo; public FromGuice(IPojo plainPojo) { this.plainPojo = plainPojo; } public void setGuicePojo(org.jboss.demos.models.guice.Pojo guicePojo) { this.guicePojo = guicePojo; } public void start() { f (plainPojo != guicePojo.getMcPojo()) throw new IllegalArgumentException("Pojos are not the same: " + plainPojo + "!=" + guicePojo.getMcPojo()); } } Ejemplo 6.11, “Prueba de la funcionalidad de Guice” solo proporciona un modelo de componentes alias. El alias es una funcionalidad trivial pero necesaria. Se debe introducir como un modelo nuevo de componentes dentro del microcontenedor con el fin de implementarlo como una dependencia verdadera. Los detalles de la implementación se pueden ver en Ejemplo 6.12, “Código fuente de AbstractController”. Ejemplo 6.12. Código fuente de AbstractController <deployment xmlns="urn:jboss:bean-deployer:2.0"> <alias name="SpringPojo">springPojo</alias> </deployment> Este descriptor mapea el nombre del SpringPojo al alias del springPojo. El beneficio de los aliases como verdaeros modelos de componentes es que el momento de la implementación del bean se vuelve menos importante. El alias espera en un estado no-instalado hasta que el bean real lo dispara. 55 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Capítulo 7. Inyección avanzada de dependencias y ldC Hoy en día la Inyección de Dependencias (ID), también llamada Inversión de Control (IdC), se encuentra en la parte central de muchos marcos de trabajo que adoptan la noción de un contenedor o un modelo de componentes. En un capítulo anterior abordamos los modelos de componentes. El kernel JBoss JMX, el precursor al microcontenedor, solo proporcionaba soporte ligero para ID/IdC , principalmente debido a las limitaciones del acceder los MBeans por medio del servidor MBeans. Sin embargo, con el nuevo modelo de componentes basados en POJO se encuentran disponibles varias nuevas e interesantes funcionalidades. Este capítulo muestra la manera en que puede aplicar diferentes conceptos ID con la ayuda del microcotenedor JBoss. Estos conceptos se expresarán por medio del código XML pero también puede aplicar la mayoría de estas funcionalidades utilizando las anotaciones. 7.1. Fábrica de valores Una fábrica de valores es un bean, el cual tiene uno o más métodos dedicados a generar valores por usted. Consulte Ejemplo 7.1, “Fábrica de valores”. 56 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.1. Fábrica de valores <bean name="Binding" class="org.jboss.demos.ioc.vf.PortBindingManager"> <constructor> <parameter> <map keyClass="java.lang.String" valueClass="java.lang.Integer"> <entry> <key>http</key> <value>80</value> </entry> <entry> <key>ssh</key> <value>22</value> </entry> </map> </parameter> </constructor> </bean> <bean name="PortsConfig" class="org.jboss.demos.ioc.vf.PortsConfig"> <property name="http"><value-factory bean="Binding" method="getPort" parameter="http"/></property> <property name="ssh"><value-factory bean="Binding" method="getPort" parameter="ssh"/></property> <property name="ftp"> <value-factory bean="Binding" method="getPort"> <parameter>ftp</parameter> <parameter>21</parameter> </value-factory> </property> <property name="mail"> <value-factory bean="Binding" method="getPort"> <parameter>mail</parameter> <parameter>25</parameter> </value-factory> </property> </bean> Ejemplo 7.2, “PortsConfig” muestra la manera en que el bean PortsConfig usa el bean de enlace para obtener sus valores por medio de la invocación del método getPort. 57 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 7.2. PortsConfig public class PortBindingManager { private Map<String, Integer> bindings; public PortBindingManager(Map<String, Integer> bindings) { this.bindings = bindings; } public Integer getPort(String key) { return getPort(key, null); } public Integer getPort(String key, Integer defaultValue) { if (bindings == null) return defaultValue; Integer value = bindings.get(key); if (value != null) return value; if (defaultValue != null) bindings.put(key, defaultValue); return defaultValue; } } 7.2. Callbacks El descriptor que se puede ver en Ejemplo 7.3, “Callbacks para reunir y filtrar beans” le permite reunir todos los beans de un cierto tipo e incluso limitar el número de beans que coincidan. Junto con el descriptor en Ejemplo 7.3, “Callbacks para reunir y filtrar beans”, el código Java que se puede ver en Ejemplo 7.4, “Un analizador sintáctico para reunir todos los editores” muestra un Parser, el cual reune todos los Editores. 58 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.3. Callbacks para reunir y filtrar beans <bean name="checker" class="org.jboss.demos.ioc.callback.Checker"> <constructor> <parameter> <value-factory bean="parser" method="parse"> <parameter> <array elementClass="java.lang.Object"> <value>http://www.jboss.org</value> <value>SI</value> <value>3.14</value> <value>42</value> </array> </parameter> </value-factory> </parameter> </constructor> </bean> <bean name="editorA" class="org.jboss.demos.ioc.callback.DoubleEditor"/> <bean name="editorB" class="org.jboss.demos.ioc.callback.LocaleEditor"/> <bean name="parser" class="org.jboss.demos.ioc.callback.Parser"> <incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/> </bean> <bean name="editorC" class="org.jboss.demos.ioc.callback.LongEditor"/> <bean name="editorD" class="org.jboss.demos.ioc.callback.URLEditor"/> Ejemplo 7.4 . Un analizador sintáctico para reunir todos los editores public class Parser { private Set<Editor> editors = new HashSet<Editor>(); ... public void addEditor(Editor editor) { editors.add(editor); } public void removeEditor(Editor editor) { editors.remove(editor); } } Observe que incallback y uncallback usan el nombre del método para buscar. 59 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer <incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/> Un límite inferior controla cuántos editores de hecho hacen que el bean progrese de un estado configurado: cardinality=4 ..n/> Eventualmente se crea Checker y corrige el analizador sintáctico. Esto se ilustra en Ejemplo 7.5, “El corrector para el analizador sintáctico”. Ejemplo 7.5. El corrector para el analizador sintáctico public void create() throws Throwable { Set<String> strings = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); for (Object element : elements) strings.add(element.toString()); if (expected.equals(strings) == false) throw new IllegalArgumentException("Illegal expected set: " + expected + "!=" + strings); } 7.3. Modo de acceso del Bean Con el BeanAccessMode predeterminado no se inspeccionan los campos de un bean. Sin embargo, si especifica un BeanAccessMode diferente entonces los campos son accesibles como parte de las propiedades del bean. Consulte Ejemplo 7.6, “Definiciones posibles para BeanAccessMode”, Ejemplo 7.7, “Configuración de BeanAccessMode” y Ejemplo 7.8, “La clase FieldsBean” para una implementación. Ejemplo 7.6. Definiciones posibles para BeanAccessMode public enum BeanAccessMode { STANDARD(BeanInfoCreator.STANDARD), // Getters and Setters FIELDS(BeanInfoCreator.FIELDS), // Getters/Setters and fields without getters and setters ALL(BeanInfoCreator.ALL); // As above but with non public fields included } Aquí un valor de cadena se configura como un campo de cadena privado: 60 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.7. Configuración de BeanAccessMode <bean name="FieldsBean" class="org.jboss.demos.ioc.access.FieldsBean" accessmode="ALL"> <property name="string">InternalString</property> </bean> Ejemplo 7.8. La clase FieldsBean public class FieldsBean { private String string; public void start() { if (string == null) throw new IllegalArgumentException("Strings should be set!"); } } 7.4. Alias Bean Cada bean puede tener cualquier número de alias. Ya que los nombres de los componentes del microcontenedor se tratan como objetos, el tipo del alias no es limitado. Por defecto no se realiza un reemplazo de la propiedad del sistema; es necesario que establezca la etiqueta de reemplazo de manera explícita como se puede ver en Ejemplo 7.9, “Un alias simple de Bean”. Ejemplo 7.9. Un alias simple de Bean <bean name="SimpleName" class="java.lang.Object"> <alias>SimpleAlias</alias> <alias replace="true">${some.system.property}</alias> <alias class="java.lang.Integer">12345</alias> <alias><javabean xmlns="urn:jboss:javabean:2.0" class="org.jboss.demos.bootstrap.Main"/></alias> </bean> 7.5. Soporte para anotaciones XML (o metadatos) Una de las funcionalidades principales en el microcontenedor de JBoss es el soporte para AOP. Puede 61 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer utilizar aspectos AOP y beans simples combinadas de cualquier manera. Ejemplo 7.10, “Interceptar un método basado en una anotación” trata de interceptar una invocación de método con base en una anotación. La anotación puede provenir de cualquier lado. Puede ser una anotación de clase verdadera o puede ser una anotación agregada por medio de la configuración xml. Ejemplo 7.10. Interceptar un método basado en una anotación <interceptor xmlns="urn:jboss:aop-beans:1.0" name="StopWatchInterceptor" class="org.jboss.demos.ioc.annotations.StopWatchInterceptor"/> <bind xmlns="urn:jboss:aop-beans:1.0" pointcut="execution(* @org.jboss.demos.ioc.annotations.StopWatchLog->*(..)) OR execution(* *>@org.jboss.demos.ioc.annotations.StopWatchLog(..))"> <interceptor-ref name="StopWatchInterceptor"/> </bind> </interceptor> public class StopWatchInterceptor implements Interceptor { ... public Object invoke(Invocation invocation) throws Throwable { Object target = invocation.getTargetObject(); long time = System.currentTimeMillis(); log.info("Invocation [" + target + "] start: " + time); try { return invocation.invokeNext(); } finally { log.info("Invocation [" + target + "] time: " + (System.currentTimeMillis() time)); } } } Ejemplo 7.11, “Un verdadero ejecutor anotado de clase” and Ejemplo 7.12, “Ejecutor simple con anotaciones XML” muestra algunas maneras diferentes de implementar los ejecutores. 62 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.11. Un verdadero ejecutor anotado de clase <bean name="AnnotatedExecutor" class="org.jboss.demos.ioc.annotations.AnnotatedExecutor"> public class AnnotatedExecutor implements Executor { ... @StopWatchLog // <-- Pointcut match! public void execute() throws Exception { delegate.execute(); } } Ejemplo 7.12. Ejecutor simple con anotaciones XML <bean name="SimpleExecutor" class="org.jboss.demos.ioc.annotations.SimpleExecutor"> <annotation>@org.jboss.demos.ioc.annotations.StopWatchLog</annotation> // <-Pointcut match! </bean> public class SimpleExecutor implements Executor { private static Random random = new Random(); public void execute() throws Exception { Thread.sleep(Math.abs(random.nextLong() % 101)); } } Después de agregar los beans invocadores del ejecutor, puede ver los ejecutores en acción durante el empleo, buscando salidas del registro tal como Ejemplo 7.13, “Salida del registro del ejecutor”. 63 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 7.13. Salida del registro del ejecutor JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] start: 1229345859234 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] time: 31 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] start: 1229345859265 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] time: 47 7.6. Autowire Autowiring o la inyección contextual, es una funcionalidad común con con marcos de trabajo IdC. Ejemplo 7.14, “Incluir y excluir con autowiring” le muestra cómo utilizar o excluir beans con autowiring. Ejemplo 7.14 . Incluir y excluir con autowiring <bean name="Square" class="org.jboss.demos.ioc.autowire.Square" autowirecandidate="false"/> <bean name="Circle" class="org.jboss.demos.ioc.autowire.Circle"/> <bean name="ShapeUser" class="org.jboss.demos.ioc.autowire.ShapeUser"> <constructor> <parameter><inject/></parameter> </constructor> </bean> <bean name="ShapeHolder" class="org.jboss.demos.ioc.autowire.ShapeHolder"> <incallback method="addShape"/> <uncallback method="removeShape"/> </bean> <bean name="ShapeChecker" class="org.jboss.demos.ioc.autowire.ShapesChecker"/> En ambos casos - ShapeUser y ShapeChecker - solo Circle se debe utilizar ya que Square se excluye en el enlace contextual. 7.7. Fábrica de beans Cuando quiere más de una instancia de un bean en particular, necesita utilizar el patrón de fábrica del bean. El trabajo del microcontenedor es configurar e instalar la fábrica de beans como si fuera un bean simple. Luego necesita invocar el método createBean de la fábrica de beans. Por defecto, el microcontenedor crea una instancia GenericBeanFactory, pero puede configurar su propia fábrica. La única limitación es que su firma y ganchos de configuración son similares al de AbstractBeanFactory. 64 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.15. Fábrica de beans genéricas <bean name="Object" class="java.lang.Object"/> <beanfactory name="DefaultPrototype" class="org.jboss.demos.ioc.factory.Prototype"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="EnhancedPrototype" class="org.jboss.demos.ioc.factory.Prototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="ProxiedPrototype" class="org.jboss.demos.ioc.factory.UnmodifiablePrototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <bean name="PrototypeCreator" class="org.jboss.demos.ioc.factory.PrototypeCreator"> <property name="default"><inject bean="DefaultPrototype"/></property> <property name="enhanced"><inject bean="EnhancedPrototype"/></property> <property name="proxied"><inject bean="ProxiedPrototype"/></property> </bean> Consulte Ejemplo 7.16, “BeanFactory extendido” para ver el uso de un BeanFactory extendido. 65 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 7.16. BeanFactory extendido public class EnhancedBeanFactory extends GenericBeanFactory { public EnhancedBeanFactory(KernelConfigurator configurator) { super(configurator); } public Object createBean() throws Throwable { Object bean = super.createBean(); Class clazz = bean.getClass(); if (clazz.isAnnotationPresent(SetterProxy.class)) { Set<Class> interfaces = new HashSet<Class>(); addInterfaces(clazz, interfaces); return Proxy.newProxyInstance( clazz.getClassLoader(), interfaces.toArray(new Class[interfaces.size()]), new SetterInterceptor(bean) ); } else { return bean; } } protected static void addInterfaces(Class clazz, Set<Class> interfaces) { if (clazz == null) return; interfaces.addAll(Arrays.asList(clazz.getInterfaces())); addInterfaces(clazz.getSuperclass(), interfaces); } private class SetterInterceptor implements InvocationHandler { private Object target; private SetterInterceptor(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.startsWith("set")) throw new IllegalArgumentException("Cannot invoke setters."); return method.invoke(target, args); } } } 66 Capítulo 7. Inyección avanzada de dependencias y ldC public class PrototypeCreator { ... public void create() throws Throwable { ValueInvoker vi1 = (ValueInvoker)bfDefault.createBean(); vi1.setValue("default"); ValueInvoker vi2 = (ValueInvoker)enhanced.createBean(); vi2.setValue("enhanced"); ValueInvoker vi3 = (ValueInvoker)proxied.createBean(); try { vi3.setValue("default"); throw new Error("Should not be here."); } catch (Exception ignored) { } } 7.8. Constructor de metadatos Bean Al utilizar el microcontenedor en su código, use BeanMetaDataBuilder para crear y configurar los metadatos de su bean. 67 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 7.17. BeanMetaDataBuilder <bean name="BuilderUtil" class="org.jboss.demos.ioc.builder.BuilderUtil"/> <bean name="BuilderExampleHolder" class="org.jboss.demos.ioc.builder.BuilderExampleHolder"> <constructor> <parameter><inject bean="BUExample"/></parameter> </constructor> </bean> Utilizando este concepto no expone su código a ninguno de los detalles de implementación del microcontenedor. public class BuilderUtil { private KernelController controller; @Constructor public BuilderUtil(@Inject(bean = KernelConstants.KERNEL_CONTROLLER_NAME) KernelController controller) { this.controller = controller; } public void create() throws Throwable { BeanMetaDataBuilder builder = BeanMetaDataBuilder.createBuilder("BUExample", BuilderExample.class.getName()); builder.addStartParameter(Kernel.class.getName(), builder.createInject(KernelConstants.KERNEL_NAME)); controller.install(builder.getBeanMetaData()); } public void destroy() { controller.uninstall("BUExample"); } } 7.9. ClassLoader personalizado En el microcontenedor puede definir un ClassLoader personalizado por bean. Al definir un cargador de clase para toda la implementación, asegúrese de no crear una dependencia cíclica -- por ejemplo, un cargador de clase recién definido que dependa de sí mismo. 68 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.18. Definición de un cargador de clase por Bean <classloader><inject bean="custom-classloader:0.0.0"/></classloader> <!-- this will be explained in future article --> <classloader name="custom-classloader" xmlns="urn:jboss:classloader:1.0" exportall="NON_EMPTY" import-all="true"/> <bean name="CustomCL" class="org.jboss.demos.ioc.classloader.CustomClassLoader"> <constructor> <parameter><inject bean="custom-classloader:0.0.0"/></parameter> </constructor> <property name="pattern">org\.jboss\.demos\.ioc\..+</property> </bean> <bean name="CB1" class="org.jboss.demos.ioc.classloader.CustomBean"/> <bean name="CB2" class="org.jboss.demos.ioc.classloader.CustomBean"> <classloader><inject bean="CustomCL"/></classloader> </bean> Ejemplo 7.19, “Prueba del cargador de clase personalizado” muestra una prueba para verificar que el bean CB2 usa un cargador de clase personalizado, el cual limita el ámbito del paquete cargable. Ejemplo 7.19. Prueba del cargador de clase personalizado public class CustomClassLoader extends ClassLoader { private Pattern pattern; public CustomClassLoader(ClassLoader parent) { super(parent); } public Class<?> loadClass(String name) throws ClassNotFoundException { if (pattern == null || pattern.matcher(name).matches()) return super.loadClass(name); else throw new ClassNotFoundException("Name '" + name + "' doesn't match pattern: " + pattern); } public void setPattern(String regexp) { pattern = Pattern.compile(regexp); } } 7.10. Modo controlador Por defecto el microcontenedor usa el modo controlador AUT O. Lleva a los beans tan lejos como es posible con respecto a las dependencias. Pero hay otros dos modos: MANUAL y ON_DEMAND. Si el bean está marcado como ON_DEMAND entonces no se utilizará ni se instalará hasta que otro 69 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer bean dependa explícitamente de este. En el modo MANUAL, el usuario microcontenedor debe llevar al bean hacia adelante y atrás junto con el estado. Ejemplo 7.20. Modo controlador Bean <bean name="OptionalService" class="org.jboss.demos.ioc.mode.OptionalService" mode="On Demand"/> <bean name="OptionalServiceUser" class="org.jboss.demos.ioc.mode.OptionalServiceUser"/> <bean name="ManualService" class="org.jboss.demos.ioc.mode.ManualService" mode="Manual"/> <bean name="ManualServiceUser" class="org.jboss.demos.ioc.mode.ManualServiceUser"> <start> <parameter><inject bean="ManualService" fromContext="context" state="Not Installed"/></parameter> </start> </bean> Nota Usando el atributo fromContext de la clase inject puede inyectar beans así como su representación de componentes del microcontenedor inmodificables. Revise el código de OptionalServiceUser y ManualServiceUser para ver cómo se utiliza la API del microcontenedor para el manejo de beans ON_DEMAND y MANUAL. 7.11. Ciclo Los beans pueden depender entre ellos en un ciclo. Por ejemplo, A depende de B en la construcción, pero B depende de A durante la configuración. Debido a la separación del ciclo de vida del estado detallado del microcontenedor, este problema se puede resolver fácilmente. 70 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.21. Separación del ciclo de vida del Bean <bean name="cycleA" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleB"/></property> </bean> <bean name="cycleB" class="org.jboss.demos.ioc.cycle.CyclePojo"> <constructor><parameter><inject bean="cycleA" state="Instantiated"/></parameter></constructor> </bean> <bean name="cycleC" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleD"/></property> </bean> <bean name="cycleD" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleC" state="Instantiated"/></property> </bean> 7.12. Oferta y demanda Algunas veces así como con una inyección, es posible que una dependencia entre dos beans no sea aparente. T ales dependencias se deben expresar de manera clara tal como se puede ver en Ejemplo 7.22, “Uso estático del código”. Ejemplo 7.22. Uso estático del código <bean name="TMDemand" class="org.jboss.demos.ioc.demandsupply.TMDemander"> <demand>TM</demand> </bean> <bean name="SimpleTMSupply" class="org.jboss.demos.ioc.demandsupply.SimpleTMSupplyer"> <supply>TM</supply> </bean> 7.13. Instalaciones A medida que el bean se mueve a través de diferentes estados es posible que quiera invocar algunos métodos en otros beans o en el mismo bean. Ejemplo 7.23, “Invocación de métodos en diferentes estados” muestra cómo Entry invoca a los métodos add y rem oveEntry del RepositoryManager pararegistrar y desregistrarse a sí mismo. 71 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 7.23. Invocación de métodos en diferentes estados <bean name="RepositoryManager" class="org.jboss.demos.ioc.install.RepositoryManager"> <install method="addEntry"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall method="removeEntry"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean> <bean name="Entry" class="org.jboss.demos.ioc.install.SimpleEntry"> <install bean="RepositoryManager" method="addEntry" state="Instantiated"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall bean="RepositoryManager" method="removeEntry" state="Configured"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean> 7.14. Imitación perezosa Es posible que tenga una dependencia en un bean que se utilice con muy poca frecuencia, pero toma mucho tiempo en configurar. Puede utilizar la imitación perezosa del bean que se demuestra en Ejemplo 7.24, “Imitación perezosa” para resolver la dependencia. Cuando de hecho necesita el bean, invoque y use el bean destino esperando que para ese momento ya se haya instalalado. 72 Capítulo 7. Inyección avanzada de dependencias y ldC Ejemplo 7.24 . Imitación perezosa <bean name="lazyA" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyB"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <bean name="lazyB" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyA"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <lazy name="anotherLazy" bean="Pojo" exposeClass="true"/> <bean name="Pojo" class="org.jboss.demos.ioc.lazy.Pojo"/> 7.15. Ciclo de vida Por defecto el microcontenedor usa los métodos create, start y destroy cuando avanza a través de los variados estados. Sin embargo, es posible que no quiera que el microcontenedor los invoque. Por esta razón, se encuentra disponible una etiqueta ignore. Ejemplo 7.25. Ciclos de vida del Bean <bean name="FullLifecycleBean-3" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"/> <bean name="FullLifecycleBean-2" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <create ignored="true"/> </bean> <bean name="FullLifecycleBean-1" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <start ignored="true"/> </bean> 73 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Capítulo 8. El sistema virtual de archivos El duplicado del código de manejo de recursos es un problema común para los desarrolladores. En la mayoría de los casos, el código ayuda a determinar la información sobre un recurso en particular, el cual puede ser un archivo, un directorio o en el caso de una JAR, una URL remota. Otro problema de duplicación es el código para el procesamiento de ficheros anidados. Ejemplo 8.1, “Problema de duplicación de recursos” ilustra el problema. 74 Capítulo 8. El sistema virtual de archivos Ejemplo 8.1. Problema de duplicación de recursos 75 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer public static URL[] search(ClassLoader cl, String prefix, String suffix) throws IOException { Enumeration[] e = new Enumeration[]{ cl.getResources(prefix), cl.getResources(prefix + "MANIFEST.MF") }; Set all = new LinkedHashSet(); URL url; URLConnection conn; JarFile jarFile; for (int i = 0, s = e.length; i < s; ++i) { while (e[i].hasMoreElements()) { url = (URL)e[i].nextElement(); conn = url.openConnection(); conn.setUseCaches(false); conn.setDefaultUseCaches(false); if (conn instanceof JarURLConnection) { jarFile = ((JarURLConnection)conn).getJarFile(); } else { jarFile = getAlternativeJarFile(url); } if (jarFile != null) { searchJar(cl, all, jarFile, prefix, suffix); } else { boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix); if (searchDone == false) { searchFromURL(all, prefix, suffix, url); } } } } return (URL[])all.toArray(new URL[all.size()]); } private static boolean searchDir(Set result, File file, String suffix) throws IOException { if (file.exists() && file.isDirectory()) { File[] fc = file.listFiles(); String path; for (int i = 0; i < fc.length; i++) { path = fc[i].getAbsolutePath(); if (fc[i].isDirectory()) { searchDir(result, fc[i], suffix); } else if (path.endsWith(suffix)) { 76 Capítulo 8. El sistema virtual de archivos result.add(fc[i].toURL()); } } return true; } return false; } T ambién hay muchos problemas con el bloqueo de archivos en los sistemas Windows, lo cual ha forzado a los desarrolladores a copiar todos los ficheros de implementación en vivo a otra ubicación para prevenir el bloqueo de aquellos que se encuentran en las carpetas de implementación (lo cual evitaría el borrarlos y el borrado de la implementación con base en el sistema de archivos). El bloqueo de archivos es un problema importante, cuya única solución solía ser el centralizar todo el código de carga de recursos en un lugar. El proyecto VFS se creó para resolver todos estos problemas. VFS son las siglas en ingles de Virtual File System - sistema virtual de archivos. 8.1. API pública VFS VFS se utiliza para dos propósitos principales tal como se puede ver en Usos para VFS. Usos para VFS navegación simple de recursos API (del inglés Application Programmer Interface) del patrón del visitante Como se mencionó anteriormente, en JDK simple, el manejo y navegación de recursos es complejo. Siempre debe chequear el tipo de recurso y estos chequeos pueden llegar engorrosos. VFS abstrae los recursos en un solo tipo de recurso, VirtualFile. 77 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 8.2. El tipo de recurso VirtualFile 78 Capítulo 8. El sistema virtual de archivos public class VirtualFile implements Serializable { /** * Get certificates. * * @return the certificates associated with this virtual file */ Certificate[] getCertificates() /** * Get the simple VF name (X.java) * * @return the simple file name * @throws IllegalStateException if the file is closed */ String getName() /** * Get the VFS relative path name (org/jboss/X.java) * * @return the VFS relative path name * @throws IllegalStateException if the file is closed */ String getPathName() /** * Get the VF URL (file://root/org/jboss/X.java) * * @return the full URL to the VF in the VFS. * @throws MalformedURLException if a url cannot be parsed * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed */ URL toURL() throws MalformedURLException, URISyntaxException /** * Get the VF URI (file://root/org/jboss/X.java) * * @return the full URI to the VF in the VFS. * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed * @throws MalformedURLException for a bad url */ URI toURI() throws MalformedURLException, URISyntaxException /** * When the file was last modified * * @return the last modified time * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getLastModified() throws IOException /** * Returns true if the file has been modified since this method was last called * Last modified time is initialized at handler instantiation. * * @return true if modifed, false otherwise * @throws IOException for any error 79 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer */ boolean hasBeenModified() throws IOException /** * Get the size * * @return the size * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getSize() throws IOException /** * Tests whether the underlying implementation file still exists. * @return true if the file exists, false otherwise. * @throws IOException - thrown on failure to detect existence. */ boolean exists() throws IOException /** * Whether it is a simple leaf of the VFS, * i.e. whether it can contain other files * * @return true if a simple file. * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isLeaf() throws IOException /** * Is the file archive. * * @return true if archive, false otherwise * @throws IOException for any error */ boolean isArchive() throws IOException /** * Whether it is hidden * * @return true when hidden * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isHidden() throws IOException /** * Access the file contents. * * @return an InputStream for the file contents. * @throws IOException for any error accessing the file system * @throws IllegalStateException if the file is closed */ InputStream openStream() throws IOException /** * Do file cleanup. * * e.g. delete temp files */ 80 Capítulo 8. El sistema virtual de archivos void cleanup() /** * Close the file resources (stream, etc.) */ void close() /** * Delete this virtual file * * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete() throws IOException /** * Delete this virtual file * * @param gracePeriod max time to wait for any locks (in milliseconds) * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete(int gracePeriod) throws IOException /** * Get the VFS instance for this virtual file * * @return the VFS * @throws IllegalStateException if the file is closed */ VFS getVFS() /** * Get the parent * * @return the parent or null if there is no parent * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ VirtualFile getParent() throws IOException /** * Get a child * * @param path the path * @return the child or <code>null</code> if not found * @throws IOException for any problem accessing the VFS * @throws IllegalArgumentException if the path is null * @throws IllegalStateException if the file is closed or it is a leaf node */ VirtualFile getChild(String path) throws IOException /** * Get the children * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildren() throws IOException 81 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer /** * Get the children * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildren(VirtualFileFilter filter) throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildrenRecursively() throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildrenRecursively(VirtualFileFilter filter) throws IOException /** * Visit the virtual file system * * @param visitor the visitor * @throws IOException for any problem accessing the virtual file system * @throws IllegalArgumentException if the visitor is null * @throws IllegalStateException if the file is closed */ void visit(VirtualFileVisitor visitor) throws IOException } T odas las operaciones del sistema de archivos de sólo lectura están disponibles además de unas pocas opciones para limpiar o borrar el recurso. El manejo de la limpieza o el borrado se necesita al tratar con algunos archivos internos temporales tal como archivos creados para manejar jars anidadas. Para cambiar el manejo de recursos File o URL de JDK al nuevo VirtualFile necesita un VirtualFile raíz, el cual los proporciona la clase VFS con la ayuda de la URL o el parámetro URI. 82 Capítulo 8. El sistema virtual de archivos Ejemplo 8.3. Uso de la clase VFS 83 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer public class VFS { /** * Get the virtual file system for a root uri * * @param rootURI the root URI * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URI rootURI) throws IOException /** * Create new root * * @param rootURI the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URI rootURI) throws IOException /** * Get the root virtual file * * @param rootURI the root uri * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VirtualFile getRoot(URI rootURI) throws IOException /** * Get the virtual file system for a root url * * @param rootURL the root url * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URL rootURL) throws IOException /** * Create new root * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URL rootURL) throws IOException /** * Get the root virtual file * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile getRoot(URL rootURL) throws IOException 84 Capítulo 8. El sistema virtual de archivos /** * Get the root file of this VFS * * @return the root * @throws IOException for any problem accessing the VFS */ VirtualFile getRoot() throws IOException } Los tres métodos diferentes se ven similares. getVFS createNewRoot getRoot getVFS retorna una instancia VFS pero todavía no crea una instancia VirtualFile. Esto es importante ya que hay métodos que ayudan con la configuración de una instancia VFS (consulte la clase VFS API javadocs) antes de ordenarle que cree una raíz VirtualFile. Por otro lado, los otros dos métodos usan configuraciones predeterminadas para la creación de root. La diferencia entre createNewRoot y getRoot es en los detalles de caché, los cuales abordaremos más adelante. Ejemplo 8.4 . Uso de getVFS URL rootURL = ...; // get root url VFS vfs = VFS.getVFS(rootURL); // configure vfs instance VirtualFile root1 = vfs.getRoot(); // or you can get root directly VirtualFile root2 = VFS.crateNewRoot(rootURL); VirtualFile root3 = VFS.getRoot(rootURL); Otra cosa útil de la API VFS es su implementación de un patrón visitante apropiado. Es muy simple el reunir recursos de manera recursiva, una tarea dificil de realizar con la carga de recursos JDK simple. 85 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 8.5. Reunión de recursos de manera recursiva public interface VirtualFileVisitor { /** * Get the search attribues for this visitor * * @return the attributes */ VisitorAttributes getAttributes(); /** * Visit a virtual file * * @param virtualFile the virtual file being visited */ void visit(VirtualFile virtualFile); } VirtualFile root = ...; // get root VirtualFileVisitor visitor = new SuffixVisitor(".class"); // get all classes root.visit(visitor); 8.2. Arquitectura VFS Aunque la API pública es bastante is quite intuitiva, los detalles de la implementación real agregan complejidad. Es necesario explicar algunos conceptos en más detalle. Cada vez que crea una instancia VFS también se crea su instancia correspondiente VFSContext. Esta creación se realiza por medio de VFSContextFactory. Protocolos diferentes mapean a diferentes instancias VFSContextFactory. Por ejemplo, file/vfsfile mapea a FileSystem ContextFactory mientras que zip/vfszip mapea a ZipEntryContextFactory. Cada vez que se crea una instancia VirtualFile también se crea su VirtualFileHandler correspondiente. Esta instancia VirtualFileHandler sabe cómo manejar diferentes tipos de recursos apropiadamente; el API VirtualFile solo delega invocaciones a su referencia VirtualFileHandler. La instancia VFSContext sabe cómo crear instancias VirtualFileHandler de acuerdo con el tipo de recurso. Por ejemplo, Z ipEntryContextFactory crea ZipEntryContext, el cual luego crea ZipEntryHandler. 8.3. Implementaciones existentes Aparte de archivos, directorios (FileHandler) y ficheros zip (Z ipEntryHandler), el microcontenedor también soporta otros casos más avanzados. El primero es Assembled, el cual es similar a lo que Eclipse llama Linked Resources. Su propósito es tomar recursos ya existentes de diferentes árboles y "simular" un sólo árbol de recursos. 86 Capítulo 8. El sistema virtual de archivos Ejemplo 8.6. Implementación de VirtualFileHandlers reunidos AssembledDirectory sar = AssembledContextFactory.getInstance().create("assembled.sar"); URL url = getResource("/vfs/test/jar1.jar"); VirtualFile jar1 = VFS.getRoot(url); sar.addChild(jar1); url = getResource("/tmp/app/ext.jar"); VirtualFile ext1 = VFS.getRoot(url); sar.addChild(ext); AssembledDirectory metainf = sar.mkdir("META-INF"); url = getResource("/config/jboss-service.xml"); VirtualFile serviceVF = VFS.getRoot(url); metainf.addChild(serviceVF); AssembledDirectory app = sar.mkdir("app.jar"); url = getResource("/app/someapp/classes"); VirtualFile appVF = VFS.getRoot(url); app.addPath(appVF, new SuffixFilter(".class")); Otra implementación son los archivos en-memoria. Esta implementación surgió de la necesidad de manejar fácilmente bytes generados por AOP. En lugar de utilizar archivos temporales, puede poner bytes en VirtualFileHandlers en la memoria. Ejemplo 8.7. Implementación de VirtualFileHandlers en memoria URL url = new URL("vfsmemory://aopdomain/org/acme/test/Test.class"); byte[] bytes = ...; // some AOP generated class bytes MemoryFileFactory.putFile(url, bytes); VirtualFile classFile = VFS.getVirtualFile(new URL("vfsmemory://aopdomain"), "org/acme/test/Test.class"); InputStream bis = classFile.openStream(); // e.g. load class from input stream 8.4. Ganchos de extensión Es fácil extender VFS con un nuevo protocolo, similar a lo que hicimos con Assem bled y Mem ory. T odo lo que necesita es una combinación de las implementaciones VFSContexFactory, VFSContext, VirtualFileHandler, FileHandlerPlugin y URLStream Handler. La VFSContextFactory es trivial mientras que las otras dependen de la complejidad de su tarea. Puede implementar el acceso rar, tar, gzip, o incluso rem ote. Después de implementar un nuevo protocolo, registre el nuevo VFSContextFactory con VFSContextFactoryLocator. 8.5. Funcionalidades Uno de los problemas principales que los desarrolladores del microcontenedor enfrentaron fue el uso 87 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer apropiado de los recursos anidados, más especificamente de los archivos jar anidados: por ejemplo, implementaciones ear normales: gem a.ear/ui.war/WEB-INF/lib/struts.jar. Con el fin de leer el contenido de struts.jar tenemos dos opciones: manejar los recursos en la memoria crear copias temporales de jars a nivel superior de manera recursiva La primera opción es más fácil de implementar, pero consume mucha memoria, lo cual requiere potencialmente que las aplicaciones grandes residan en la memoria. El otro enfoque deja atrás un gran número de archivos temporales, los cuales deben ser invisibles para el usuario y por lo tanto deben desaparecer después del borrado de la implementación. Considere el siguiente escenario: un usuario accede uns intstancia URL VFS, la cual apunta a algun recurso anidado. La manera en que el VFS simple manejaría esto es re-creando toda la ruta desde el principio: desempacaría los recursos anidados y de nuevo. Esto crea un gran número de archivos temporales. El microcontenedor evita esto utilizando VFSRegistry, VFSCache y T em pInfo. Cuando solicita VirtualFile sobrer VFS (getRoot no createNewRoot), VFS le pide a la implementación VFSRegistry que proporcione el archivo. El DefaultVFSRegistry existente primero chequea si extiste un VFSContext raíz que coincida para la URI dada. Si sí existe entonces DefaultVFSRegistry primero trata de navigar al T em pInfo existente (enlace a un archivo temporal), regresando a la navegación normal si no existe dicho archivo temporal. De esta manera reutiliza completamente cualuqier archivo temporal que ya hayan sido desempacados, ahorrando tiempo y espacio en el disco. Si no se encuentra un VFSContext que coincida en el caché entonces el código creará una nueva entrada VFSCache y continuará con la navegación predeterminada. El determinar la manera en que el VFSCache maneja las entradas VFSContext en caché depende de la implementación utilizada. VFSCache es configurable por medio de VFSCacheFactory. Por defecto, nada va en el caché, pero hay unas pocas implementaciones VFSCache útiles existentes, utilizando algoritmos tal como Least Recently Used (LRU) o tim ed cache. 88 Capítulo 9. La capa ClassLoading Capítulo 9. La capa ClassLoading JBoss siempre ha tenido una manera única de tratar con la carga de clase y la nueva capa classloading que viene junto con el microcontenedor no es la excepción. ClassLoading es una funcionalidad agregada que puede utilizar cuando prefiera la carga de clase no predeterminada. Con una mayor demanda por la carga de clase de estilo OSGi y un número de especificaciones nuevas de carga de clase Java en el horizonte, los cambios a la capa ClassLoading de EAP 5.1 son útiles y muy oportunos. La capa ClassLoading del microcontenedor es una capa de abstracción. La mayoría de los detalles se esconden detrás de métodos privados y de paquetes privados sin comprometer la extensibilidad y funcionalidad disponibles por medio de las clases y métodos públicos que hacen el API. Esto significa que usted escribe código frente a la política y no frente a los detalles del cargador de clase. El proyecto ClassLoader se divide en 3 sub-proyectos el cargador de clase - classloader la carga de la clase - classloading classloading-vfs classloader contiene una extensión java.lang.ClassLoader personalizada sin ninguna política de carga de clase específica. Una política de carga de clase incluye el saber desde dónde carga y cómo cargar. Classloading es una extensión de los mecanismos de dependencia del microcontenedor. Su implementación respaldada por VFS es classloading-vfs. Consulte Capítulo 8, El sistema virtual de archivos para obtener mayor información sobre VFS. 9.1. ClassLoader La implementación ClassLoader soporta políticas enchufables y es una clase final, se supone que no se debe alterar. Para escribir sus propias implementaciones ClassLoader, escriba una ClassLoaderPolicy, la cual proporciona una API más simple para ubicar las clases y recursos y para especificar otras reglas asociadas con el cargador de clase. Para personalizar la carga de clase, instance una ClassLoaderPolicy y regístrela con un ClassLoaderSystem para crear un ClassLoader personalizado. T ambién puede crear un ClassLoaderDom ain para realizar la partición en el ClassLoaderSystem . La capa ClassLoader también incluye la implementación de cosas como el modelo DelegateLoader, la carga de clase, filtros de recursos y políticas de delegación padre-hijo. El tiempo de ejecución está habilitado para JMX para exponer la política utilizada para cada cargador de clase. T ambién proporciona estadísticas de carga de clase y métodos de depuración para ayudar a determinar desde dónde se cargan las cosas. 89 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 9.1. La clase ClassLoaderPolicy La ClassLoaderPolicy controla la manera en que funciona la carga de clase. public abstract class ClassLoaderPolicy extends BaseClassLoaderPolicy { public DelegateLoader getExported() public String[] getPackageNames() protected List<? extends DelegateLoader> getDelegates() protected boolean isImportAll() protected boolean isCacheable() protected boolean isBlackListable() public abstract URL getResource(String path); public InputStream getResourceAsStream(String path) public abstract void getResources(String name, Set<URL> urls) throws IOException; protected ProtectionDomain getProtectionDomain(String className, String path) public PackageInformation getPackageInformation(String packageName) public PackageInformation getClassPackageInformation(String className, String packageName) protected ClassLoader isJDKRequest(String name) } } Los siguientes dos ejemplos de ClassLoaderPolicy. El primero recupera los recursos con base en expresiones regulares, mientras que el segundo maneja los recursos encriptados. 90 Capítulo 9. La capa ClassLoading Ejemplo 9.2. ClassLoaderPolicy con soporte para expresiones regulares 91 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer public class RegexpClassLoaderPolicy extends ClassLoaderPolicy { private VirtualFile[] roots; private String[] packageNames; public RegexpClassLoaderPolicy(VirtualFile[] roots) { this.roots = roots; } @Override public String[] getPackageNames() { if (packageNames == null) { Set<String> exportedPackages = PackageVisitor.determineAllPackages(roots, null, ExportAll.NON_EMPTY, null, null, null); packageNames = exportedPackages.toArray(new String[exportedPackages.size()]); } return packageNames; } protected Pattern createPattern(String regexp) { boolean outside = true; StringBuilder builder = new StringBuilder(); for (int i = 0; i < regexp.length(); i++) { char ch = regexp.charAt(i); if ((ch == '[' || ch == ']' || ch == '.') && escaped(regexp, i) == false) { switch (ch) { case '[' : outside = false; break; case ']' : outside = true; break; case '.' : if (outside) builder.append("\\"); break; } } builder.append(ch); } return Pattern.compile(builder.toString()); } protected boolean escaped(String regexp, int i) { return i > 0 && regexp.charAt(i - 1) == '\\'; } public URL getResource(String path) { Pattern pattern = createPattern(path); for (VirtualFile root : roots) { URL url = findURL(root, root, pattern); if (url != null) return url; } return null; } 92 Capítulo 9. La capa ClassLoading private URL findURL(VirtualFile root, VirtualFile file, Pattern pattern) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) return file.toURL(); List<VirtualFile> children = file.getChildren(); for (VirtualFile child : children) { URL url = findURL(root, child, pattern); if (url != null) return url; } return null; } catch (Exception e) { throw new RuntimeException(e); } } public void getResources(String name, Set<URL> urls) throws IOException { Pattern pattern = createPattern(name); for (VirtualFile root : roots) { RegexpVisitor visitor = new RegexpVisitor(root, pattern); root.visit(visitor); urls.addAll(visitor.getUrls()); } } private static class RegexpVisitor implements VirtualFileVisitor { private VirtualFile root; private Pattern pattern; private Set<URL> urls = new HashSet<URL>(); private RegexpVisitor(VirtualFile root, Pattern pattern) { this.root = root; this.pattern = pattern; } public VisitorAttributes getAttributes() { return VisitorAttributes.RECURSE_LEAVES_ONLY; } public void visit(VirtualFile file) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) 93 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer urls.add(file.toURL()); } catch (Exception e) { throw new RuntimeException(e); } } public Set<URL> getUrls() { return urls; } } } RegexpClassLoaderPolicy usa un mecanismo simplistico para encontrar los recursos que coinciden. Las implementaciones del mundo real serían más completas y elegantes. public class RegexpService extends PrintService { public void start() throws Exception { System.out.println(); ClassLoader cl = getClass().getClassLoader(); Enumeration<URL> urls = cl.getResources("config/[^.]+\\.[^.]{1,4}"); while (urls.hasMoreElements()) { URL url = urls.nextElement(); print(url.openStream(), url.toExternalForm()); } } } El servicio de expresiones regulares usa el patrón de la expresión regular config/[^.]+\\.[^.]{1,4 } para listar los recursos bajo el directorio config// . La longitud del sufijo es limitado de tal manera que los nombres de archivos tal como excluded.properties se ignorarán. 94 Capítulo 9. La capa ClassLoading Ejemplo 9.3. ClassLoaderPolicy con soporte para codificación public class CrypterClassLoaderPolicy extends VFSClassLoaderPolicy { private Crypter crypter; public CrypterClassLoaderPolicy(String name, VirtualFile[] roots, VirtualFile[] excludedRoots, Crypter crypter) { super(name, roots, excludedRoots); this.crypter = crypter; } @Override public URL getResource(String path) { try { URL resource = super.getResource(path); return wrap(resource); } catch (IOException e) { throw new RuntimeException(e); } } @Override public InputStream getResourceAsStream(String path) { InputStream stream = super.getResourceAsStream(path); return crypter.crypt(stream); } @Override public void getResources(String name, Set<URL> urls) throws IOException { super.getResources(name, urls); Set<URL> temp = new HashSet<URL>(urls.size()); for (URL url : urls) { temp.add(wrap(url)); } urls.clear(); urls.addAll(temp); } protected URL wrap(URL url) throws IOException { return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile(), new CrypterURLStreamHandler(crypter)); } } Ejemplo 9.3, “ClassLoaderPolicy con soporte para codificación” muestra cómo encriptar JARs. Puede configurar cuáles recursos codificar especificando un filtro apropiado. Aquí todo está encriptado a excepción del contenido del directorio MET A-INF/. 95 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer public class EncryptedService extends PrintService { public void start() throws Exception { ClassLoader cl = getClass().getClassLoader(); URL url = cl.getResource("config/settings.txt"); if (url == null) throw new IllegalArgumentException("No such settings.txt."); InputStream is = url.openStream(); print(is, "Printing settings:\n"); is = cl.getResourceAsStream("config/properties.xml"); if (is == null) throw new IllegalArgumentException("No such properties.xml."); print(is, "\nPrinting properties:\n"); } } Este servicio imprime el contenido de dos archivos de configuración. Muestra que se esconde la decodificación de cualquier recurso encriptado detrás de la capa de carga de clase. Para probar esto apropiadamente puede encriptar el módulo de política usted mismo o puede utilizar uno que ya esté encriptado. Para poner esto en acción es necesario que uan apropiadamente EncryptedService con ClassLoaderSystem y los programas de implementación. Más adelante en este capítulo abrodamos el particionamiento del ClassLoaderSystem . 9.2. ClassLoading En lugar de utilizar la abstracción ClassLoader directamente puede crear módulos ClassLoading, los cuales contienen declaraciones de dependencias ClassLoader. Una vez se especifican las dependencias se construyen las ClassLoaderPolicys y se conectan de manera respectiva. Para facilitar la definición de los ClassLoaders antes de que existan, la abstracción incluye un modelo ClassLoadingMetaData. El ClassLoadingMetaData se puede presentar como un objeto administrado dentro del nuevo servicio de perfil JBoss EAP. Esto le ayuda a los administradores de sistemas a tratar con los detalles de la política abstracta en lugar de los detalles de la implementación. 96 Capítulo 9. La capa ClassLoading Ejemplo 9.4 . ClassLoadingMetaData presentada como un objeto administrado public class ClassLoadingMetaData extends NameAndVersionSupport { /** The serialVersionUID */ private static final long serialVersionUID = -2782951093046585620L; /** The classloading domain */ private String domain; /** The parent domain */ private String parentDomain; /** Whether to make a subdeployment classloader a top-level classloader */ private boolean topLevelClassLoader = false; /** Whether to enforce j2se classloading compliance */ private boolean j2seClassLoadingCompliance = true; /** Whether we are cacheable */ private boolean cacheable = true; /** Whether we are blacklistable */ private boolean blackListable = true; /** Whether to export all */ private ExportAll exportAll; /** Whether to import all */ private boolean importAll; /** The included packages */ private String includedPackages; /** The excluded packages */ private String excludedPackages; /** The excluded for export */ private String excludedExportPackages; /** The included packages */ private ClassFilter included; /** The excluded packages */ private ClassFilter excluded; /** The excluded for export */ private ClassFilter excludedExport; /** The requirements */ private RequirementsMetaData requirements = new RequirementsMetaData(); /** The capabilities */ private CapabilitiesMetaData capabilities = new CapabilitiesMetaData(); ... setters & getters 97 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 9.5, “API ClassLoading definida en XML” y Ejemplo 9.6, “API ClassLoading definida en Java” muestran la API ClassLoading definida en XML y Java respectivamente. Ejemplo 9.5. API ClassLoading definida en XML <classloading xmlns="urn:jboss:classloading:1.0" name="ptd-jsf-1.0.war" domain="ptd-jsf-1.0.war" parent-domain="ptd-ear-1.0.ear" export-all="NON_EMPTY" import-all="true" parent-first="true"/> Ejemplo 9.6. API ClassLoading definida en Java ClassLoadingMetaData clmd = new ClassLoadingMetaData(); if (name != null) clmd.setDomain(name + "_Domain"); clmd.setParentDomain(parentDomain); clmd.setImportAll(true); clmd.setExportAll(ExportAll.NON_EMPTY); clmd.setVersion(Version.DEFAULT_VERSION); Puede agregar ClassLoadingMetaData a su implementación ya sea programáticamente o declarativamente por medio de jboss-classloading.xm l. Ejemplo 9.7. Agregar ClassLoadingMetaData por medio de jboss-classloading.xm l <classloading xmlns="urn:jboss:classloading:1.0" domain="DefaultDomain" top-level-classloader="true" export-all="NON_EMPTY" import-all="true"> </classloading> El DefaultDomain se comparte entre todas las aplicaciones que no definen sus propios dominios. 98 Capítulo 9. La capa ClassLoading Ejemplo 9.8. Aislamiento típico a nivel de dominio <classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedDomain" export-all="NON_EMPTY" import-all="true"> </classloading> Ejemplo 9.9. Aislamiento con un padre específico <classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedWithParentDomain" parent-domain="DefaultDomain" export-all="NON_EMPTY" import-all="true"> </classloading> Ejemplo 9.10. No cumple con los requerimientos de j2seClassLoadingCompliance <classloading xmlns="urn:jboss:classloading:1.0" parent-first="false"> </classloading> Las implementaciones .war usan este método por defecto. En lugar de realizar bśuqeudas de padres primero por defecto primero chequea sus propios recursos. Ejemplo 9.11. Implementación típica OSGi <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="org.jboss.dependency.spi"/> </requirements> <capabilities> <package name="org.jboss.cache.api"/> <package name="org.jboss.kernel.spi"/> </capabilities> </classloading> 99 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 9.12. Importing and Exporting Whole Modules and Libraries, Rather than FineGrained Packages <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <module name="jboss-cache.jar"/> </capabilities> </classloading> <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="si.acme.foobar"/> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <package name="org.alesj.cl"/> <module name="jboss-cache.jar"/> </capabilities> </classloading> T ambién puede mezclar los requerimientos y los tipos de funcionalidades usando paquetes y módulos. El sub-proyecto de carga de clase usa una implementación muy pequeña del patrón recurso-visitante. En el proyecto ClassLoader, la conexión entre implementación y carga de clase se realiza por medio de la clase Module, la cual mantiene toda la información requerida para aplicar restricciones apropiadamente en el patrón del visitante tal como los filtros. 100 Capítulo 9. La capa ClassLoading Ejemplo 9.13. Las interfaces ResourceVisitor y ResourceContext public interface ResourceVisitor { ResourceFilter getFilter(); void visit(ResourceContext resource); } public interface ResourceContext { URL getUrl(); ClassLoader getClassLoader(); String getResourceName(); String getClassName(); boolean isClass(); Class<?> loadClass(); InputStream getInputStream() throws IOException; byte[] getBytes() throws IOException; } Para usar el módulo, instancie su instancia ResourceVisitor y pásela al método Module::visit. Esta funcionalidad se utiliza en el marco d etrabajo de la implementación para indexar los usos de anotacions en las implementaciones. 9.3. Carga de clase VFS Esros ejemplos proporcionan una implementación de la ClassLoaderPolicy que usa un proyecto de sistema de archivos virtual JBoss para cargar clases y recursos. Puede utilizar esta idea directamente o en conjunto con un marco de trabajo de carga de clase. Opcionalmente puede configurar sus módulos dentro de la configuración del microcontenedor. 101 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Ejemplo 9.14 . Programa de implementación del módulo de carga de clases <deployment xmlns="urn:jboss:bean-deployer:2.0"> <classloader name="anys-classloader" xmlns="urn:jboss:classloader:1.0" importall="true" domain="Anys" parent-domain="DefaultDomain"> <capabilities> <package name="org.jboss.test.deployers.vfs.reflect.support.web"/> </capabilities> <root>${jboss.tests.url}</root> </classloader> <bean name="AnyServlet" class="org.jboss.test.deployers.vfs.reflect.support.web.AnyServlet"> <classloader><inject bean="anys-classloader:0.0.0"/></classloader> </bean> </deployment> La clase VFSClassLoaderFactory transforma el programa de implementación XML en un VFSClassLoaderPolicyModule, el cual luego crea la instacia real del ClassLoader. Luego puede utilizar directamente esta nueva instancia ClassLoader con sus beans. Nota VFSClassLoaderFactory extiende ClassLoadingMetaData así que todos los ejemplos relacionados con ClassLoadingMetaData aplican en este caso también. 102 Capítulo 10. Marco de trabajo de la implementación virtual Capítulo 10. Marco de trabajo de la implementación virtual El nuevo Marco d etrabajo de la implementación virtual (VDF del inglés Virtual Deployment Framework) es una manera mejorada de administrar los programas de implementación en el microcontenedor. Este capítulo detalla algunas de sus funcionalidades más útiles. 10.1. Manejo agnóstico de tipos de implementación El tipo tradicional de implementación virtual se basa en clases que ya existen en un espcio de clase o dominio compartido. En este caso, el producto final es un nuevo servicio instalado en el servidor desde el cliente principal. La manera tradicional de lograr esto es cargar un archivo del programa de implementación. El nuevo VDF simplifica este proceso pasando los bytes y serialiándolos en una instancia Deploym ent. El otro tipo de implementación, el cual extiende el primero, es una implementanción basada en el sistema simple de archivos, respaldado po el microcontenedor VFS. Este enfoque se describe con más detalle en Capítulo 8, El sistema virtual de archivos. 10.2. Separación del reconocimiento de la estructura de la lógica del ciclo de vida de la implementación Con el fin de realizar cualquier trabajo real encima de una implementación primero debe comprender su estructura incluyendo sus rutas de clase y la ubicación de sus metadatos. La ubicación de los metadatos incluye los archivos de configuración tal como m y-jboss-beans.xm l, web.xm l, ejb-jar.xm l. Las rutas de clase son raíces del cargador de clase tal como WEBINF/classes o m yapp.ear/lib. T eniendo esta estructura en mente puede proceder al manejo de la implementación. Ciclo de vida de una implementación típica 1. El MainDeployer le pasa la implementación al grupo de StructuralDeployers para un reconocimiento y recibe un contexto de implementación. 2. Luego, el MainDeployer le pasa el contexto de implementación que resulta a los Deployers para que el Deployer apropiado lo maneje. De esta manera, el MainDeployer es un agente con la responsibilidad de decidir qué programas de implementación utilizar. En el caso de una implementación virtual o programática, una información StructureMetaData predeterminada ya existente lee la información de la estructura y la maneja de una de las maneras que se explicó en Manejo de la información StructuredMetaData. Manejo de la información StructuredMetaData Implementaciones basadas en VFS el reconocimiento de la estructura se re-envía a un grupo de StructureDeployers. estructuras definidas de la especificación JEE tenemos implementaciones StructureDeployer que coinciden: EarStructure 103 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer WarStructure JarStructure DeclarativeStructures busca el archivo MET A-INF/jboss-structure.xm l dentro de su implementación y lo analiza sintácticamente para construir un StructureMetaData apropiado. FileStructures solo reconoce los archivos de configuración conocidos tal como -jboss-beans.xm l o service.xm l. Ejemplo 10.1. Un ejemplo de jboss-structure.xm l <structure> <context comparator="org.jboss.test.deployment.test.SomeDeploymentComparatorTop"> <path name=""/> <metaDataPath> <path name="META-INF"/> </metaDataPath> <classpath> <path name="lib" suffixes=".jar"/> </classpath> </context> </structure> En el caso de EarStructure, primero reconozca una implementación a nivel superior y luego procese recursivamente las sub-implementaciones. Puede implementar un StructureDeployer personalizado con la ayuda de la clase genérica GroupingStructure proporcionada por la interfaz genérica StructureDeployer. Después de tener una estructura de implementación reconocida se la puede pasar a los programas de implementación reales. El objeto Deployers sabe cómo encargarse de los programas de implementación reales, utilizando una cadena de programas de implementación por Deploym entStage. 104 Capítulo 10. Marco de trabajo de la implementación virtual Ejemplo 10.2. Etapas de la implementación public interface DeploymentStages { /** The not installed stage - nothing is done here */ DeploymentStage NOT_INSTALLED = new DeploymentStage("Not Installed"); /** The pre parse stage - where pre parsing stuff can be prepared; altDD, ignore, ... */ DeploymentStage PRE_PARSE = new DeploymentStage("PreParse", NOT_INSTALLED); /** The parse stage - where metadata is read */ DeploymentStage PARSE = new DeploymentStage("Parse", PRE_PARSE); /** The post parse stage - where metadata can be fixed up */ DeploymentStage POST_PARSE = new DeploymentStage("PostParse", PARSE); /** The pre describe stage - where default dependencies metadata can be created */ DeploymentStage PRE_DESCRIBE = new DeploymentStage("PreDescribe", POST_PARSE); /** The describe stage - where dependencies are established */ DeploymentStage DESCRIBE = new DeploymentStage("Describe", PRE_DESCRIBE); /** The classloader stage - where classloaders are created */ DeploymentStage CLASSLOADER = new DeploymentStage("ClassLoader", DESCRIBE); /** The post classloader stage - e.g. aop */ DeploymentStage POST_CLASSLOADER = new DeploymentStage("PostClassLoader", CLASSLOADER); /** The pre real stage - where before real deployments are done */ DeploymentStage PRE_REAL = new DeploymentStage("PreReal", POST_CLASSLOADER); /** The real stage - where real deployment processing is done */ DeploymentStage REAL = new DeploymentStage("Real", PRE_REAL); /** The installed stage - could be used to provide valve in future? */ DeploymentStage INSTALLED = new DeploymentStage("Installed", REAL); } La etapas de implementación pre-existentes se mapean a los estados del controlador incorporado del microcontenedor. Proporcionan una vista céntrica del ciclo de vida de la implementación de los estados del controlador genérico. Dentro de los programas de implementación, la implementación se convierte en el componente Deploym entControllerContext del microcontenedor. La máquina de estado del microcontenedor maneja las dependencias. Las implementaciones se manejan secuencialmente por etapa de implementación. Para cada porgrama de implementación se maneja todo el orden de la jerarquía implementada usando la propiedad parentfirst del programa de implementación. Esta propiedad se configura como true por defecto. T ambién puede especificar los niveles de jerarquía que el programa de implementación maneja. Puede seleccionar all, top level, com ponents only o no com ponents. La manera en que el microcontendor maneja los modelos de componentes y el manejo de las 105 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer La manera en que el microcontendor maneja los modelos de componentes y el manejo de las dependencias también aplica aquí. Si hay dependencias no resueltas entonces la implementación empezará en el estado actual, potencialmente reportando un error si el estado actual no es el estado requerido. El agregar un nuevo programa de implementación se logra extendiendo uno de los muchos programas de implementación de ayuda que existen. Algunos programas de implementacióin de hecho necesitan una implementación respaldada por VFS mientras que otros pueden utilizar una implementación general. En la mayoría de los casos los programas de instalación de análisis sintáctico son los que necesitan respaldo de VFS. Aviso T ambién tenga en cuenta que los programas de implementación ejecutan recursivamente en toda implementación, sub-implementación y componentes. Su código necesita determinar tan pronto como sea posible en el proceso, si el programa de implementación debe manejar la implementación actual o no. Ejemplo 10.3. Programa de implementación simple, el cual presenta información sobre su implementación public class StdioDeployer extends AbstractDeployer { public void deploy(DeploymentUnit unit) throws DeploymentException { System.out.println("Deploying unit: " + unit); } @Override public void undeploy(DeploymentUnit unit) { System.out.println("Undeploying unit: " + unit); } } Agregue esta descripción en uno de los archivos -jboss-beans.xm l en el directorio deployers/ en el servidor de aplicaciones JBoss y el bean MainDeployerIm pl reconocerá este programa de implementación por medio del manejo callback loC del microcontenedor. Ejemplo 10.4 . Descriptor de implementación simple <bean name="StdioDeployer" class="org.jboss.acme.StdioDeployer"/> 10.3. Control de flujo natural en forma de anexos VDF incluye un mecanismo llamado anexos -attachments, el cual facilita el paso de la información de un programa de implementación al siguiente. Los anexos se implementan como un java.util.Map con mejoras, cuyas entradas representan un anexo. Algunos programas de implementación son productores mientras que otros son consumidores. El 106 Capítulo 10. Marco de trabajo de la implementación virtual mismo programa de implementación también puede realizar ambas funciones. Algunos programas de implementación crean metadatos o instancias de funcionalidades poniéndolos en el mapa de anexos. Otros programas de implementación solo declaran su necesidad de estos anexos y toman los datos del mapa de anexos, antes de realizar trabajo adicional en esos datos. El orden natural se refiere a la manera en que los porgramas de implementación se ordenan. Un orden natural común usa los términos relativos antes y después. Sin embargo, con el mecanismo de anexos en uso puede ordenar los programas de implementación de acuerdo a la manera en que producen y/o consumen los anexos. Cada anexo tiene una llave y los programas de implementación pasan las llaves a los anexos que producen. Si el programa de implementación produce un anexo entonces se llama a la llave output. Si el programa de implementación consume un anexo entonces esa llave se llama input. Los programas de implementación tienen entradas ordinarias y entradas requeridas. Las entradas ordinarias sólo se utilizan para ayudar a determinar el orden natural. Las entradas requeridas también ayudan a determinar el orden, pero también tienen otra función. Ayudan a determinar si el programa de implementación de hecho es relevante para la implementación dada, chequeando a ver si existe un anexo correspondiente a esa entrada requerida en el mapa de anexos. Aviso Aunque se soporta el ordenamiento relativo, este se considera como mala práctica y es posible que no haya soporte para este en lanzamientos futuros. 10.4. Detalles de la implementación y del cliente, usuario y uso del servidor Estas funcionalidades esconden los detalles de la implementación, lo cual hace que el uso sea menos propenso a errores y al mismo tiempo optimiza el proceso de desarrollo. La meta es que los clientes solo vean una API de desarrollo mientras que los desarrolladores vean un DeploymentUnit y los detalles de la implementación del servidor se encuentren en un DeploymentContext. Solo se presenta la información necesaria en un nivel en particular del ciclo de vida de la implementación. Ya mencionamos los componentes como parte del manejo de la jerarquía de los desarrolladores. Aunque la implementación y sub-implementación a nivel superior son una representación natural de la jerarquía de la estructura de la implementación, los componentes son un nuevo concepto VDF. La idea de los componentes es que tengan un mapeo 1:1 con el ControllerContexts dentro del microcontenedor. Consulte ¿Por qué los componentes mapean 1:1 con los ControllerContexts para ver las razones que respaldan esta afirmación. ¿Por qué los componentes mapean 1:1 con los ControllerContexts Nombrado El nombre de la unidad del componente es el mismo que el nombre del ControllerContext. get*Scope() and get*MetaData() retorna el mismo contexto MDR que el microcontenedor utilizará para esa instancia. 107 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer IncompleteDeploymentException (IDE) Con el fin de que el IDE imprima las dependencias que faltan en una implementación, necesita saber los nombres del ControllerContext. descubre el nombre reuniendo los nombres de las DeploymentUnit's componentes en los programas de implementación de componentes que los especifican tal como los métodos BeanMetaDataDeployer o setUseUnitNam e() en AbstractRealDeployer. 10.5. Máquina de estado único T odos los componentes del microcontenedor son manejados por un solo punto de entrada o una sola máquina de estado. Las implemtaciones no son la excepción. Puede sacar ventaja de esta funcionalidad utilizando el archivo de configuración jbossdependency.xm l en sus implementaciones. Ejemplo 10.5. jboss-dependency.xml <dependency xmlns="urn:jboss:dependency:1.0"> <item whenRequired="Real" dependentState="Create">TransactionManager</item> (1) <item>my-human-readable-deployment-alias</item> (2) </dependency> Note las llamadas artificiales en el XML: (1) y (2). (1) muestra cómo describir la dependencia en otro servicio. Este ejemplo requiere el crear un T ransactionManager antes de que la implementación se encuentre en la etapa 'real'. (2) es un poco más complejo ya que le falta información adicional. Por defecto, los nombres de las implementaciones dentro del microcontenedor son nombres URI, lo cual hace que el escribirlos a mano sea un proceso propenso a errores. Así que con el fin de declarar dependencias fácilmente en otras implementaciones, necesita un mecanismo de alias para evitar nombres URI. Puede agregar un archivo de texto simple llamado aliases.txt en su implementación. Cada línea del archivo contiene un alias, dándole al fichero de implementación uno o más nombres simples utilizados para referirse a este. 10.6. Escaneo de clases en busca de anotaciones Las especificaciones JEE actuales reducen el número de archivos de configuración, pero ahora se requiere que el contenedor realice la mayoría del trabajo utilizando @annotations. Con el fin de obtener información sobre @annotation, los contenedores deben escanear las clases. Este escaneo crea una penalidad en el rendimiento. Sin embargo con el fin de reducir la cantidad de escaneo, el microcontenedor proporciona otro gancho descriptor por medio de jboss-scanning.xm l. 108 Capítulo 10. Marco de trabajo de la implementación virtual Ejemplo 10.6. jboss-scanning.xml <scanning xmlns="urn:jboss:scanning:1.0"> <path name="myejbs.jar"> <include name="com.acme.foo"/> <exclude name="com.acme.foo.bar"/> </path> <path name="my.war/WEB-INF/classes"> <include name="com.acme.foo"/> </path> </scanning> Este ejemplo muestra una descripción simple de rutas relativas para incluir o excluir cuando se escanea en busca de información de metadatos anotados para Java Enterprise Edition versión 5y posteriores. 109 JBoss Enterprise Application Platform 5 Manual del usuario de JBoss Microcontainer Historial de revisiones Revisión 5.1.0-3.4 00 Rebuild with publican 4.0.0 2013-10-31 Rüdiger Landmann Revisión 5.1.0-3 Rebuild for Publican 3.0 2012-07-18 Anthony T owns Revisión 5-1 Wed Sep 15 2010 Misty Stanley-Jones JBPAPP-5076 - Arreglar los ejemplos y sus leyendas Se cambió el número de la versión en relación con los requerimientos de la versión. Revisado para JBoss Enterprise Application Platform 5.1.0.GA. 110