Sinistra <- Installazione e configurazione di un server Linux - Indice Generale - Copertina - Indice -> Destra

PLUTOWare


Maven: accumulator of knowledge

di Michele Caini

L'articolo...

Nella programmazione non si può evitare di scontrarsi, prima o poi, con strumenti per la gestione di progetti. Chi ha una minima esperienza con linguaggi come il C avrà avuto modo di fare i conti con gli autotools, così come chi ha sposato la causa Java avrà probabilmente avuto a che fare con Ant. Maven si propone come una valido sostituto di quest'ultimo, senza limitarsi ad offrire le funzioni basilari rese appetitose condendole con artefatti di dubbia utilità, ma cambiando radicalmente il modo di vedere la gestione di un progetto Java.
Programmare nel terzo millennio diventerà ancora più piacevole, grazie a Maven.



The #1 programmer excuse for legitimately slacking off: "my code's compiling"

xkcd


Due parole su Maven

Cosa è Maven? A questa domanda non è facile dare una risposta precisa. Maven fornisce un approccio completamente nuovo alla questione della gestione di progetti Java che spazia a 360°, quindi la risposta alla domanda di cui sopra varia in relazione a chi viene posta e all'uso che questa persona fa di Maven.
Si può però facilmente dire cosa non è Maven. Maven non è solo uno strumento per compilare e testare il proprio codice, è molto di più. Maven non è solo uno strumento per impacchettare e distribuire il proprio codice, è molto di più. Con Maven il nostro progetto può godere ad esempio della generazione automatica della documentazione e di pagine per un potenziale sito web, può usufruire del supporto allo sviluppo in un team e della generazione di report, così come di molte altre caratteristiche che raramente si trovano concentrate in un unico strumento per la gestione di progetti.

Come riportato in [2], ecco una definizione formale per Maven:

Maven è uno strumento per la gestione di progetti che prevede un modello a oggetti, un insieme di standard, trattazione per il ciclo di vita di un progetto, un sistema di gestione delle dipendenze e logiche per l'esecuzione di obiettivi in forma di plugin in fasi specifiche del ciclo di vita. Usando Maven si descrive un progetto usando un ben definito modello ad oggetti, sul quale Maven stesso può applicare una logica trasversale a partire da un insieme di plugin condivisi o personalizzati.

Cosa è, quindi, Maven?
Maven può essere la risposta per chi cerca un semplice strumento per la compilazione automatica del proprio software così come per chi ha bisogno di un approccio più complesso ed articolato alla gestione di un progetto, riuscendo a sopperire alle richieste e alle esigenze del singolo in un modo tanto semplice quanto completo. Non deve spaventare la sua apparente complessità, di fatto infondata, così come è necessario superare la sensazione di perdita di controllo che qualcuno ha di fronte a Maven e al suo comportamento. Una volta imparato ad apprezzarne le caratteristiche e le funzionalità ogni sviluppatore non vorrà più fare a meno di Maven, neanche nel più classico degli ''Hello World!''.

Primi passi

In [1] è presente una breve introduzione, Maven in 5 Minutes che rende fede alla semplicità e immediatezza di questo strumento guidando il lettore nella sua prima, breve esperienza. Il consiglio è, ovviamente, quello di darci un'occhiata. Purtroppo, per problemi di spazio, non sarà possibile dilungarsi attraverso troppi esempi in questo articolo, quindi verranno citati laddove possibile riferimenti utili in tal senso. Allo stesso pari può essere illuminante leggere Getting Started Tutorial, sempre reperibile in [1], una sorta di guida che approfondisce e completa le tematiche lasciate aperte nella breve introduzione precedente.

POM

Maven descrive il singolo progetto attraverso un file POM (Project Object Model), senza il quale Maven non può fare niente. Il file POM guida l'esecuzione in Maven e definisce in modo chiaro l'identità e la struttura di un progetto in ogni suo aspetto e sfaccettatura.
Un esempio di file pom.xml in [3] è il seguente:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

