Contenido


5 cosas que usted no sabía acerca de...Apache Maven

Consejos para administrar el ciclo de vida de proyectos con Maven

Comments

Maven es una excelente herramienta para desarrolladores Java™ y también es posible usarla para gestionar el ciclo de vida de sus proyectos. Como herramienta de gestión de ciclos de vida, Maven funciona en torno a fases y no con el desarrollo estilo Ant por "tareas". Maven maneja todas las fases del ciclo de vida de los proyectos, incluyendo validación, generación de código, compilación, pruebas, empaquetamiento, pruebas de integración, verificación, instalación, despliegue y creación e implementación de sitios.

Para entender la diferencia entre Maven y una herramienta de compilación tradicional, considere el proceso de desarrollar un archivo JAR y un archivo EAR. Al usar Ant, usted h necesidad de definir tareas específicas para ensamblar cada artefacto. Maven, por otra parte, hace la mayoría del trabajo por usted: usted simplemente le dice si el proyecto es un archivo JAR o EAR y luego le da instrucciones para que procese la fase de "paquete". Maven encontrará los recursos requeridos y dirección IP desarrollará los archivos.

Usted encontrará gran cantidad de tutoriales de introducción para iniciarse en Maven, incluyendo algunos listados en la sección Recursos de este artículo. Los cinco consejos listados aquí tienen por objeto ayudarle con lo que sigue: los escenarios de programación que surgen cuando se usa Maven para manejar el ciclo de vida de sus aplicaciones.

1. Archivos JAR ejecutables

Desarrolle habilidades de este tema

Este contenido es parte de un knowledge path progresivo para avanzar en sus habilidades. Vea Conviértase en desarrollador Java

Desarrollar un archivo JAR con Maven es bastante fácil: simplemente defina el empaquetamiento del proyecto como "jar" y luego ejecute la fase de ciclo de vida del paquete. Pero definir un archivo JAR ejecutable es más complicado. Hacer esto de forma efectiva incluye los siguientes pasos:

  1. Defina una clase main en su archivo MANIFEST.MF de JAR que define la clase ejecutable. (MANIFEST.MF es el archivo que Maven genera cuando empaqueta su aplicación).
  2. Encuentre todas las bibliotecas de las cuales depende su proyecto.
  3. Incluya esas bibliotecas en su archivo MANIFEST.MF de manera que las clases de su aplicación puedan encontrarlas.

Es posible hacer todo esto manualmente, o puede hacerlo con mayor eficiencia con la ayuda de dos plug-ins Maven: maven-jar-plugin y maven-dependency-plugin.

maven-jar-plugin

El maven-jar-plugin realiza varias funciones, pero aquí nos interesa usarlo para modificar el contenido de un archivo MANIFEST.MF. En la sección plug-ins de su archivo POM, añada el código mostrado en el Listado 1:

Listado 1. Usando un maven-jar-plugin para modificar MANIFEST.MF
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.mypackage.MyClass</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

Todos los plug-in Maven exponen su configuración mediante un elemento <configuration> . En este ejemplo, el comando maven-jar-plugin modifica su atributo archive y específicamente el atributo manifest del archivo, el cual controla el contenido del archivo MANIFEST.MF. Este incluye tres elementos:

  • addClassPath: Configurar este elemento como true le dice al maven-jar-plugin que añada un elemento Class-Path al archivo MANIFEST.MF y que incluya todas las dependencias de ese elemento Class-Path .
  • classpathPrefix: Si planea incluir todas sus dependencias en el mismo directorio que el JAR que está construyendo, entonces es posible omitir este elemento; en caso contrario use classpathPrefix p ara especificar los prefijos de todos los archivos JAR dependientes. En el Listado 1, classpathPrefix especifica que todas las dependencias deben estar ubicadas en una carpeta "lib" relativa al archivo.
  • mainClass: Use este elemento para definir el nombre de la clase a ejecutar cuando el usuario ejecute el archivo JAR con un comando java -jar .

maven-dependency-plugin

Una vez que haya configurado el archivo MANIFEST.MF con estos tres elementos, su siguiente paso es copiar efectivamente todas las dependencias en la carpeta lib .Para esto, usted utiliza maven-dependency-plugin, como se muestra en el Listado 2:

Listado 2. Usando el maven-dependency-plugin para copiar dependencias en lib
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>install</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                              ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

El maven-dependency-plugin tiene una meta copy-dependencies que copiará sus dependencias en el directorio de su elección. En este ejemplo, copié las dependencias en el directorio lib bajo el directorio build (project-home/target/lib).

Con sus dependencias y el MANIFEST.MF modificado listos, es posible iniciar la aplicación con un comando simple:

