Nov 25

Archiva vs Nexus

 

En su día nos instalamos un repositorio propio para nuestros jar, de forma que estuvieran accesibles para todos los desarrolladores. Para ello usamos archiva, y ha funcionado más o menos bien con sus cosillas. Hace además las veces de proxy con los repositorios de maven que hay por internet. De esta forma, cada desarrollador únicamente debe configurar maven para que busque los jars en el repositorio de archiva y es este el que se encarga de acceder a internet y buscarlos si es necesario.

No hace mucho descubrí que había otra herramienta similar llamada nexus. Como archiva nos hacía cosas raras de vez en cuando (no traía las cosas de los repositorios de internet, no sé muy bien si por culpa de archiva o de nuestra conexión a internet, que va con proxy autentificado. También dejaba ficheros tmp vacíos en el repositorio de vez en cuando). Así que hoy me he decidido a instalar nexus y probar.

La instalación sencilla, un zip que te bajas, desempaquetas y tienes los scripts necesarios de windows, linux, solaris… para instalar nexus como servicio y arrancarlo y pararlo. Eso sí, hay dos versiones, la gratis con menos posibilidades de autentificación/seguridad, y la de pago que tiene de todo. La gratis en principio tiene lo necesario: gestión de usuarios y permisos propia, funciones de proxy y repositorios propios.

La interface web mucho mejor que la de archiva. Bastante más bonita y agradable. Rápidamente me puse en ella a configurar nuestros repositorios, tanto los propios, como los repositorios que son proxy de los estándar de internet (que ya vienen configurados los de maven central, apache y codehaus).

Y vamos a las cosas que me han gustado y que me han decidido a intentar el cambio en serio:

  1. Es más estricto que archiva con los SNAPSHOTS y las releases. Archiva permite subir y bajar jars snapshots y no snapshots de repositorios snapshots y no snapshots indistintamente. Somos los desarrolladores los que tenemos que tener cuidado de dónde subimos los jar. Nexus es más estricto, si intentamos subir un jar no snapshot a un repositorio que hemos marcado como snapshot, protesta. Y al revés también.
  2. Permite programar tareas de mantenimiento periódicas y entre ellas, la que veo más útil en nuestro caso: permite que se limpien automáticamente las versiones snapshots más antiguas o indicar cuántos snapshots quieres como máximo por cada jar. En un entorno de desarrollo como el nuestro en el que Hudson genera y sube muchos snapshots gigantes todas las noches, una limpieza periódica se hace imprescindible.
  3. Cuando configuras un repositorio como proxy de uno externo, tienes más visibilidad de si tiene o no conexión con el repositorio externo, si se ha bajado algo de él y qué se ha bajado.

Para ser justos, estoy comparando una versión antigua de archiva, que instalé hace mucho, con la última de nexus. Es posible que las versiones más modernas de archiva hayan mejorado o permitan hacer estas cosas que digo que hace nexus.

May 08

Sobre dependencias

 

Un par de cosillas/problemillas que me he encontrado sobre las dependencias de unos jar con otros.

Maven por un lado

Supón que tenemos un proyecto A con maven que tiene a su vez dos subproyectos B y C. Si le decimos a maven a través de los ficheros pom.xml que B necesita de C para compilar y que C necesita de B para compilar, maven, obviamente, protesta. La excusa es que no sabe qué debe compilar primero, ya que el uno depende del otro. Hasta aquí todo parece correcto.

Supongamos ahora la misma estructura de proyectos de antes, pero esta vez decimos que B necesita a C para compilar y que C necesita de B sólo en tiempo de ejecución (runtime). Pues bien, maven protesta igualmente. Sin embargo, esta vez, la excusa ya no vale. Si B necesita de C para compilar y C sólo necesita a B en runtime, entonces está claro que se puede compilar primero C y luego B. Esto ya es algo que puede en un momento dado molestar, aunque quizás no sea muy correcto un proyecto con esta dependencia.

