Maven, nunca antes resultó tan fácil compilar, empaquetar, …

24
154459

Creación: 16-09-2006

Índice de contenidos

1. Introducción
2. Entorno
3. Instalación de Maven
4. Creando proyectos y los arquetipos
5. El fichero pom.xml
6. Como compilar,empaquetar, …
7. El repositorio de Maven
8. Creando el proyecto Web
9. Definiendo dependencias
10. Un pom.xml para controlarlos a todos
11. Configurando los plugins de Maven y heredando la configuración
12. Creación de perfiles
13. Como publicar una nueva “release” de nuestro proyecto
14. Conclusiones
15. Sobre el autor

1. Introducción

Maven (http://maven.apache.org) es una herramienta para la gestión de proyectos de software, que se basa en el concepto de POM (Proyect Object Model). Es decir, con Maven vamos a poder compilar, empaquetar, generar documentación, pasar los test, preparar las builds, …

Ahora muchos pensaréis que eso ya los sabemos hacer con Ant (http://ant.apache.org), pero no debemos confundirnos, ya que Maven y Ant son cosas totalmente diferentes. Algunas de las principales ventajas de Maven frente a Ant, podrían ser:

  • Maven se basa en patrones y en estándares. Esto permite a los desarrolladores moverse entre proyectos y no necesitan aprender como compilar o empaquetar. Esto mejora el mantenimiento y la reusabilidad.
  • Mientras que con Ant escribimos una serie de tareas, y unas dependencias entre estas tareas; con el POM hacemos una descripción del proyecto. Es decir, decimos de que se compone nuestro proyecto (nombre, versión, librerías de las que depende, …), y Maven se encargará de hacer todas las tareas por nosotros.
  • Maven hace la gestión de librerías, incluso teniendo en cuenta las dependencias transitivas. Es decir, si A depende de B y B depende de C, es que A depende de C. Esto quiere decir que cuando empaquetemos A, Maven se encargará de añadir tanto B como C en el paquete.

En cualquier caso no tienen por que ser herramientas enfrentadas, sino que pueden ser complementarias. Usando cada una según nos interese.

En este tutorial vamos a ver como trabajar con Maven, y como explotar algunas de sus características. El caso de ejemplo que vamos a utilizar será un proyecto java normal que llamaremos autentiaNegocio y que depende del driver de MySQL (http://www.mysql.com) para poder acceder a la base de datos, y un proyecto web que depende de “autentiaNegocio” y que llamaremos “autentiaWeb”.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Ahtec Signal 259M MXM (Sonoma 2.1
    GHz, 2048 MB RAM, 100 GB HD).
  • Sistema Operativo: GNU / Linux, Debian Sid (unstable), Kernel
    2.6.17, KDE 3.5
  • Máquina Virtual Java: JDK 1.5.0_08 de Sun Microsystems
  • Maven 2.0.4

3. Instalación de Maven

Debemos descargarnos el paquete de Maven (por ejemplo maven-2.0.4-bin.tar.bz2) de
http://maven.apache.org/download.html#installation (es recomendable elegir un mirror para la descarga).

Una vez descargado, tenemos que descomprimirlo en nuestro ordenador. Por ejemplo, suponiendo que nos hemos descargado el paquete de Maven en /download, podemos hacer:

Esto nos creará el directorio /opt/maven-2.0.4.

Ahora basta con añadir el directorio /opt/maven-2.0.4/bin al PATH del sistema:

Para comprobar que está correctamente instalado podemos probar a ejecutar:

Si tenemos algún problema podemos comprobar si la variable JAVA_HOME apunta correctamente a la localización donde está instalada nuestra JDK.

4. Creando proyectos y los arquetipos

Podríamos decir que un “arquetipo” para Maven es una plantilla. Es decir, gracias a un arquetipo Maven es capaz de generar una estructura de directorios y ficheros.

Con los arquetipos se acabo “el miedo al folio en blanco” a la hora de empezar un proyecto, ya que basta con decirle a Maven que tipo de proyecto queremos y nos creará la estructura base.

Por ejemplo, para crear nuestro proyecto java basta con hacer:

Donde groupId es el identificador único de la organización o grupo que crea el proyecto (se podría decir que es el identificador de la aplicación), y artifactId es el identificador único del artefacto principal de este proyecto (se podría decir que es el identificador del módulo
dentro de la aplicación), es decir, este será el nombre del jar.

En el comando no ha hecho falta decir que queremos construir un proyecto java, ya que esta es la opción por defecto.

El resultado de ejecutar este comando es la siguiente estructura de directorios y ficheros:

 

Esta estructura de directorios es estándar, siendo la misma en todos los proyectos. Maven nos permite cambiar esta estructura, pero no sería recomendable ya que el hecho de que sea estándar permite a los desarrolladores moverse entre proyectos con mayor comodidad, ya que siempre sabrán donde encontrar las cosas. Para más información sobre la estructura de directorios se puede consultar http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

5. El fichero pom.xml

El fichero pom.xml es donde vamos a describir nuestro proyecto.
Vamos a echar un vistazo al fichero pom.xml que nos ha generado Maven:

Con packaging se indica el tipo de empaquetado que hay que hacer con el proyecto. Podemos usar jar, war, ear, pom.

Con version se indica la versión del proyecto con la que estamos trabajando. Al indicar SNAPSHOT se quiere decir que es una versión evolutiva, es decir que estamos trabajando para obtener al versión 1.0.

También podemos ver como dentro de dependecies se describen las dependencias del proyecto. Ahora tenemos una dependencia de junit para poder compilar y ejecutar los test. Dentro de la descripción de las dependencias es interesante destacar el elemento scope que indica que tipo de librería se trata. Podemos distinguir:

  • compile – es el valor por defecto. Se utiliza en todos los casos (compilar, ejecutar, …).
  • provided – también se utiliza en todos los casos, pero se espera que el jar sea suministrado por la JDK o el contenedor. Es decir, no se incluirá al empaquetar el proyecto, ni en el repositorio.
  • runtime – no se utiliza para compilar, pero si es necesario para ejecutar.
  • test – Sólo se utiliza para compilar o ejecutar los test.
  • system – es similar a provided, pero eres tu el que tiene que suministrar el jar. No se incluirá al empaquetar el proyecto, ni en el repositorio.

Para saber más se puede consultar http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

6. Como compilar, empaquetar, …

Ahora mismo, sin haber escrito ni una línea ya podemos hacer todas las tareas habituales (esto con Ant no sería tan fácil ;):

  • $ mvn compile – compila el proyecto y deja el resultado en target/classes
  • $ mvn test – compila los test y los ejecuta
  • $ mvn package – empaqueta el proyecto y lo dejará en taget/autentiaNegocio-1.0-SNAPSHOT.jar
  • $ mvn install – guarda el proyecto en el repositorio
  • $ mvn clean – borra el directorio de salida (target)

Estas tareas son estándar, por lo que un desarrollador puede saltar de un proyecto a otro y siempre sabrá compilar, empaquetar, …

Maven define un ciclo de vida por lo que al ejecutar un objetivo, antes se ejecutan los sus antecesores en el ciclo de vida. Por ejemplo, si ejecutamos package, antes se ejecutará compile y test.

Para saber más sobre el ciclo de vida de Maven se puede leer http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

7. El repositorio de Maven

Ya hemos hablado en un par de ocasiones del “repositorio”. Maven guarda todas las dependencias y proyectos en un único repositorio.

Este repositorio está situado en <USER_HOME>/.m2/repository (aunque Maven nos permite cambiar esta localización por defecto.

También existe un repositorio remoto desde el cual se descargan los diferentes jars según los vamos necesitando. Esto ya lo habréis notado al ejecutar los primeros comando de Maven; seguro que habéis visto como Maven se ponía a descargar varias cosas (ojo, si salís a Internet a traves de un proxy, lo tendréis que configurar en Maven http://maven.apache.org/guides/mini/guide-proxies.html).

Este repositorio remoto también podemos cambiarlo, e incluso podemos tener más de uno. Sería muy interesante que dentro de nuestra organización tuviéramos un repositorio remoto donde publicar todos nuestros proyectos.

Para saber más podéis leer http://maven.apache.org/guides/introduction/introduction-to-repositories.html

En general esta idea es bastante buena porque no hace falta que tengamos los jar duplicados en el sistema de control de versiones, dentro de cada proyecto. Sobre todo con los jar de terceros, ya que ocupan espacio y es algo que no tiene utilidad que versionemos (lo interesante es saber en cada versión de nuestro proyecto que dependencias tenía pero no versionar la dependencia en sí).

Además el repositorio de Maven guardar las diferentes versiones de cada proyecto o dependencia, con lo cual podemos actualizar una dependencia de un proyecto a una versión superior, sin que afecte al resto de nuestros proyectos (que seguirán usando la versión anterior).

8. Creando el proyecto Web

Basándonos en los arquetipos, basta con ejecutar:

Como antes, indicamos el groupId y el artifactId, pero esta vez también indicamos el archetypeArtifactId. En este último atributo, con el valor maven-archetype-webapp, estamos indicando que queremos usar la plantilla de aplicaciones web.

A parte de los arquetipos que nos proporciona Maven, es interesante saber que podemos creara nuestros propios arquetipos: http://maven.apache.org/guides/mini/guide-creating-archetypes.html

Echemos un vistazo al pom.xml generado:

Destacaremos como en este caso el elemento packaging es igual a war, y la aparición del elemento finalName.

Con finalName estamos indicando el nombre del archivo que se genera al empaquetar, en este caso el nombre del archivo war. En el proyecto anterior no usamos este elemento porque queríamos el comportamiento por defecto: el nombre del archivo se genera como el artifactId + version. Pero este comportamiento no nos suele interesar en un archivo war, porque sino al desplegar la aplicación habría que poner la versión de la aplicación en la URL para acceder a ella.

9. Definiendo dependencias

Hasta ahora no hemos escrito ni una sola línea, bueno ya es hora de ponerse a trabajar y hacer algo 😉

Vamos a indicar que el proyecto autentiaNegocio depende del driver de MySQL. Para ello basta con editar el fichero autentiaNegocio/pom.xml y añadir:

Nótese como se indica scope runtime, es decir este jar sólo se tendrá en cuenta a la hora de ejecutar, pero no se usará para compilar.

Para indicar que autentiaWeb depende de autentiaNegocio, habrá que añadir al fichero autentiaWeb/pom.xml:

Ahora al empaquetar autentiaWeb, veremos que en el directorio WEB-INF/lib se ha añadido el jar de autentiaNegocio, y el jar del driver de MySQL, ya que este último es dependencia de autentiaNegocio.

10. Un pom.xml para controlarlos a todos

En este punto podemos construir cada proyecto con sus dependencias correctas, pero tenemos las restricción de que tenemos que construir primero autentiaNegocio (hay que hacer install para que el jar se guarde en el repositorio) y luego autentiaWeb, porque si lo hacemos al contrario no se encontrará la dependencia correctamente.

Imaginemos que tenemos ahora 20 proyectos con dependencias cruzadas, la situación se hace inmanejable. Pero Maven ha pensado en todo. Lo que necesitamos es un pom.xml que los controle a todos.

Este pom.xml se suele colocar en directorio padre de los proyectos (en nuestro caso autentiaNegocio y autentiaWeb), pero yo lo voy a colocar en el directorio “autentiademoapp”. Este directorio está a la misma altura que autentiaNegocio y autentiaWeb (podríamos decir que son hermanos). Esto lo hago para poder gestionarlo todo más fácilmente cuando esté trabajando con el Eclipse y el sistema de control de versiones (Eclipse sólo puede interactuar con el sistema de control de versiones a través de proyectos o carpetas del workspace).

Este pom.xml tendrá el siguiente aspecto:

Nótese como el valor del elemento packaging es pom. Vease también como dentro del elemento modules se listan todos los módulos que forman parte de la aplicación.

Al ejecutar cualquier objetivo sobre este pom.xml, Maven se encarga de ejecutarlo sobre todos los módulos, pero tiene en cuenta las dependencias entre ellos, de forma que compilará primero la dependencia y luego al dependiente.

Para terminar vamos a modificar el pom.xml de autentiaNegocio y autentiaWeb para indicar cual es su proyecto padre. Sí, en Maven podemos establecer “herencia” entre proyectos, esto nos va a resultar muy útil para definir configuración compartida por los proyectos. Basta con añadir en cada pom.xml “hijo”:

11. Configurando los plugins de Maven y heredando la configuración

En ciertas ocasiones nos puede interesar cambiar la manera en que Maven hace las cosas. Para ello podemos redefinir las propiedades usadas por Maven. Por ejemplo vamos ha cambiar las propiedades para compilar nuestros proyectos con la versión 1.5 de Java.

Basta con añadir lo siguiente al fichero autentiademoapp/pom.xml (el pom “padre”):

Como este pom.xml está definido como padre del resto de proyectos, todos “heredarán” esta configuración.

12. Creación de perfiles

Maven nos permite crear diferentes perfiles para la construcción de nuestros proyectos. El perfil nos va a permitir definir propiedades concretas para la construcción del proyecto en distintas situaciones. Por ejemplo, si estamos construyendo el proyecto para un entorno de producción, pruebas o desarrollo,

Vamos a ver un ejemplo en el que crearemos un perfil para construir el proyecto sin información de depuración y con optimización. Basta con añadir lo siguiente al fichero autentiademoapp/pom.xml (el pom “padre”):

Para activar un perfil existen varias formas (el perfil puede estar activo siempre, en función de si está definida una variable de entorno, dependiendo de la versión de JDK que tengamos instalada, con la opción -P, …). En nuestro caso cuando queramos construir el proyecto con este perfile ejecutaremos:

13. Como publicar una nueva “release” de nuestro proyecto

Dentro de Maven tenemos un plugin que nos gestiona la elaboración de nuevas release.

Antes de hacer nada vamos a configurar el proyecto para decirle con que sistema de control de versiones tiene que trabajar. Editamos autentiaNegocio/pom.xml y añadimos:

Con los elementos connection y developerConnection le estamos diciendo donde está el proyecto dentro del sistema de control de versiones (en nuestro caso el Subversion). Para saber como escribir esta URL consultar http://maven.apache.org/scm/scm-url-format.html

Con el elemento tagBase definimos donde se tienen que guardar los diferentes tags que hagamos del proyecto, dentro del sistema de control de versiones.

Con el elemento password definimos la clave para tiene que usar para conectar con el sistema de control de versiones (el usuario será el que estemos usando en el sistema, pero se le podría indicar otro con el elemento username). Se ve que, en vez de poner un valor fijo, se está usando una propiedad.

Para dar valor a la propiedad scm.password, podemos pasarla con -D al ejecutar mvn o definirla en el fichero de configuración del usuario. Vamos a usar este último método; para ello añadimos al fichero ~/.m2/settings.xml:

Se puede ver como estamos definiendo un perfil que está siempre activo, y en este perfil estamos definiendo la propiedad scm.password.

Ahora que ya hemos configurado el proyecto para que sepa donde está nuestro repositorio podemos hacer:

Este comando nos hace tres preguntas:

  • What is the release version for «Proyecto de prueba de Autentia: autentiaNegocio»? (com.autentia.demoapp:autentiaNegocio) 1.0: : Nos pregunta cual es la versión de la release que estamos publicando. Por defecto Maven quita “-SNAPSHOT” a la versión que tenemos definida en el pom.xml en la etiqueta version.
  • What is SCM release tag or label for «Proyecto de prueba de Autentia: autentiaNegocio»? (com.autentia.demoapp:xx) xx-1.0: : Nos pregunta como queremos llamar a la etiqueta que se creará en el sistema de control de versiones. Por defecto Maven añade como prefijo el artifactId del proyecto a lo contestado en la pregunta anterior.
  • What is the new development version for «Proyecto de prueba de Autentia: autentiaNegocio»? (com.autentia.demoapp:autentiaNegocio) 1.1-SNAPSHOT: : Nos pregunta como queremos que se llame la nueva versión sobre la que seguiremos trabajando. Por defecto Maven incrementa el último dígito de la versión que tenemos definida en el pom.xml en la etiqueta version.

En la mayoría de los casos podemos contestar a estas tres preguntas simplemente pulsando “Intro” y que coja los valores por defecto.

Una vez terminada la ejecución ya se habrá creado la etiqueta en el sistema de control de versiones. Ahora sólo nos queda ejecutar:

Con esto Maven se baja la versión que acabamos de etiquetar, la compila, le pasa los test, hace un jar con los fuentes, hace un jar con el javadoc y lo publica todo en el repositorio. También lo publica en el repositorio remoto (en nuestro caso, como no hemos definido un repositorio remoto nos dará un error). Es decir, la deja lista para que la utilice el resto del equipo.

14. Conclusiones

Como véis, con un mínimo esfuerzo por nuestra parte hemos conseguido mucho. Pero lo mejor de todo no es el haber escrito poco (que ya es una ganancia), lo mejor es estandarizar este tipo de procesos.

Con Ant todo el mundo se acababa definiendo más o menos las mismas tareas, pero cada vez que se iba a un nuevo proyecto había que aprender que tareas concretas estaban definidas y como se comportaban estas tareas.

Otra de las grandes ventajas es la gestión de dependencias. Para hacer lo mismo con Ant nos tocaba escribir bastante.

Desde Autentia (http://www.autentia.com) siempre os recomendamos la adopción de estándares, así que esta no podía ser una excepción.

15. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software)

Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

http://www.autentia.com

24 Comentarios

  1. Para el resto de la gente que sigue este tutorial. Me ha pasado que en el punto 10. no podía relacionar el proyecto hijo con el padre, me daba un error de relativePath cuando compilaba. Esto se soluciona poniendo:

    com.autentia.demoapp
    autentiademoapp
    1.0-SNAPSHOT
    ../autentiademoapp/pom.xml

    en cada uno de los hijos.
    Quizás la diferencia sea por la versión de Maven, yo estoy usando la 3.0

  2. Alex, una errata localizada, revisa la entrada developerConnection del pom.xml (en el punto 13) y la verás tú mismo.

    He vuelto a leer de nuevo el tutorial y está de lujo, muy buen trabajo.

  3. Una cosa para crear los miniproyectos hijos no es necesario que esten en la misma maquina del padre verdad? Es decir yo puedo tenerlos en red?

    Cuento con un servidor en internet y he estado haciendo pruebas ahi, me gustaria que mis programadores pudieran subir sus codigos como proyectos hijos de mi proyecto padre, para que en un momento dado no puedan accesar a todo el cogido pero que si lo puedan estar compilando y juntando a mi proyecto, es esto posible con Maven?

  4. Gracias por el tutorial, tengo una pregunta, tengo un proyecto padre con 3 perfiles (desarrollo, test, producción) y un proyecto hijo que dependiendo del perfil con que se compila setea unas propiedades, quisiera saber si puedo de alguna forma determinar el nombre final del jar que se copia al repositorio local del maven (/home/diego/.m2)
    por ejemplo quisiera que cuando compile con el perfil de test se copie al m2 con:
    tes-registro-1.0.jar
    si lo hago con perfil de producción:
    prod-registro-1.0.jar

    Muchas gracias por cualquier ayuda y gracias por el tutorial, saludos

    • Hola Diego,

      te lo estoy diciendo un poco de memoria, pero sí que puedes.

      Existe un elemento dentro del pom.xml que define el nombre final del artefacto que estás construyendo. El valor por defecto de este elemento es el siguiente:


      <build>
          <finalName>${project.artifactId}-${project.version}</finalName>

      Suponiendo que la propiedad que comentas se llamase «miEntorno», si quisieras añadir el valor de esta propiedad debería bastarte con hacer:


      <build>
          <finalName>${miEntorno}-${project.artifactId}-${project.version}</finalName>

      Saludo!

  5. Saludos

    Es posible conectar maven y postgres sin usar hibernate ni ningun otro framework de persistencia?

    Si es que es posible, como se realiza la conexion ?

    es decir como se especifica, el driver, la url, el usuario y la clave

    saludos

    • Hola Edgar,

      Maven y PosrgreSQL no tienen nada que ver, es decir Maven sirve para gestionar el ciclo de construcción nuestras aplicaciones (compilación, tests, empaquetado, subida a repositorio de los artefactos generados, …).

      Cuando en Maven especificamos el driver de PostgreSQL es para que, cuándo se construya nuestra aplicación, este driver vaya como una dependencia y nuestra aplicación lo pueda utilizar, pero Maven no se conecta a PostgreSQL.

  6. Buenas, excelente artículo, pero quisiera saber TODOS los atributos que puede tener el pom.xml y una descripcion de cada, donde puedo verlo, si es posible en español, aunque no es imprescindible.
    Gracias

  7. Hola, antes de usar este comando:
    mvn archetype:create -DgroupId=com.autentia.demoapp -DartifactId=autentiaNegocio

    Defines algun pom? debes estar en alguna carpeta en especifico? es necesario tener el proyecto autentiaNegocio?

    • No Iván, no hace falta que definas ningún pom, ni tener ninguna carpeta específica ni proyecto creado. Precisamente la gracia del «archetype» es que te va a generar de forma automática todos estos elementos.
      Eso sí, ojo porque el comando a día de hoy ha cambiado y en vez de archetype:create debes usar: archetype:generate

  8. Buenas, tengo un proyecto web services gestionado por módulos de maven. Justo como está aquí explicado es muy potente.. Os felicito por el tutorial me ha ayudado mucho.. Coincido en que esta página es una bendición :D.
    Ahora tengo la siguiente duda, si necesito acceder a un archivo de recursos desde un módulo x.. hacia el módulo principal (por ejemplo el xsd que está en el módulo web de mi webservices).. como lo puedo hacer.. por ahora sólo consigo llegar a los recursos del módulo desde donde lo llamo con el método getResource, pero a los recursos de los otros módulos.. en especial a la carpeta de (META-INF) no consigo acceder a ellos
    Alguna idea???
    De nuevo os agradezco infinitamente vuestra ayuda por la buena calidad de tutoriales, soys geniales.

    • Cuando tus módulos son módulos web se empaquetan en un war que contiene tanto el código Java como los recursos web (html, css, …), desde uno de estos módulos no puedes acceder directamente a los recursos de otro puesto que son módulos independientes.
      Para poder reutilizar recursos tienes varias alternativas:
      – Paquetizar estos recursos comunes en un jar poner este como dependencia de los módulos web y acceder a los recursos a través del classpath.
      – Hacer un módulo web común que sea padre de los otros móudlos web. Maven tiene una especia de «herencia» (https://maven.apache.org/plugins/maven-war-plugin/overlays.html) donde los módulos web hijos heredan todos los recursos definidos en el padre y además los puedes sobreescribir (si en el hijo hay un fichero con le mismo nombre, este tiene precedencia sobre el del padre).
      – Si sabes la localización física siempre podrías acabar accediendo por la ruta del fichero y hacer una especie de plugin de Maven que te copie los recursos de un módulo en otro.

      Espero que esto te de la pista para resolver tu problema.

Dejar respuesta

Please enter your comment!
Please enter your name here