La Coctelera

Categoría: Informática

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.

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.

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.

Participando en un meme

Hace tiempo publiqué una frikada del 15, consistente en un script que devolvía los diez comandos más usados. Navegando por Internet me he dado cuenta de dos cosas, a saber:

  1. que es un script más viejo que el mearla tira de viejo.
  2. que forma parte de un meme consistente en contar cuál son tus diez comandos más usados.

Así que, ni corto ni perezoso me he puesto a ver cuáles serían los míos y a compartirlos con el mundo:

Microsoft Windows XP [Versión 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\workspaces>history | awk '{print $2}' | awk 'BEGIN {FS="|"} {print $1}' | sort | uniq -c | sort -nr | head -10
"history" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.

C:\workspaces>

Odio los memes. Lo que hace el aburrimiento.

Acceso al contexto de Spring mediante código

Cuando se usa Spring, normalmente no hay que preocuparse de lo que hay por detrás de la inyección de dependencias: se obtienen los beans indicados en los ficheros de configuración por "arte de magia". Sin embargo, hay situaciones en las que es necesario acceder a dichos beans y la inyección de dependencias no es automática o, dicho de otro modo, hay que acceder manualmente al contexto de Spring para tener acceso a los beans.

A continuación, el listado de todos los casos en que esta situación puede darse (si hay alguno más o hay algo incorrecto, por favor deja un comentario, que yo solo escribo un blog O:-)):

1.- Acceso genérico en una aplicación web.
Aunque en la mayoría de los casos la inyección de dependencias se realiza configurando referencias en los archivos de Spring (los típicos ficheros applicationContext*.xml), puede que en alguna ocasión especial se necesite acceder directamente al contexto para obtener un bean por programación. Esto se haría así:

WebApplicationContext springCtx =
    WebApplicationContextUtils.getRequiredWebApplicationContext(
        ServletActionContext.getServletContext()
    );
ClaseDeBean miBean = ( ClaseDeBean )springCtx.getBean( "idDelBean" );

2.- Acceso desde un servlet.
Para acceder al contexto de Spring desde un Servlet, hay que definir un bean en el applicationContext*.xml de turno tal que:

<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
  <property name="attributes">
    <map>
      <!-- inyecta los siguientes beans en el servlet context para que los servlets puedan acceder a ellos -->
      <entry key="projectManager" value-ref="projectManager" />
      <entry key="userManager" value-ref="userManager" />
    </map>
  </property>
</bean>

De este modo, los beans indicados en el map estarán disponibles en los servlets como si fueran un atributo más del ServletContext:

ProjectManager projectManager = ( ProjectManager )request.getSession().getServletContext().getAttribute( "projectManager" );

3.- Acceso desde un Custom Tag.
Se debe heredar de la clase org.springframework.web.servlet.tags.RequestContextAwareTag, que a su vez hereda de TagSupport. Los beans se declaran en la clase, como se hace en Spring normalmente, declarando los objetos de turno con sus getter y setter. El método "equivalente" a doStartTag() es doStartTagInternal(), dónde se debe implementar la lógica del Custom Tag. Se obtienen los beans de Spring sin necesidad de configuraciones adicionales:

WebApplicationContext springCtx = getRequestContext().getWebApplicationContext();
userManager = ( UserManager )springCtx.getBean( "userManager" );

4.- Acceso en los tests unitarios (JUnit).
Para acceder al contexto de Spring en los tests unitarios, la clase con los mismos debe heredar de -respira hondo que va la clase- org.springframework.test.AbstractTransactionalDataSourceSpringContextTests.

Esta clase funciona como cualquier otra clase de JUnit, en la que adicionalmente hay que implementar el método protected String[] getConfigLocations(), que debe devolver un String[] con las localizaciones de los ficheros dónde están definidos los beans de Spring que se quieren inyectar en los tests. Además, se debe indicar el modo de inyección (autowire) de los beans. Un ejemplo de dicho método sería:

protected String[] getConfigLocations() {
  setAutowireMode(AUTOWIRE_BY_NAME);
  return new String[] {
      "classpath:/applicationContext-resources.xml",
      "classpath:/applicationContext-dao.xml",
      "classpath*:/applicationContext.xml",
      "classpath:**/applicationContext*.xml"
  };
}