Pero ahora viene lo peor. Si en vez de un proyecto A con subproyectos B y C tenemos directamente dos proyectos maven independientes, B y C, maven teóricamente sería capaz de detectar igualmente las dependencias. En el repositorio de jars que usemos estarán los jar de B y C y sus ficheros pom.xml con las dependencias. Sin embargo, al ser independientes, NO protesta. En principio no podemos llegar a esta situación directamente, ya que si B necesita de C para compilar y C necesita de B para compilar, no podemos compilar ninguno de los dos, ya que en ningún caso estará el jar del otro en el repositorio (ya que no hemos podido compilarlo). Pero sí podemos hacerlo poco a poco, en unos proyectos reales que evolucionan con el tiempo. Podemos empezar creando los proyectos B y C como indpendientes e ir compilándolos. Según avanzan los proyectos, en un momento dado podemos necesitar la dependencia de B con C y la añadimos. Todo bien. Pero según avanzamos más y por descuido, podemos añadir la dependencia de C con B…. y maven no protesta, puesto que encuentra los jar anteriores en el repositorio y no revisa las dependencias entre proyectos independientes.

Usar muchas librerías, por otro lado

Este otro problema es más filosófico que de una herramienta concreta, aunque me he tropezado con él en la realidad. Supón que haces un proyecto java y decides usar librerías de terceros, de esas libres que hay por ahí, por ejemplo, jasperreport, jfreechart, ibatis, hibernate, etc. Digamos, por ejemplo, que decides usar la A y la B. Pues bien, es bastante fácil que ambas necesiten de alguna librería más de terceros, por ejemplo, log4j. Por generalizar, tanto A como B necesitan que nos descarguemos e incluyamos en nuestro proyecto a C. Sin problemas de momento.

Pero, ¿qué pasa si A necesita C-1.0 y B necesita C-2.0 y las versiones 1.0 y 2.0 son incompatibles?. Pues básicamente que la hemos cagado. Para que todo vaya bien, necesitamos las dos versiones de la librería y tendremos problemas a la hora de configurar el classpath, porque muchas clases estarán repetidas en ambas versiones y se encontrará la primera que pongamos en el classpath, que puede ser la que no le gusta a la librería A y que de paso le sienta mal a la B. Y si ponemos sólo una, A no funciona y si ponemos sólo la otra, es B el que protesta.

Desgraciadamente, estas situaciones van siendo cada vez más comunes. Hay librerías java como las apache-commons, log4j, etc que son muy, pero que muy utilizadas por muchísimas librerías de más alto nivel, como hibernate, jfreechart, etc. Todas estas librerías de alto nivel no avanzan a la vez ni se actualizan con la misma frecuencia, por lo que puede ser normal que una necesite log4j-1.2.13 y otra log4j-1.2.15. Con log4j en concreto no hay mucho problema, pero sí hay otras más conflictivas.

A cuento con lo de elegancia o sencillez, este parece un motivo para ir más a la sencillez que a la elegancia. Las liberías de alto nivel que pretendan ser compatibles con otras librerías de alto nivel que no tienen nada que ver ellas, deberían casi casi reinventar la rueda cada vez en vez de usar librerías de más bajo nivel. O quizás estas librerías de muy bajo nivel y mayoritariamente usadas deberían venir prácticamente incluidas en el JDK de java.

No se, veo difícil solución y preveo que a la larga la cosa ira empeorando. De momento es difícil tropezarse con estas situaciones, pero a mí ya me ha ocurrido alguna vez.

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.

Apr 15

¿Qué debemos meter en el sistema de control de versiones?

 

