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

Consejos para administrar el ciclo de vida de proyectos con Maven

Usted debe estar familiarizado(a) con los perfiles, pero ¿sabía usted que puede utilizarlos en Maven para ejecutar comportamientos específicos en diferentes entornos? Esta entrega de la serie 5 cosas va más allá de los recursos de desarrollo de Maven, e incluso de sus herramientas básicas para gestionar el ciclo de vida de los proyectos, al ofrecer cinco consejos que mejorarán la productividad y la facilidad con que usted administra aplicaciones en Maven.

Steven Haines, Fundador y CEO, GeekCap Inc.

Steven Haines es arquitecto técnico en ioko y es el fundador de GeekCap Inc. Ha escrito tres libros sobre programación Java y análisis de desempeño, así como cientos de artículos y una docena de White Papers. Steven también ha sido orador en conferencias de la industria como JBoss World y STPCon y antes enseñaba programación Java en la Universidad de California, Irvine y en la Learning Tree University. Vive cerca a Orlando, Florida.



29-10-2012

Acerca de esta serie

¿Así que usted considera que sabe acerca de programación Java? El hecho es que la mayoría de los desarrolladores rasguñan la superficie de la plataforma Java, aprendiendo apenas lo necesario para realizar su trabajo. En esta continuación de la serie, los sabuesos de la tecnología Java profundizan hacia el núcleo de la funcionalidad de la plataforma Java, revelando consejos y trucos que pueden ayudarle a resolver incluso los desafíos de programación más complicados.

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>

Maven 3

Maven 2 estableció su posición como una de las herramientas Java de fuente abierta más populares y mejor usadas para gestión de ciclo de vida. Maven 3, promovida a alpha 5 en septiembre del 2010, trae consigo algunos cambios ansiosamente esperados para Maven. Consulte la sección Recursos para conocer qué hay de nuevo en Maven 3.

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

Aprender

  • "5 cosas que usted no sabía acerca de... : Entérese de cuántas cosas no sabía usted acerca de la plataforma Java en esta serie dedicada a convertir trivialidades de la tecnología Java en útiles consejos de programación.
  • "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.
  • El área zona de tecnología developerWorks Java: Cientos de artículos acerca de cada aspecto de la programación Java.

Obtener los productos y tecnologías

Comentar

  • Participe en la Comunidad My developerWorks. Conéctese con otros usuarios de developerWorks mientras explora los blogs conducidos por desarrolladores, foros, grupos y wikis.

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


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