El uso de comodines aplica exactamente igual que cuándo se definen en el fichero web.xml.

5.- Acceso genérico en una aplicación stand-alone.
Por último, para acceder al contexto de Spring desde una clase de una aplicación de escritorio:

ClassPathXmlApplicationContext springCtx =
  new ClassPathXmlApplicationContext( new String[] {
      "classpath:/applicationContext-resources.xml",
      "classpath:/applicationContext-dao.xml",
      "classpath*:/applicationContext.xml" });
UserManager userManager = ( UserManager )springCtx.getBean( "userManager" );

En el array de String se pueden incluir comodines como de costumbre, solamente hay que tener en cuenta que los ficheros de configuración de los beans tienen que estar cargados en el classpath de la aplicación.

El Capitán Obvio advierte: hay que tener en cuenta que instanciar un ClassPathXmlApplicationContext es costoso, dado que carga todos los beans definidos en los ficheros xml, por lo que el procedimiento normal a seguir es instanciarlo cuando arranque la aplicación, por ejemplo en un Singleton, y luego obtener los beans a través de dicho objeto.

Generación automática de diagramas UML a partir del código fuente

Es posible generar diagramas de clase automáticamente a partir del código fuente usando Maven 2. Así, una vez se parte de los diagramas originales, no hace falta irlos actualizando a la par que el código para mantenerlos al día. Como pre-requisito es necesario tener instalado Graphviz, un programa open source con versiones para Windows, Linux y Mac, y tener en el path el subdirectorio bin de dicha aplicación.

Una vez hecho esto, se configura el plugin de generación del javadoc en la sección de reporting para que use un doclet que genere los diagramas UML en vez del que se usa por defecto:

<reporting>

 [...]

 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <configuration>
    <show>private</show>
    <doclet>gr.spinellis.umlgraph.doclet.UmlGraphDoc</doclet>
    <docletArtifact>
      <groupId>gr.spinellis</groupId>
      <artifactId>UmlGraph</artifactId>
      <version>4.6</version>
    </docletArtifact>
    <additionalparam>
      -inferrel
      -collpackages java.util.*
      -hide java.*
    </additionalparam>
  </configuration>
 </plugin>

 [...]

</reporting>

Al generar el site asociado al proyecto, si se verifican los javadocs generados uno se encuentra con algo similar a esto:

Si se tiene un proyecto que actúe como parent POM, lo mejor es definir la configuración anterior en él, de este modo la generación de diagramas de clase estará disponible para todos los proyectos hijo. ¡Listo!

(Leído en el wiki de VLab).

Primeras impresiones usando Maven 2

Por fin he podido retomar un proyecto interesante (esto es, tecnológicamente avanzado), que hace uso de, entre otras cosas Maven2. Maven1 ya era de por si muy bueno, así que tenía expectativas de ver cuánto de bueno tenía la versión 2. Tras un par de días pegándome con el programilla de marras, me he quedado con un sabor un tanto agridulce: por una parte es indudable que, bien configurado, Maven2 es una roca. Por otro lado, puede ser un auténtico dolor en el c*** conseguir que funcione como uno quiere.

La curva de aprendizaje es bastante elevada (si vienes de Maven1 tienes mucho ganado), la documentación escasa en el mejor de los casos y es una herramienta que hay que conocer muy bien si se quiere parametrizar a gusto del consumidor. Hasta que te haces con él, sólo dos palabras acuden a tu mente para describir su funcionamiento: "magia negra". Y hoy toca centrarse en estas dos palabras tan inocentes, aunque eso sí, una vez aprendido a usarse y una vez tu proyecto está configurado, ¡joé, qué gustazo de programa!

