Apache Maven  è un potente strumento per la gestione dei progetti per la piattaforma Java, in termini di compilazione del codice, distribuzione, documentazione e collaborazione del team di sviluppo. Può essere paragonato al più noto Apache Ant, ma fornisce funzionalità più avanzate.
I vantaggi principali di Maven sono i seguenti:

  • Standardizzazione della struttura di un progetto e del processo di compilazione.
  • Test ed esportazione automatizzati.
  • Gestione e download automatico delle librerie necessarie al progetto con risoluzione delle eventuali dipendenze transitive.
  • Facilità di ampliarne le funzionalità iniziali tramite l’utilizzo di plugin.
  • Creazione automatica di un semplice sito di documentazione del progetto.
  • Molto altro ancora.

I principali componenti di Maven sono:

  • pom.xml (vedi sezione…): file xml di configurazione, contiene tutte le informazioni sul progetto (dipendenze, test, documentazione, etc…).
  • Goal (vedi sezione…): singola funzione che può essere eseguita sul progetto, l’equivalente Maven dei task Ant.
  • Plug-in (vedi sezione…): goal riutilizzabili in tutti i progetti.
  • Repository (vedi sezione…): directory strutturata destinata alla gestione delle librerie. Può essere locale o remoto.

Un POM di teoria …

POM sta per Project Object Model e ogni singolo progetto è descritto attraverso il file di configurazione pom.xml, senza il quale Maven non può fare niente. Il POM è un file XML e guida l’esecuzione in Maven definendo in modo chiaro l’identità e la struttura di un progetto in ogni suo aspetto. Tutto è descritto nel POM: informazioni generali  del progetto, dipendenze, processo di compilazione e fasi secondarie come la generazione di documentazione.

Un POM ai minimi termini è composto da un tag root <project> che conterrà i tag:

  • <modelVersion> che dichiara a quale versione di POM questo progetto è conforme (a noi ci basta sapere che deve essere settato a 4.0.0)
  • <groupId> ID del gruppo del progetto
  • <artifactId> ID dell’artefatto (del progetto)
  • <version> cioè la versione del progetto
  • <packaging> che è il tipo di archivio che vogliamo esportare (jar, war o ear, come vedremo troveremo in una cartella chiamata “target” l’archivio esportato)

I parametri groupId, artifactId, version e packaging identificheranno univocamente un progetto. Se packaging non è specificato nel POM, assumerà “jar” a valore di default.
Ecco un esempio di pom.xml minimale con packaging non specificato:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

Come abbiamo detto la gestione e download automatico delle librerie necessarie al progetto è uno dei punti di forza di Maven e non è cosa da poco. Non so se vi è mai capitato di impazzire facendolo “a mano”. Supponiamo pertanto di voler includere nel nostro progetto la libreria log4j. Maven di default si collegherà al repository remoto e scaricherà lui tutti i pacchetti di cui necessita, comprese le dipendenze degli stessi.
Per inserire questa dipendenza è sufficiente aggiungere nel file pom.xml un tag <dependencies> e dentro questo mettiamo quanto segue:

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.13</version>
</dependency>

Scoprirete che per ogni libreria che vi servirà (alla versione che vi occorre) vi basterà spesso andare nel sito della stessa per trovare la stringa <dependency> che vi occorre. Se proprio siete sfortunati il tutto si risolve con una ulteriore ricerca con Google, al più, se siete proprio sfigati, dovrete aggiungere un ulteriore repository. Ma anche in questi casi vi renderete conto che non dovrete più mettervi a scaricare tutte le librerie richeste e dunque risolverne tutte le dipendenze, risparmiandovi i grattacapi che ben conoscete. 😀
Per ogni dipendenza è possibile anche definire uno scope:

  • compile (default) – le dipendenze sono disponibili in tutti i classpath del progetto
  • provided – è simile a compile, ma prevede che a runtime le dipendenze siano rese disponibili dall’ambiente di esecuzione (per esempio le JavaEE APIs per un’applicazione enterprise)
  • runtime – le dipendenze sono richieste solo in esecuzione
  • test – le dipendenze sono richieste solo per la compilazione e l’esecuzione dei test
  • system – la dipendenza non viene recuperata tramite repository, ma ne viene esplicitamente dichiarata la posizione locale

In pratica ci basta aggiungere dentro <dependency> il tag <scope>, il cui valore è lo scope da voi desiderato.

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.13</version>
      <scope>test</scope>
    </dependency>

Repository

