Un'introduzione a Maven 2

Maven è un popolare strumento di compilazione open source per progetti Java aziendali, progettato per eliminare gran parte del duro lavoro dal processo di creazione. Maven utilizza un approccio dichiarativo, in cui vengono descritti la struttura e il contenuto del progetto, piuttosto che l'approccio basato su attività utilizzato in Ant o nei file make tradizionali, ad esempio. Questo aiuta a far rispettare gli standard di sviluppo a livello aziendale e riduce il tempo necessario per scrivere e mantenere gli script di compilazione.

L'approccio dichiarativo e basato sul ciclo di vita utilizzato da Maven 1 è, per molti, un allontanamento radicale dalle tecniche di costruzione più tradizionali e Maven 2 va ancora oltre in questo senso. In questo articolo, esaminerò alcuni dei principi di base dietro Maven 2 e poi passo attraverso un esempio funzionante. Iniziamo esaminando i fondamenti di Maven 2.

Il modello a oggetti del progetto

Il cuore di un progetto Maven 2 è il modello a oggetti del progetto (o POM in breve). Contiene una descrizione dettagliata del progetto, comprese informazioni sul controllo delle versioni e sulla gestione della configurazione, dipendenze, applicazioni e risorse di test, membri del team e struttura e molto altro. Il POM assume la forma di un file XML ( pom.xml ), che si trova nella directory home del progetto. Un semplice file pom.xml è mostrato qui:

 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 struttura delle directory di Maven 2

Gran parte del potere di Maven deriva dalle pratiche standard che incoraggia. Uno sviluppatore che ha precedentemente lavorato a un progetto Maven si sentirà immediatamente familiare con la struttura e l'organizzazione di uno nuovo. Non è necessario sprecare tempo per reinventare strutture di directory, convenzioni e script di compilazione Ant personalizzati per ogni progetto. Sebbene tu possa sovrascrivere qualsiasi posizione di directory particolare per i tuoi scopi specifici, dovresti davvero rispettare la struttura di directory standard di Maven 2 il più possibile, per diversi motivi:

  • Rende il tuo file POM più piccolo e più semplice
  • Rende il progetto più facile da capire e rende la vita più facile al poveretto che deve mantenere il progetto quando te ne vai
  • Semplifica l'integrazione dei plug-in

La struttura di directory standard di Maven 2 è illustrata nella Figura 1. Nella home directory del progetto va il POM (pom.xml) e due sottodirectory: src per tutto il codice sorgente e target per gli artefatti generati.

La directory src ha un numero di sottodirectory, ognuna delle quali ha uno scopo chiaramente definito:

  • src / main / java: il tuo codice sorgente Java va qui (stranamente!)
  • src / main / resources: altre risorse necessarie alla tua applicazione
  • src / main / filters: filtri di risorse, sotto forma di file di proprietà, che possono essere utilizzati per definire variabili conosciute solo in fase di esecuzione
  • src / main / config: file di configurazione
  • src / main / webapp: la directory dell'applicazione Web per un progetto WAR
  • src / test / java: unit test
  • src / test / resources: risorse da utilizzare per gli unit test, ma non verranno distribuite
  • src / test / filters: filtri delle risorse da utilizzare per gli unit test, ma non verranno distribuiti
  • src / site: file utilizzati per generare il sito Web del progetto Maven

Cicli di vita del progetto

I cicli di vita del progetto sono fondamentali per Maven 2. La maggior parte degli sviluppatori ha familiarità con il concetto di fasi di compilazione come compilazione, test e distribuzione. Ant ha obiettivi con nomi come quelli. In Maven 1, i plug-in corrispondenti vengono chiamati direttamente. Per compilare il codice sorgente Java, ad esempio, javaviene utilizzato il plug-in:

$maven java:compile

In Maven 2, questa nozione è standardizzata in una serie di fasi del ciclo di vita ben note e ben definite (vedere la Figura 2). Invece di invocare i plug-in, lo sviluppatore Maven 2 invoca una fase del ciclo di vita: $mvn compile.

Alcune delle fasi del ciclo di vita di Maven 2 più utili sono le seguenti:

  • generate-sources: Genera qualsiasi codice sorgente aggiuntivo necessario per l'applicazione, che in genere viene realizzato utilizzando i plug-in appropriati
  • compile: Compila il codice sorgente del progetto
  • test-compile: Compila gli unit test del progetto
  • test: Esegue gli unit test (in genere utilizzando JUnit) nella directory src / test
  • package: Impacchetta il codice compilato nel suo formato distribuibile (JAR, WAR, ecc.)
  • integration-test: Elabora e distribuisce il pacchetto, se necessario, in un ambiente in cui è possibile eseguire i test di integrazione
  • install: Installa il pacchetto nel repository locale per utilizzarlo come dipendenza in altri progetti sulla macchina locale
  • deploy: Fatto in un ambiente di integrazione o rilascio, copia il pacchetto finale nel repository remoto per condividerlo con altri sviluppatori e progetti

