Maven es una herramienta que ayuda a desarrollar un proyecto basado en el entorno de una JDK (Java, Xtend, Scala, Groovy, etc.)
Maven cumple con las siguientes funciones principales que vamos a explicar en las siguientes secciones:
Java no trabaja la idea de proyecto, no lo representa como concepto. Entonces, cada uno de los IDEs pensados para Java agregan su propia forma de definirlo: en el caso de Eclipse tenemos
.classpath
que define los directorios donde compilar y las dependencias que necesita.project
que contiene el nombre del proyecto, entre otras cosasSi nosotros trabajamos con otro IDE (como IntelliJ IDEA o NetBeans) tenemos que adaptar estos archivos para generar el proyecto con sus dependencias adecuadamente. Maven permite trabajar entre IDEs con su propio modelo de proyecto, que se guarda en el archivo pom.xml
(de Project Object Model)
En el archivo pom se declaran, entre otras cosas, un identificador único de nuestro proyecto/artefacto, que resulta de la unión de tres identificadores:
org.uqbar
.commons-collections
, eg-seguros-xtend
, tp-futbol5-grupo01
, etc.RELEASE
(para versiones estables) o SNAPSHOT
(para versiones intermedias que pueden estar sujetas a cambios)A continuación un ejemplo básico.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.uqbar</groupId>
<artifactId>uqbar-commons</artifactId>
<version>1.1.2</version>
</project>
Cuando se publica un componente, se empaquetan todas las clases compiladas (
.class
) en un archivo comprimido que tiene la extensión.jar
(de Java Archive). Opcionalmente podemos tener un archivo comprimido extra con los fuentes.
Un repositorio Maven es un lugar donde están los artefactos Maven, estructurados en cierta forma estándar para hacer las descargas de las dependencias. Cuando instalamos Maven, se crea un repositorio Maven local en una carpeta que por defecto suele ser HOME/.m2
. Si queremos ubicar al componente cuyo identificador es org.eclipse.xtend:org.eclipse.xtend.core:2.21.0.M1
podremos encontrarlo localmente en
%M2_HOME%/repository
└── org
└── eclipse
└── xtend
└── org.eclipse.xtend.core
├─ 2.19.0 (otra versión)
└─ 2.21.0.M1 --> dentro de esta carpeta estará el .jar
HOME/.m2
con el nombre M2_HOME
M2_HOME
puede haber opcionalmente un archivo settings.xml
que veremos más adelanterepository
están todos los componentes que descargamos localmenteVemos un video de ejemplo, en una máquina Linux. El comportamiento en una máquina Windows es exactamente igual, hay que explorar los directorios incluyendo los que son ocultos, y navegar a partir de la carpeta de usuario.m2:
Es posible referenciar a otros proyectos maven desde un POM, esto es muy útil para cuando necesitamos
a la hora de compilar nuestro proyecto.
Las dependencias se definen dentro de un tag <dependencies>
:
<project ...>
<...>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
En este caso, estamos definiendo que nuestro proyecto tiene como pre-requisito el componente junit-jupiter-params
asociado a org.junit.jupiter
. Si utilizamos Maven como un plugin de Eclipse
Maven > Update project
)Al agregar una dependencia a un proyecto es posible especificar el type
(por ej., jar
), el scope
(por ejemplo: test), y si es o no optional
.
Otra variante es utilizar un proyecto padre mediante el tag <parent>
, como ocurría en versiones anteriores de los ejemplos de Uqbar hasta 2019:
<parent>
<groupId>org.uqbar-project</groupId>
<artifactId>uqbar-xtend-parent</artifactId>
<version>2.17.1</version>
</parent>
El parent project permite reutilizar definiciones comunes entre varios proyectos. En este caso particular, uqbar-xtend-parent (el nombre que le dimos a este artefacto) sirve para definir
Los plugins de Maven no solo permiten reutilizar lógica sino que además ejecutan acciones cuando son descargados. Así funciona el núcleo central de Maven: uno de los plugins más conocidos es clean
, que elimina el directorio de destino (en el caso de Xtend, donde están los archivos Java y los .class que se generan a partir de los fuentes originales). Otro plugin conocido es Surefire
, que ejecuta los tests de un proyecto basado en JDK.
Distintos proyectos maven requieren/ofrecen distintos settings al ser referenciados como plugins. Veamos un ejemplo de la configuración del plugin de xtend
:
<plugins>
<plugin>
<groupId>org.eclipse.xtend</groupId>
<artifactId>xtend-maven-plugin</artifactId>
<version>2.20.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/xtend-gen/main</outputDirectory>
<testOutputDirectory>${project.build.directory}/xtend-gen/test</testOutputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Aquí le estamos indicando a maven las características de la ejecución del plugin, es decir:
compile
y testCompile
.
Lo recomendable en cada caso es siempre revisar la documentación oficial del proyecto maven que queremos referenciar, para entender qué settings son requeridos o convenientes para nuestro proyecto.
Un detalle no menor de la resolución de dependencias de maven es que también funciona para las dependencias transitivas.
Por ejemplo:
Al resolver las dependencias, el proyectoA necesitará descargar los componentes B, C, D, E y F. Incluso podríamos requerir diferentes versiones de los mismos componentes.
Noten que un proyecto comercial “normal” o mediano, puede incluir decenas y hasta cientos de dependencias. Eclipse permite integrar todas las definiciones de un pom junto con sus parents en la solapa Effective POM
:
La pregunta que puede hacerse el lector es: ¿desde dónde se descargan los componentes la primera vez? ¿Cómo es que Maven sabe dónde ubicarlos, y cómo saber a qué versión apuntar? Por defecto, Maven apunta a un repositorio central donde se suben todos los componentes que utiliza la comunidad que trabaja con la JDK: https://repo.maven.apache.org/maven2/
Cuando tenemos dudas sobre las versiones de algún componente, podemos hacer una búsqueda en
http://search.maven.org
por ejemplo, podemos buscar el componente uqbar-domain, que direcciona a https://search.maven.org/search?q=uqbar-domain. Allí tenemos todos los releases, con sus fechas correspondientes:
Para publicar un componente en el repositorio Sonatype que está relacionado con Maven Central, hay que cumplir algunas reglas. En particular, para los componentes Uqbar hay que seguir estas instrucciones.
En nuestro pom.xml podemos definir repositorios adicionales donde encontrar componentes de Maven:
<project>
...
<repositories>
<repository>
<id>my-internal-site</id>
<url>http://myserver/repo</url>
</repository>
</repositories>
...
</project>
Otra opción es escribir esto en el archivo settings.xml
del directorio raíz de nuestro M2, como cuenta este artículo.
A continuación mostraremos el algoritmo que utiliza Maven para encontrar las dependencias de nuestro proyecto: si no lo tenemos en nuestro repositorio local, lo irá a buscar al repositorio Maven Central, y en última instancia, en los repositorios adicionales definidos por nuestro proyecto o la configuración general de Maven (el archivo settings.xml
).
Una alternativa es trabajar directamente con Maven desde la consola, algo que puede ser útil para automatizar tareas, como cuando trabajemos con herramientas de integración continua.
mvn clean compile
Esto ejecuta varios plugins en forma sincronizada:
.xtend
a .java
(y luego generar los .class
)Si queremos ver el árbol de dependencias transitivas, podemos escribir
mvn dependency:tree
Pero ¿cómo sabemos qué comando debemos ejecutar? Para eso hay que entender el ciclo de vida de un build de Maven.
Maven está pensado para todo el ciclo de vida del proyecto. Lo vamos a usar para compilar, para generar código (de ser necesario), para correr los tests, para empaquetar, y hasta para publicar nuestros artefactos generando releases con trazabilidad.
Maven define un conjunto de etapas en la construcción (build) de nuestro proyecto. Resumimos algunas acá:
Podemos indicar a Maven que ejecute hasta cierta fase. Por ejemplo:
mvn compile
Va a ejecutar las dos primeras fases.
mvn test
Las tres primeras. Es decir es igual a mvn compile + correr los tests.
De la misma manera podemos trabajar en Eclipse con el plugin M2:
A continuación dejamos un diagrama básico que muestra la relación entre plugin y el ciclo de vida del build:
El gráfico no incluye plugins que estamos usando en Algoritmos 2: Xtend, cobertura de tests, etc. pero sirve como referencia para entender qué goals se ejecutarán en base al comando Maven pedido.
Para más información recomendamos leer la documentación oficial del proyecto Maven:
Y estos links: