Organizziamoci con Maven – Parte I

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 che conterrà i tag:

  • che dichiara a quale versione di POM questo progetto è conforme (a noi ci basta sapere che deve essere settato a 4.0.0)
  • ID del gruppo del progetto
  • ID dell’artefatto (del progetto)
  • cioè la versione del progetto
  • 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:


  4.0.0
  com.mycompany.app
  my-app
  1

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 e dentro questo mettiamo quanto segue:


  log4j
  log4j
  1.2.13

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 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 il tag , il cui valore è lo scope da voi desiderato.

    
      log4j
      log4j
      1.2.13
      test
    

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


  
    id_nuovo_repository
    http://nuovo_repository
  

Se mettiamo insieme il tutto…


  4.0.0
  com.mycompany.app
  my-app
  1
  jar
  
    
      id_nuovo_repository
      http://nuovo_repository
    
  
  
    
      log4j
      log4j
      1.2.13
      test
    
  

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 :[] [{}]

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

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:


 4.0.0
 it.cosenonjaviste
 javaModule
 1.0
 jar
 javaModule
 http://maven.apache.org
 
  
   junit
   junit
   3.8.1
   test
  
 

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

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 che in questo caso sarà javaWebModule e tranne che per il valore del tag 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:


  provaMaven Maven Project
  4.0.0
  it.cosenonjaviste
  1.0
  provaMaven
  pom
  
    javaModule
    javaWebModule
  

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:


  it.cosenonjaviste
  javaModule
  1.0

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 vedremo come integrare Maven ed Eclipse.

Gianni Amendola

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.

  • Pingback: ()

  • Fabrizio

    Complimenti vivissimi a tuttto lo staff di CNJ, le vostre guide/tutorial/chicche sono sempre chiare,semplici ma soprattutto utili. Se posso permettermi un solo suggerimento, sarebbe comodo consultare gli articoli anche offline magari dando la possibilità di salvarli in pdf o simili. Ad ogni modo, complimenti ancora.

    • Ciao Fabrizio, grazie mille per i complimenti. Ho aggiunto un plugin che fa comparire in alto a destra sul post un pulsante Print per creare un PDF. Continua a seguirci e ad inviarci i tuoi suggerimenti, sono ben graditi. Saluti

  • Pingback: ()

  • Gab Laurenza

    Complimenti per il tutorial. E’ scritto davvero in maniera molto chiara. Avrei soltanto una domanda: come faccio a sapere quale versione delle librerie di java ho utilizzato, di modo tale da inserire il valore corretto nel file pom.xml?

  • Pingback: ()

  • Pingback: ()

  • Pingback: ()

  • Paolop

    Grazie! Complimenti per il tutorial, chiaro e conciso.

  • Lorenzo

    Istallazione o Installazione

  • Open Source Hardware

    Molto chiaro l’articolo. Un dubbio tra le tante configurazioni di Maven…..quando in una dipendenza non si specifica la versione…che versione scarica da il repository?? O forse dipende proprio dalle impostazioni del repository?

  • Pingback: ()

  • andriun

    Buonasera, ho un problema nel produrre il package dopo aver aggiunto le 2 righe di dipendenza relative al progetto jar.

    it.cosenonjaviste
    javaModule
    1

    Senza queste righe funziona tutto correttamente. Il messaggio di errore che ottengo da riga di comando dopo avere lanciato mvn package dalla direttori javaWebModule e il seguente:

    [ERROR] Failed to execute goal on project javaWebModule: Could not resolve depen

    dencies for project it.cosenonjaviste:javaWebModule:war:1: Failure to find it.co

    senonjaviste:javaModule:jar:1 in https://repo.maven.apache.org/maven2 was cached

    in the local repository, resolution will not be reattempted until the update in

    terval of central has elapsed or updates are forced -> [Help 1]

    [ERROR]

    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit

    ch.

    [ERROR] Re-run Maven using the -X switch to enable full debug logging.

    [ERROR]

    [ERROR] For more information about the errors and possible solutions, please rea

    d the following articles:

    [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyReso

    lutionException

    ———————————-

    Leggendo quanto riportato dal spiegazione dell’errore(link relativo all’ultima riga) sembra che il problema sia dovuto alla mancanza della definizione di un “server” nel file settings.xml, presente nella direttiva {home-user}.m2. Dal momento però, che come dicevo, senza le righe della dipendenza funziona tutto correttamente, non credo che questo sia il vero motivo; tanto più che sotto tale direttiva non esiste alcun file settings.xml. Qualcuno può gentilmente aiutarmi?

    • andriun

      Non ho più necessità di avere aiuto in relazione al post da me inviato, in quanto sembra che il problema si sia risolto da sè. Forse, ma non ne sono sicuro, era sufficiente chiudere e riaprire il prompt dei comandi.

  • Pingback: maven-dipendenze-transitive | mauroprogram's Blog()

  • Pasquale

    Salve Ragazzi,
    sto iniziando ad usare questo potente strumento e voglio innanzitutto ringraziarvi per il tutorial.

    Dunque correggetemi se sbaglio: ho notato che nell’esempio della creazione del progetto maven-archetype-quickstart, usate come groupId da riga di comando -DgroupId=it.cosenonjaviste, mentre quando illustrate il codice nel pom usate it.html. pare essere un errore … Inoltre nell’esempio della dipendenza del modulo javaWebModule riportate it.cosenonjaviste. E’ giusta questa osservazione oppure mi sono perso qualcosa?
    Grazie
    Pasquale

    • Ciao Pasquale,
      grazie della segnalazione! La tua osservazione è corretta, abbiamo modificato il post per evitare sviste future!

  • Pingback: 6 risorse gratuite per imparare Maven - Alessandro Nizzo()

  • Daniel Mutu

    Ciao, ottima guida e molto chiara, una precisione visto che l’articolo è un po’ datato.
    CON GLI ULTIMI AGGIORNAMENTI SE SI VUOLE CREARE UN MAVEN-ARCHETYPE-QUICKSTART SI UTILIZZA:
    —————————————————————————————————————————–
    mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.1
    —————————————————————————————————————————–
    La fonte è: http://stackoverflow.com/questions/40428891/maven-quickstart-archetype-does-not-exist
    Testato e funzionante, inoltre credo che anche la guida ufficiale in http://maven.apache.org/archetypes/maven-archetype-quickstart/ non funzioni (o almeno a me è così)