La Coctelera

El niño ya tiene cuenta en Flickr

Se pueden ver sus fotos organizadas en álbumes, por etiquetas o en la galería. El feed RSS correspondiente ya ha sido añadido en la sección de enlaces de la derecha. Y de regalo, una fotico:

habilitar JMX en un entorno de producción

Esta anotación describe como habilitar las extensiones JMX con autenticación y/o SSL. Hace poco tuvimos que hacerlo en el trabajo y dado que no he encontrado documentación al respecto en casi ningún sitio, se queda aquí para que no se me olvide.

JMX por cierto, es una de las técnicas que se usa para interactuar con una JVM remota:

Casi cualquier tutorial disponible en Internet tiende a centrarse más en el uso del API que en su configuración, la cual se suele resumir en arrancar la máquina virtual con al menos tres parámetros (java -D..):

  • com.sun.management.jmxremote.port: puerto a través del cuál nos conectamos a la JVM remota, por poner un ejemplo, el 8888.
  • com.sun.management.jmxremote.ssl: establece el uso de SSL; para no complicarse mucho la vida se suele poner a false.
  • com.sun.management.jmxremote.authenticate: establece si hay que identificarse para poder conectarse a la JVM remota; para no complicarse mucho la vida se suele poner a false.

Esto, que está muy bien para hacer pruebecillas caseras, no es muy aconsejable en entornos de producción. De hecho, no es nada aconsejable: los servidores de aplicaciones suelen publicar MBeans JMX que permiten interactuar con prácticamente cualuiqer funcionalidad del servidor, por ejemplo, pueden permitir ejecutar una query (cualquier query) SQL para "comprobar" el estado de un pool de conexiones, se puede mandar una orden al servidor para que se apague... Como éstas, se pueden hacer 200 más, por lo que se hace necesario securizar el acceso por JMX a una JVM. Para esto tenemos dos opciones, no exclusivas:

1) Securizar el acceso usando usuario/contraseña

Tal cómo se explica en javapassion, hay que configurar dos ficheros, la propiedad com.sun.management.jmxremote.authenticate debe ir a true, y deben añadirse dos propiedades más cuándo se lanza la JVM:

  • com.sun.management.jmxremote.access.file: especifica la ruta al fichero de acceso.
  • com.sun.management.jmxremote.password.file: especifica la ruta al fichero de contraseñas.

Por motivos de seguridad, ambos ficheros deben ser accesibles únicamente por el usuario que arranca la JVM. Bajo Linux/Unix esto se consigue con las órdenes:

$ chmod 0600 jmx.access
$ chmod 0600 jmx.password

Bajo Windows hay que dar alguna vuelta más, o bien se puede ejecutar desde una consola de comandos las siguientes órdenes, que para el caso va a ser lo mismo:

$ cacls jmxremote.password /P [username]:R
$ cacls jmxremote.access /P [username]:R

siendo [username] el usuario con el que queremos tener acceso al fichero. En caso de no aplicar estas políticas de seguridad a los ficheros, nos encontraremos con errores del tipo "Password file read access must be restricted".

2) Securizar el acceso usando SSL

Por un lado, la JVM que monitoriza (el cliente JMX) debe arrancarse con la propiedad com.sun.management.jmxremote.ssl.need.client.auth a true, mientras que la JVM monitorizada debe ir con la propiedad com.sun.management.jmxremote.ssl a true. Una vez esta última propiedad está a true, se deben indicar el resto de propiedades com.sun.management.jmxremote.ssl.*, que vienen convenientemente detalladas en el enlace anterior.

restrospecter - el scout corrupto

Detalle de la estatua de Daniel O'Connell, O'Connell street, Dublín

Uso del plugin Cobertura dentro de un proyecto multi-módulo en Maven 2

Situación inicial: supongamos la siguiente estructura de proyecto

./project
|-pom.xml
|-project-api
| |- pom.xml
|-project-impl-1
| |- pom.xml
|-project-tests
| |- pom.xml
  • En este proyecto los tests correspondientes a project-api se encuentran en el módulo project-tests, lo cuál está considerado como una buena práctica. Este módulo únicamente contiene los tests, es decir, las carpetas ./src/main/java, ./src/main/resources, etc. están vacías.
  • El plugin de Cobertura se encuentra configurado correctamente.

El problema: los tests de project-tests se ejecutan correctamente, si bien no se está obteniendo la cobertura de los mismos, el plugin de Cobertura solamente es capaz de dar la cobertura del código dentro de un mismo módulo.

Análisis: Cobertura sólo instrumenta las clases dentro de un mismo módulo, dejándolas en el directorio ./target/generated-classes/cobertura, para luego usarlas al calcular la cobertura del código. Buscando la manera de solucionar todo esto, la única referencia la he encontrado en la documentación de Seam, el framework open source de la gente de Hibernate y compañía (de hecho este post ha sido fusiladotiene exactamente la misma estructura que la página referenciada).

