Una introducción a Maven 2

Maven es una popular herramienta de compilación de código abierto para proyectos empresariales Java, diseñada para eliminar gran parte del trabajo duro del proceso de compilación. Maven utiliza un enfoque declarativo, donde se describen la estructura y el contenido del proyecto, en lugar del enfoque basado en tareas utilizado en Ant o en los archivos make tradicionales, por ejemplo. Esto ayuda a hacer cumplir los estándares de desarrollo en toda la empresa y reduce el tiempo necesario para escribir y mantener scripts de compilación.

El enfoque declarativo basado en el ciclo de vida utilizado por Maven 1 es, para muchos, una desviación radical de las técnicas de construcción más tradicionales, y Maven 2 va aún más lejos en este sentido. En este artículo, repaso algunos de los principios básicos detrás de Maven 2 y luego paso a través de un ejemplo funcional. Comencemos revisando los fundamentos de Maven 2.

El modelo de objetos del proyecto

El corazón de un proyecto de Maven 2 es el modelo de objetos del proyecto (o POM para abreviar). Contiene una descripción detallada de su proyecto, incluida información sobre la gestión de versiones y configuración, dependencias, aplicaciones y recursos de prueba, miembros del equipo y estructura, y mucho más. El POM toma la forma de un archivo XML ( pom.xml ), que se coloca en el directorio de inicio de su proyecto. Aquí se muestra un archivo pom.xml simple:

 4.0.0 com.javaworld.hotels HotelDatabase war 1.0-SNAPSHOT Maven Quick Start Archetype //maven.apache.org   junit junit 3.8.1 test   

La estructura de directorios de Maven 2

Gran parte del poder de Maven proviene de las prácticas estándar que fomenta. Un desarrollador que haya trabajado anteriormente en un proyecto de Maven se familiarizará inmediatamente con la estructura y organización de uno nuevo. No es necesario perder tiempo reinventando estructuras de directorios, convenciones y scripts de construcción Ant personalizados para cada proyecto. Aunque puede anular cualquier ubicación de directorio en particular para sus propios fines específicos, realmente debe respetar la estructura de directorio estándar de Maven 2 tanto como sea posible, por varias razones:

  • Hace que su archivo POM sea más pequeño y simple
  • Hace que el proyecto sea más fácil de entender y le facilita la vida al pobre que debe mantener el proyecto cuando te vas.
  • Facilita la integración de complementos

La estructura de directorios estándar de Maven 2 se ilustra en la Figura 1. En el directorio de inicio del proyecto se encuentra el POM (pom.xml) y dos subdirectorios: src para todo el código fuente y destino para los artefactos generados.

El directorio src tiene varios subdirectorios, cada uno de los cuales tiene un propósito claramente definido:

  • src / main / java: su código fuente de Java va aquí (¡curiosamente!)
  • src / main / resources: otros recursos que su aplicación necesita
  • src / main / filtros: filtros de recursos, en forma de archivos de propiedades, que se pueden usar para definir variables que solo se conocen en tiempo de ejecución
  • src / main / config: archivos de configuración
  • src / main / webapp: el directorio de la aplicación web para un proyecto WAR
  • src / test / java: pruebas unitarias
  • src / test / resources: recursos que se utilizarán para pruebas unitarias, pero no se implementarán
  • src / test / filters: filtros de recursos que se utilizarán para las pruebas unitarias, pero no se implementarán
  • src / site: archivos utilizados para generar el sitio web del proyecto Maven

Ciclos de vida del proyecto

Los ciclos de vida de los proyectos son fundamentales para Maven 2. La mayoría de los desarrolladores están familiarizados con la noción de fases de compilación, como compilar, probar e implementar. Ant tiene objetivos con nombres como esos. En Maven 1, los complementos correspondientes se llaman directamente. Para compilar el código fuente de Java, por ejemplo, javase utiliza el complemento:

$maven java:compile

En Maven 2, esta noción está estandarizada en un conjunto de fases del ciclo de vida bien conocidas y definidas (consulte la Figura 2). En lugar de invocar plug-ins, el desarrollador Maven 2 invoca una fase de ciclo de vida: $mvn compile.

