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.