Etiqueta: maven

Guía de migración de proyectos Java a Maven

16 oct 2009

Objetivos

El objetivo de este artículo es servir de guía básica para la migración manual de proyectos Java no Maven a entorno Maven. Pensado y dirigido especialmente a personas con poca  experiencia previa en Maven, pero con algún conocimiento previo.

Preparación del proyecto

Como es obvio, para migrar un proyecto Java a Maven, deberemos disponer de sus fuentes y todas las librerías dependientes.

Ya que vamos a realizar modificación estructurales importantes en el proyecto, conviene asegurarnos de que los fuentes no están conectados a ningún sistema de control de versiones como Subversion. Para realizar esto, podemos o bien eliminar manualmente todos los directorios .svn de nuestro proyecto o bien desde Eclipse desconectar el proyecto.

Si lo vamos a realizar de forma manual, debemos asegurarnos de eliminar todos los directorios .svn (o .CVS si el proyecto a migrar estuviese conectado a un CVS) A modo de ejemplo, en Linux podemos ejecutar el siguiente comando desde el directorio del proyecto para eliminar todas las referencias a carpetas ‘.svn’:

 

rm -rf `find . -type d -name .svn`

Será mucho más sencillo hacerlo de forma automática desde Eclipse. Para desconectar el proyecto, teniéndolo seleccionado pulsaremos sobre el botón derecho del ratón: Team -> Disconnect.

Desconectar SVN desde Eclipse

Adaptación del proyecto

Aunque la estructura por defecto Maven puede ser modificada, es muy recomendable adaptar la organización del proyecto a migrar a la estructura por defecto más generalizada:

estructura Maven

Para hacer esta modificación de la estructura hay que tener especial cuidado en algunos aspecto o convenciones Maven:

  • Los ficheros de recursos: Maven hace una distinción entre los ficheros de clases java y los ficheros de recursos .properties y XML’s y, de hecho, salvo que le digamos lo contrario, no hará ningún tratamiento de los ficheros no java que se encuentren en el directorio src/main/java. Por este motivo, tendremos que localizar todos los ficheros de recursos (properties, XML’s) que se encuentran en el directorio java y moverlos al directorio resources (src/main/resources).
  • Eliminar la carpeta classes. Es muy común que proyectos no Maven tengan en su carpeta WEB-INF/classes los compilados del proyecto. Esta situación no sólo no es recomendable sino que puede producir problemas, por lo que los eliminaremos confiando en Maven para su generación cuando sea necesario.

Definir el pom.xml del proyecto

Una vez que la estructura del proyecto se ha adaptado a la filosofía Maven, el siguiente paso es definir el pom.xml del proyecto.

Partiremos de un fichero pom.xml básico. Este fichero de origen no tiene dependencias; durante el proceso lo adaptaremos y posteriormente iremos refinando la configuración hasta alcanzar una “mavenización” completa del proyecto.

Una vez colocado el pom.xml en el raíz de la aplicación, lo modificamos indicando el nombre del proyecto y la información básica relativa a los desarrolladores, organización y repositorio de dependencias utilizado.

Antes de continuar y añadir las dependencias, podemos hacer una comprobación previa y verificar que efectivamente nuestro IDE, en este caso Eclipse, reconoce correctamente la nueva estructura del proyecto.

Para ello nos vamos a la consola y en el directorio en el que se encuentra nuestro proyecto ejecutamos la siguiente orden:

mvn clean eclipse:eclipse

(Requiere disponer de Maven correctamente instalado en la máquina).

Si la configuración del entorno es correcta, obtendremos una salida similar a esta:

 

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'eclipse'.
[INFO] ------------------------------------------------------------------------
[INFO] Building ejemplo
[INFO]    task-segment: [clean, eclipse:eclipse]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting file set: /home/felix/workspaces/ejemplo/target (included: [**], excluded: [])
[INFO] Preparing eclipse:eclipse
[INFO] No goals needed for project - skipping
[INFO] [eclipse:eclipse]
[INFO] Adding support for WTP version 1.5.
[INFO] Using source status cache: /home/felix/workspaces/ejemplo/target/mvn-eclipse-cache.properties
[INFO] File /home/felix/workspaces/ejemplo/.project already exists.
       Additional settings will be preserved, run mvn eclipse:clean if you want old settings to be removed.
[INFO] Wrote Eclipse project for "ejemplo" to /home/felix/workspaces/ejemplo.
[INFO]
       Sources for some artifacts are not available.
       Please run the same goal with the -DdownloadSources=true parameter in order to check remote repositories for sources.
       List of artifacts without a source archive:
         o javax.servlet:servlet-api:2.4
         o javax.servlet.jsp:jsp-api:2.1
         o junit:junit:3.8.1

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Wed Sep 3 11:54:28 CET 2008
[INFO] Final Memory: 10M/80M
[INFO] ------------------------------------------------------------------------

Nos vamos a Eclipse, refrescamos el proyecto (F5 teniendo seleccionado el proyecto) y comprobamos que la nueva estructura es correcta.

Ahora que hemos comprobado que eclipse ha reconocido la nueva estructura, el siguiente paso es definir en nuestro pom.xml las dependencias. Para hacer esto eliminamos todos los .jar que se encuentran en el directorio /WEB-INF/lib y nos quedamos con una copia de ellos para posteriormente ir resolviendo las dependencias del proyecto original. Una recomendación es cortar la carpeta /WEB-INF/lib a otra ruta de nuestro disco.

Resolución de dependencias

Esta es la fase más delicada del proceso, ya que debemos asegurarnos de que todas las dependencias del proyecto quedan correctamente declaradas en el fichero pom.xml. Para realizar esta tarea es recomendadle utilizar alguna aplicación web que nos ayude a la localización de las librerías, como por ejemplo MvnRepository o MvnBrowser (recomendada), en la que podremos buscar cada uno de nuestros jar y encontrar la definición en el pom.xml que debemos utilizar.

Migración de proyectos maven con mvnBrowser

Gracias a esta web, una vez que tengamos localizada la dependencia, solo tendremos que copiar el bloque POM Dependency a nuestro fichero pom.xml.

Iremos repitiendo este proceso de forma iterativa para todos nuestras librerías .jar y añadiendo la definición de la dependencia a nuestra zona de dependencies, con ciertas salvedades y consideraciones que introducimos a continuación.

Consideraciones especiales

Hay ocasiones en las que determinar la definición de la dependencia en nuestro pom.xml puede no ser una tarea trivial. Se comentan a continuación los mecanismos de actuación recomendados según diversas casuísticas:

  • No se indica la versión de la librería en el nombre del fichero.

Por ejemplo, podemos encontrarnos una librería llamada axis-ant.jar, por lo que tendremos que averiguar previamente la versión de la librería de cara a definir la dependencia en el fichero pom.xml. Para esto suele ser muy efectivo descomprimir la librería y consultar el fichero /META-INF/MANIFEST.MF. A modo de ejemplo, en dicha librería mencionada observaríamos una linea similar a la siguiente: “Implementation-Version: 1.2.1 2243 June 14 2005″. Una vez que conozcamos la versión podremos realizar la búsqueda de la forma usual. , 1.1.1.1. Sabemos que la librería fue generada por maven, pero no conocemos su definición.

No conocemos el groupId, artifactId, etc. Podemos descomprimir el .jar y acceder al fichero pom.xml que se encuentra en /META-INF/maven para comprobar el groupId, artifactId y versión de la dependencia.

  • Dependencias especiales JEE.

Algunos jars, como por ejemplo servlet.jar, son aportados por el contenedor de aplicaciones, por lo que tendremos que definirlas marcándolas como provided. Esto le indica a Maven que estas librerías pueden ser utilizadas en tiempo de compilación, pero no serán agregadas al empaquetado final. Para declarar una dependencia de esta forma incorporaremos en nuestro pom.xml un código como este:

 

<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
</dependency>
  • Librerías ya consideradas en las dependencias transitivas

Muchas de las dependencias dependen a su vez de otras librerías, por lo que Maven añadirá de forma transitiva nuevas librerías a nuestro proyecto. En estos casos no necesitamos definir estas nuevas dependencias transitivas en nuestro pom.xml. Como ejemplo, supongamos que un proyecto depende de estas librerías:

  • xalan-2-6.0.jar
  • xercesImpl
  • xml-apis

Al buscar la librería xalan-2-6.0.jar, observamos que la librería depende a su vez de las otras dos librerías. Por ello, para este ejemplo bastaría con declarar la dependencia a xalan-2-6.0.jar en nuestro pom.xml. Dependencias transitivas.

  • Conflictos entre librerías

En ocasiones nos podemos encontrar con que dos librerías tengan en sus dependencias a una misma tercera librería, pero de distinta versión. Ello haría que maven incluyese en nuestro Classpath 2 versiones de una misma librería, originando conflictos por duplicado de clases. En ese caso debe declararse en la dependencia de una de ellas una directiva para excluir la descarga de la librería conflictiva.

Por ejemplo, en un proyecto deseamos utilizar la librería siguiente:

 

<artifactId>jaxws-api</artifactId>
<groupId>javax.xml.ws</groupId>
<version>2.1-1</version>