Sono disponibili molte altre fasi del ciclo di vita. Vedi risorse per maggiori dettagli.

Queste fasi illustrano i vantaggi delle pratiche consigliate incoraggiate da Maven 2: una volta che uno sviluppatore ha familiarizzato con le principali fasi del ciclo di vita di Maven 2, dovrebbe sentirsi a proprio agio con le fasi del ciclo di vita di qualsiasi progetto Maven.

La fase del ciclo di vita richiama i plug-in necessari per eseguire il lavoro. Il richiamo di una fase del ciclo di vita richiama automaticamente anche eventuali fasi del ciclo di vita precedenti. Poiché le fasi del ciclo di vita sono in numero limitato, facili da capire e ben organizzate, acquisire familiarità con il ciclo di vita di un nuovo progetto Maven 2 è facile.

Dipendenze transitive

Uno dei punti salienti di Maven 2 è la gestione transitiva delle dipendenze. Se hai mai usato uno strumento come urpmi su una macchina Linux, saprai cosa sono le dipendenze transitive. Con Maven 1, devi dichiarare ogni JAR che sarà necessario, direttamente o indirettamente, dalla tua applicazione. Ad esempio, puoi elencare i JAR necessari per un'applicazione Hibernate? Con Maven 2, non devi. Devi solo dire a Maven di quali librerie hai bisogno e Maven si prenderà cura delle librerie di cui hanno bisogno le tue librerie (e così via).

Supponi di voler utilizzare Hibernate nel tuo progetto. Dovresti semplicemente aggiungere una nuova dipendenza alla dependenciessezione in pom.xml, come segue:

  hibernate hibernate 3.0.3 compile 

E questo è tutto! Non è necessario cercare in giro per sapere in quali altri JAR (e in quali versioni) è necessario eseguire Hibernate 3.0.3; Maven lo farà per te!

La struttura XML per le dipendenze in Maven 2 è simile a quella utilizzata in Maven 1. La differenza principale è il scopetag, che viene spiegato nella sezione seguente.

Ambiti di dipendenza

In un'applicazione aziendale reale, potrebbe non essere necessario includere tutte le dipendenze nell'applicazione distribuita. Alcuni JAR sono necessari solo per i test unitari, mentre altri verranno forniti in fase di esecuzione dal server delle applicazioni. Usando una tecnica chiamata ambito delle dipendenze , Maven 2 ti consente di utilizzare determinati JAR solo quando ne hai veramente bisogno e li esclude dal classpath quando non lo fai.

Maven fornisce quattro ambiti di dipendenza:

  • compile: Una dipendenza dell'ambito di compilazione è disponibile in tutte le fasi. Questo è il valore predefinito.
  • provided: Una dipendenza fornita viene utilizzata per compilare l'applicazione, ma non verrà distribuita. Si utilizzerà questo ambito quando si prevede che JDK o il server delle applicazioni forniscano il JAR. Le API servlet sono un buon esempio.
  • runtime: Le dipendenze dell'ambito di runtime non sono necessarie per la compilazione, ma solo per l'esecuzione, come i driver JDBC (Java Database Connectivity).
  • test: Le dipendenze dell'ambito del test sono necessarie solo per compilare ed eseguire i test (JUnit, ad esempio).

Comunicazione del progetto

Una parte importante di qualsiasi progetto è la comunicazione interna. Sebbene non sia un proiettile d'argento, un sito web di progetto tecnico centralizzato può fare molto per migliorare la visibilità all'interno del team. Con il minimo sforzo, puoi avere un sito web di progetto di qualità professionale attivo e funzionante in pochissimo tempo.

Ciò assume una dimensione completamente nuova quando la generazione del sito Maven viene integrata in un processo di compilazione utilizzando l'integrazione continua o anche build notturne automatiche. Un tipico sito Maven può pubblicare, su base giornaliera:

  • Informazioni generali sul progetto come archivi di origine, tracciamento dei difetti, membri del team, ecc.
  • Report di test unitari e di copertura dei test
  • Revisioni automatiche del codice e con Checkstyle e PMD
  • Informazioni sulla configurazione e sul controllo delle versioni
  • Dipendenze
  • Javadoc
  • Codice sorgente in formato HTML indicizzato e con riferimenti incrociati
  • Elenco dei membri del team
  • E altro ancora

Ancora una volta, qualsiasi sviluppatore esperto di Maven saprà immediatamente dove cercare per acquisire familiarità con un nuovo progetto Maven 2.

Un esempio pratico

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

Ora implementiamo la logica di business. La Hotelclasse è un semplice JavaBean. La HotelModelclasse implementa due servizi: il findAvailableCities()metodo, che elenca le città disponibili, e il findHotelsByCity()metodo, che elenca tutti gli hotel in una data città. Di seguito HotelModelviene presentata una semplice implementazione basata sulla memoria della classe:

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; } }