Il Repository è una directory strutturata destinata alla gestione delle librerie. Maven ne crea uno in locale sotto la home dell’utente al fine di evitare accessi alla rete inutili. Di default, il repository si trova sotto Linux in:

/home/${user}/.m2/repository

e sotto Windows Xp in:

C:\Documents and Settings\${user}\.m2\repository

Nel processo di gestione del progetto Maven verifica in locale l’esistenza della libreria desiderata e in caso negativo interroga un repository remoto.
Nel POM minimale non è specificato alcun repository remoto. Se si svolge il building del progetto, allora verrà ereditato quello di default che corrisponde a:

http://repo1.maven.org/maven2/

Qualora fosse necessario, è pero’ possibile specificare altri repository dove cercare librerie e dipendenze. Per integrarlo all’interno del nostro progetto è sufficiente aggiungere nel pom.xml la sezione <repositories>

<repositories>
  <repository>
    <id>id_nuovo_repository</id>
    <url>http://nuovo_repository</url>
  </repository>
</repositories>

Se mettiamo insieme il tutto…

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>jar</packaging>
  <repositories>
    <repository>
      <id>id_nuovo_repository</id>
      <url>http://nuovo_repository</url>
    </repository>
  </repositories>
  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.13</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Plugin & Goal

Un goal è una singola funzione che può essere eseguita sul progetto, l’equivalente Maven dei task Ant. I Goal possono essere sia specifici per il progetto dove sono inclusi, sia riusabili. I Plugin sono goal riutilizzabili in tutti i progetti.
Maven ha una serie di plugin built-in disponibili, i principali sono i seguenti:

  • clean: che permette di cancellare i compilati dal progetto;
  • compiler: che permette di compilare i file sorgenti;
  • deploy: che permette di depositare il pacchetto generato nel repository remoto;
  • install: che permette di depositare il pacchetto generato nel repository locale;
  • site: che permette di generare la documentazione del progetto;
  • archetype: che permette di generare la struttura di un progetto a partire da un template.

Ciascun plugin mette a disposizione dei goal specifici o più di un goal (per esempio, come vedremo, il plugin di compilazione, compiler, prevede un goal per la compilazione del codice sorgente e un altro per la compilazione dei casi di test). Ciascun goal, a sua volta, riceve in ingresso specifici parametri, facoltativi o obbligatori.
Altri plugin possono essere realizzati qualora sia necessario estendere ulteriormente le capacità di Maven a causa di particolari esigenze.

Maven è un tool da riga di comando e i comandi hanno la seguente struttura:

mvn <nome-plugin>:[<nome_goal>] [{<parametro>}]

Un’altra tipologia di comandi Maven è:

mvn [phase]

Maven permette di lanciare anche le fasi, cioè prevede l’esecuzione di un set di determinate coppie [plugin]:[goal].

Se per esempio si lancia la fase package:

mvn package

vengono eseguite le seguenti fasi con relative coppie plugin-goal;

  • resources:resources – copia tutti i file di resources nella directory di output
  • compiler:compile – compila il codice sorgente
  • resources:testResources – copia tutti i file di resources di test nella directory di output di test
  • compiler:testCompile – compila le classi di test
  • surfire:test – esegue le classi di test terminando il processo in caso di fallimento
  • jar:jar – crea il jar di distribuzione nella directory di output

In pratica avremmo avuto lo stesso risultato lanciando:

mvn resources:resources compiler:compile resources:testResources compiler:testCompile surefire:test jar:jar

Istallazione

È possibile scaricare il progetto dal sito ufficiale. Per poterlo installare è sufficiente scompattare lo zip, ad esempio nella cartella C:\MavenDir, e aggiungere la cartella “bin” alla variabile di ambiente PATH (PATH=...;C:\MavenDir\bin;).
Su Linux invece potete usare strumenti come apt o yum e tutto è più semplice ed automatico. Per esempio su Ubuntu è sufficente:

sudo apt-get install maven2

Per verificare l’avvenuta istallazione basta lanciare:

mvn -version

Creare progetti Maven

Maven permette di creare un progetto a partire da un template, chiamato archetype, che stabilisce la struttura base delle cartelle e dei file che devono essere creati. Noi vedremo maven-archetype-quickstart per i progetti Java e maven-archetype-webapp per i progetti web.
Creiamo una directory che conterrà i progetti che vedremo (ad esempio C:\provaMaven) e poi eseguire il comando:

mvn archetype:create  -DgroupId=it.cosenonjaviste -DartifactId=javaModule -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0
struttura progetto Java