Y tenemos dependencia a com.sun.xml.ws / jaxws-rt, esta librería se trae como dependencia transitiva otra versión de la librería mencionada anteriormente. Declaramos una exclusión para evitarlo:

 

<dependency>
  <groupId>com.sun.xml.ws</groupId>
  <artifactId>jaxws-rt</artifactId>
  <version>2.1.4</version>
  <exclusions>
    <exclusion>
      <artifactId>jaxws-api</artifactId>
      <groupId>javax.xml.ws</groupId>
    </exclusion>
  </exclusions>
</dependency>
  • Tenemos una librería que se encuentra en los repositorios, pero no hemos podido averiguar el número de versión.

En esta situación, si la librería no parece problemática, podríamos optar por elegir la última versión de la dependencia. En el caso de que consideremos que la versión que utiliza el proyecto puede originar problemas, lo mejor es añadir esta librería manualmente a nuestro repositorio (como si fuese una librería específica del proyecto), según se explica en el próximo punto.

  • La librería es específica del proyecto.

En estos casos tendremos que subir la dependencia a nuestro repositorio de librerías y, para evitar conflictos posteriores, se recomienda que sea subida con el mismo groupId de nuestro proyecto. Supongamos que migrando un proyecto nos topamos con librería example1.jar que no se encuentra en ningún repositorio público. En este caso la subimos a nuestro repositorio por ejemplo con la siguiente definición:

 

<dependency>
    <groupId>com.viavansi.examples</groupId>
    <artifactId>example1</artifactId>
    <version>0.0.1</version>
</dependency>

En la siguiente figura vemos cómo desplegamos una librería a Artifactory:

Artifactory

Comprobación de dependencias

Una vez que hayamos finalizado este proceso de definición de todas las dependencias en nuestro pom.xml, ejecutaremos en consola mvn dependency:tree (situados en el directorio donde se encuentre el fichero pom.xml del proyecto) para comprobar que las dependencias que se han incorporado al proyecto son las correctas. Este paso de verificación es muy importante en las migraciones de este tipo de proyectos, ya que es muy posible que alguna dependencia transitiva genere problemas. Como ejemplo, podemos observar la siguiente salida en la que comprobamos que no hay dependencias transitivas que entren en conflicto con alguna de nuestras librerías:

 

[INFO] [dependency:tree]
[INFO] com.viavansi.examples.examples1:war:0.0.1
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] +- javax.servlet.jsp:jsp-api:jar:2.1:provided
[INFO] +- javax.servlet:servlet-api:jar:2.4:provided (scope not updated to compile)
[INFO] +- javax.activation:activation:jar:1.1:compile
[INFO] +- avalon-framework:avalon-framework-impl:jar:4.1.5:compile
[INFO] +- axis:axis-ant:jar:1.2.1:compile
[INFO] +- axis:axis:jar:1.2.1:compile
[INFO] +- javax.mail:mail:jar:1.3.1:compile
[INFO] +- commons-beanutils:commons-beanutils:jar:1.6:compile
[INFO] +- commons-codec:commons-codec:jar:1.3:compile
[INFO] +- commons-collections:commons-collections:jar:3.2:compile
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.2:compile
[INFO] +- commons-digester:commons-digester:jar:1.6:compile
[INFO] |  - xml-apis:xml-apis:jar:1.0.b2:compile
[INFO] +- commons-discovery:commons-discovery:jar:0.2:compile
[INFO] +- commons-httpclient:commons-httpclient:jar:3.1:compile
[INFO] +- commons-io:commons-io:jar:1.3.1:compile
[INFO] +- commons-lang:commons-lang:jar:2.2:compile
[INFO] +- commons-logging:commons-logging:jar:1.1:compile
[INFO] |  +- logkit:logkit:jar:1.0.1:compile
[INFO] |  - avalon-framework:avalon-framework:jar:4.1.3:compile
[INFO] +- commons-pool:commons-pool:jar:1.3:compile
[INFO] +- displaytag:displaytag:jar:1.1:compile
[INFO] +- displaytag:displaytag-export-poi:jar:1.1:compile
[INFO] +- dom4j:dom4j:jar:1.6.1:compile
[INFO] +- org.extremecomponents:extremecomponents:jar:1.0.1:compile
[INFO] +- com.lowagie:itext:jar:1.4.8:compile
[INFO] +- oro:oro:jar:2.0.7:compile
[INFO] +- javax.xml:jaxrpc-api:jar:1.1:compile
[INFO] +- javax.servlet:jstl:jar:1.1.2:compile
[INFO] +- log4j:log4j:jar:1.2.8:compile
[INFO] +- com.oracle:ojdbc14:jar:9.0.2.0.0:compile
[INFO] +- poi:poi:jar:2.5.1-final-20040804:compile
[INFO] +- javax.xml.soap:saaj-api:jar:1.2:compile
[INFO] +- org.springframework:spring:jar:2.0.5:compile
[INFO] +- taglibs:standard:jar:1.1.2:compile
[INFO] +- struts:struts:jar:1.2.9:compile
[INFO] |  +- commons-fileupload:commons-fileupload:jar:1.0:compile
[INFO] |  +- commons-validator:commons-validator:jar:1.1.4:compile
[INFO] |  - antlr:antlr:jar:2.7.2:compile
[INFO] +- wsdl4j:wsdl4j:jar:1.5.1:compile
[INFO] +- wss4j:wss4j:jar:1.5.0:compile
[INFO] +- xalan:xalan:jar:2.6.0:compile
[INFO] +- org.apache.xmlgraphics:xmlgraphics-commons:jar:1.2:compile
[INFO] +- xml-security:xmlsec:jar:1.2.1:compile
[INFO] +- org.directwebremoting:dwr:jar:2.0.3:compile
[INFO] +- com.viavansi:plantilla-client:jar:0.0.13:compile
[INFO] +- net.sourceforge.barbecue:barbecue:jar:1.5-beta1:compile

En caso de detectar librerías duplicadas a causa de las dependencias transitivas, deberemos proceder a definir las exclusiones necesarias en las dependencias afectadas, según se indicó anteriormente.

Hudson. Parte 2. Crea tus propios plugins

28 abr 2009

Una de las ventajas de Hudson es la posibilidad que nos ofrece de ampliar su funcionalidad mediante su sistema de plugins. De esta forma, podemos realizar acciones sobre los builds, tales como generación de informes, controladores, acciones sobre el código fuente, etc. En Internet podemos encontrar algunas páginas de donde descargar plugins para Hudson, como por ejemplo http://hudson.gotdns.com/wiki/display/HUDSON/Plugins.

 

Aún disponiendo de una cantidad considerable de plugins en la red que cubran la mayoría de las necesidades, nos podemos encontrar en la situación de tener que crear uno propio. Si te has planteado desarrollar uno te habrás dado cuenta de que la documentación en Internet sobre cómo crear un plugin es muy escasa, estando el grueso de la información en sólo dos páginas: http://hudson.gotdns.com/wiki/display/HUDSON/Plugin+tutorial y http://javaadventure.blogspot.com/2008/01/writing-hudson-plug-in-part-1.html.

 

Pues bien, en Viavansi surgió la necesidad de crear un plugin para Hudson que subiera el código fuente de un proyecto a un repositorio de Subversion, de forma que se automatizara esta tarea cada vez que Hudson hiciera un build.

 

El plugin constará de una parte de configuración en donde el usuario encargado de cada proyecto introducirá la url del repositorio de Subversion donde desea subir el código, los parámetros relativos al repositorio (nombre de usuario y password) así como dos entradas para ficheros que no se quieren subir o ficheros que no se quieren sobreescribir en el destino. Además dispondrá de una opción para habilitar o deshabilitar el plugin en todos los proyectos de forma global.

 

 

Creación de la estructura del plugin

 

Lo primero que vamos a tener que usar para crear el plugin es la herramienta Maven, la cual nos creará la estructura de paquetes, además de algunos ficheros de ejemplos. Esto era de esperar, pues Hudson internamente usa Maven para la creación de los builds de los proyectos.

 

Empezamos situandonos en una consola sobre nuestro directorio de proyectos y tecleamos:

 

mvn hpi:create

 

Después de pedirnos algunos datos como el identificador del grupo y el nombre del plugin, Maven nos creará la siguiente estructura:

 

+ src

+ main

+ java

+ groupid.name

+- PluginNamePublisher.java

+- PluginImpl.java

+ resources

+ groupid.name.project

+- config.jelly

+- global.jelly

+- index.jelly

+ webapp

+- help-globalConfig.html

+- help-projectConfig.html

 

Por defecto, se crean unos ficheros de ejemplo (el archiconocido HelloWorld) que podemos usar para ver cómo está implementado un plugin sencillo.

 

Si queremos tener soporte en nuestro IDE para la implementación del plugin deberemos, en el caso de Eclipse, tendremos que introducir lo siguiente:

 

mvn -DdownloadSources=true eclipse:eclipse

 

Si tenemos otros entornos como Netbeans o IntelliJ, existen plugins específicos de Maven para estos IDEs.

 

Bien, acabamos de crear la estructura de nuestro primer plugin, nos falta decirle a Hudson qué tipo de plugin hemos desarrollado, agregar la lógica y crear los campos visuales para que se añadan a las páginas de configuración de los proyectos.

 

 

Creando el plugin

 