In un progetto gestito con Maven tutto è descritto in un file POM: dipendenze, processo di compilazione, artefatti e fasi secondarie come la generazione di documentazione. Questo è l'analogo dei Makefile per gli autotools o del file build.xml per Ant. Ma a differenza dei suoi illustri colleghi non racchiude codice, non riporta direttive di alcun genere, è un file dichiarativo che racchiude in poche righe un grande potere espressivo sollevando lo sviluppatore dalla necessità di scendere nei dettagli delle diverse fasi.
Il file POM è diviso principalmente in quattro parti, ovvero:

Nella pratica ogni POM estende quello che è chiamato il Super POM, contenuto nel file maven-x.y.z-uber.jar all'interno del sistema ed ereditato automaticamente, il quale fornisce un insieme di dichiarazioni standard condivise da ogni progetto. Per brevità non è riportato ma si consiglia di dargli un'occhiata, a partire dal proprio sistema o come riportato in [2] per Maven 2.0.9. Nel Super POM è riportato ad esempio il repository remoto da cui ogni client attingerà per completare ed aggiornare la base locale, la struttura per l'albero delle directory come indicata dallo Standard Directory Layout e molto altro ancora.

Nella sua semplicità, anche il solo POM riportato nel riquadro precedente mette in campo alcuni dei campi chiave di questo oggetto. Ovviamente non è questo il luogo per discutere la sintassi di un file pom.xml ma questa è ampiamente illustrata sia a partire da [1] che in [2] e [3], ai quali si può fare riferimento per approfondimenti. Ci si accorgerà col tempo che la stesura di file POM è cosa immediata e molto semplice, ma può farsi anche complicata seguendo comunque in modo naturale la curva d'apprendimento dell'utente di Maven.

Build Life Cycle

Il build lifecycle è il concetto centrale intorno al quale si sviluppa Maven. Consiste in una serie di fasi durante le quali si possono avere più operazioni, dette goal, come ad esempio durante la fase di compilazione si avranno una serie di chiamate per compilare appunto un insieme di classi.
È utile capire fin da subito che alcune fasi sono strettamente legate ad altre e quindi l'invocazione delle prime causerà necessariamente anche l'esecuzione delle seconde. In altri termini, laddove l'esecuzione di una fase sia logica conseguenza di un'altra (come la creazione di un package che segue alla compilazione dei sorgenti, obbligatoriamente) verranno prese in considerazione nell'ordine corretto tutte le fasi che portano a quella richiesta. L'aggiunta di funzionalità nel circuito è possibile tramite l'uso di plugin, i quali possono andare a collocarsi in un punto qualsiasi della filiera produttiva a seconda delle necessità.
Maven si propone con un lifecycle predefinito che prevede diverse fasi, la validazione del progetto, la compilazione, il test, la creazione di pacchetti e così via. Allo stesso tempo è fornito di un gran numero di plugin per le necessità più disparate, ufficiali e non, i quali contribuiscono ad allargare il parco fasi e aggiungono a quelli appartenenti al core di Maven tutta una serie di caratteristiche come la possibilità di creare file war, automatizzare la generazione della documentazione basata su JavaDoc, creare una firma digitale e tanto altro ancora (si veda a tal proposito [4]).

Il primo progetto con Maven

A questo punto, fornita un'idea di base di cosa è Maven, sarebbe forse opportuno dare anche un esempio che, attraverso alcune semplici istruzioni, illustri come si può facilmente creare un progetto basato su Maven. Questo è però un argomento trattato in più e più articoli apparsi sulla rete ed è ampiamente documentato in ogni suo passo tanto in [2] e [3] quanto in [1], nelle due guide Getting Started in 5 Minutes e Getting Started in 30 Minutes; tutta documentazione facilmente e legalmente reperibile su Internet.
Per uscire un po' dal coro e al contempo dare un esempio di un progettino basato su Maven, che rispetti la struttura di directory standard proposta e si articoli in un pom.xml completo, viene descritto come approcciare il problema delle applicazioni web. Supponiamo quindi che si voglia dare luogo ad un immancabile Hello World! sul web basato su Maven. La struttura della directory come consigliata in [3] e prima ancora in [1], un po' rimaneggiata e ridotta per soddisfare le necessità in base ai problemi di spazio, è la seguente:

project-name
  |--> src
  |   |--> main
  |       |--> java
  |       |   |--> org
  |       |       |--> company-name
  |       |--> webapp
  |           |--> WEB-INF
  |           |   |--> web.xml
  |           |--> index.jsp
  |--> pom.xml

Il file pom.xml associato può assomigliare al seguente:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.company-name</groupId>
  <artifactId>project-name</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>project-name</name>
  <description>project description</description>
  <url>http://www.company-name.org/project-name</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <organization>
    <name>company-name</name>
    <url>http://www.company-name.org</url>
  </organization>
  <build>
    <finalName>${artifactId}-${version}</finalName>
  </build>
</project>

Con queste premesse dovrebbe essere sufficiente eseguire Maven package per trovarsi impacchettato sul proprio sistema il file project-name-1.0-SNAPSHOT.war. Il tutto dovrebbe avvenire dopo un po' di tempo durante il quale viene rifocillato il repository locale con i pacchetti mancanti, i quali vengono scaricati da una locazione remota predefinita (questa fase avviene solo al primo utilizzo di Maven, dopo il quale i pacchetti saranno già presenti sul proprio sistema e non si avrà la necessità di scaricarli ancora), e quindi vengono attraversate un certo numero di fasi che portano di fatto alla compilazione.

Maven e il modello a plugin

Nell'ultima sezione si vuole dare una panoramica dello sviluppo di plugin in Maven, ovvero di come sia possibile estendere in modo semplice e veloce le funzionalità di questo strumento adattandolo alle proprie esigenze. Maven, come citato sul sito ufficiale, è di fatto un framework per l'esecuzione di plugin ed effettivamente anche il suo funzionamento di base si appoggia ad alcuni di questi oggetti distribuiti in maniera predefinita col pacchetto.
Per ampliare le funzionalità di Maven si ha già, nonostante il progetto sia relativamente giovane, una nutrita lista di plugin extra che spazia su ogni tipologia di tematica e ambiente, come si può notare in [4].

Per tutti i plugin, ufficiali e non, si ha una breve documentazione che rende quindi del tutto vano il tentativo di darne un'introduzione in questa sede, poiché sono in realtà molto semplici da usare e configurare. Più interessante, invece, è l'aspetto di sviluppo di questi oggetti, così da poter aggiungere qualcosa di personale al proprio progetto.

Discesa al centro di Maven