La solución que se propone en el enlace anterior pasa por incrustar un script de Ant que copia las clases instrumentadas de un módulo a otro, ejecuta el plugin de Cobertura y deja los resultados en el proyecto inicial. La solución que se va a exponer deja los resultados del test de cobertura en el proyecto correspondiente a los tests (lo cual me parece más correcto), es más concisa y más a la Maven way.

Solución: el plugin de Cobertura se ejecuta al lanzar el site del proyecto, por lo que vamos a aprovechar para montar un jar con las clases instrumentadas y subirlo a nuestro repositorio local (es decir, ejecutar un install y no un deploy, ya que no nos interesará que este nuevo .jar esté en nuestro repositorio público). Enlazaremos la ejecución de los plugins a la fase de site, que es cuándo se generan las clases instrumentadas de Cobertura.

Así pues, en el pom.xml del módulo project-impl-1 incluimos las siguientes entradas:

[...]
<build>
[...]
<plugins>
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution><!-- jar con las clases instrumentadas por cobertura -->
<id>cobertura-jar</id>
<phase>site</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>cobertura</classifier>
<classesDirectory>${basedir}/target/generated-classes/cobertura</classesDirectory>
</configuration>
</execution>
</executions>
</plugin>

<plugin><!-- instalamos el jar en el repositorio local -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<executions>
<execution>
<id>cobertura-install</id>
<phase>site</phase>
<goals>
<goal>install</goal>
</goals>
<configuration>
<classifier>cobertura</classifier>
</configuration>
</execution>
</executions>
</plugin>
[...]
</plugins>
[...]
</build>
[...]

La clave está en el elemento classifier, que nos permite generar el jar que queremos añadiéndole el sufijo que le indicamos, lo cual nos permite generar varios .jars distintos dentro de un mismo proyecto Maven 2.

En el módulo de tests, configuramos el plugin de Cobertura para que utilice el .jar con las clases instrumentadas (esto es, lo añadimos como dependencia al plugin) y sobreescribimos el elemento sourceDirectory. Este último paso es necesario debido a que Cobertura necesita tener acceso a los fuentes para generar el informe de cobertura del código. En fin, que en el pom.xml correspondiente a project-tests incluimos las siguientes entradas:

<build>
<sourceDirectory>${basedir}/../project-impl-1/src/main/java</sourceDirectory>

<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<dependencies>
<dependency><!-- cobertura necesita tener las clases instrumentadas en el classpath -->
<groupId>atlas.dpugd</groupId>
<artifactId>project-impl-1</artifactId>
<version>1.0.0</version><!-- se sobreentiende que esta versión es la generada en el pom de <tt>project-impl-1</tt> -->
<classifier>cobertura</classifier>
</dependency>
</dependencies>
</plugin>
</plugins>

</build>

et le vôila, ya hemos conseguido la cobertura de los tests dentro de un proyecto multi-módulo de Maven 2 y lo más importante, integrándolo de un modo transparente dentro del proceso de generación de la documentación, sin tener que efectuar ningún paso intermedio adicional.

Pintada en Lavapiés

Incluir fuentes y javadocs en un proyecto Maven 2

Por defecto, cuando se instala un artefacto en el repositorio de Maven 2, se incluye nada más que el ejecutable (.jar, .war, etc.) generado. Si más gente va a trabajar con nuestro desarrollo puede ser buena idea incluir en nuestro repositorio tanto los fuentes como los javadocs asociados al proyecto, cosa que hacen la gran mayoría de proyectos open source que usan Maven 2. Vamos a ver cómo se hace:

Para instalar los fuentes, en el pom.xml deberemos añadir la siguiente configuración:

<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

Para adjuntar los javadocs hay que incluir una configuración similar, pero dado que generarlos provoca que el proceso de build sea bastante más largo, compensa incluirlo dentro de un profile. De este modo, incluiremos los javadocs únicamente cuando nos interese (normalmente, cuando vayamos a liberar una nueva versión estable). La configuración a incluir es ésta:

<project>
...
<profiles>
<profile>
<id>install-javadocs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<verify</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
...
</project>

Pila de ejecución Java - desde HTTP hasta JDBC de un vistazo

Buscando información esta tarde en el trabajo he ido a dar a parar con un post que ilustra una pila de ejecución completa de una aplicación web escrita en Java, desde que entra la petición HTTP hasta la ejecución del código JDBC.

Es un post que ya tiene algo más de dos años, pero que sigue siendo interesante de ver. Alrededor del código desarrollado, el código servidor de aplicaciones, Acegi, Spring MVC, Spring WebFlow, AOP, el wrapper de Spring para Hibernate, el propio Hibernate y su traducción a JDBC.

Como una imagen vale más que mil palabras (click en ella para ver una versión algo más grande):

No me voy a para a hacer consideraciones, ya están hechas en el anotación enlazada. A todo esto, en dicho post también se puede leer cómo se ha obtenido la traza y descargar una versión en PDF con la imagen anterior a una resolución bastante mayor.