Uno de los aspectos más importantes en la creación de un plugin para Hudson consiste en determinar el tipo de proyecto para el que va a ser utilizado. En nuestro caso utilizamos proyectos de tipo Maven, por lo cual necesitamos publicar el plugin en Hudson a través de un Publisher; en caso contrario (para proyectos de tipo Ant, por ejemplo) necesitaríamos publicarlo usando un Builder. Otra de las características de usar un Publisher frente a un Builder consiste en que la acción se ejecuta cuando un proyecto ha terminado de hacer el build.

 

Para publicar el plugin mediante un Publisher basta con tocar la clase PlugImp quedando de la siguiente forma:

 

public class PluginImpl extends Plugin {

public void start() throws Exception {

//Publicamos el descriptor del plugin

Publisher.PUBLISHERS.add(SVNCopyPublisher.DESCRIPTOR);

}

}

 

En este código podemos ver como se ha modificado la clase inicialmente creada por Maven, añadiendo la línea donde le decimos a Hudson que registre nuestro plugin. Debido a que la funcionalidad que queremos desarrollar consiste en copiar el código fuente de un proyecto a un repositorio de Subversion, el nombre de nuestro plugin será SVNCopyPlugin (original, verdad?), creando así la clase SVNCopyPublisher que será la que contenga el descriptor y la lógica de negocio.

 

Los elementos más importantes de la clase SVNCopyPublisher son:

 

  • Atributos necesarios para la ejecución de la lógica del plugin. En nuestro caso, url del repositorio, nombre de usuario, contraseña y listas con los ficheros que no queremos subir o que no queremos sobreescribir. Cada atributo deberá tener un método público get y set.
  • Un constructor público en el que pasaremos los valores de los campos rellenados por el usuario en la configuración del plugin e inicializaremos los atributos definidos en el punto anterior. Además, este constructor deberá estar anotado con @DataBoundConstructor.

  • Un método perform en donde se colocará la lógica de nuestro plugin.

  • Un atributo estático y final llamado DESCRIPTOR que será instancia de una clase interna de nombre DescriptorImpl.

A su vez, la clase DescriptorImpl contendrá la configuración global del plugin. En el caso de SVNCopyPlugin hemos usado en la configuración local un checkbox con el cual activaremos el uso del plugin y sólo realizará la copia cuando esté activado.

 

Además esta clase debe contener:

 

  • Los atributos propios de la configuración global del plugin, con sus gets y sets.

  • Un constructor protected sin parámetros.

  • El método configure que hará persistente la configuración global.

  • Un método newInstance que devolverá una instancia de nuestra clase Publisher con los valores introducidos por el usuario.

Hasta aquí ha sido todo teoría, veamos ahora como queda la clase SVNCopyPublisher y su clase interna DescriptorImpl:

 

public class SVNCopyPublisher extends Publisher {

private Boolean enable;

private final String urlTarget, nameTarget, passTarget, filesTargetException, filesSourceException;

@DataBoundConstructor

public SVNCopyPublisher(String urlTarget, String nameTarget, String passTarget, String filesSourceException, String filesTargetException) {

this.urlTarget = urlTarget;

this.nameTarget = nameTarget;

this.passTarget = passTarget;

this.filesTargetException = filesTargetException;

this.filesSourceException = filesSourceException;

}

//GETTERS y SETTERS de los atributos

public boolean needsToRunAfterFinalized() {

return true;

}

/*

* Contiene la lógica del plugin, que se ejecutará cuando termine el build de Hudson

*/

@Override

public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {

if(enable && build.getResult().equals(Result.SUCCESS)){

//Aquí debe ir la lógica de negocio del plugin

}

return true;

}

public Descriptor<Publisher> getDescriptor() {

return DESCRIPTOR;

}

public Action getProjectAction(AbstractProject< ?, ?> project) {

return null;

}

/**

* Descriptor should be singleton.

*/

public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

/**

* Descriptor for {@link SVNCopyPublisher}. Used as a singleton.

* The class is marked as public so that it can be accessed from views.

*/

public static final class DescriptorImpl extends Descriptor<Publisher> {

 

/**

* If you don’t want fields to be persisted, use transient

*/

private boolean enable;

protected DescriptorImpl() {

super(SVNCopyPublisher.class);

load();

}

/**

* This human readable name is used in the configuration screen.

*/

public String getDisplayName() {

return “SVNCopy Plugin Project Configuration”;

}

/**

* Get the fields from the global configuration form and persist them.

*/

public boolean configure(StaplerRequest req, JSONObject o ) throws FormException {

enable = o.getBoolean(“enable”);

save();

return super.configure(req,o);

}

 

/**

* Creates a new instance of {@link SVNCopyPublisher} from a submitted form.

*/

@Override

public SVNCopyPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {

SVNCopyPublisher pub = req.bindJSON(SVNCopyPublisher.class, formData);

pub.setEnable(enable);

return pub;

}

public boolean isEnable(){

return enable;

}

}

}

 

 

Del código de arriba podemos destacar el método perform, que es el encargado de llamar a la lógica del plugin, recibiendo como parámetros una instancia de AbstractBuild (usado para lanzar el plugin si el build ha terminado satisfactoriamente), una de Launcher que contiene datos de la máquina que realiza el build y otra de Listener, que entre otras cosas contiene el logger con el cual podemos imprimir en la consola web de Hudson nuestra salida del plugin. Otros métodos importantes son configure y newInstance que reciben como parámetros la petición del formulario de configuración y los datos introducidos por el usuario en forma de JSON. El parámetro StaplerRequest forma parte del framework Stapler utilizado por Hudson para la aplicación web y es útil si queremos obtener datos de la petición usando getParameter.

 

Generando los formularios y uniéndolos al código del plugin

 

Para terminar con el desarrollo de nuestro plugin nos falta crear los formularios de entrada en la parte de configuración global de Hudson, así como en la configuración particular de cada proyecto. Maven nos crea por defecto los ficheros global.jelly y config.jelly, además de unos htmls de ayuda. Estos ficheros siguen un esquema XML basandose en un framework ligero llamado Jelly http://commons.apache.org/jelly/.

 

Para comenzar, tenemos que incluir en la configuración global de Hudson el checkbox que habilitará de forma general el uso del plugin para todos los builds que se hagan (además se dispone de otro checkbox que habilita el plugin para cada proyecto). Dentro de la carpeta resources se encontrarán como mínimo tres ficheros .jelly: index, global y config. El primer fichero contiene una descripción del plugin y en los otros dos se deberá colocar los elementos htmls para la configuración global y específica del mismo.

 

En el fichero global.jelly queremos colocar el checkbox que hará que se active el plugin para todos los proyectos. Para ellos basta con colocar las siguientes lineas:

 

<j:jelly xmlns:j=”jelly:core” xmlns:st=”jelly:stapler” xmlns:d=”jelly:define” xmlns:l=”/lib/layout” xmlns:t=”/lib/hudson” xmlns:f=”/lib/form”>

 

<f:section title=”SVNCopyPlugin Global Configuration”>

 

<f:entry title=”Enable”

description=”Check if you want to perform a copy of your project from your repository to another one”>

 

<f:checkbox name=”enable” checked=”${descriptor.isEnable()}” />

 

</f:entry>

</f:section>

 

</j:jelly>

 

Como se puede apreciar el código es bastante autoexplicativo. Se usan unos tags propios de Jelly para añadir el checkbox a la configuración de Hudson (Manage Hudson / Configure System). El valor del checkbox estará bindeado al atributo enable ya visto en el descriptor.

 

 Configuración global del plugin

 

Por otra parte el fichero config.jelly contendrá la configuración específica del plugin para cada proyecto. Su contenido será el siguiente:

 

<j:jelly xmlns:j=”jelly:core” xmlns:st=”jelly:stapler” xmlns:d=”jelly:define” xmlns:l=”/lib/layout” xmlns:t=”/lib/hudson” xmlns:f=”/lib/form”>

<!–

Cajas de texto para introducir las url de los dos repositorios, los nombres y los passwords

–>

<f:entry title=”URL del repositorio destino”>

<f:textbox field=”urlTarget” value=”${descriptor.urlTarget}”/>

</f:entry>

<f:entry title=”Nombre de usuario del repositorio destino”>

<f:textbox field=”nameTarget” value=”${descriptor.nameTarget}”/>

</f:entry>

<f:entry title=”Password del repositorio destino”>

<f:password field=”passTarget” value=”${descriptor.passTarget}”/>

</f:entry>

<f:entry title=”Ficheros y carpetas que no se desean sobreescribir en el destino”>

<f:textbox field=”filesTargetException” value=”${descriptor.filesTargetException}”/>

</f:entry>

<f:entry title=”Ficheros y carpetas que no se desean subir al destino”>

<f:textbox field=”filesSourceException” value=”${descriptor.filesSourceException}”/>

</f:entry>

</j:jelly>

 

Gracias a este fichero se incluirá en la configuración del proyecto las cajas de texto para que cada usuario introduzca sus datos sobre el repositorio. Cada elemento f:textbox leerá/escribirá su valor en un atributo del descriptor.

 

 Configuración del plugin para cada proyecto

 

 

Ejecución y debug

 