Nello sviluppo di plugin è utile capire come funziona in realtà Maven al suo interno, il che semplifica non poco il resto del compito. Il cuore di Maven si basa su Plexus, un container per gestire e mettere in relazione fra loro i diversi componenti, nato come progetto indipendente e adottato da Maven durante il concepimento poiché altri strumenti dello stesso tipo (anche ad oggi più noti, come lo spring framework) non erano ancora maturi.
Plexus mette in pratica i principi dell'inversione di controllo (Inversion of Control, o IoC), una modalità di progettazione del software in cui il flusso di controllo è invertito rispetto ai metodi usuali. Come spesso riportato parlando di IoC, esso si basa sul principio di Hollywood: "non ci chiami, la chiameremo noi"; al di là della definizione forse un po' scherzosa, questo significa che in framework di questo genere non si è soliti chiamare i metodi messi a disposizione (come ad esempio nell'uso di librerie) ma piuttosto il proprio software viene invocato sulla base di determinate interfacce, offrendo i propri servizi all'ambiente che lo circonda. Non sarà quindi il proprio codice ad andare a dipendere pesantemente dalla libreria o dal framework utilizzato, quanto piuttosto sarà favorito lo sviluppo di componenti riutilizzabili che vanno ad integrarsi in modo trasparente e senza nessun riferimento esplicito all'interno di un ambiente che provvederà alla loro invocazione ed esecuzione.
Spostandoci quindi su Plexus, questo lascia all'utente il compito di dichiarare le relazioni esistenti fra insiemi di componenti, i quali si limitano a fornire specifiche interfacce di input/output. Gli oggetti da esso trattati non saranno e non devono essere istanziati dall'utente ma sarà Plexus a farlo e allo stesso modo sarà Plexus a farsi carico della gestione del loro stato e del loro ciclo di vita. Questo, come detto, favorisce il disaccoppiamento fra oggetti e framework e incoraggia lo sviluppo di software riutilizzabile.

L'idea sostanziale è quindi quella di rimuovere completamente dal codice il trattamento per la creazione e la gestione del ciclo di vita degli oggetti, evitando al contempo ogni sorta di dipendenza dal container in cui si va ad operare. Un altro degli aspetti interessanti è poi quello del Dependency Injection. In container come Plexus è spronata la programmazione basata su interfacce, tale da permettere la configurazione dei componenti e la cattura delle dipendenze che fra loro intercorrono, il tutto in un semplice file xml che definisca chi partecipa e come collabora. L'intuizione, senza scendere ulteriormente in dettagli, è che si possa gestire il ciclo di vita degli oggetti, ad esempio istanziandoli in copia unica (evitando il proliferare dei tanto amati e al contempo criticati Singleton), con conseguente capacità di passarli come argomenti all'invocazione di metodi per altri oggetti, il tutto configurato solo ed esclusivamente via xml (quindi senza interferire col codice). In altri termini, si possono scrivere elementi che non sanno niente l'uno dell'altro e vengono legati fra loro tramite direttive via xml.
Nei container Java tipici si hanno più tipi di iniezione delle dipendenze ma Maven si appoggia solamente a due di questi:

Quanto sopra non può e non vuole essere esaustivo e fatica a tratti anche ad essere chiaro, ma vuole dare un'infarinatura sul cosa sta alla base di Maven e qual è il motore che permette ad esso di funzionare. Già adesso forse è possibile avere un po' più chiaro il funzionamento, capire intuitivamente come vengono trattati i plugin e quindi i mojo in Maven (ovvero da Plexus, che sta di fatto alla base di tutto) e farsi un'idea di come sarà orientato lo sviluppo di nuovi componenti.

Mojo

La componente fondamentale in applicazioni basate su Maven è il Mojo (Maven plain Old Java Object), che va a collocarsi nella filiera produttiva rappresentando un singolo passo per il trattamento del software. Nello specifico non è il singolo mojo ma più mojo che concorrono per uno stesso obiettivo a dar luogo ad un plugin, come ad esempio nel caso del plugin maven-compiler-plugin che comprende i due mojo compile e testCompile. Intuitivamente, guardando la cosa dall'altro lato, se un mojo individua un singolo passo in un processo di generazione allora si può vedere quest'ultimo come l'esecuzione in un ordine fissato di una quantità di mojo per raggiungere un prefissato obiettivo (ogni mojo contribuirà permettendo il raggiungimento di una parte di esso, in base al proprio ruolo).

Descrittori

All'interno di un plugin, intuitivamente, così da permettere a Maven di utilizzarlo in modo corretto all'interno della filiera produttiva, si ha un descrittore che fornisce indicazioni sulla sua struttura e funzione, ovvero il file META-INF/maven/plugin.xml.
Per la verità, nello sviluppare plugin personalizzati non si avrà quasi mai la necessità di scrivere descrittori, poiché questi possono essere generati automaticamente a partire da annotazioni presenti direttamente nel codice del plugin.

Sviluppo di plugin

Senza scendere ulteriormente in particolari, quanto introdotto dovrebbe essere sufficiente a dare un'idea di come sia al contempo flessibile e semplice da usare il modello a plugin di Maven e, quindi, come sia facilmente estendibile con strumenti personalizzati a seconda dei propri desideri e delle proprie necessità. La stesura di un plugin si riassume di fatto nello sviluppo di una serie di mojo (ovvero di singole classi Java contenenti una serie di annotazioni per la generazione automatica dei descrittori) e poco più, un'operazione che con tutta probabilità non spaventerà ma, piuttosto, stuzzicherà l'interesse e motiverà gli sviluppatori che si avvicinano a Maven.
Un esempio di mojo, senza una descrizione e spiegazione accurata ma solo ed esclusivamente a titolo informativo, come riportato in [2] è il seguente:

  package org.sonatype.mavenbook.plugins;
  import org.apache.maven.plugin.AbstractMojo;
  import org.apache.maven.plugin.MojoExecutionException;
  import org.apache.maven.plugin.MojoFailureException;
  /**
    * Echos an object string to the output screen.
    * @goal echo
    * @requiresProject false
    */
  public class EchoMojo extends AbstractMojo
  {
    /**
      * Any Object to print out.
      * @parameter expression="${echo.message}" default-value="Hello World..."
      */
    private Object message;

    public void execute()
         throws MojoExecutionException, MojoFailureException
    {
         getLog().info( message.toString() );
    }
  }

Come si può notare, tanto chiaro quanto semplice!

Conclusioni

Questa breve gita nel mondo di Maven è finalmente o purtroppo terminata. Qualcuno potrà forse obiettare che in questo articolo si è detto tanto ma non si è detto niente e proprio questo, in realtà, era lo scopo.
Maven è un mondo relativamente nuovo e abbastanza articolato, può mostrare risvolti complessi ma allo stesso tempo guidare con grazia e semplicità gli utenti che vi si avvicinano in un percorso attraverso un uso immediato, intuitivo e completo fin da subito arrivando ad una consapevolezza più dettagliata delle sue potenzialità. Maven non è forse lo strumento definitivo nel suo campo ma senza dubbio ha tutte le caratteristiche per poter dire la sua e ritagliarsi una discreta fetta di utenza che, per quanto esigua possa essere al giorno d'oggi, sarà contagiosa con la sua soddisfazione.

La speranza è quella, con questi pochi cenni, di aver trasmesso la completa soddisfazione di chi vi scrive nell'usare Maven in ogni nuovo progetto, da alcuni mesi ad oggi. La speranza è che sia stuzzicata la curiosità e attraverso i riferimenti indicati in bibliografia, liberamente scaricabili dal web come riportato anche sul sito stesso di Maven, il lettore possa muovere i primi passi nel mondo di Maven e chiedersi (come in una nota pubblicità) perché nessuno gliene avesse mai parlato prima...

Riferimenti bibliografici

[1] Apache Maven Project:
http://maven.apache.org

[2] Maven - The Definitive Guide:
Readable HTML and Free PDF Download

[3] Better Builds with Maven:
Free PDF Download

[4] Apache Maven Project (Available Plugins):
http://maven.apache.org/plugins/index.html

[5] Plexus:
http://plexus.codehaus.org/index.html

[6] Maven Repository:
http://mvnrepository.com/



L'autore

Michele Caini è studente nel corso di laurea specialistica in Ingegneria Informatica presso l'Università degli Studi di Firenze.
Fedele utente Gentoo e appassionato programmatore, predilige l'uso di C/C++ e Perl fra tutti i linguaggi conosciuti ma non pone limiti allo studio e l'uso di nuovi linguaggi e strumenti di ogni genere, è da poco approdato al mondo di JavaEE e ne è rimasto affascinato.
Cerca di dare il suo contributo come sviluppatore quando e come possibile anche se (non smetterà di ripeterselo) non è mai abbastanza per i suoi gusti.




Sinistra <- Installazione e configurazione di un server Linux - Indice Generale - Copertina - Indice -> Destra