Entre Maven1 y Maven2 hay algunas características que se han perdido por el camino, debido a decisiones de diseño que me resultan incomprensibles. La primera de ellas tiene que ver con la configuración por convención. En ambos programas se parte de la idea de que hay un fichero maestro (por llamarlo de algún modo) que define todos y cada uno de los aspectos de un proyecto: dónde están los fuentes, los resources, dónde se generan los compilados, etc., etc., etc., de tal modo que, cuando se define un nuevo proyecto se sobreescriben solamente las partes imprescindibles. Esto resulta en ficheros de configuración extremadamente pequeños, que definen la estructura y el comportamiento del proyecto. Al menos en Maven1.

En Maven2 me está siendo muy común ver cómo enseguida se hinchan desmesuradamente los ficheros de configuración. Un ejemplo, si tu proyecto usa características de Java5, tendrás que sobrescribir la configuración del plugin que se encarga de compilar y añadir este trozo de código en tu pom.xml:

<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <source>1.5</source>
    <target>1.5</target>
  </configuration>
</plugin>

Por lo visto este plugin está configurado por defecto para compilar código Java 1.3 ¡1.3! ¡A estas alturas! En fin, comprendo que, hasta cierto punto, esto tenga su pase y no sé cuánto de posible/complicado sería que estos parámetros se cambiasen dinámicamente en función del JDK usado. Así que mejor me voy a otro ejemplo que me ha resultado mucho más clamoroso: el uso de propiedades definidas en ficheros externos. Una de las cosas más chocantes que me he encontrado pasando de Maven1 a Maven2 es que en Maven1 había un fichero de propiedades, llamado project.properties, que podía usarse dentro del fichero project.xml. Este fichero desaparece en Maven2.

En su lugar aparece la sección <properties> en el pom.xml. Asimismo, también pueden usarse las propiedades de java (p.ej. ${java.io.tmpdir}) y las que se le pasen al comando mvn mediante el flag -D. Es algo que se pregunta muchísimo en las listas de correo, foros y similares, y a lo que no se suele responder ni mucho ni bien. Me ha costado encontrar el porqué de esta decisión, pero después de bucear unas horas por listas de correo llegué a la respuesta, dada por Jason van Zyl, líder del proyecto Maven: de este modo protegen la portabilidad del build respecto de variables externas. Una decisión totalmente debida al diseño funcional.

El workaround o solución que se propone: definir las distintas secciones <profile> en el pom.xml (o en el settings.xml). Post Data: para lo que antes se podía escribir en cuatro líneas en un fichero, lo que se está sugiriendo es incluir algo parecido a esto en el POM, por cada posible valor de la propiedad de marras:

<profile>
  <id>entorno</id>
  <activation>
    <property>
      <name>entorno</name>
      <value>desarrollo</value>
    </property>
  </activation>
  [...]
</profile>

Y dentro de ese mismo bloque <profile> se sobrescribe todo lo que vaya cambiando dentro de cada perfil. Se me ocurren mil razones de por qué esto no es una buena decisión, entre otras:

  • Primero, se está suponiendo que solo se utiliza Maven2 para todas las tareas de gestión de la configuración del proyecto cuando, por ejemplo, es bastante común querer lanzar los tests unitarios desde las facilidades dadas por el IDE de turno.
     
  • Segundo, aún aceptando que funcionalmente tiene más sentido definir los cuatro kilómetros de los que consta una sección <profile> y, dentro de ellos, las propiedades que hacerlo en un .properties, dado que así se definen perfiles distintos de construcción en función del tipo de entorno (desarrollo, producción, etc.) esto no tiene sentido entre distintos desarrolladores en un mismo entorno. Siguiendo esta lógica ¿qué se hace, definir tantos <profile> como personas pasen por el proyecto? ¿Se define una sección <profile> por cada entorno y cada desarrollador se ocupa de tener su pom.xml MODIFICADO? dado este caso, no es muy descabellado pensar que habrá problemas con el control de versiones de este archivo. A mi personalmente me parece que así lo que pasará es que se pisarán los cambios de unos y otros ¿No suena mucho más razonable tener un .properties de pocas líneas donde cada uno define su entorno, y donde si ocurre algo de todo lo anterior, el fichero que define la configuración del proyecto no corre peligro de ser modificado accidentalmente?
     
  • Tercero, el pom.xml resultante empieza a sobrecargarse a pasos agingantados, sin aportar ventajas evidentes sobre el uso de un fichero .properties.
     
  • Cuarto, si se utilizan proyectos padre, se dan casos donde las propiedades pueden no leerse, o sobrescribirse cuando no debieran, debido a un bug de Maven (esto viene mucho más detallado en un artículo de un tal Zarar Siddiqi que estoy dejando para un poco más adelante).
     
  • Quinto, meter propiedades que afectan a la construcción del proyecto en el settings.xml, ese fichero que está en ${m2.home} y fuera del control de versiones del proyecto... Mejor no comento nada acerca de esto.
     
  • Sexto, estos bloques <profile> se pueden activar de varias maneras distintas pero, a mi entender, cualquiera de éstas protege tanto la portabilidad del build como el uso de un fichero de propiedades, ya que se sigue dependiendo de algo externo al POM para activar uno u otro perfil.

