Jul 10

Amago de pasar a maven 3.0.4

Hace tiempo que está maven 3.x disponible para usar y hace tiempo que tengo pendiente probarlo, sigo con mi maven 2.2.1. Hoy, como tenía un rato y estaba investigando algunas cosillas de maven, decidí bajarme y probar con la 3.0.4

Me puse con un proyecto relativamente sencillo, no es más que un jar con un main que hace consultas a unos web services (usando apache CXF) y mete los resultados en una base de datos (con Hibernate y PostgreSQL). Por supuesto, tiene un assembly para generar un zip con todas las dependencias y poder instalarlo fácilmente en el servidor de producción.

Pues nada, pruebo con maven 3.0.4 y lo primero que veo es que saca más log y es más "protestón". Donde maven 2.2.1 hace y no dice ni mú, este saca warnings de cosas que tienes mal y deberías arreglar. Le voy haciendo caso, puesto que es más sabio que yo y hay que hacerle caso. Al final consigo compilar todo sin warnings con maven 3.0.4. Genero mi zip assembly, me lo llevo al servidor de pruebas, lo desempaqueto, lo arranco y …. "NoDefClassFound".

Horror. Búsqueda de la clase pérdida, está en un jar stax2-api, necesitado por un woodstox-core-asl, que a su vez es necesitado por una librería de CXF. Curioso, ese jar parece estar dentro del zip en su sitio correcto. Reviso el fichero de manifiesto del jar con el main y la dependencia está. La clase perdida efectivamente está dentro del jar. ¡Qué cosa más rara!. Un rato mirando y remirando… hasta que me doy cuenta que la versión del stax2-api no es la misma en el fichero de manifiesto que la del jar que hay en el assembly. Una es la 3.0.5 y la otra la 3.1.1.

A revisar. Pues no, el pom.xml está bien, de hecho,  no referencia directamente esa librería para nada. mvn dependency:tree me dice que necesito la 3.0.5. El fichero de manfiesto generado con el maven-jar-plugin añade al Class-Path la 3.0.5, pero el maven assembly mete la 3.1.1.

Vuelta a maven 2.2.1 (sólo cambiar el path donde se busca maven) y listo, arreglado, ya funciona bien y no hay incoherencia de versiones. Así que nada, me sigo quedando con 2.2.1. O bien el plugin assembly no es muy compatible con maven 3.0.4, o bien tengo alguna cosa rara en algún sitio que afecta sólo a esa versión nueva de maven y no a la vieja.

Apr 25

¿Elegancia o sencillez?

 

En el trabajo llevamos varios días peleándonos con la instalación de una versión de nuestro software en uno de nuestros sistemas. El sistema consta de unas diez estaciones de trabajo solaris y unos veinte PCs con Windows. En todos ellos corren aplicaciones nuestras, en su mayoría java. Estas aplicaciones tienen algunas partes comunes, pero son distintas en cada una de las estaciones y de los PCs (cada uno está especializado en diversas funciones, algunas comunes, otras no y comparten mucha información entre ellos). En las estaciones hay bases de datos Oracle, con muchas tablas comunes, pero otras distintas en cada estación. Y en todo esto reside el problema de la instalación.

La gente está dividida en dos posibles tipos de instalación.

Junto con algunos, yo soy partidario de implementar las distintas funcionalidades del sistema en jar distintos e instalar en cada estacion/PC sólo aquellos jar que son necesarios, de forma que ninguna estación/PC lleve más jar o ficheros de configuración que no va a usar. Esta es la solución que considero elegante, pero es más compleja. Requiere generar instaladores/zips disintos para cada estación/PC, así como ser mucho más cuidadoso en esta generación de instaladores/zips, muchos jar, muchos grupos de ficheros de configuración, partes comunes y partes específicas.

Otros piensan que es mejor hacer un único mega-jar, o unos pocos jar grandes, un único mega-grupo de ficheros de configuración e instalar todo en todos lados. De esta forma, un único instalador o un único zip vale para todas las estaciones/PCs. Luego es el propio software el que mirando el nombre de la estación/PC en el que corre, sabe qué fichero concreto de configuración leer, de qué clase principal hacer el new y actuar como lo que le toca. Esta instalación es, desde mi punto de vista, más chapuza, pero es innegable que es infinitamente más sencilla.