java -jar jarfilename.jar

2. Personalizando el MANIFEST.MF

Aunque maven-jar-plugin le permite modificar porciones comunes de un archivo MANIFEST.MF , en algunas ocasiones es necesario un MANIFEST.MF más personalizado. La solución a esto es duplicar:

  1. Defina todas sus configuraciones personalizadas en un archivo "plantilla" MANIFEST.MF.
  2. Configure el maven-jar-plugin para que use su archivo MANIFEST.MF y auméntelo con cualquier personalización Maven.

Como ejemplo, considere un archivo JAR que contenga un agente Java. Para que un agente Java se ejecute, este necesita definir una Premain-Class y permisos. El Listado 3 muestra el contenido de dicho archivo MANIFEST.MF:

Listado 3. Definición de Premain-Class en un archivo MANIFEST.MF personalizado
Manifest-Version: 1.0
Premain-Class: com.geekcap.openapm.jvm.agent.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true

Enel Listado 3 especifiqué que a la Premain-Classcom.geekcap.openapm.jvm.agent.Agent se le concederá permiso para redefinir y retransformar clases. Luego, actualizo el maven-jar-plugin para incluir el archivo ANIFEST.MF, como se muestra en el Listado 4:

Listado 4. Incluyendo Premain-Class
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestFile>
                          src/main/resources/META-INF/MANIFEST.MF
                        </manifestFile>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>
                              com.geekcap.openapm.ui.PerformanceAnalyzer
                            </mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

Este es un ejemplo interesante porque define tanto una Premain-Class que permite al archivo JAR trabajar como un agente JAVA, como una mainClass que le permite ejecutarse como un archivo JAR ejecutable. En este ejemplo en particular utilicé OpenAPM (una herramienta para rastreo de código que construí) para definir rastreo de código que será grabado por el agente Java y una interfaz de usuario, lo cual facilitará el análisis de las pistas grabadas. En pocas palabras, este ejemplo muestra el poder de combinar un archivo manifiesto explícito con modificaciones dinámicas.

3. Árboles de dependencia

Uno de los recursos más útiles de Maven es su soporte para gestión de dependencia: usted simplemente define las bibliotecas de las cuales depende su aplicación y Maven las localiza (bien sea en su repositorio local o en uno central), las descarga y las utiliza para compilar su código.

Eventualmente, es posible necesitar conocer el origen de una dependencia en particular — tal como si usted necesitase encontrar versiones diferentes e incompatibles del mismo archivo JAR en su desarrollo. En este caso, usted necesitará evitar que una versión del archivo JAR sea incluida en su desarrollo, pero es necesario primero ubicar la dependencia que contiene el JAR.

Ubicar dependencias resulta ser algo sorprendentemente fácil una vez que usted conoce el siguiente comando:

mvn dependency:tree

El argumento dependency:tree muestra todas sus dependencias directas y luego muestra todas las sub-dependencias (y sus sub-dependencias, etc.). Por ejemplo, el Listado 5 es un fragmento de una biblioteca cliente requerida por una de mis dependencias:

Listado 5. Árbol de dependencia Maven
[INFO] ------------------------------------------------------------------------
[INFO] Building Client library for communicating with the LDE
[INFO]    task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.lmt.pos:sis-client:jar:2.1.14
[INFO] +- org.codehaus.woodstox:woodstox-core-lgpl:jar:4.0.7:compile
[INFO] |  \- org.codehaus.woodstox:stax2-api:jar:3.0.1:compile
[INFO] +- org.easymock:easymockclassextension:jar:2.5.2:test
[INFO] |  +- cglib:cglib-nodep:jar:2.2:test
[INFO] |  \- org.objenesis:objenesis:jar:1.2:test

Es posible ver en el Listado 5 que el proyecto sis-client requiere las bibliotecaswoodstox-core-lgpl y easymockclassextension . La biblioteca easymockclassextension , a su vez, requiere de las bibliotecas cglib-nodep y objenesis .Si yo estuviera teniendo problemas con objenesis, como tener dos versiones, 1.2 y 1.3, entonces este árbol de dependencia me mostraría que el artefacto 1.2 estaba siendo importado directamente por la biblioteca easymockclassextension .

El argumento dependency:tree me ha ahorrado muchas horas de depuración de desarrollos defectuosos. Espero que haga lo mismo por usted.

4. Usando perfiles

Los proyectos más sustanciales tienen al menos un grupo principal de entornos que consisten en tareas relacionadas con el desarrollo, el aseguramiento de la calidad (QA), la integración y la producción. El desafío de administrar todos estos entornos está en configurar su desarrollo, el cual necesita conectarse con la base de datos correcta, ejecutar el conjunto correcto de scripts, e desplegar los artefactos adecuados para cada entorno. Usar perfiles Maven le permite hacer todo esto sin tener que desarrollar instrucciones explícitas para cada entorno de manera individual.