Algunas de las fases del ciclo de vida de Maven 2 más útiles son las siguientes:

  • generate-sources: Genera cualquier código fuente adicional necesario para la aplicación, que generalmente se logra mediante los complementos adecuados
  • compile: Compila el código fuente del proyecto
  • test-compile: Compila las pruebas unitarias del proyecto
  • test: Ejecuta las pruebas unitarias (normalmente usando JUnit) en el directorio src / test
  • package: Empaqueta el código compilado en su formato distribuible (JAR, WAR, etc.)
  • integration-test: Procesa e implementa el paquete si es necesario en un entorno donde se pueden ejecutar pruebas de integración
  • install: Instala el paquete en el repositorio local para usarlo como una dependencia en otros proyectos en su máquina local
  • deploy: Realizado en un entorno de integración o lanzamiento, copia el paquete final en el repositorio remoto para compartirlo con otros desarrolladores y proyectos.

Hay muchas otras fases del ciclo de vida disponibles. Consulte Recursos para obtener más detalles.

Estas fases ilustran los beneficios de las prácticas recomendadas alentadas por Maven 2: una vez que un desarrollador está familiarizado con las principales fases del ciclo de vida de Maven 2, debe sentirse cómodo con las fases del ciclo de vida de cualquier proyecto de Maven.

La fase del ciclo de vida invoca los complementos que necesita para hacer el trabajo. La invocación de una fase del ciclo de vida también invoca automáticamente cualquier fase anterior del ciclo de vida. Dado que las fases del ciclo de vida son limitadas en número, fáciles de entender y bien organizadas, familiarizarse con el ciclo de vida de un nuevo proyecto de Maven 2 es fácil.

Dependencias transitivas

Uno de los aspectos más destacados de Maven 2 es la gestión de la dependencia transitiva. Si alguna vez ha utilizado una herramienta como urpmi en una caja de Linux, sabrá qué son las dependencias transitivas. Con Maven 1, debe declarar todos y cada uno de los JAR que necesitará, directa o indirectamente, su aplicación. Por ejemplo, ¿puede enumerar los archivos JAR que necesita una aplicación de Hibernate? Con Maven 2, no es necesario. Usted acaba de decir Maven que las bibliotecas que necesita, y Maven se hará cargo de las bibliotecas que sus bibliotecas necesitan (y así sucesivamente).

Suponga que desea utilizar Hibernate en su proyecto. Simplemente agregaría una nueva dependencia a la dependenciessección en pom.xml, de la siguiente manera:

  hibernate hibernate 3.0.3 compile 

¡Y eso es! No tiene que buscar para saber en qué otros JAR (y en qué versiones) necesita ejecutar Hibernate 3.0.3; ¡Maven lo hará por ti!

La estructura XML para las dependencias en Maven 2 es similar a la utilizada en Maven 1. La principal diferencia es la scopeetiqueta, que se explica en la siguiente sección.

Ámbitos de dependencia

En una aplicación empresarial del mundo real, es posible que no necesite incluir todas las dependencias en la aplicación implementada. Algunos JAR son necesarios solo para pruebas unitarias, mientras que el servidor de aplicaciones proporcionará otros en tiempo de ejecución. Usando una técnica llamada alcance de dependencia , Maven 2 le permite usar ciertos JAR solo cuando realmente los necesita y los excluye de la ruta de clases cuando no lo necesita.

Maven proporciona cuatro ámbitos de dependencia:

  • compile: Una dependencia de alcance de compilación está disponible en todas las fases. Este es el valor predeterminado.
  • provided: Una dependencia proporcionada se utiliza para compilar la aplicación, pero no se implementará. Utilizaría este ámbito cuando espere que el JDK o el servidor de aplicaciones proporcionen el JAR. Las API de servlet son un buen ejemplo.
  • runtime: Las dependencias de alcance en tiempo de ejecución no son necesarias para la compilación, solo para la ejecución, como los controladores JDBC (Java Database Connectivity).
  • test: Las dependencias de alcance de prueba solo son necesarias para compilar y ejecutar pruebas (JUnit, por ejemplo).

Comunicación del proyecto