Y después de la pelea de estos días atrás para la instalación según mi punto de vista (disintos zips/instaladores que instalan en cada estación/PC sólo lo necesario), creo que estoy empezando a cambiar de opinión. Los instaladores/zips, desde luego, se hacen con procesos automáticos, pero alguien tiene que decirle a ese proceso qué debe meter. Según evoluciona el software y va llevando más funcionalidades y ficheros, hay que tocar la configuración de la herramienta que genera los instaladores/zips (izpack, maven assembly,…) y hay que hacerlo con cuidado. Este proceso es manual y está sujeto a errores humanos, por lo que a nuestros instaladores siempre les acaba faltando alguna cosa y necesitan su proceso de "depuración".

En fin, no me gustan las chapuzas y tengo que pensar seriamente la forma de mejorar el proceso de generar los zips/instaladores, pero desde luego, es difícil resistirse a la facilidad de instalación de "todo va en todos sitios, aunque no se use". Es mucho más fácil instalar un solo mega-jar en todos lados que instalar varios jar distintos en cada estación/PC.

Nov 05

maven, jar, assembly y snapshots (y III)

 

Bueno, pues al final la solución al problema del assembly, el jar y los snapshots no ha sido tan compleja. Complejo, complejo, lo que se dice complejo, ha sido encontrarla por la web. La implementación una chorrada.

Resulta que el plugin assembly admite que digamos cómo queremos que se llamen los jar que va a meter en el zip, por lo que podemos cambiar el nombre para que no tengan el timestamp yyyymmdd.hhmmss-x y sí salga la palabra SNAPSHOT. Para ello, en el fichero de configuración de assembly (dep.xml suelo llamarlo), basta poner algo como esto

<dependencySets>
   <dependencySet>
      <outputDirectory>/Jar</outputDirectory>
      <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}
                   ${dashClassifier?}.${artifact.extension}</outputFileNameMapping>

      <scope>runtime</scope>
   </dependencySet>
</dependencySets>

 El "truco" está en el tag <outputFileNameMapping>. Con él podemos decir qué nombre queremos que tengan los jar y para ello podemos usar variables que nos proporciona el plugin para cada jar. Así, por ejemplo, ${artifact.artifactId} es el artifactId del jar en cuestión.

La diferencia con la opción por defecto es ${artifact.baseVersion}. Esta variable contiene la versión con la palabra SNAPSHOT. Si usamos la opción por defecto, ${artifact.version}, esta contiene el timestamp en vez de SNAPSHOT.

Curiosamente, descubrí que también forma parte del nombre una cosa que se llama ${dashClassifier}. Cuando uso rmi, uso un plugin que genera los stubs. Ese plugin genera dos jar. Uno de ellos, el normal de maven, por ejemplo proyecto-1.0.jar. El otro, con los stubs e interfaces para el cliente rmi, al que llama proyecto-1.0-client.jar. Pues bien, el dashClassifier es ese "-client" que se añade al nombre del fichero.

Más detalles en "Hacer un zip para distribuir".

Nov 04

maven, jar, assembly y snapshots (II)

 

Bueno, pues después de compilar el maven-assembly-plugin, lo he intentado en el trabajo y me has pasado las siguientes cosas:

Hago el mvn install. Me pide uno de los jar que mencioné en el post anterior. Le hago a ese jar el mvn install:install-file para instalarlo a mano. Sigo compilando y no me pide el segundo. Sin embargo, fallan los test de compilado.

Vuelvo a recompilar sin pasar los test   ( mvn -Dmaven.test.skip=true install ) y compilado, sin test. Me pongo a ejecutar el mvn assembly:assembly en un proyecto … y excepción, class not found de una de las clases del plugin

Después de pelearme, buscar ese class not found a ver de dónde es para poner la dependencia y desesperarme un rato gordo, se me ocurre hacer el mvn install:install-file del segundo jar mencionado en el post anterior. Cosa curiosa, aunque ese mismo jar me lo bajó maven de algún repositorio, y aunque el número de versión coincide, no es el mismo. Con este segundo jar ya compila todo pasando los test y no da excepción en el mvn assembly:assembly. Supongo que este tipo de cosas es lo que tiene el tirar de versiones beta-SNAPSHOT. De todas formas, también deja claro que no son demasiado cuidadosos con los jar que suben a internet, ya que con el mismo número de versión parece que puedes encontrar versiones distintas.

