domingo, 15 de abril de 2012

Weld y Arquetipo de Maven para JBoss 7

Con este post voy a dar comienzo a una serie de entradas que en este blog etiquetaré como Java EE 6 in Action, y que espejaré, como vengo haciendo últimamente en mi otro blog Tecnologías Java, para facilitar las búsquedas.

Mi objetivo es que juntos nos vayamos adentrando en el mundo de Java EE 6 (JSR-316) de la forma más ágil posible. La especificación de la nueva plataforma empresarial de Java se lanzó en diciembre de 2009. Si bien hace tiempo que ya está disponible la implementación de referencia, Glassfish 3, las empresas todavía no han adoptado completamente este estándar (por lo menos acá en Argentina), y es lógico, ya que sería una pésima estrategia migrar todo el software construido cada vez que hay una nueva actualización.

Pero es razonable esperar que los nuevos desarrollos comiencen a construirse sobre esta plataforma.

I - CDI y Weld

Vamos a comenzar con la especificación estrella, la nueva niña mimada de Java EE: CDI (JSR-299), el Contenedor de Contextos e Inyección de Dependencia (Java Contexts and Dependency Injection for the Java EE platform).

Wallpaper de Weld hecho por la comunidad de JBoss.
CDI fue influenciado por varios frameworks Java de inyección de dependencia como Seam, Guice y Spring. Con tantas herramientas dando vuelta era necesaria una especificación y la JCP se encargó de que CDI fuera más typesafe que Seam, más stateful y menos XML-centric que Spring, y mejor preparado para aplicaciones enterprise que Google Guice.

CDI es un contenedor liviano (aunque el adjetivo "liviano" está empezando a deprecarse) que provee servicios para:
  • Manejar el ciclo de vida de objetos stateful, asociados a contextos bien definidos
  • Inyección de dependencia typesafe
  • Interacción entre objetos mediante notificación de eventos
  • Interceptores de objetos para implementar orientación a aspectos (mucho mejor diseñado que los interceptores de EJB 3.0)
  • una SPI (service provider interface) para desarrollar extensiones portables
Weld es la implementación de referencia de CDI y, a la vez, el core de Seam 3. Seam 3, de JBoss, pasó a ser un conjunto de módulos que extienden las funcionalidades de CDI a través de la SPI provista por el estándar. No es el único. Hay otras extensiones menos conocidas como CODI, de Apache MyFaces, o CDI Source. Y la buena noticia es que la historia se repite. Las extensiones de CDI van camino a la unificación (probablemente para que el día de mañana formen parte de nuevas especificaciones). La semana pasada, InfoQ publicó una entrevista a Pete Muir, donde cuenta que se está incubando un nuevo proyecto en Apache que ya va por su release 0.1. DeltaSpike, como se llama el proyecto, es una fusión entre Seam 3 y CODI con el fin de tomar lo mejor de ambos mundos. Muir tiene la esperanza de que en el futuro otras extensiones del CDI se quieran unir a la fiesta.

II - Arquetipo de Maven para Weld

Para comenzar, qué mejor que tener a mano un código de ejemplo y que además lo podamos ejecutar.

Si descargamos Weld de la página de Seam, la última versión Final publicada a la fecha en la página es la 1.1.6 (aunque ya existe la 1.1.7, con fixes menores, que se subirá a la página en los próximos días), tendremos un zip con las librerías, más varios ejemplos ubicados en la carpeta "examples". Estos ejemplos se pueden seguir con la excelente documentación oficial de Weld. Si usamos Maven podemos usar las librerías que están subidas en los repositorios públicos.

En lo que queda del post, vamos a utilizar un arquetipo para crear todo un proyecto Weld que ya en sí mismo es un ejemplo que incluye un código bastante lindo para comenzar a inspeccionar. Las instrucciones para usar el arquetipo se pueden revisar aquí. El proyecto que genera funciona perfecto para la versión 6.1.0.Final de JBoss AS, pero yo lo voy a aggiornar un poco para que funcione para la versión 7.1.1.Final, la última publicada a la fecha.

El comando que usé para generar el proyecto fue:

mvn archetype:generate -DarchetypeArtifactId=jboss-javaee6-webapp -DarchetypeGroupId=org.jboss.weld.archetypes -DarchetypeVersion=1.0.1.CR2 -DarchetypeRepository=central

La versión de Maven que usé fue la 3.0.3, simplemente porque ya la tenía instalada en mi máquina.

III - Modificando el Proyecto para que Funcione en JBoss 7.1.1.Final


Paso 1: Descargar el JBoss AS 7.7.1.Final:


El JBoss AS se puede descargar de su página oficial. Se puede decir que JBoss AS 7 tiene cambios radicales con respecto a las versiones anteriores. A partir de la versión 7.1.0 implementa todos los perfiles de la especificación de Java EE 6.

Si quieren conocer más de JBoss AS 7 pueden leer el bloque de conocimiento de Epidata Consulting creado por Marcos Reynoso.

Paso 2: Configurar la variable jboss.home:


Si uno configura la variable de entorno JBOSS_HOME en el sistema operativo este cambio no hay que hacerlo. Como en mi rutina diaria utilizo varias versiones de JBoss, yo prefiero no configurar esta variable, ya que me trae problemas. La alternativa es hardcodear la ruta directamente en el mismo pom.xml.