Una parte importante de cualquier proyecto es la comunicación interna. Si bien no es una fórmula mágica, un sitio web de proyecto técnico centralizado puede ser de gran ayuda para mejorar la visibilidad dentro del equipo. Con un mínimo esfuerzo, puede tener un sitio web de proyectos de calidad profesional en funcionamiento en muy poco tiempo.

Esto adquiere una dimensión completamente nueva cuando la generación de sitios de Maven se integra en un proceso de construcción mediante la integración continua o incluso las compilaciones nocturnas automáticas. Un sitio típico de Maven puede publicar, a diario:

  • Información general del proyecto, como repositorios de origen, seguimiento de defectos, miembros del equipo, etc.
  • Informes de pruebas unitarias y de cobertura de pruebas
  • Revisiones automáticas de código y con Checkstyle y PMD
  • Información de configuración y versiones
  • Dependencias
  • Javadoc
  • Código fuente en formato HTML indexado y con referencias cruzadas
  • Lista de miembros del equipo
  • Y mucho más

Una vez más, cualquier desarrollador experto en Maven sabrá inmediatamente dónde buscar para familiarizarse con un nuevo proyecto de Maven 2.

Un ejemplo practico

Now that we have seen a few of the basic notions used in Maven 2, let's see how it works in the real world. The rest of this tutorial examines how we would use Maven 2 on a simple Java Enterprise Edition project. The demo application involves an imaginary (and simplified) hotel database system. To demonstrate how Maven handles dependencies between projects and components, this application will be built using two components (see Figure 3):

  • A business logic component: HotelDatabase.jar
  • A Web application component: HotelWebApp.war

You can download the source code to follow along with the tutorial in Resources.

Set up your project environment

We start by configuring your work environment. In real-world projects, you will often need to define and configure environment or user-specific parameters that should not be distributed to all users. If you are behind a firewall with a proxy, for example, you need to configure the proxy settings so that Maven can download JARs from repositories on the Web. For Maven 1 users, the build.properties and project.properties files do this job. In Maven 2, they have been replaced by a settings.xml file, which goes in the $HOME/.m2 directory. Here is an example:

     http scott tiger 8080 my.proxy.url    

Create a new project with the archetype plug-in

The next step is to create a new Maven 2 project template for the business logic component. Maven 2 provides the archetype plug-in, which builds an empty Maven 2-compatible project directory structure. This plug-in proves convenient for getting a basic project environment up and running quickly. The default archetype model will produce a JAR library project. Several other artifact types are available for other specific project types, including Web applications, Maven plug-ins, and others.

Run the following command to set up your HotelDatabase.jar project:

mvn archetype:create -DgroupId=com.javaworld.hotels - DartifactId=HotelDatabase -Dpackagename=com.javaworld.hotels

Now you have a brand new Maven 2 project directory structure. Switch to the HotelDatabase directory to continue the tutorial.

Implementing the business logic

Ahora implementamos la lógica empresarial. La Hotelclase es un JavaBean simple. La HotelModelclase implementa dos servicios: el findAvailableCities()método, que enumera las ciudades disponibles, y el findHotelsByCity()método, que enumera todos los hoteles en una ciudad determinada. HotelModelAquí se presenta una implementación simple de la clase basada en memoria :

package com.javaworld.hotels.model;

import java.util.ArrayList; import java.util.List;

import com.javaworld.hotels.businessobjects.Hotel;

public class HotelModel {

/** * The list of all known cities in the database. */ private static String[] cities = { "Paris", "London", }; /** * The list of all hotels in the database. */ private static Hotel[] hotels = { new Hotel("Hotel Latin","Quartier latin","Paris",3), new Hotel("Hotel Etoile","Place de l'Etoile","Paris",4), new Hotel("Hotel Vendome","Place Vendome","Paris",5), new Hotel("Hotel Hilton","Trafalgar Square","London",4), new Hotel("Hotel Ibis","The City","London",3), }; /** * Returns the hotels in a given city. * @param city the name of the city * @return a list of Hotel objects */ public List findHotelsByCity(String city){ List hotelsFound = new ArrayList(); for(Hotel hotel : hotels) { if (hotel.getCity().equalsIgnoreCase(city)) { hotelsFound.add(hotel); } } return hotelsFound; } /** * Returns the list of cities in the database which have a hotel. * @return a list of city names */ public String[] findAvailableCities() { return cities; } }