Cuando trabajamos en proyectos que usan sistema de control de versiones, pero todavía no tenemos muy afianzado qué cosas debemos meter en dicho sistema, pueden aparecer determinados ficheros en los que nos entra la duda de si debemos meterlos o no. Por ejemplo, ¿metemos o no los ficheros de proyecto de eclipse o netbeans? ¿metemos o no las librerías externas a nuestro proyecto, como log4j, drivers de bd, etc?.  La decisión, por supuesto, depende mucho de los gustos personales de la gente que lleva el proyecto, pero también depende en parte de las herramientas que usemos que no tienen mucho que ver con el sistema de control de versiones. Voy a comentar aquí un posible criterio más o menos lógico.

En el sistema de control de versiones deben ir todos aquellos ficheros que nosotros debemos hacer o proporcionar manualmente al proyecto. No deben ir todos aquellos ficheros que se generan de forma automática.

En el primer grupo, los que nosotros generamos, están claramente los fuentes de nuestro código (los .java por ejemplo), los scripts de compilado o de arranque de la aplicación, los ficheros de configuración (properties de java), los iconos de nuestra aplicación, etc, etc. Quedarían excluidos, por ejemplo, los fuentes que genere automáticamente alguna herramienta o script a partir de otros ficheros que sí hacemos manualmente.

Es muy importante meter en el sistema de control de versiones absolutamente todo lo necesario para construir desde cero y ejecutar nuestra aplicación tal cual era en un momento dado. De nada sirve tener versiones de los fuentes si no tenemos también almacenadas las versiones correspondientes de los scripts de arranque, de los ficheros de configuración (properties) o de creación de las tablas de base de datos.

En el segundo grupo, los generados automáticamente y que no van en el sistema de control de versiones, están los ejecutables y librerías que genera nuestro proyecto. Ni siquiera tendríamos que meterlos para conservar una versión especialmente estable o buena, ya que los sistema de control de versiones suelen soportar cosas como etiquetas o ramas y bastaría con marcar los fichero que sí van con una etiqueta para ser capaces de generar ese ejecutable estupendo.

Bien, esos son los ficheros más o menos claros, pero, ¿qué hacemos con las librerías externas?. Esta decisión ya puede depender de gustos y de las herramientas que usemos. Si usamos, por ejemplo, maven, todos los desarrolladores tendrán acceso a esos jar a través de internet de forma automática y sin preocuparse en absoluto de ello, por lo que con este tipo de herramientas no es necesario en absoluto meter en el sistema de control de versiones cosas como log4j.jar, junit.jar o ojdbc14.jar. Si no usamos una herramienta de este tipo, para comodidad para los desarrolladores, debemos tener todos estas librerías accesibles de forma fácil en algún sitio y una buena opción es meterlas en el sistema de control de versiones. Normalmente metemos cada una de estas librerías una sola vez y no van a tener modificaciones. Un desarrollador nuevo tendrá todas las necesarias símplemente con hacer un checkout y los scripts de compilado sabrán dónde buscar estas librerías.

¿Y qué hacemos con los ficheros de proyecto del IDE de trabajo, como los .project, .classpath o .settings de eclipse?. Nuevamente, la decisión puede depender de gustos y de las herramientas a usar. Nuevamente, si usamos maven, el comando mvn eclipse:eclipse nos genera estos ficheros automáticamente, por lo que es cómodo generarlos para un desarrollador recién incorporado al grupo y no sería necesario meterlos en el sistema de control de versiones. Si no usamos este tipo de herramientas, podemos meter estos ficheros en el sistema de control de versiones, pero tenemos que ser muy conscientes de que pueden ser problemáticos:

  • A veces estos ficheros llevan paths absolutos, por lo que todos los desarrolladores deben sacar el proyecto en el mismo directorio absoluto en su ordenador (por ejemplo, C:\PROYECTO) y posiblemente, tener las librerías externas en el mismo path absoluto (sea dentro de control de versiones o no).
  • Si un desarrollador quiere modificar algo en su IDE que afecte a estos ficheros (por ejemplo, añadir una dependencia nueva para probar o símplemente, cambiar el nombre del proyecto porque no le gusta el oficial), debe ser muy cuidadoso para no hacer un commit de esos ficheros descuidadamente.

Si decidimos no meterlos, debemos ser conscientes de que los desarrolladores nuevos deben montar manualmente el proyecto. Y si decidimos hacer cambios "oficiales" (añadir una dependencia, por ejemplo), todos deben realizar ese cambio manualmente.

 

Mar 29

Más sobre pom_version

 

Hace tiempo comenté que me había hecho un plugin de maven, pom_version, para ayudarme a cambiar los números de versión en los pom.xml de los proyectos maven. El problema era que en proyectos grandes, con muchos subproyectos, muchos pom.xml y muchas dependencias, era un pequeño infierno cambiar la versión de uno de los jar, ya que requería ir buscando y editando todos los pom.xml uno a  uno. El plugin hacía esto de forma más o menos automática en un proyecto maven.

El otro día me tocó hacer una rama de CVS para un hito con el cliente. Por un lado, eché casi un día entero haciendo sólo la rama de CVS. Los proyectos son grandes y los cvs tag recorren todos los ficheros uno a uno, por lo que sólo hacer los "cvs tag" en todos los proyectos implicados me llevó todo el día.

Al día siguiente me dediqué al cambio de números de versione en los pom.xml. Al crear una rama para un hito de cliente, todos los jar deben pasar a un número de versión nuevo, el del entregable y la rama en cuestión. Pensé que con el plugin el tema iba a ser rápido, pero no lo fue. Eché toda la mañana y no terminé.

El primer problema es que el plugin sólo maneja un proyecto maven con todos sus subproyectos. Pero nuestros proyectos tiran de librerías propias que a su vez tiran de librerías propias y muchas de ellas son grandes, con subproyectos y más subproyectos. La ejecución del plugin es inmediata en un proyecto maven, pero tengo que ir pasando manualmente por todos los proyectos, empezando por los más básicos hasta el de más alto nivel, revisando que no se quedan dependencias sin cambiar, que todo compila como debe, etc, etc.

El segundo problema, menor, es que el plugin no me soporta las propiedades en los números de versión. Si un pom.xml se define una propiedad "numeroVersion=1.2.3" y luego en las dependencias unos usas ${numeroVersion} y otros 1.2.3, el plugin sólo cambia los que usan variable o los que usan número, según se le diga.

Así que me he replanteado el tema, no puedo echar dos días cada vez que quiera hacer una rama general de todo el proyecto.

  1. Sólo para entretenerme, porque quizás deje de usarlo, en el plugin le he hecho un arreglo para que soporte bien las propiedades. Ahora está en versión pom_version 1.1.0. He aprovechado además para quitarle la dependencia que tenía con Xerces y hacer algo más de test junit (he intentado aplicar TDD para estas mejoras).
  2. La primera medida seria va a ser pasar de CVS a Subversion. En Subversion la creación de un tag o rama tarda entre dos o tres segundos, independientemente del tamaño del proyecto. A veces me gusta darle un comando al ordenador y luego estar un rato rascándome la barriga (por no decir otra cosa) mientras el ordenador trabaja, pero estar rascándome un día entero, acaba escociendo.
  3. La segunda medida seria y es sólo una idea, es hacerme una pequeña aplicación a la que se le configure diciéndole dónde están todos los proyectos maven que usa un determinado proyecto de cliente, decirle qué número de versión quiero para cada proyecto y que él se encarge de hacer todos los cambios en todos los proyectos. Por supuesto, esa pequeña configuración por proyecto, debe guardarse en algún sitio para no tener que indicar todos los directorios cada vez. No creo que una herramienta así me lleve mucho hacerla, ya que es simplemente buscar ficheros xml de nombre pom y hacer cambios en ellos. En el punto 1 comenté que quizás dejaría de usar el plugin y lo dejare si hago esta herramienta.