con il quale creiamo il progetto Java javaModule con la struttura in figura.
Poichè abbiamo utilizzato il template maven-archetype-quickstart per generare lo scheletro, viene creata anche una classe di esempio con la corrispondente classe per il test di JUnit (AppTest.java), che è possibile eliminare.
Come si può vedere tra le dipendenze richieste dal progetto, il template provvede a inserire automaticamente la libreria JUnit per l’esecuzione delle classi di test.

Adesso vediamo il POM generato:

<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>it.cosenonjaviste</groupId>
 <artifactId>javaModule</artifactId>
 <version>1.0</version>
 <packaging>jar</packaging>
 <name>javaModule</name>
 <url>http://maven.apache.org</url>
 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

Spostiamoci nella root del nostro progetto e lanciamo il comando:

mvn package

La prima volta che il progetto viene compilato, Maven scarica tutte le librerie dipendenti necessarie alla compilazione. Questa fase è un po’ lunga, ma le prestazioni non saranno sempre le stesse. Infatti, le compilazioni successive saranno molto più veloci perchè Maven non avrà più bisogno di scaricare dal repository remoto le librerie.
Quando Maven avrà finito troveremo  nella cartella target l’esito delle compilazioni, compreso il file jar finale.

In modo analogo usando l’archetipo maven-archetype-webapp si procede alla creazione di un progetto web. Vediamo…

mvn archetype:create  -DgroupId=it.cosenonjaviste -DartifactId=javaWebModule -DarchetypeArtifactId=maven-archetype-webapp -Dversion=1.0
struttura progetto web

E abbiamo il progetto web javaWebModule con la struttura in figura.
ll pom.xml sarà pressochè identico al precedente, tranne che per il valore del tag <artifactId> che in questo caso sarà javaWebModule e tranne che per il valore del tag <packaging> che in questo caso sarà war.
Una volta lanciato mvn package e terminata la compilazione, nella root del nuovo progetto nella cartella target dello stesso troveremo questa volta il file war finale.

Progetti modulari

Come è possibile notare abbiamo due file pom.xml, quindi 2 progetti Maven indipendenti. Se avessimo voluto creare un terzo progetto composto dai due precedenti ( il modulo con il sorgente Java e il modulo con il sorgente Web), una volta creati (tralasciando quindi la fase di compilazione, mvn package per intenderci), avremmo dovuto inserire nella directory principale C:\provaMaven un file pom.xml con il seguente contenuto:

<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/maven-v4_0_0.xsd">
  <name>provaMaven Maven Project</name>
  <modelVersion>4.0.0</modelVersion>
  <groupId>it.cosenonjaviste</groupId>
  <version>1.0</version>
  <artifactId>provaMaven</artifactId>
  <packaging>pom</packaging>
  <modules>
    <module>javaModule</module>
    <module>javaWebModule</module>
  </modules>
</project>

Così facendo, eseguendo un comando Maven sul progetto principale, questo sarà eseguito anche sui suoi moduli. Come avete notato abbiamo scelto pom come valore di packaging , in quanto questo progetto non conterrà codice e quindi non dovrà essere creato un jar corrispondente.
Ora dobbiamo indicare che il modulo Web dipende dal modulo Java. Facciamo in modo infatti che il jar del modulo Java debba essere contenuto nelle librerie del war dell’applicazione Web.
Aggiungiamo al file pom.xml del modulo Web nella sezione dependencies la seguente dipendenza:

<dependency>
  <groupId>it.cosenonjaviste</groupId>
  <artifactId>javaModule</artifactId>
  <version>1.0</version>
</dependency>

Fatto ciò possiamo finalmente compilare:

mvn package

Adesso nella cartella target del progetto web troveremo il war pronto per essere usato, con il progetto Java già incluso nel war stesso.

A questo punto non ci resta che “metter mano” nei nostri progetti Java. Per farlo abbiamo bisogno, come ben sappiamo, di un IDE come il nostro amato Eclipse. Pertanto nella seconda parte di questo articolo vedremo come integrare Maven ed Eclipse.

9 Posts

Sono laureato in Ingegneria Informatica e lavoro dal 2008 come sviluppatore Java presso OmniaGroup. Sviluppo principalmente applicazioni web soprattutto su piattaforme alternative come ad esempio lo stack JSF+Spring+Hibernate (o altri ORM) oppure lo stack JavaScript(JQuery o ExtJS)+Spring+Hibernate su Tomcat, o sui principali AS (WebSphere,Weblogic,Glassfish, Jboss). Nei ritagli di tempo mi diletto a programmare su piattaforma Android e con altri linguaggi, quali ad esempio Python. LinkedIn profile