Y al final resulta que no funciona. Efectivamente, el mvn assembly:assembly te mete en el zip las versiones snapshots de los jar de los que dependes sin poner la fecha y manteniendo la palabra snapshot …. simpre y cuando en tu repositorio local hayas hecho un mvn install de ese snapshot con fecha posterior a la más moderna del repositorio de snapshot. O sea, que para que funcione, tienes que sacar el cvs de todos los proyectos snapshot de los que dependes y hacer mvn install de todos ellos y rezar porque nadie suba una snapshot al repositorio común en el entretanto.

En fin, estoy planteándome hacer un plugin que llame a assembly:directory. Este goal deja todos los ficheros en el target, desempaquetados. Luego, mi plugin, cambiará los ficheros-fecha.jar por fichero-snapshot.jar y hará el zip. Este bug lleva ya al menos un año en la página de bugs y todavía da la impresión de que no tiene una corrección seria.

Se me ocurrió mirar a mi los fuentes del plugin assembly a ver si podía hacer un arreglo…. pero de svn se baja la friolera de unos 6000 ficheros en los que hay 286 ficheros fuente java. No sé, creo que me da un poco de pereza….

Nov 01

maven, jar, assembly y snapshots

 

Hace tiempo comenté que había un problema entre el plugin de maven que mete el fichero de manifiesto en el jar y el plugin de maven que hace un zip para distribuir. Este problema se presenta cuando entre los jar de los que dependemos hay versiones SNAPSHOT.

Estas son versiones de jar que están todavía en desarrollo y que por tanto no tienen una versión fija estable. Cuando generas un jar de una versión SNAPSHOT y lo subes al repositorio, maven cambia la palabra SNAPSHOT por la fecha de compilado. Así, por ejemplo, un jar descargador-0.1.0-SNAPSHOT.jar se sube al repositorio de jars como descargador-0.1.0-20081101.155652-1.jar, en el que las cifras, si te fijas, son la fecha y hora de compilado.

Pues bien, el plugin que genera el fichero de manifiesto y lo pone dentro del jar, pone el Class-Path usando descargador-0.1.0-SNAPSHOT.jar, mientras que el plugin assembly que hace el zip para distribuir mete dentro el jar descargador-0.1.0-20081101.155652-1.jar, por lo que al desempaquetarlo todo símplemente no funciona.

Esto es un bug conocido que todavía no tiene solución oficial. Y esto ha hecho que me echara atrás en meter un respositorio de snapshots en condiciones, ya que luego no podía generar los zip bien. Sin embargo, el repositorio de snapshot en condiciones se hace muy necesario en proyectos grandes, compuestos por muchos jar y en los que desarrolla mucha gente. La ventaja de un repositorio de estos es que yo puedo tener sacada del sistema de control de versones sólo aquella parte en la que estoy trabajando y cuando hago un mvn install o un mvn eclipse:eclipse, maven busca las versiones snapshot más modernas que mis compañeros han hecho para bajarmelas y ponerlas como parte de mi proyecto. Este mecanismo evita que tenga que tener sacados todos los fuentes, o que tenga que preocuparme manualmente de actualizar los jar de los demás. Así que me he puesto a investigar una posible solución.

Rebuscando en internet, he visto que hay una versión 2.2-beta-3 que corrige el bug (fijaos que en el enlace anterior el bug está marcado como fixed en dicha versión), así que me he puesto a buscarla. Dicha versión no está disponible todavía en ningún repositorio de maven, sólo están disponibles sus fuentes a través de subversion. Así que a ello, me los bajo y a compilar.

$ svn co http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-assembly-plugin/ maven_assembly
$ cd maven_assembly
$ mvn install

y ya la hemos liado. Después de un buen rato de bajarse jars y cosas de la web, da el error de que no encuentra un artifact. Nueva búsqueda por internet. Al final encuentro que es un problema también real y que hay que bajarse a mano un par de jars. Una vez hecho esto y el correspondiente mvn install:install-file para subirlo a mi repositorio local, el compilado ha ido sin problemas.

Ahora sólo queda lo más importante: probarlo.