La duda que tengo respecto a la herramienta, es si hacerla yo mismo en mis ratos libres (y así disfrutar de un rato de codificación) o pasársela a uno de los FP que van a venir a hacer tres meses de prácticas…

Jan 28

Más de Sonar

Comenté ayer que acababa de instalar Sonar para la medición de métricas en nuestros proyectos. Pues bien, hoy he estado intentando hacer una instalación en serio y me he encontrado con varias pegas.

Lo he instalado en 3 ordenadores. En dos de ellos me ha dado un error curioso al arrancarlo. Busca en la instalación de java un fichero bin/server/jvm.dll. Pues sólo en uno de los ordenadores lo hay. En los otros dos, lo que tengo es un bin/client/jvm.dll. Así que sin ganas de ponerme a investigar sobre la instalación de java, la solución rápida: copie el directorio client con el nombre de server en el mismo sitio y el mismo contenido. Con eso parece que se le engaña para que arranque.

En la documentación de Sonar comenta que viene con la base de datos derby db embebida, pero que es recomendable usar una base de datos más seria. Pues nada, vamos con mysql, que lo tengo instalado. Entro en mysql, creo una base de datos para sonar, voy al fichero de configuración de sonar, quito derby y pongo mysql (esa parte viene muy bien, porque la cadena de conexión de mysql viene comentada y sólo hay que descomentarla y cometar la de derby, además, el driver de mysql en java viene con Sonar, así que ni siquiera hay que poner el jar con el conector en ningún sitio). Más adelante, en el mismo fichero, viene el nombre y usuario para acceso. Y aquí viene otra pega más. Hay que configurar el dialecto de la base de datos, pero en la documentación de Sonar no pone nada de eso. Así que arranco Sonar, abro el navegador, le doy al botón de crear las tablas, empieza a crear las tablas y acaba fallando. Búsqueda del error en google y en el mismo fichero, más adelante hay que indicarle al hibernate de Sonar el dialecto de la base de datos. Dos propiedades más, una con el dialecto mysql y la otra con la clase que sabe de ese dialecto. Eso sí, hay que volver a fijarse porque la clase con el dialecto es propia de Sonar, y no la de hibernate (otro ensayo y error y van dos, en ambos casos con borrado previo de la base de datos, que se había creado a medias).

Listo todo lo referente al servidor. Viene ahora ejecutar el plugin de maven en un proyecto maven para empezar a meter datos.

Mi primera prueba con la base de datos derby funcionó a la primera, así que sin mirar más, le dí al comando. Error. No encuentra la base de datos. Me pongo a buscar por google y resulta que el plugin de maven inserta directamente en la base de datos, por lo que necesita todos los parámetros de conexión. La configuración necesaria para maven viene en la documentación de sonar, así que culpa mía por no haberla leído.  Pongo todo eso, arranco de nuevo…. y otro error. Esta vez porque no conoce el dialecto de la base de datos. La documentación NO pone nada del tema. Así que me lo invento. En el trozo de xml de configuración meto dos tags para el dialecto, exactamente con los mismos nombres que el fichero de configuración del servidor y los mismos valores. Estupendo, ya va.

Me pongo con varios proyectos, poco a poco, y voy consiguiendo los resultados. Pero me salen dos pegas:

  • En algunos proyectos se nos han colado acentos y eñes en los nombres de variables y métodos. Java lo admite. Sonar no. Salta una excepción y falla todo el proceso. Por más que jugué con los encoding, no hubo manera. Al final quité los acentos, pero digo yo que lo suyo es que metiera ese fallo en las métricas en vez de saltar la excepción.
  • En otros proyectos uso una extension wagon-ftp para el deploy. Pues bien, los wagon son incompatibles con los Sonar. El Sonar entra en una llamada recursiva a sí mismo y da una excepción de StackOverflowError. En fin, habrá que buscarle arreglo.

Mañana me toca otro rato de pelea, ya que el plugin de maven no se conforma sólo con insertar directamente en base de datos, sino que accede al servidor Sonar a través de http también para algo y me está fallando esa conexión, supongo que por timeout, o por el proxy corporativo o vete tú a saber por qué.