Con todo esto ya hemos terminado de programar el plugin, ahora sólo queda ejecutarlo y ver que todo va como deseamos. Para ello usaremos el comando mvn hpi:run desde la carpeta del plugin, el cual nos montará un Hudson en nuestra máquina y con el que podremos trastear antes de instalarlo en el Hudson de la empresa.

 

También es interesante la opción de debuguear para seguir la traza y ver qué valores van tomando cada uno de los elementos que intervienen en la ejecución del plugin. Para tener la posibilidad de debuguear antes de llamar a mvn hpi:run tendremos que setear las variables de entorno con los comandos:

 

export MAVEN_OPTS=”-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n”

 

si usamos Unix o

 

set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n

 

si usamos Windows.

 

 

Empaquetando y exportando el plugin

 

Para finalizar sólo queda crear el fichero .hpi para importarlo desde el Hudson instalado en uno de los servidores de la empresa, algo tan sencillo como ejecutar mvn hpi:package y coger el fichero con esta extensión creado en la carpeta target.

 

Con esto ya tienes para empezar a customizar tu Hudson añadiendole la funcionalidad que necesitas y no te la dan otros plugins. Ya se sabe, si no está: hazlo tu mismo.

 

 

Próximamente…

 

En breve pondremos una entrada explicando cómo sacarle más partido a los plugin de Hudson, añadiendo interacción con el usuario, generación de informes del plugin por cada build, etc.

Nueva versión de Maven 2.1.0

24 mar 2009

Acaba de salir publicada la nueva versión 2.1.0 de Maven. Se pueden consultar las modificaciones incluidas en las Release Notes de la versión.

Está calentito calentito; esta misma semana empezamos a trabajar con esta versión, e iremos comentando las novedades que vemos. De momento os puedo anticipar que por fin las contraseñas almacenadas en el settings.xml están ofuscadas :-)

Problemas entre Maven y Trend Micro InterScan Web Security Suite

07 dic 2008

Este post no pretende ser más que una ayuda en castellano (encontrable vía Google) para aquellos que se encuentren con este problema en el futuro, ya que hemos encontrado bastantes referencias, pero pocas respuestas.

Los antecedentes

Nos encontrábamos en las instalaciones de un cliente, en un equipo Windows XP al cual le habíamos montado un kit de desarrollo completo: Eclipse (con diversos plugins), Maven, JDK, Tomcat’s de diversas versiones, etc., que tenía que integrarse con Hudson. En ese momento estábamos probando Maven sobre un sencillo proyecto de ejemplo; Maven debía descargar de un repositorio de librerías Artifactory recién descargado. Pero nos encontramos con efectos bastante raros:

  • Al hacer un ‘mvn eclipse:eclipse’ en consola, observábamos que todos los JAR’s descargados daban un error de verificación de checksum.  Es decir, parecía ser que el contenido del JAR descargado no era exactamente el esperado. Y además no conseguíamos cargarnos la librería jline, una de las dependencias de las librerías de Plexus. Raro raro, porque nunca habíamos visto ese error. ¿Estaba pasando algo en algún repositorio público?
  • Al hacer un’mvn clean’, algo mucho más raro todavía. Veíamos un log extrañísimo de repente saltaba un applet en pantalla que nos pedía confirmación de una operación. ¡Este plugin Maven nuevo!

Pantallazo

Las pesquisas

Evidentemente aquí hay algo raro. En el log Maven aparecen mensajes que no habíamos visto hasta ahora:

Downloading: http://sanisidro:8080/artifactory/public/org/apache/maven/shared/ma
ven-shared-io/1.1/maven-shared-io-1.1.jar
116K downloaded
[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = 'e67415a960
00a3110c7834caa325d965022491f6'; remote = '02e1d57be05ecac7dbe56a3c73b113e98f222
40f' - RETRYING
Downloading: http://sanisidro:8080/artifactory/public/org/apache/maven/shared/ma
ven-shared-io/1.1/maven-shared-io-1.1.jar
116K downloaded
[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = 'e67415a960
00a3110c7834caa325d965022491f6'; remote = '02e1d57be05ecac7dbe56a3c73b113e98f222
40f' - IGNORING
[INFO] [clean:clean]
Current policy properties:
mmc.sess_pe_act.block_unsigned: false
window.num_max: 5
jscan.sess_applet_act.sig_trusted: pass
file.destructive.state: disabled
jscan.sess_applet_act.block_all: false
window.num_limited: true
jscan.sess_applet_act.unsigned: instrument
mmc.sess_pe_act.action: validate
jscan.session.daemon_protocol: http
file.read.state: disabled
mmc.sess_pe_act.block_invalid: true
mmc.sess_pe_act.block_blacklisted: false
net.bind_enable: false
jscan.session.policyname: TU1DIERlZmF1bHQgUG9saWN5
mmc.sess_cab_act.block_unsigned: false
file.nondestructive.state: disabled
jscan.session.origin_uri: http://repo1.maven.org/maven2/org/apache/maven
/shared/file-management/1.2/file-management-1.2.jar
mmc.sess_cab_act.action: validate
net.connect_other: false
jscan.session.user_ipaddr: 127.0.0.1
jscan.sess_applet_act.sig_invalid: block
mmc.sess_cab_act.block_invalid: true
thread.thread_num_max: 8
jscan.sess_applet_act.sig_blacklisted: block
net.connect_src: true
thread.thread_num_limited: true
jscan.sess_applet_act.stub_out_blocked_applet: true
mmc.sess_cab_act.block_blacklisted: true
jscan.session.user_name: 127.0.0.1
thread.threadgroup_create: false
file.write.state: disabled
-->> returning Frame NULL
BaseDialog: owner frame is a java.awt.Frame

Empezamos a pensar. Tras unos minutos de desconcierto, eso de jscan huele a antivirus. A ver si está haciendo algo, modificando los JAR’s al descargarlos, modificando alguna Java Policy, modificando la JDK, o incluso interactuando con las políticas de seguridad en runtime… efectivamente, buscando en Google nos lleva a comprobar que el culpable parece ser el antivirus de Trend Micro. Debe estar colocando el applet Java como medida de seguridad, interceptando el código original. El antivirus ha debido pensar que el JAR es un applet y, como hace operaciones de disco (un ‘mvn clean’ debe borrar ficheros), pide permiso. Pero, ¿qué y cómo lo está haciendo? Realizamos unas cuantas tareas:

  • Paramos todos los servicios del antivirus. Probamos, sigue fallando. Este antivirus es un poco retorcido, ha debido modificar la JVM o alguna Java Policy.
  • Restauramos la JVM, nos aseguramos de estar utilizando lo mismo. Sigue fallando, con el antivirus parado… ¿qué pasa aquí? ¿Dónde está la coleta de este Sansón de la seguridad?
  • Probamos el applet de firma de Viafirma, que tampoco funciona. En la Java console observamos un error de un paquete com.trend (si al menos funcionara bien!)… El error era “Exception at com.trend.iwss.jscan.appscan.runtime …”. Nos bajamos el JAR de firma, lo descomprimimos y ¡voilá! Hay un buen conjunto de paquetes nuevos dentro de JAR. El antivirus modifica los JAR’s, lo cual explica todo: el misterioso applet del mvn clean, la rotura del applet de viafirma, los fallos de checksum… pero ¡el antivirus está apagado! ¿Cómo lo hace?

Las conclusiones

Si el antivirus en local no es culpable, debe haber un antivirus a nivel de red, junto al proxy. Efectivamente. El cliente, que por fortuna es técnico y de los que programan, no de los que sólo hablan, nos confirma que en muchas ocasiones ha tenido que utilizar otro proxy para descargarse JAR’s. El invento enemigo de los JAR’s no afecta sólo a Maven, obviamente, sino a cualquier JAR descargado por un proxy que disponga de este mecanismo de seguridad, y que realice determinadas operaciones. De hecho la librería JLine era parada por este producto llamado “Trend Micro InterScan Web Security Suite” que, de hecho, publicita este servicio tan majo y maligno a la vez.

Así que si en algún momento os encontráis este tipo de errores tan extraños, ya tenéis otra posibilidad latente.

El epílogo

Nos cambiamos de proxy y todo fue bien, previa limpieza del repositorio de librerías Maven.

Conclusiones finales. Eligiendo nuestro entorno de Integración Continua (V).

17 nov 2008

Después de una serie de artículos que se iniciaron con Eligiendo nuestro entorno de Integración Continua (I) , tenemos las conclusiones finales de nuestra comparativa.

  • Tabla resumen

A modo de resumen hemos agrupado en una sola tabla todos las parámetros evaluados en la comparativa, para permitirnos una comparación rápida entre las diferentes herramientas.

 

Apache
Continuum

LuntBuild

Hudson

Instalación
Documentación
de instalación

Suficiente

Buena

Buena.
Con ayuda contextual

Dificultad
de la instalación

Fácil.
Requiere la configuración de recursos JNDI ( SMTp, base de datos,
etc.)

Fácil.
puede requerir configuración de ficheros externos

Muy
Fácil. Es posible su ejecución directamente o sobre cualquier
servidor de aplicaciones ligero, sin requerir edición de ficheros
de configuración

Configuraciones
avanzadas

Compleja

Fácil

Fácil

Administración
¿permite la
configuración completa desde la interfaz Web?

Sí.
A excepción de algunos parámetros como el SMTp, conexión base
de datos, parámetros JNDI, …

Completa

Completa

Documentación
oficial de Administrador

Suficiente

Suficiente

Buena.
Con ayuda contextual

¿permite
copias de Seguridad desde la herramienta?

No

No

Mantiene
un histórico de compilaciones (build)

Gestión de
tareas programadas

Rendimiento

Bueno

Muy
bueno, permite gestión de hilos de ejecución y tareas
distribuidas

Muy
bueno, permite gestión de hilos de ejecución y tareas
distribuidas

Seguridad
Gestión de
permisos basados en perfiles

Sí,
pero sólo con un conjunto de perfiles predefinido.

Gestión de
permisos específicos por proyecto

Facilidad de
configuración de la seguridad

Fácil

Fácil

Muy
Fácil

Integración
con sistemas externos
Integración
con Sistemas de control de Versiones

Suficiente

Muy
Buena

Muy
Buena

Integración
con plataformas de gestión de incidencias (bugtrackers)

Mala

Mala

Regular

Integración
con herramientas de generación de informes (reporting)

Buena,
basada en Maven

Buena

Muy
Buena. plugins Hudson integrados

Desarrollo
de nuevos plugins o mecanismos de extensión

Complejo
(Malo)

Suficiente

Muy
Bueno

Integración
con herramientas de testeo

Buena

Buena

Muy
buena, con agregación de los resultados en la propia interfaz
web.

Tipos de
proyectos Soportados
proyectos
Java

Muy
Buena

Muy
Buena

Excelente
(permite un gran número de posibilidades)

proyectos No
Java

Mala.
Solo basados en Shell scripts

Mala.
Solo basados en Shell scripts

Buena,
mediante plugins

Facilidad
de uso

Documentación
de usuario

Buena

Buena

Muy
Buena, con ayuda contextual

Interfaz

Fácil

Complejidad
media

Muy
fácil

Estabilidad
Estabilidad
general de la herramienta

Buena

Buena

Buena

¿permite
compilación distribuida?

No

¿permite la
gestión de hilos de ejecución?

No

  • Conclusiones finales.

Sin duda nuestra propuesta se inclina por Hudson. Brevemente vamos a enumerar y resumir (de entre los detalles de esta comparativa) ciertos aspectos que hacen inclinar la balanza hacia este software:

  • Extensibilidad. La posibilidad de desarrollar plugins que permitan adaptar e inyectar funcionalidad de forma personalizada es una característica casi imprescindible.  Apache Continuum no dispone de esta posibilidad de integrar plugins (deben ser plugins Maven, utilizados específicamente en cada uno de los proyectos Java), lo cual prácticamente hace descartar esta plataforma. LuntBuild dispone de ciertas opciones de inyección de código, si bien a este respecto las mejores características las tiene Hudson.
  • Calidad de la documentación. En esta materia Hudson también dispone de la mejor documentación, por delante de LuntBuild y Continuum.
  • Integración de resultados de plugins (centralizados o Maven) en pantalla. Un aspecto de interés es que el resultado de los diversos plugins pueda verse en la interfaz web de la aplicación. Tan sólo Hudson proporciona este nivel de integración por defecto.
  • Sistema de seguridad y permisos. En esta funcionalidad tan importante lideran la comparativa Hudson y Continuum. Ambas disponen de la posibilidad de gestión de permisos basados en perfiles, y posibilidad de personalizar los permisos por proyectos. Continuum puede requerir modificaciones en ficheros de configuración y reinicio del sistema, mientras que Hudson permite configuración integral en pantalla.
  • Estabilidad. Las tres plataformas disponen de buenas características de estabilidad, si bien destacan Hudson y LuntBuild, que permiten la gestión multihilo y compilación distribuida.
  • Soporte de la comunidad. A este respecto lideran la comparativa tanto Hudson como Continuum, desarrollos de Sun y Apache respectivamente.

Como se puede observar, Hudson auna las principales características que nosotros esperabamos de un sistema de Integración Continua, y por ello es nuestra propuesta y elección para servir de plataforma base en la infraestructura de desarrollo.

Hudson. Eligiendo nuestro entorno de Integración Continua (IV).

05 nov 2008

Nos encontramos ante un software relativamente novedoso, pero que se ha convertido en uno de los referentes en cuanto a sistemas de Integración Continua. Es una herramientas software libre desarrollada en Java y mantenida por Sun Microsystems desde su web java.net. La herramienta puede ser descargada desde https://hudson.dev.java.net/.

  • Instalación

La instalación de Hudson es extremadamente sencilla, pudiéndose realizar una evaluación del software utilizando un instalador Java Web Start desde la web del producto. Así mismo, se puede descargar e instalar la aplicación de forma standalone, o se puede desplegar sobre un servidor de aplicaciones mediante el fichero empaquetado hudson.war.

El sistema está pensado para que nunca sea necesario editar ningún fichero de configuración externo, ofreciéndonos una administración web sencilla pero que contempla una configuración visual integral (incluyendo por ejemplo la parametrización de SMTP, base de datos, seguridad, etc.).

La documentación de instalación oficial del producto es buena, e incluye incluso algunos vídeos screencasts. Por otro lado, si en algún momento nos sentimos confundidos con la configuración, el sistema ofrece una completa ayuda en linea y contextual que facilita mucho la tarea de configuración para personas no familiarizadas a priori con este tipo de sistemas.

  • Administración, gestión de proyectos

La herramienta ofrece una interfaz muy intuitiva para su administración, por lo que como hemos comentado antes, no será necesario personal especializado para su administración, pudiéndose realizar todas las tareas desde la propia consola web.

La herramienta permite la definición de tareas periódicas desde su interfaz web mediante un editor similar a cron dotado de una completa ayuda contextual.

Por otro lado, durante las pruebas, la migración de los proyectos a Hudson ha resultado ser muy rápida y sencilla, consiguiendo un entorno totalmente configurado y funcional en poco tiempo.

Respecto a su mecanismo de actualización, Hudson es el mejor de los sistemas analizados, ya que sólo es necesario descargar el nuevo empaquetado .war y sustituir el existente, asegurándose la plataforma de mantener la compatibilidad de forma automática. Por otro lado, es el propio Hudson el que nos avisará de la existencia de nuevas versiones, tanto del sistema completo como de sus plugins.

  • Seguridad

El sistema ofrece un sistema de seguridad completo y muy flexible, y totalmente configurable desde la consola web, permitiéndonos o bien integrarnos con sistemas externos de autenticación (LDAP, bases de datos externas) o delegar la seguridad en el propio Hudson.

Si optamos por que Hudson gestione la seguridad, dispondremos de múltiples opciones que cubren todo el abanico de necesidades, como seguridad basada en perfiles, asociación de permisos por proyectos o por matriz de acciones.

  • Integración con sistemas externos

Hudson ofrece un mecanismo de extensión muy completo, que junto con el gran número de plugins ya existentes le permiten su integración con un gran número de sistemas y herramientas. Por otro lado, aunque está preparado (al igual que Continuum o LuntBuild) para utilizar directamente los plugins Maven, proporciona los mecanismos adecuados para no depender de una correcta configuración de estos plugins en el fichero pom.xml de los proyectos. Esta centralización de la configuración constituye una ventaja muy importante respecto a sus competidores, ya que garantiza que, independientemente de lo contenido en el pom.xml, se puedan ejecutar los procesos de chequeo y validación deseados.

Por otro lado, existe una integración plena entre Hudson y sus plugins. Los resultados de éstos podrán ser visualizados en la herramienta web, incluyendo tanto conclusiones de testeo, estilos, pruebas unitarias, etc… como los resultados de los procesos de despliegue. Nuevamente Hudson es la única plataforma en permitir este tipo de funcionalidad avanzada.

Por otro lado, permite la integración con la mayoría de sistemas de control de versiones (SVN, CVS, etc.), ya sea de base, o mediante la inclusión de los plugins necesarios.

Es de especial interés el mecanismo de instalación y gestión de plugins, ya que la herramienta nos permite la descarga e instalación automática desde su interfaz web, avisándonos incluso de la aparición de nuevas versiones de los mismos de forma automática.

Otro factor muy importante es la completa documentación para poder desarrollar nuestros propios plugins, que nos permitirán adaptar el sistema a las particularidades propias de cada entorno.

También resulta curiosa la integración con sistemas como Twitter, Jabber, etc., que nos permiten hacernos una idea del avanzadísimo nivel de los mecanismos de integración incorporados en la plataforma. Sin duda, esta es la característica en la que más se destaca Hudson frente a Continuum o LuntBuild.

  • Tipos de proyectos

Aunque la herramienta esta principalmente enfocada a proyectos Java, gracias sus plugins tiene soporte para gran multitud de formatos como por ejemplo Ivy, Nant, Rake, Ant, Maven, Phing, Shell scripts, y para lenguajes como .Net, Groovy, Rails, PHP.

  • Facilidad de uso

La herramienta ofrece una interfaz potente y muy usable, que permite que personal no cualificado o con poca experiencia en este tipo de herramientas se adapte a su uso con facilidad.

La documentación oficial es completa, y junto con la ayuda contextual en todos los elementos de la interfaz hace que la curva de aprendizaje sea muy rápida.

  • Estabilidad

Nos encontramos ante un software muy estable, al igual que ocurría con sus dos competidores directos. Prueba de ello es que está siendo utilizado para todos sus proyectos en grupos de desarrollo como JBoss. Como sistema de Integración Continua, tiene una política de publicación de actualizaciones muy dinámica que hace que se liberen versiones cada pocas semanas, lo cual podría ser un inconveniente de no ser por las posibilidades de actualización automática comentadas. A continuación se indican las principales versiones liberadas:

En lo referido al rendimiento, el sistema permite la gestión de hilos de ejecución dedicados a tareas de compilación y la ejecución de compilación distribuidas, lo que le confiere excelentes características de escalabilidad y adaptabilidad al entorno disponible.

Hudson 1.256

Julio 2008

Hudson 2.00

Marzo 2008

Hudson 1.100

Abril 2007

  • Conclusiones sobre Hudson

La herramienta, gracias a su sistemas de plugins, ha resultado ser muy versátil y fácilmente adaptable a necesidades particulares. Por otro lado, su sencilla interfaz la hacen ideal para su uso por personal no especializado en este tipo de herramientas.

Aunque la herramienta es la más joven de las estudiadas, está teniendo una rápida aceptación y divulgación en el mercado, y ha demostrado completamente su fiabilidad.

Próximamente: Conclusiones finales. Eligiendo nuestro entorno de Integración Continua (V).

Apache Continuum. Eligiendo nuestro entorno de Integración Continua (II).

16 oct 2008

Apache Continuum

Continuum es una potente herramienta de Integración Continua desarrollada por Apache; su descarga está accesible en la URL: http://continuum.apache.org/download.html

  • Instalación

La instalación de Continuum Server es sencilla; para una instalación básica basta con descargar el paquete de instalación apropiado de la web de Continuum, descomprimir el paquete en el directorio destino escogido y configurar las conexiones a base de datos a utilizar. Una vez instalado, para su ejecución solo se requiere lanzar uno de los scripts incluidos en el directorio bin.

Como hemos podido comprobar, la configuración básica es realmente sencilla, pero en la mayoría de los casos necesitaremos configurar conexiones a bases de datos, sistemas de correo, gestión de permisos avanzada, etc., además de desplegar Continuum sobre un servidor de aplicaciones. Para este tipo de configuraciones deberemos modificar los ficheros de configuración basados en xml o properties según el caso.

En términos generales la documentación del producto es buena, si bien resulta algo pobre. Esto suele ser tradicional en la mayoría de los proyectos del grupo Apache; existen algunos apartados con documentación muy completa, pero quedando otros aspectos de la documentación por terminar.

En Continuum nos enfrentamos a una curva de aprendizaje relativamente lenta para personal no especializado, debido a algunas configuraciones no documentadas y multitud de conceptos que hay que saber manejar desde el principio. Por ejemplo, se suele requerir adaptar el fichero continuum/conf/application.conf, o los ficheros pom.xml de los proyectos Maven de forma acorde a lo que espera Continuum.

  • Administración, gestión de proyectos

Con esta herramienta muchas de las tareas administrativas podrán ser realizadas mediante la consola web. Aunque otras  tareas avanzadas requieran la modificación de ficheros de configuración.

La herramienta permite la definición de tareas periódicas desde su interfaz web de una forma sencilla, pudiendo automatizar tareas. Si bien la configuración de estas operaciones es sencilla, la documentación oficial al respecto es nula.

Respecto a las gestión de copias de seguridad, sólo se contempla un backup basado en un cliente xml-rpc, no existiendo documentación asociada a este cliente. Por otro lado, la opción alternativa se basa en realizar copias externas de la base de datos y los ficheros de trabajo, en función del tipo de instalación y base de datos utilizada.

Se puede destacar que no existe documentación en linea mientras se utiliza la herramienta. Ello, unido a la escasa documentación, provocó que durante nuestra evaluación tardásemos más tiempo del esperado en configurar nuestras primeros proyectos Maven sobre Continuum.

Continuum es un software en constante crecimiento, el cual ha mejorado mucho en las últimas versiones. Sin embargo, la actualización a nuevas versiones no resulta siempre directa; en la mayoría de los casos resulta necesario ejecutar alguna herramienta de migración para actualizar desde versiones anteriores.

  • Seguridad

El sistema dispone de un mecanismo de seguridad muy completo, basado en usuarios, roles, y permisos sobre operaciones o proyectos, permitiéndonos la definición de una matriz completa de permisos. Sólo necesitaremos modificar el fichero de configuración security.properties cuando estemos realizando configuraciones avanzadas.

  • Integración con sistemas externos

Apache Continuum no ofrece mecanismos de extensión o plugins, limitándose a permitirnos la llamada a plugins Maven desde los comandos de compilación asociados a los proyectos. Esto hace que dependa de la correcta configuración de plugins Maven report y build en los ficheros pom.xml de los proyectos gestionados.

Por este motivo, si requerimos que Continuum ejecute procesos de testeo, bugtrackers, validación de reglas de estilo, etc., necesitaremos definir éstos en el fichero pom.xml del proyecto. Sin embargo, no obtendremos una integración completa entre Continuum y los plugins Maven utilizados (Continuum lanza los procesos, pero no integra sus respuestas para plasmar los resultados en pantalla). Del mismo modo, la integración con el sistema de control de versiones dependerá de la correcta configuración del plugin SCM en el fichero pom.xml del proyecto gestionado.

Otro aspecto que se echa de menos es un mecanismo para despliegue de aplicaciones y librerías que sea independiente de la definición de estos mecanismos en Maven. Nuevamente se exige una correcta configuración de estos aspectos en el pom.xml del proyecto.

En general, esta parece una debilidad grave de Continuum, ya que depende en exceso de que los ficheros pom.xml estén perfectamente configurados, y por ello la realización de procesos de verificación es dependiente de la buena voluntad del desarrollador, no de las personas responsables de estas tareas (como gente de Desarrollo o de Calidad).

  • Tipos de proyectos soportados

Apache Continuum está especialmente enfocado a proyectos Maven, si bien también ofrece soporte a proyectos con scripts Ant y otro tipo de proyectos sin formato basados en scripts de compilación. Aunque las limitaciones se podrían solventar mediante proyectos shell, se echa en falta la integración con otros lenguajes o mecanismos como ivy, rake, etc.

  • Facilidad de uso

Nos encontramos ante una herramienta con una potente interfaz web, que utiliza un conjunto de iconos preestablecidos para indicar los estados y acciones disponibles, ofreciéndonos a su vez una leyenda para su fácil interpretación.

El único apartado negativo es que no aporta ayuda en línea sobre los diversos conceptos y elementos de la interfaz, por lo que es necesario un mínimo conocimiento previo de los conceptos tratados.

Respecto a la documentación oficial, ofrece una documentación muy escueta con multitud de apartados tan sólo esbozados, siendo por ello recomendable recurrir a artículos externos o libros especializados.

  • Estabilidad

Estamos ante un software estable y fiable como la mayoría del software desarrollado por el grupo Apache. A continuación se detallan las últimas versiones liberadas de la plataforma:

Apache Continuum 1.2

Septiembre 2008

Apache Continuum 1.1

Noviembre 2007

Apache Continuum 1.0

Octubre 2005

Una buena prueba de estabilidad del software es que está siendo utilizado internamente por varios de los proyectos gestionados por la propia Apache.

Respecto al rendimiento, el principal defecto es que no dispone de gestión de hilos de compilación, ni de mecanismos para la compilación distribuida, aunque es posible ver el listado de tareas pendientes y detenerlas si se desea. La ausencia de aquellas opciones hace que en ocasiones se puedan causar picos de caída de rendimiento, o falta de adaptación a las prestaciones de estaciones multiprocesador.

  • Conclusiones finales sobre Continuum

Apache Continuum es aún un producto relativamente joven con mucho espacio para crecer y mejorar. Ha avanzado en buena medida desde sus primeras versiones, las cuales no alcanzaban el nivel de calidad mínimo exigido a este tipo de soluciones. Sin embargo, las nuevas funcionalidades que han ido apareciendo en las últimas versiones, unido a su intuitiva consola de administración, lo hacen una buena herramienta y una opción seria para implementar una infraestructura de desarrollo basada en esta plataforma de Integración Continua.

Los principales puntos negativos encontrados han sido la falta de algunas funcionalidades típicas disponibles en otras soluciones de Integración Continua, donde destaca sin duda la inexistencia de un mecanismo de extensión de funcionalidad que permita la inyección de plugins. Continuum se basa al 100% en la configuración previa establecida en el pom.xml (reporting, checkstyle, scm, etc.), exigiendo así que la definición del proyectos sea completa para su correcto funcionamiento. Por decirlo de una forma reducida, ello implica que todos y cada uno de los proyectos deben disponer de la misma configuración, y si se desea ampliar una funcionalidad, ello implica la modificación del fichero pom.xml de todos los proyectos. Sin duda, Continuum adolece de una gestión centralizada de extensiones que permitiese configurar este tipo de funcionalidades a nivel plataforma, no a nivel proyecto.

  • Comentario personal

Apache Continuum resultó un software genial durante el tiempo que estuvimos utilizándolo en los proyectos internos del departamento de I+D, pero causó demasiados problemas cuando comenzamos la migración de todos nuestros proyectos de desarrollo a un entorno de integración continua. Hay que aclarar que la mayoría de los problemas que encontramos no son directamente achacables a Continuum, sino a la poca experiencia con este tipo de soluciones del grupo de desarrolladores y a las exigencias que se imponían sobre proyectos que, por falta de tiempo, nunca eran configurados al 100%. Finalmente lo descartamos eligiendo una solución más sencilla en su manejo y con menos exigencias respecto a la configuración completa de los proyectos gestionados.

Continuación: LuntBuild. Eligiendo nuestro entorno de Integración Continua (III).

Eligiendo nuestro entorno de Integración Continua (I).

08 oct 2008

Objetivos

El objeto de este artículo es en plasmar la metodología y conclusiones obtenidas en una comparativa técnica que hemos realizado sobre algunas de las herramientas de integración  continua disponibles en el mercado. Para ello se establecen unas bases para la elección de la plataforma y se realiza una comparativa completa partiendo de una selección de herramientas.

Existe un buen número de herramientas de Integración Continua en el mercado: Continuum, CruiseControl, LuntBuild, Hudson, Atlassian Bamboo, etc… Para facilitar el estudio se ha realizado una primera preselección consistente en las herramientas estrictamente Open Source más utilizadas en el mercado: Apache Continuum, LuntBuild y Hudson.

Antes de entrar en detalles, comentaremos cúales son los principales objetivos funcionales que se buscan a la hora de implantar un sistema de Integración Continua:

  • Reducción del tiempo de integración. Se trata de agilizar el proceso de integración de los diversos proyectos y aplicaciones, buscando un sistema que se responsabilice de automatizar la compilación, ejecución de tests y despliegues, así como de avisar de posibles problemas a los diversos participantes en el desarrollo de un proyecto.
  • Reducción de errores. Uno de los objetivos más importantes que se persiguen con este tipo de sistemas es la detección de errores y conflictos en un estado temprano.
  • Testeo de la calidad de código y reglas de estilo. Para asegurar un mínimo de coherencia de los nuevos desarrollos con las políticas de calidad establecidas, se busca un sistema capaz de automatizar este tipo de tareas de manera sencilla.
  • Reducción del riesgo. Permitiendo que versiones tempranas del producto estén disponibles lo antes posible, con un número de incidencias minimizado de forma automática, y con una gestión de la configuración automática que reduce errores en despliegues.
  • Disponibilidad continua de la última versión del código para pruebas, agilizando los procesos de despliegue de nuevas versiones en los diferentes entornos.

Metodología del análisis

Para seleccionar el sistema de Integración Continua más adecuado, hemos procedido a analizar las diversas características funcionales y técnicas de interés. Para ello se ha procedido a instalar las tres plataformas objeto del estudio (Continuum, LuntBuild y Hudson) y se ha puesto en marcha al menos un proyecto Maven sobre ellas, tratando de evaluar una serie de variables de interés.

A continuación se indican los criterios de selección utilizados en la comparativa:

  • Instalación.

Evaluaremos la facilidad de instalación y configuración de las herramientas, con parámetros como la documentación oficial de instalación, posibilidades de configuraciones avanzadas, dificultad de la instalación, etc.

  • Administración, gestión de proyectos.

Partiendo del hecho de que el entorno en el que se va a realizar la implantación no tiene por qué contar con personal especializado y dedicado exclusivamente a la gestión y administración de la plataforma, será recomendable que las tareas de administración asociadas al sistema elegido sean las mínimas, y que su ejecución sea lo más sencilla posible. Se analizarán parámetros como la documentación oficial destinada a esta temática o si el sistema permite una fácil generación y restauración de copias de seguridad (tanto de la definición de los proyectos y tareas configuradas como del histórico de compilaciones). También se estudiará si las tareas de administración relacionadas con la plataforma se puedan realizar mediante la interfaz Web. Así mismo, verificaremos si se permite recuperar el historial de las últimas tareas de compilación realizadas, así como poder configurar el número de días y compilaciones que se mantendrán en el historial. Con respecto a la gestión de tareas periódicas, comprobaremos si el sistema permite la definición de tareas periódicas, para facilitar la automatización de tareas repetitivas. Además, teniendo en cuenta que este tipo de herramientas están en continua evolución, se evaluará la facilidad de instalación de nuevas actualizaciones. Por último se estudiará el rendimiento de la solución. Dado que las compilaciones son tareas muy pesadas, se evaluarán los diferentes mecanismos que aporta cada sistema de Integración Continua para la gestión de hilos de compilación y la ejecución distribuida de tareas.

  • Seguridad.

Se evaluará la versatilidad y potencia en la gestión de roles, perfiles y permisos asociados a proyectos y acciones disponibles en la herramienta. Resulta interesante además poder definir perfiles de usuario específicos. Así mismo, se estudiará la posibilidad de definición de permisos especiales para proyectos concretos. Por último evaluaremos la complejidad a la hora de establecer una política avanzada de permisos, observando el nivel de personalización que permite la plataforma.

  • Integración con sistemas externos.

En la mayoría de los casos es necesario que la herramienta de Integración Continua interactúe con otros sistemas específicos, por lo que puede ser necesario el desarrollo de nuevos plugins que extiendan la funcionalidad de la plataforma. Por este motivo evaluaremos la sencillez y los mecanismos que ofrecen estos sistemas a la hora de inyectar funcionalidad personalizada.

Evaluaremos también el nivel de integración que tienen las diferentes herramientas de Integración Continua con sistemas de control de versiones, así como el nivel de integración con herramientas de evaluación de código (como checkstyle, PMD, etc…). Analizaremos además el nivel de integración con sistemas de gestión de incidencias, particularizando en el sistema Mantis BT. Por último evaluaremos el nivel de integración de soluciones con sistemas de test como Junit o JMeter .

  • Tipos de Proyectos soportados.

Evaluaremos los diferentes tipos de proyectos soportados, así como sus posibilidades de extensión para futuros modelos. Aunque haremos especial hincapié en el soporte completo de proyectos basados en Maven2, si bien también es relevante que el sistema permita la gestión del mayor número posible de formatos.

  • Facilidad de uso.

Ya que la plataforma de Integración Continua elegida deberá convertirse en una pieza central de nuestro entorno de software, la herramienta deberá ofrecer una interfaz sencilla, usable e intuitiva, de forma que se facilite la interacción con la herramienta por personal no especializado, sin problemas serios de aprendizaje. Haremos especial hincapié en las ayudas en línea disponibles y en la organización de la interfaz.

  • Documentación de usuario.

Evaluaremos la cantidad y calidad de la documentación de usuario disponible.

  • Estabilidad.

La herramienta elegida deberá disponer de unas excelentes características de estabilidad. El objetivo no es elegir la última tecnología relacionada con la Integración Continua, sino disponer de un entorno funcional operativo lo más robusto posible.

Próximamente:
Apache Continuum. Eligiendo nuestro entorno de Integración Continua (II).

Desarrollando un plugin Maven Report (I)

04 ene 2008

Hace varios meses me enfrenté al primer desarrollo de un plugin Maven para generar un report en el site, y comprobé en mis carnes que había muy muy poca documentación, y en castellano era totalmente inexistente. Por ello considero interesante describir paso a paso el proceso para desarrollar un plugin sencillo del tipo Hola Mundo!, dejando para un post futuro otros detalles interesantes, como la utilización de ficheros properties, introducción de parámetros de configuración, generación de ficheros externos al report, etc.

Este post supone que el lector ya está suficientemente familiarizado con la utilización de Maven, por lo que no entraré en detalles excesivamente básicos, como explicar lo que es un pom.xml, cómo se utiliza Maven, etc. Lo único básico que explicaré es que un plugin Maven Report sirve para generar un informe que se anexa al site del proyecto (se invoca, por ejemplo, ejecutando ‘mvn site’) siempre que esté referenciado en el pom.xml del proyecto llamante, dentro de <reporting><plugins>. El propio site de Maven ya embebe varios plugins Report como el de dependencias.

Con esta sencilla introducción, vamos al lío: lo primero, un plugin report es un proyecto Maven, por lo que tiene su pom.xml y su directorio /src/main/java. Veamos cómo sería nuestro pom.xml:

<?xml version="1.0" encoding="UTF-8"?><project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.viavansi</groupId>
<artifactId>report-plugin-demo</artifactId>
<packaging>maven-plugin</packaging>
<version>0.0.1</version>
<name>Plugin demo Report</name>
<inceptionYear>2008</inceptionYear>
<url>http://www.viavansi.com</url>
<organization>
<name>Servicios Avanzados para las Instituciones S.L. (VIAVANSI )</name>
<url>http://www.viavansi.com</url>
</organization>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.reporting</groupId>
<artifactId>maven-reporting-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.reporting</groupId>
<artifactId>maven-reporting-impl</artifactId>
<version>2.0.2</version>
</dependency>

</dependencies>
</project>

He marcado en negrita los puntos clave: por un lado, debemos indicarle a Maven que este proyecto es un plugin mediante la etiqueta <packaging>, y además declaramos las dependencias a los APIs de construcción de plugins y reporting de Maven.

Una vez creado nuestro fichero pom.xml, estamos en disposición de crear la clase del plugin en el paquete que deseemos, obviamente dentro de /src/main/java.

Un plugin report debe extender de la clase org.apache.maven.reporting.AbstractMavenReport, lo cual obliga a sobrescribir los siguientes métodos:

  • protected String getOutputDirectory(): devuelve el directorio donde se generará el fichero HTML del report
  • protected MavenProject getProject(): devuelve una referencia objetual al pom.xml del proyecto padre que llama al report
  • protected Renderer getSiteRenderer(): devuelve la instancia del objeto que rendea el site Maven
  • public String getDescription(Locale locale): devuelve la descripción del plugin, permitiendo internacionalización en función del Locale.
  • public String getName(Locale locale): devuelve el nombre del plugin, permitiendo internacionalización en función del Locale.
  • public String getOutputName(): devuelve el nombre del fichero HTML que se va a generar.
  • protected void executeReport(Locale locale): esta es la madre del cordero, es el método que se invoca para generar el informe.

Para implementar los 3 primeros métodos, es interesante saber que Maven entregará a nuestra clase 3 objetos:

  • Una instancia de Renderer
  • Una instancia de MavenProject
  • Una instancia de File, que se corresponde con el directorio de salida por defecto (el /target/site)

De forma que los utilizaremos para implementar esos 3 métodos. Para que Maven nos entregue esas 3 instancias, deberemos declarar esos 3 atributos de la clase y utilizar Annotations. Veremos esto al incluir el código entero de la clase.

Otro punto clave es que es obligatorio incluir un goal Maven a cada plugin, con una anotación @goal.

Y un último concepto de importancia es cómo escribir el HTML. Para ello se hace uso del API Sink perteneciente al proyecto Doxia. Utilizando el método getSink() obtenemos el objeto Sink que nos permite escribir información al informe.

Veamos el código Java del plugin. He marcado en negrita algunas de las líneas clave, siguiendo las instrucciones que he comentado anteriormente. El código está comentado para explicar paso por paso lo que es está haciendo:

package com.viavansi.maven.report;

import java.io.File;
import java.util.Locale;

import org.apache.maven.doxia.siterenderer.Renderer;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.doxia.sink.Sink;

/**
 * Genera un Hola Mundo en un site
 * @goal demo
 */

public class DemoMojo extends AbstractMavenReport {

    /**
     * Directorio donde se generará este report
     *
     * @parameter expression="${project.reporting.outputDirectory}"
     * @required
     */
        private File outputDirectory;

    /**
     * Maven nos pasa este valor, no debemos configurarlo.
     *
     * @component
     */
    private Renderer siteRenderer;

    /**
     * Instancia del proyecto (pom) que nos llamará.
     * De aquí se puede sacar cualquier información del pom.
     *
     * Maven nos pasa este valor, no debemos configurarlo.
     *
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

        @Override
        protected String getOutputDirectory() {
                return this.outputDirectory.getAbsolutePath();
        }

        @Override
        protected MavenProject getProject() {
                return this.project;
        }

        @Override
        protected Renderer getSiteRenderer() {
                return this.siteRenderer;
        }

        public String getDescription(Locale locale) {
                return "Nuestro primer plugin Maven Report";
        }

        public String getName(Locale locale) {
                return "DemoMavenReport";
        }

        public String getOutputName() {
                return "demo";
        }

        @Override
        protected void executeReport(Locale locale) throws MavenReportException {
                //Ejemplo de log en consola
                getLog().info("Iniciando plugin Demo");

                //Hago las tareas que deba hacer, lo lógico sería delegar la responsabilidad

                //Recupero el API Sink de Doxia para "pintar" resultados
                Sink sink = (Sink)getSink();

                //Cabecera del report
        sink.head();
        sink.title();
        sink.text("Nuestro primer Report Maven");
        sink.title_();
        sink.head_();    //Se cierran las maquetaciones de forma similar a HTML

        //Cuerpo del report
        sink.body();
        sink.section1();
        sink.sectionTitle1();
        sink.text( "Apartado 1" );
        sink.sectionTitle1_();
        sink.paragraph();

        sink.text("Hola Mundo!");
        sink.lineBreak();

        //Ejemplo de utilización de información del pom del proyecto que llama
        sink.text("Detectamos en el pom: "
                        +project.getGroupId()+"."+project.getArtifactId()+"-"+project.getVersion());

        sink.paragraph_();
        sink.section1_();

        sink.body_();

        }

}

Y listo. Para probar nuestro plugin, simplemente podríamos instalarlo en nuestro repositorio local (mvn install) o desplegarlo en algún repositorio de librerías, y referenciarlo desde otro proyecto Maven, en el apartado <reporting><plugins>…</plugins></reporting>. Veremos cómo se agregará nuestro report al site generado.

En un siguiente post mostraremos cómo podemos añadir características avanzadas a nuestros reports. Hasta entonces, feliz año a todos.

Configurando nuestro propio repositorio Maven

15 jul 2007

¿Para que queremos un repositorio propio?

  • Para agilizar los tiempos de descarga, manteniendo una cache de las librerías utilizadas.
  • Para reducir los conflictos entre librerías, controlando en todo momento las librerías disponibles.
  • Mantener un repositorio central en el que localizar las librerías de la empresa.
  • Un uso mas eficiente de ancho de banda.

¿ Que opciones tenemos?

Tenemos varias posibilidades, aunque la mas adecuada es crear un repositorio mixto, que por un lado almacene las librerías internas de la empresa, además de las librerías y drivers que no esten disponibles en los repositorios de públicos y por otro haga de proxy de las librerías de los repositorios centrales Maven.

De entres las diferentes alternativas:

  • El estandar maven proxy (codehaus) y DSMP se descartan por ser demasiado simple, sin administrado, ni navegador visual.
  • Artifactory de los que he probado es el mas completo, no es un mero proxy, sino que tiene funciones de backup y de búsqueda de librerías.

Proceso de instalación

La instalación es muy sencilla:

  • Nos descargamos la última versión estable de http://www.jfrog.org/sites/artifactory/latest/ , actualmente la versión 1.2.1
  • Descomprimimos el zip en el directorio base elegido, en adelante $ARTIFACTORY_HOME.
  • Configuramos Artifactory, modificando el fichero $ARTIFACTORY_HOME/etc/artifactory.config.xml, dejando como están los repositorios y modificando en nuestro caso solamente el backup para que se realice todos los viernes

<backupCronExp>0 0 24 ? * FRI</backupCronExp>

  • Desplegamos el $ARTIFACTORY_HOME/webapps/webapps/artifactory.war en el Tomcat 5+.
  • Antes de iniciar el servidor es necesario declarar la variable de entorno artifactory.home, para que artifactory.war sepa donde localizar el directorio home.

en mi caso: export JAVA_OPTS=-Dartifactory.home=/home/felix/java/artifactory-1.2.1

  • Arrancamos el servidor.

Configuración

  • Accedemos a la aplicación http://localhost:8080/artifactory y accedemos al sistema por primera vez con el usuario “admin”, clave “password”.

Pantalla de login artifactory

  • Lo primero que hacemos es configurar el usuario administrador, en el menú security/user seleccionamos el administrador y modificamos su password. También creamos un nuevo usuario deployer sin permisos de administración.

Administración de usuarios con artifactory

  • Hacemos una carga inicial de todas las librerias que usamos y no se encuentran disponibles en ningun repositorio público. Tambien es posible importar un repositorio ya existente.

Desplegando librerias en artifactory

  • Ya estamos listos para empezar a utilizar el nuevo repositorio, modificamos el pom de nuestros proyectos para que utilicen por defecto este repositorio.

<repositories>
<repository>
<id>Viavansi</id>
<name>Viavansi Repositorio</name>
<url>http://localhost:8080/artifactory/repo</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>Viavansi</id>
<url>http://localhost:8080/artifactory/repo</url>
</pluginRepository>
</pluginRepositories>

El resultado

Probamos a ejecutar cualquier comando sobre nuestro proyecto, por ejemplo mvn clean eclipse:eclipse y comprobamos que las dependencias son descargadas desde nuestro nuevo repositorio.

Downloading: http://localhost:8080/artifactory/repo/javax/activation/activation/1.1/activation-1.1.jar
61K downloaded

Tambien podemos probar a buscar una dependencia concreta, como por ejemplo de búsqueda, buscamos el artifac myfaces y vemos sus dependencias:

Search artifact Locator

Y lo mas importante, tenemos todas las dependencias de nuestros proyectos perfectamente organizadas y localizables.

Algunos problemas

En nuestro caso, ya teníamos en Viavansi un repositorio interno Maven que gestionábamos manualmente y que mantenia desde hace casi un año cientos de librerias congeladas, esto nos ha causado muchos problemas ya que TODOS los proyectos que han empezado a utilizar el nuevo repositorio han requerido algún tipo de modificación, ya sea por cambios en las dependencias transitivas, por nuevas versiones de librerías, o por librerías que hemos tenido que volver a desplegar. En resumen, aunque la instalación de Artifactory es muy sencilla, dependiendo de cantidad de proyectos, la adaptación al nuevo repositorio puede ser compleja, en nuestro caso fueron dos duros días solucionando dependencias.

Mas información en la faq de Artifactory y articulo en Ingles en Theserverside.com.