El secreto está en combinar perfiles de entorno con los que están orientados a tareas. Cada perfil de entorno define sus ubicaciones, scripts y servidores específicos(as). Así, en mi archivo pom.xml, definiría el perfil orientado a tareas "deploywar" como se muestra en el Listado 6:

Listado 6. Un perfil de implementación
    <profiles>
        <profile>
            <id>deploywar</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>net.fpic</groupId>
                        <artifactId>tomcat-deployer-plugin</artifactId>
                        <version>1.0-SNAPSHOT</version>
                        <executions>
                            <execution>
                                <id>pos</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>deploy</goal>
                                </goals>
                                <configuration>
                                    <host>${deploymentManagerRestHost}</host>
                                    <port>${deploymentManagerRestPort}</port>
                                    <username>${deploymentManagerRestUsername}</username>
                                    <password>${deploymentManagerRestPassword}</password>
                                    <artifactSource>
                                      address/target/addressservice.war
                                    </artifactSource>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

Este perfil, identificado con la ID "deploywar", ejecuta el tomcat-deployer-plugin, el cual es configurado para conectarse a un host y puerto específicos y a credenciales específicas de nombre de usuario y contraseña. Toda esta información se define usando variables, como ${deploymentmanagerRestHost}. Estas variables están definidas en mi archivo profiles.xml bajo una dinámica 'por entorno' como se muestra en el Listado 7:

Listado 7. profiles.xml
        <!-- Defines the development deployment information -->
        <profile>
            <id>dev</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>dev</value>
                </property>
            </activation>
            <properties>
                <deploymentManagerRestHost>10.50.50.52</deploymentManagerRestHost>
                <deploymentManagerRestPort>58090</deploymentManagerRestPort>
                <deploymentManagerRestUsername>myusername</deploymentManagerRestUsername>
                <deploymentManagerRestPassword>mypassword</deploymentManagerRestPassword>
            </properties>
        </profile>

        <!-- Defines the QA deployment information -->
        <profile>
            <id>qa</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>qa</value>
                </property>
            </activation>
            <properties>
                <deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost>
                <deploymentManagerRestPort>58090</deploymentManagerRestPort>
                <deploymentManagerRestUsername>
                  myotherusername
                </deploymentManagerRestUsername>
                <deploymentManagerRestPassword>
                  myotherpassword
                </deploymentManagerRestPassword>
            </properties>
        </profile>

Implementando perfiles Maven

En el archivo profiles.xml del Listado 7, definí dos perfiles y los activé con base en el valor de la propiedad env (entorno). Si la propiedadenv estuviese configurada como dev , entonces se utilizaría la información de implementación de desarrollo. Si la propiedad env estuviese configurada como qa, entonces se utilizaría la información de implementación de QA, etc.

Este es el comando para desplegar el archivo:

mvn -Pdeploywar -Denv=dev clean install

La banderilla -Pdeploywar le dice a Maven que incluya explícitamente el perfil deploywar . El enunciado -Denv=dev crea una propiedad de sistema llamada env y establece su valor como dev, lo cual activa la configuración de desarrollo. Pasar -Denv=qa activará la configuración de QA.

5. Plug-ins Maven personalizados

Maven pone a su disposición docenas de plug-ins pre-compilados, pero en algún momento usted necesitará un plug-in personalizado. Compilar un plug-in Maven es algo simple:

  1. Cree un nuevo proyecto con el paquete POM configurado como "maven-plugin."
  2. Incluya una invocación del maven-plugin-plugin que defina sus metas de plug-in expuestas.
  3. Cree una clase Maven plug-in "mojo" (una clase que extiende AbstractMojo).
  4. Anote los comentarios Javadoc para la clase, para definir metas y para las variables que se expondrán como parámetros de configuración.
  5. Implemente un método execute() que será invocado cuando se invoque su plug-in.

A manera de ejemplo, el Listado 8 muestra porciones relevantes de un plug-in personalizado diseñado para desplegar Tomcat:

Listado 8. TomcatDeployerMojo.java
package net.fpic.maven.plugins;

import java.io.File;
import java.util.StringTokenizer;

import net.fpic.tomcatservice64.TomcatDeploymentServerClient;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import com.javasrc.server.embedded.CommandRequest;
import com.javasrc.server.embedded.CommandResponse;
import com.javasrc.server.embedded.credentials.Credentials;
import com.javasrc.server.embedded.credentials.UsernamePasswordCredentials;
import com.javasrc.util.FileUtils;