<!-- To specify the JBoss AS directory the "Maven way", set the jboss.home 
property in an active profile in $HOME/.m2/settings.xml -->
<!-- By default, we assign it to the value of the JBOSS_HOME envrionment 
variable -->
<jboss.home>/opt/jboss/jboss-as-7.1.1.Final</jboss.home>

Paso 3: Nuevo Perfil Maven para JBoss 7:


El proyecto viene con cuatro perfiles Maven:

  • default: activo por defecto; funciona para JBoss 6.1.0.Final; por defecto no ejecuta los unit test
  • arq-glassfish-embedded: funciona con un Glassfish 3.1 embebido (standalone); sirve para correr los tests hechos con Arquillian
  • arq-jbossas-remote: sirve para correr los test hecho con Arquillian en un JBoss 6.1.0.Final remoto
  • arq-weld-ee-embedded: funciona con un contenedor Weld embebido; también para correr los tests hechos con Arquillian
Nosotros agregaremos un perfil nuevo llamado jboss7 que, tras aplicarle los siguientes pasos, quedará de la siguiente forma:

<profile>
 <!-- The default profile skips all tests, though you can tune it to run 
  just unit tests based on a custom pattern -->
 <!-- Seperate profiles are provided for running all tests, including Arquillian 
  tests that execute in the specified container -->
 <id>jboss7</id>
 <dependencies>
  <!-- Java EE 6 API dependency -->
  <!-- This one dependency imports all APIs available for a Java EE 6.0 
   application -->
  <dependency>
   <groupId>org.jboss.spec</groupId>
   <artifactId>jboss-javaee-6.0</artifactId>
   <version>${jboss-javaee6-spec.version}</version>
   <type>pom</type>
   <scope>provided</scope>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.4.3</version>
    <configuration>
     <skip>true</skip>
    </configuration>
   </plugin>
   <!-- Optional plugin deploys your war to a local JBoss AS container -->
   <!-- To use, set the JBOSS_HOME environment variable (or jboss.home 
    in $HOME/.m2/settings.xml) and run: mvn package jboss:hard-deploy -->
   <plugin>
    <groupId>org.jboss.as.plugins</groupId>
    <artifactId>jboss-as-maven-plugin</artifactId>
    <version>7.1.1.Final</version>
    <executions>
     <execution>
      <id>add-datasource</id>
      <phase>package</phase>
      <goals>
       <goal>add-resource</goal>
      </goals>
      <configuration>
       <address>subsystem=datasources,data-source=java:jboss/myDs</address>
       <resource>
        <enable-resource>true</enable-resource>
        <properties>
         <jndi-name>java:/jdbc/__default</jndi-name>
         <enabled>true</enabled>
         <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
         <driver-class>org.h2.Driver</driver-class>
         <driver-name>h2</driver-name>
         <user-name>sa</user-name>
         <password>sa</password>
         <pool-name>mypool</pool-name>
         <use-java-context>false</use-java-context>
        </properties>
       </resource>
      </configuration>
     </execution>
    </executions>
   </plugin>
  </plugins>
 </build>
</profile>

Paso 4: Plugin de JBoss 7 para Maven


Lo primero que le hice al perfil fue agregarle el plugin Maven para JBoss 7: jboss-as-maven-plugin, para poder desplegar la aplicación usando el comando jboss-as:deploy.

Paso 5: Aggiornamiento del DataSource


Todo cambió en JBoss AS 7, incluso la forma de escribir un datasource. El datasource para Hypersonic que viene con el proyecto, ubicado en src/main/resources-jbossas/default-ds.xml sirve para JBoss 6 pero no para JBoss 7. Ya que el jboss-as-maven-plugin permite agregar un recurso (como un datasource) en la misma configuración, no vamos a usar el default-ds.xml. Por eso verán que le agregué el goal add-resource a la configuración del plugin.

Paso 6: Deploy


Simplemente ejecutando:

mvn clean install jboss-as:deploy -Pjboss7

Problema con el Nuevo Classloader


Esto realmente no es un problema. Al desplegar, la aplicación tira una excepción de ClassNotFound:

Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Priority from [Module "org.jboss.logging:main" from local module loader @199836ed (roots: /jboss-as-7.1.1.Final/modules)]

Esta excepción no impide el correcto funcionamiento de la aplicación. Pero resulta visualmente molesto que en el log aparezca este error. Según este thread del foro de JBoss, esto se debe a un bug en el módulo de JBoss Logger y se arregla agregando manualmente los siguientes módulos al archivos $JBOSS_HOME/modules/org/jboss/logging/main/module.xml:

<!-- added these 2 dependencies -->
<module name="org.apache.log4j"/>
<module name="org.slf4j"/>

Si les interesa conocer más del nuevo manejo de classloader de JBoss 7 pueden leer estos artículos:


Paso 7: Ir a la Aplicación


Para terminar y ver la aplicación de ejemplo funcionando, abrimos un navegador web y vamos a la URL: http://localhost:8080/jboss-javaee6-webapp/. Si todo salió bien, deberíamos ver nuestro formulario de registración de miembros hecho con JSF 2.

http://localhost:8080/jboss-javaee6-webapp/