Hay que reconocer que la herramienta es muy vistosa y que permite ver el estado del proyecto de un vistazo. Pero la instalación y la documentación deja bastante que desear, sobre todo teniendo en cuenta que sólo son cuatro cosas lo que deberían haber puesto, ya que no es una instalación compleja.

Nov 07

Plugin de maven para cambio de versiones en los pom.xml

 

Hace tiempo que tengo una necesidad en el trabajo y que he ido dejando olvidada por falta de tiempo. Cuando en maven manejas proyectos grandes, cada uno con varios subproyectos  y encima esos proyectos grandes dependen de otros proyectos grandes que también tienen sus subproyectos, llevar un versionado en condiciones de cada jar puede ser un infierno.

Me explico, tu proyecto grande lleva un fichero pom.xml. Cada uno de los subproyectos también. En los demás proyectos también y en sus subproyectos también. Algunos de los jar de alguno de esos subproyectos pueden ser librerías básicas, por ejemplo, una librería de componentes swing o de utilidades para acceso a base de datos, de gráficos, o lo que sea. Aparecen dependencias de esa librería en los pom.xml de muchos de los proyectos y subproyectos.

¿Qué pasa si cambiamos la versión de esa librería?. Que no queda más remedio que ir en todos los proyectos y subproyectos en los que queremos actualizar la versión buscando dicha librería por los pom.xml y cambiando el número de versión. Desde luego, una tarea rutinaria, pero algo aburrida, que lleva un tiempo y susceptible de error "humano" al hacerla a mano.

Así que pensé en hacer un script o algo que buscara por los pom.xml y cambiara automáticamente las versiones que se le dijeran. Así que por fin me puse a ello y como siempre, para jugar y aprender algo nuevo, en vez de un script, decidí hacerlo como plugin de maven.

El resultado es un plugin de maven con dos goals:

  • lista saca en un fichero un listado de todos los artifacts que aparecen en el pom.xml del proyecto y subproyectos actuales y con la nueva versión o groupId que queremos para cada sub/proyecto. Ese fichero lo utilizará el siguiente goal para hacer los cambios en el pom.xml.
  • cambia realiza los cambios indicado en el fichero anterior en todos los pom.xml que encuentre del proyecto y subproyecto. 
    • Si se ejecuta este goal en el mismo proyecto en que se ejecutó el lista, obtenemos un cambio de versión en el proyecto. Quizás el plugin release de maven hace esto mismo y mejor.
    • Si se copia el fichero de cambios en otros proyecto distinto y ejecutamos allí el goal cambia, conseguimos cambiar la versión de las dependencias con el primer proyecto.

Lo he estado usando ya y probando, va bastante bien, aunque veo que el uso no es demasiado intuitivo, siempre se me olvida hacer alguna cosa.

Ahí van un par de enlaces:

Aprender, pues aprendí algunas cosillas, sobre todo, ¿cómo no?, problemas con los que me he tropezado. El más gordo ha sido que no hay forma de hacer un deploy de un plugin en condiciones. En el pom.xml del plugin, el packaging es "maven-plugin". Pues el deploy no sabe hacer un deploy de una cosa cuyo packaging es "maven-plugin", así que para que funcione el deploy, debes poner packaging "jar". Pero claro, eso sube el fichero .pom al repositorio remoto con ese packaging que no es, así que luego, a mano y en el repositorio remoto, tienes que cambiar el "jar" por "maven-plugin" otra vez. Y el problema sigue, ya que al cambiar eso a mano, los ficheros de checksum/huella o como quieras llamarle .md5 y .sha1 que se crean en el repositorio remoto ya no cuadran y al bajarte el plugin con maven dan un warning.

Y yo creo que ya está bien de proyectos. Esta tarde me dedicaré a otra cosa.

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.