Aún así y a pesar de todo, he visto gente que además sobrescribe en el POM cosas como dónde poner los fuentes, o los tests unitarios... Configuración por convención es una buena-cosa-(tm) y, junto con la gestión de dependencias, de lo mejorcito que ofrece Maven2, y todavía hay quien decide reescribir todo o casi todo un POM. Cosas veredes, amigo Sancho.. Vale, ya paro de divagar.

Volviendo al caso concreto de los ficheros de propiedades, todavía es más sangrante. Un programador llamado Zarar Siddiqi ha desarrollado un plugin para Maven que permite usar las propiedades definidas en un fichero .properties dentro del pom.xml. Lo propuso a CodeHaus para que lo albergara en su repositorio de plugins, y fue votado por el suficiente número de usuarios como para que entrara en el mismo. Actualmente se encuentra en el equivalente del entorno de desarrollo (el Sandbox) del repositorio de CodeHaus.

Los motivos por los que todavía no ha entrado, y ya han pasado varios meses desde que está ahí, se pueden leer en esta ristra de Correos. En corto, simplificando y tras habérmelo leído, yo saco la siguiente conclusión: no se llevó los votos necesarios debido a que las personas que tenían poder de votación o bien votaban +0 ó bien -1, dando como razones o a)no lo estoy usando (los +0) o b)no he mirado el código, no sé la calidad del mismo y por tanto es probable que esté mal (los -1), pero no voy a molestarme en verificarlo (?!?!?!?!). Visto lo visto, Zarar ha dedicado un extenso artículo a comentar su experiencia con Maven2 y no lo ha dejado precisamente bien.

Justamente en ese artículo he visto otra perla que me ha dejado un poco perplejo (dejando aparte la supuesta no-calidad de algunos plugins, de la que no puedo opinar porque no me he puesto a ver los fuentes, ni tengo ganas de ello) y siguiendo su propia terminología, en Maven2 la ejecución de los distintos plugins se efectúa asociándolos a las distintas fases del ciclo de vida del proyecto. Hasta aquí todo bien y no hay ningún problema. Una cosa que yo siempre me había preguntado es qué pasaría si quiero ejecutar varios plugins en una misma fase. ¿Cómo le indico el orden de ejecución al POM? Respuesta: No puedo, los plugins de cada fase se ejecutan siguiendo un orden ALEATORIO. Y este bugfeature no lo encuentras documentado en ningún sitio, lo averiguas buceando por el código fuente.

Si has llegado hasta aquí es posible que no pienses muy bien de Maven2 ahora mismo, la verdad. En su defensa he de decir que no es tan malo como puedes estar pensando, de hecho más bien lo contrario. La gestión de dependencias y la configuración por convención que ofrece a mi me compensa seguir desarrollando con este programa, es de lo mejorcito que hay por ahí afuera, but so beware! the Eye of the Beholder could catch you! Para poder ir algo más allá de las configuraciones por defecto exige conocerlo muy bien, tiempo, paciencia y, sobretodo, conocerlo muy bien.

¡Ah!, y nada de tomarse nada de este post al pie de la letra, que criticar algo siempre es más fácil que hacerlo y el niño tampoco es que se considere un experto en la materia. Aunque aspire a serlo. Algún día.