/**
 * Goal that deploys a web application to Tomcat
 *
 * @goal deploy
 * @phase install
 */
public class TomcatDeployerMojo extends AbstractMojo
{
	/**
	 * The host name or IP address of the deployment server
	 * 
	 * @parameter alias="host" expression="${deploy.host}" @required
	 */
	private String serverHost;
	
	/**
	 * The port of the deployment server
	 * 
	 * @parameter alias="port" expression="${deploy.port}" default-value="58020"
	 */
	private String serverPort;

	/**
	 * The username to connect to the deployment manager (if omitted then the plugin
	 * attempts to deploy the application to the server without credentials)
	 * 
	 * @parameter alias="username" expression="${deploy.username}"
	 */
	private String username;

	/**
	 * The password for the specified username
	 * 
	 * @parameter alias="password" expression="${deploy.password}"
	 */
	private String password;

	/**
	 * The name of the source artifact to deploy, such as target/pos.war
	 * 
	 * @parameter alias="artifactSource" expression=${deploy.artifactSource}" 
	 * @required
	 */
	private String artifactSource;
	
	/**
	 * The destination name of the artifact to deploy, such as ROOT.war. 
	 * If not present then the
	 * artifact source name is used (without pathing information)
	 * 
	 * @parameter alias="artifactDestination" 
	 *   expression=${deploy.artifactDestination}"
	 */
	private String artifactDestination;
	
    public void execute() throws MojoExecutionException
    {
    	getLog().info( "Server Host: " + serverHost + 
    			       ", Server Port: " + serverPort + 
    			       ", Artifact Source: " + artifactSource + 
    			       ", Artifact Destination: " + artifactDestination );
    	
    	// Validate our fields
    	if( serverHost == null )
    	{
    		throw new MojoExecutionException( 
    		  "No deployment host specified, deployment is not possible" );
    	}
    	if( artifactSource == null )
    	{
    		throw new MojoExecutionException( 
    		  "No source artifact is specified, deployment is not possible" );
    	}

        ...
   }
}

En el encabezado de clase, el comentario @goal especifica la meta que ejecuta este MOJO y @phase especifica la fase en la cual se ejecuta la meta. Cada propiedad expuesta tiene una anotación @parameter que especifica el alias mediante el cual se ejecutará el parámetro, además de una expresión que correlaciona hacia una propiedad de sistema que contiene el valor efectivo. Si la propiedad tiene una anotación @required , entonces es requerida. Si tiene una default-value, entonces ese valor se utilizará si no se especifica ninguno. En el método execute() es posible invocargetLog() para tener acceso al registrador Maven el cual, dependiendo del nivel de registro cronológico, arrojará el mensaje especificado hacia el dispositivo de salida estándar. Si el plug-in falla, arrojar una MojoExecutionException causará que el desarrollo falle.

En conclusión

Es posible usar Maven solo para las compilaciones, pero usada al máximo Maven es una herramienta de gestión de ciclo de vida de proyecto. Este artículo presentó cinco recursos menos conocidos que pueden ayudarle a utilizar Maven con mayor efectividad. Consulte la sección Recursos para aprender más acerca de Maven.

A continuación en la serie 5 cosas mostraremos cinco consejos para compilar bellas interfaces de usuario en Swing, así que permanezca en sintonía.


Recursos para Descargar


Temas relacionados

  • "Introduction to Apache Maven 2" (Sing Li, developerWorks, diciembre del 2006): Familiarícese con las habilidades fundamentales requeridas para trabajar en proyectos compilados usando Maven 2, comenzando con este tutorial developerWorks.
  • "Managing Java Build Lifecycles with Maven" (Steven Haines, InformIT.com, julio del 2009): Este tutorial describe los fundamentos de Maven, la arquitectura detrás de esta y cómo utilizarla para desarrollar ciclos de vida para sus proyectos Java.
  • Maven: The Complete Reference, Edition 0.7 (Tim O'Brien, et al.; Sonatype 2010): Este libro gratuito online es un recurso excelente para aprender Maven, incluyendo consejos para el uso del Maven 3 próximo. La editorial SonaType es un proveedor de software que desarrolla uno de los proyectos más populares de repositorio Maven, Nexus.
  • Maven 3 release notes: Conozca acerca de los cambios realizados a Maven en su versión 3, incluyendo mejoras en su facilidad de uso y su desempeño.
  • Página de inicio de Maven: Descargue Maven y aprenda más acerca de Apache Software Foundation.

Comentarios

Inicie Sesión o Regístrese para agregar comentarios.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=842922
ArticleTitle=5 cosas que usted no sabía acerca de...Apache Maven
publish-date=10292012