Con questo articolo si prosegue nella presentazione dei principali componenti di Maven. In particolare, viene illustrato un componente centrale del framework Maven: il file POM.xml ossia la descrizione dichiarativa, in XML, del progetto Java.
Introduzione
Con questo articolo si prosegue nella descrizione dei principali componenti di Maven avviata nell‘articolo precedente. In particolare, l‘attenzione è focalizzata su uno degli elementi fondamentali di Maven: la descrizione dichiarativa del progetto contenuta nel file pom.xml. POM è l‘acronimo di Modello a Oggetti del Progetto (Project Object Model) e contiene i meta-dati del progetto stesso memorizzati utilizzando il formato XML.
A differenza dei file di build Ant, contenenti la descrizione in termini procedurali dei vari processi (build, deployment, etc.), la filosofia del file POM è drasticamente differente e prevede la sola descrizione del progetto: i meta-dati. Questa filosofia di base, tuttavia, può essere alterata attraverso l‘inclusione di comandi Ant.
La struttura del file POM è molto ricca e non sempre immediata o intuitiva (tanto che anche la relativa descrizione, per motivi di spazio, è stata sviluppata nell‘arco di due articoli, vale a dire il presente e la prossima parte IV). Tuttavia ciò non deve spaventare, sia perché non è necessario per ogni progetto definire tutte le sezioni, sia perché Maven fornisce una serie di comportamenti di default sufficienti per la maggior parte dei progetti. Inoltre, una volta preparati i vari file POM per il primo progetto, questi si prestano a essere riutilizzati per i successivi, con minime variazioni.
La ricchezza del file POM rappresenta un punto di forza di Maven in quanto offre tantissimi punti in cui è possibile alterarne il comportamento di default. Questa elevata versatilità , se da un lato può presentare problemi di comprensione, dall‘altro dovrebbe conferire anche una certa sicurezza di utilizzo: è difficile trovarsi di fronte a scenari di build non previsti, o di fronte ad integrazioni/problematiche non gestibili dalla struttura del POM. Ciò evita di di dover ricorrere a script complicati e difficili da mantenere, come spesso avveniva in passato.
Ricordarsi sempre che i vari elementi permettono di dichiarare il comportamento desiderato da Maven e che le diverse sezioni, come per esempio le coordinate, sono ripetute in diversi elementi con significati differenti dipendenti dal contesto.
Struttura
Il diagramma di figura 1 illustra la struttura concettuale del file pom.xml la cui implementazione è riportata nel listato che segue.
4.0.0
...
...
...
... packing>
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
Figura 1. Struttura logica del file POM.xml. Gli elementi sono stati raggruppati secondo un ordine logico che non sempre rispetta quello di apparizione all‘interno del file POM.
Dall‘analisi del diagramma è possibile evidenziare una delle maggiori differenze con i file build.xml ant: il file pom.xml contiene la dichiarazione del progetto e non le azioni da eseguire durante per il processo di build (ossia le procedure). Sebbene, sia possibile includere nei file pom.xml una serie di maven-antrun-plugin che permettono di eseguire task Ant. I file pom.xml definiscono “chi”, “cosa” e “dove”, mentre i file di build si limitano al “quando” e al “come”
Come si può notare il file POM, in prima analisi, può essere suddiviso in cinque macro componenti, come segue.
POM relationships. Questa parte permette di organizzare i progetti attraverso una serie di file POM opportunamente relazionati. In particolare, le relazioni disponibili sono: dipendenza, ereditarietà e aggregazione.
build settings. In questa sezione sono definite le varie informazioni richieste dal processo di build, come, per esempio, una serie di proprietà , il goal, le varie directory, i plug-in da utilizzare incluse le relative relazioni, le risorse da utilizzare.
project information. Questa sezione è dedicata a eventuali informazioni supplementari che è possibile definire per il progetto. In particolare, in questa sezione trovano posto informazioni come il nome del progetto, la descrizione, l‘URL, le persone che vi hanno preso parte, etc.
build environment. Questo elemento contiene le varie informazioni relative all‘ambiente di build, come le impostazioni del software utilizzato per gestire i problemi, di quello per la integrazione continua, per la gestione della configurazione, etc.;
Maven environment. Questa sezione è demandata alla configurazione dell‘ambiente Maven, pertanto è possibile impostare i vari repository, la gestione della distribuzione, i profili utilizzati per modificare il comportamento di Maven in situazioni ben definite, e così via.
Le relazioni tra file POM (POM relationships)
Figura 2. Rappresentazione grafica della sezione delle dipendenze.
Coordinates (le coordinate)
I campi assolutamente obbligatori di un POM sono: groupId (identificatore del gruppo), artifactId (identificatore del manufatto) e version (versione), necessari per identificare univocamente un file di distribuzione. Di questi, i primi due possono essere ereditati da un POM genitore.
Il groupId identifica univocamente un insieme di progetti appartamenti alla stessa organizzazione, team, etc. Per esempio il core Maven dichiara la seguente stringa per il groupId: org.apache.Maven. La notazione con il punto, quantunque sia un‘ottima convenzione, non è nà© obbligatoria nà© tanto meno deve corrispondere alla struttura in package del progetto. Tuttavia, una volta memorizzato il file di distribuzione nel repository, il groupId si comporta come i package Java: rappresenta il percorso relativo all‘interno del repository a tal fine il punto viene sostituito dallo barretta obliqua dipendente dal sistema operativo sottostante (“/” in UNIX).
Il campo artifactId indica il nome del progetto. La combinazione con il campo precedente genera il nome univoco del progetto tra l‘insieme di tutti i progetti (in maniera del tutto analogo alle classi Java) a netto della versione.
Il campo version è l‘ultima parte necessaria per completare il nome. In particolare, indica una specifica versione del progetto. Questo campo dovrebbe essere mantenuto sincronizzato con le variazioni che avvengono nel sistema di versionamento che gestisce i sorgenti del progetto. Inoltre, è utilizzato nel repository dei manufatti (tipcamente file jar) per mantenerli separati (diversi sistemi, per esempio, potrebbero utilizzare diverse versioni di una stessa libreria).
Dependencies (dipendenze)
Le coordinate di un progetto sono un elemento molto importante poiché costituiscono il criterio utilizzato da Maven per referenziare i vari progetti. Queste, per esempio sono necessarie per gestire il grafo delle dipendenze. Area che tradizionalmente ha creato non pochi problemi nell‘elaborazione dei script di build. In effetti, la quasi totalità dei progetti, per poter essere costruiti (build) e eseguiti correttamente, necessita di includere opportune librerie sia appartenenti al progetto stesso (sotto-progetti), sia fornite da terze parti. La sincronizzazione delle varie librerie, il prelievo della versione corretta , la relativa distribuzione sono aspetti che da sempre hanno complicato il processo di build dei vari progetti.
Maven è in grado di gestire queste problematiche attraverso le liste di dipendenze, eseguendo il download automatico dei manufatti richiesti ed effettuando i necessari collegamenti durante le varie fasi. Maven, inoltre, risolve automaticamente eventuali dipendenze transitive, ossia esegue la stessa procedura anche per eventuali dipendenze della libreria presente nella lista delle dipendenze del progetto in esame. Il listato seguente mostra un esempio ricorrente di dipendenze: l‘inclusione del librarie di test junit e di log log4j.
junit
junit
4.0
jar
test
true
log4j
log4j
1.2.6
jar
Il campo scope (opzionale) prevede i seguenti cinque valori
- compile: valore di default, prevede che il file incluso sia disponibile per tutti i percorsi.
- provided: è molto simile al precedente, tuttavia asserisce che il file è fornito da un‘altra entità , come il JDK o un container. È disponibile solo per il classpath di compilazione e non è transitivo.
- runtime: indica che la dipendenza non è richiesta a tempo di compilazione, ma solo in esecuzione. Pertanto è disponibile solo per i percorsi di runtime e di test.
- test: specifica che la dipendenza non è richiesta per un uso tipico, ma solo per il test del progetto.
- system: è molto simile a provided con l‘eccezione che è necessario fornire esplicitamente il jar che contiene la dipendenza. Pertanto, il relativo file è sempre disponibile e non è necessario eseguire la ricerca nel repository.
Il campo optional dichiara opzionale (quindi non necessaria) una dipendenza quando il progetto è a sua volta dichiarato nella lista delle dipendenze di un altro (tipicamente, le dipendenze transitive sono dichiarate opzionali). Nel listatino 2, il JUnit è dichiarato opzionale in quanto, qualora il relativo progetto dovesse apparire nella lista delle dipendenze di un altro, non è necessario includerlo.
Un altro modo di ottenere un risultato simile consiste nel dichiarare le esclusioni all‘interno della sezione delle dipendenze. Ciò fa sì che i progetti dichiarati, contenuti all‘interno della dipendenza, non siano inclusi. Pertanto si ottiene l‘effetto di escludere specifiche dipendenze transitive.
Inheritance (ereditarietà )
Un‘altra caratteristica molto potente e unica di Maven è la possibilità di relazionare tra loro i progetti con legami di ereditarietà , un po‘ come avviene con le classi Java. Sebbene framework come Ant permettano di simulare, in qualche misura, la relazione di ereditarietà , i file POM di Maven prevedono nativamente la possibilità di dichiarare tale relazione. I progetti genitori e quelli esclusivamente aggregabili devono specificare il valore POM all‘interno del campo packaging. Questo campo serve per indicare la natura del manufatto (file di distribuzione) prodotto. Per default, Maven suppone che si voglia generare un file JAR. Altre alternative sono: POM (questo valore è riservato per progetti “genitore” e per quelli aggregabili), war, ear, Maven-plugin, ejb, ear, rar e par.
Gli elementi ereditabili dai discendenti che si possono specificare in un POM genitore sono:
- dependencies (dipendenze);
- developers and contributors (lista degli sviluppatori e contributori);
- plugin lists (elenchi dei plugin);
- reports lists (elenchi dei reports);
- plugin executions with matching ids (esecuzioni dei plugin con identificatori corrispondenti);
- plugin configuration (configurazione dei plugin).
POM genitori sono identificati da un‘apposita sezione da includere nel progetto, denominata parent. Questa, oltre a includere le usuali informazioni (groupId, artifactId, version), dispone di un ulteriore campo, non obbligatorio, denominato relativePath. Questo indica il path in cui cercare il progetto genitore specificato, prima di accedere al repository locale e a quello remoto.
Qualora in un file POM.xml si intenda ereditare da un altro file, le relative coordinate vanno specificate all‘interno della sezione
A tal proposito, vediamo questo esempio di relazione di eredità . Si noti che il file in questione non riporta i campi groupId e version in quanto ereditati dal POM genitore.
4.0.2
com.mokabyte.finance
frontoffice
2
PriceBlotter
La sezione dependencyManagement è del tutto equivalente a quella delle dipendenze (dependecies, dependency, groupId, artifactId, version, etc.). Attraverso tale sezione, i file POM genitore sono in grado di definire informazioni relative alle dipendenze utilizzate nei progetti discendenti. Un classico esempio consiste nel definire una dipendenza da una particolare versione di JUnit, dichiarata attraverso le relative coordinate (junit:junit:4.0). Qualora si decidesse di utilizzare questa caratteristica, i file POM discendenti potrebbero limitarsi a definire, nella propria lista di dipendenze, solo il campo groupId e artifactId per JUnit. La versione sarebbe invece ottenuta dal file POM genitore. Quindi, i file POM genitore, oltre a predefinire il valore di specifici campi, possono anche impostare il valore di opportune dipendenza senza che ciò incida sul proprio ciclo di vita.
Aggregation (aggregazione)
Un progetto Maven ha la possibilità di aggregare diversi moduli; in tal caso prende il nome di progetto multi-modulo (multimodule) o aggregatore (aggregator). Questi moduli sono progetti dichiarati all‘interno del file POM ed eseguiti come un gruppo. Chiaramente, il file POM aggregante è a conoscenza dei propri moduli, mentre il contrario non è vero (per default).
Abbastanza frequente è il caso in cui un file POM genitore definisce anche i moduli inclusi, ossia i progetti Maven che ereditano da questo. Ciò rende possibile eseguire un comando Maven sul file e quindi ottenere che lo stesso sia eseguito su tutti i relativi moduli. Per esempio, è possibile compilare con un solo comando un file POM e tutti i moduli associati.
client-system-endpoint
admin-dto
admin-gui
client-api
server
La sezione informazioni contiene una serie di dati a carattere generale, come per esempio la lista dei programmatori coinvolti nel progetto, sia una serie di informazioni richieste deai vari plug-in.
Build settings (impostazioni del processo di build)
Figura 3. Rappresentazione grafica della sezione relativa alle impostazioni del build.
Properties (proprietà )
Maven mette a disposizione una serie di proprietà , molto simili a quanto avviene con Ant. Queste non sono altro che dei contenitori di dati accessibili all‘interno del file POM. Si tratta di una sorta di variabili del file POM, che permettono di sostituire il riferimento ai valori. La notazione prevede un prefisso denotato dai caratteri dollaro parentesi graffa (${) ed un suffisso dato dalla parentesi graffa: ${
- env.
: il prefisso env fa riferimento alle variabili di ambiente, come per esempio il percorso (${env.PARH}); - project.
: in questo caso il suffisso project permette di accedere agli elementi del file POM. Come lecito attendersi, la notazione da utilizzare è quella con il punto. Per esempio, la proprietà ${project.version} si riferisce all‘emento ; - settings.
: consistentemente con il caso precedente, anche questa notazione permette di accedere agli elementi all‘interno di un file di configurazione di Maven. In questo contesto, tuttavia, il file acceduto è settings.xml. Per esempio, la proprietà ${settings.offline} si riferisce all‘emento ; - java.
: permette di accedere alle proprietà di sistema messe a disposizione dalla API java.lang.System.getProprietes. Per esempio, ${java.home}; : fa riferimento a valori specificati nella struttura … ;
baseBuild (informazioni base del processo build)
La struttura di build, come lecito attendersi, contiene importanti informazioni relative al processo build. Queste includono: la directory dei file sorgente, la configurazione dei vari plug-in, e così via. Come visto in precedenza, molte di queste informazioni sono ereditate dal super POM e pertanto è necessario specificarne i valori solo qualora si intenda sovrascrivere le impostazioni di default.
Da notare che l‘elemento di build è presente in due parti del file POM (vedi il listato che segue), rispettivamente:
- project build, progetto di build. Questo è l‘elemento che contiene le informazioni tipicamente utilizzate durante il progetto di build vero e proprio.
- properties build, si tratta di alterazioni delle suddette informazioni che hanno luogo solo in particolari circostanze (i profili del POM sono argomento del prossimo articolo).
Ed ecco appunto, le due componenti delle informazioni build:
...
...
La struttura riportata nel codice che segue, mostra la struttura comune di entrambi i build, per questo motivo è denominata baseBuild.
install
${basedir}/target
${artifactId}-${version}
filters/filter1.properties
...
Gli elementi contenuti sono:
- defaultGoal: rappresenta il goal o la frase da eseguire qualora nessuna venga specificata;
- directory: specifica la directory dove i file prodotti dal processo di build devono venir memorizzati (la directory di default è la directory target, sottodirectory di quella base del progetto);
- finalName: nome del progetto da assegnare al fine di distribuzione; in questo caso il default è il nome dell‘artifact, seguito da un trattino e dal numero di versione ($artifactId-$version);
- filter: permette di far riferimento a proprietà (nome, valore) definiti in file esterni (.properties).
Risorse
Altra importante sezione del build è quella relativa alle risorse. Si tratta di file che, seppur tipicamente non contengano codice sorgente, e quindi non debbano essere compilate, devono essere incluse nel file di distribuzione oppure utilizzate per altri scopi, come la generazione del codice. La sezione risorse, prevede le seguenti informazioni:
- resoruces: contiene la lista delle varie risorse. Ognuna specifica i file da includere e relativa allocazione.
- targetPath: specifica la struttura della directory dove ubicare il file delle risorse. Il default è la directory base, tuttavia la convenzione adottata nei file JAR è quella di includere le risorse a partire dalla directory META-INF.
- filtering: si tratta di un elemento booleano che indica se abilitare o meno il filtraggio della risorsa durante la rleativa copiatura.
- direcotry: specifica l‘ubicazione delle risorse. L‘ubicazione di default è: $basedir/src/main/resources.
- includes: contiene il nome dei file, o opportuni pattern, ottenuti per mezzo dei caratteri jolly, dei file da prendere in considerazione tra quelli presenti nella directory specificata al punto precedente.
- excludes: si tratta della sezione speculare a quella precedente. Infatti, in questo caso si specificano i file da escludere. Attenzione: qualora si verificassero conflitti con la sezione precedente (uno o più file compatibili con le direttive presenti in entrambe le sezioni), sarebbe l‘esclusione a prevalere;
- testResources: questa sezione è dedicata a risorse utilizzate solo a fini di test e pertanto non devono essere incluse nel file di distribuzione. La struttura è equivalente all‘intera sezione delle risorse.
...
META-INF/mokabyte
false
${basedir}/src/main/mokabyte
configuration.xml
**/*.properties
...
...
Plug-in
La sezione del build, oltre a definire le classiche informazioni necessarie al processo di build, come la directory sorgente, quella di destinazione, etc., contiene una sezione dedicata alla configurazione dei plug-in.
org.apache.Maven.plugins
Maven-compiler-plugin
1.5
Per quanto riguarda gli elementi groupId, artifactId e version (qualora sia necessario specificare la versione) non dovrebbe essere necessario specificare ulteriori informazioni: si tratta delle coordinate del plug-in.
Gli altri elementi sono:
- extensions: si tratta di un attributo booleano, il cui valore specifica se caricare, o meno, le estensioni del plug-in. Il valore di default è false.
- inherited: si tratta di un altro attributo booleano, il cui valore indica se eventuali file POM, ereditanti da quello corrente ereditino la configurazione del presente plug-in;
- configuration: si tratta di una sezione specifica di singoli plug-in. In questa sezione è possibile specificare una qualsiasi proprietà che una classe MOJO potrebbe richiedere. MOJO è un gioco di parole utilizzato per definire i Maven POJO (Plain Old Java Object, Semplici Vecchi Oggetti Java), ossia classi Java, simili a JavaBeans. Senza entrare troppo nel dettaglio dei plug-in, argomento dei prossimi numeri, ciascuno di essi è composto da uno o più MOJO e ciascun MOJO implementa un goal del plug-in. I parametri specificati in questa sezione non sono elementi specificatamente richiesti dallo schema POM, ma valori utilizzati dallo specifico plug-in.
- dependencies: si tratta di una sezione particolarmente ricorrente all‘interno del POM. Pertanto è lecito attendersi un comportamento simile. La differenzaè che le dipendenze definite in questa sezione non sono al livello di progetto, bensì al livello di plug-in. Pertanto, in questa sezione è possibile variare la lista delle dipendenze di ciascun plug-in. Per esempio, è possibile rimuovere, utilizzando la clausola di esclusione (exclusions), dipendenze runtime non utilizzate.
- executions: permette di configurare il goal di un plug-in. Ogni plug-in può definire diversi goal e ciascuno può richiedere una diversa configurazione.
executions è a sua volta caratterizzato da diversi sottocampi, che sono quelli seguenti:
- id: è l‘identifcatore del particolare blocco di esecuzione. Quando la phase è in escuzione, Maven produce il seguente output: [plugin:goal execution: id].
- goals: definisce la lista dei goals specificati dal blocco di esecuzione.
- phase: rappresenta la phase in cui la list di goal viene eseguita. Pertanto rende possibile di associare qualsiasi goal a qualsiasi phase del ciclo di build, alterando, il comportamento di default di Maven.
- inherited: like all other equivalent tag utilizzate all‘interno del file POM, l‘impostazione di questo attributo a false fa si che l‘esecuzione non venga passata ad eventuali POM ereditanti.
- configuration: come definito di sopra, con l‘eccezione che la configurazione è specifica della lista dei goals, piuttosto che a tutti i goal del plug-in.
Maven-antrun-plugin
echodir
run
verify
false
Build Dir: ${project.build.directory}
Il listatino precedente mostra una configurazione del plug-in di autorun. In particolare, si occupa di collegare il goal autorun:run alla fase di verify. Inoltre, il plug-in esegue il task atto a visualizzare la directory di build durante l‘esecuzione. Per finire, poiché l‘attributo inherited è posto a false, ne segue che l‘esecuzione non viene passata a eventuali file POM discendenti.
Plugin management (gestione dei plug-in)
Questa sezione del file POM (
...
org.apache.Maven.plugins
Maven-jar-plugin
2.0
pre-process-classes
compile
jar
pre-process
...
Esaminando il codice sopra riportato, si può notare che la struttura di pluginManagement e plugin è sostanzialmente equivalente. Tuttavia, le informazioni specificate all‘interno di quest‘ultima sezione sono destinate a rimanere confinate all‘interno di un file POM, mentre quelle specificate nella sezione pluginManagement sono visibili a tutti i file POM ereditanti. Ciò gli permette di definire sezioni plugin come mostrato di seguito:
org.apache.Maven.plugins
Maven-jar-plugin
Building Element Set (insieme degli elementi del build)
A questo punto si entra nella sezione più strettamente legata al progetto di build i cui elementi sono due: la sezione delle directory e quella delle estensioni.
Directories
Gli elementi presi in considerazione da Maven possono essere suddivisi in due grandi categorie: file sorgenti e compilati. Per quanto riguarda la prima categoria, è possibile indicare le directory dei file sorgente Java, scripts, e test. Per quanto riguarda i file compilati, invece, è possibile dichiarare le directory delle classi (.class) dei sorgenti e dei test. In termini del POM questi elementi sono contenuti prossimo listato.
I percorsi possono essere specificati sia in termini assoluti (non raccomandabile), sia in modo relativo a partire dalla directory di base (proprietà ${basedir}). Come descritto in precedenza, questa intera sezione può essere omessa qualora si accetti la configurazione di default di Maven.
${basedir}/src/main/java
${basedir}/src/main/scripts
${basedir}/src/test/java
${basedir}/target/classes
${basedir}/target/test-classes
...
Extensions (estensioni)
Le estensioni sono un elenco di manufatti da utilizzare durante il processo di build. Come suggerisce il termine permettono di variare il processo di build. Queste variazioni possono essere vere e proprie estensioni, oppure possono forzare l‘esecuzione di determinati plug-in. In questo caso si può cambiare completamente il processo di build di Maven. Un esempio è riportato nel codice che segue e consiste nell‘aggiungere il servizio ftp all‘utility Wagon, utilizzata per prelevare e memorizzare file in server remoti.
org.apache.Maven.wagon
wagon-ftp
1.0-alpha-3
Reporting (reportistica)
Maven, oltre ad eseguire le tipiche operazioni di compilazione, è in grado di generare una serie di informazioni in formato HTML, e pertanto fruibili per mezzo di un comune browser. La fase in cui si producono queste informazioni (a tutti gli effetti un sito Internet) è chiamata site. Questa si avvale delle informazioni incluse proprio nella sezione reporting, che si occupa di definire e configurare i relativi plug-in.
Questa sezione è assolutamente consistente con la sezione di build, la differenza principale è che il controllo dei goal avviene nella sezione reportSet anzichà© executions. Pertanto, avendo compreso il funzionamento della sezione dedicata al build, quella del reporting dovrebbe risultare assolutamente immediata. Gli unici elementi diversi sono: excludeDefaults e outputDirectory. Il primo, di tipo boolean, se impostato a true, forza Maven a non produrre le informazioni tipicamente generate per default. Queste sono prodotte utilizzando il plug-in: Maven-project-info-reports-plugin. L‘altro elemento diverso è outputDirectory, che, come suggerisce il nome, specifica la directory in cui produrre il sito delle informazioni. Questa, per default, è $basedir/target/site. Ecco un esempio di sezione reporting.
...
${basedir}/target/site
Maven-project-info-reports-plugin
...
Conclusioni
In questo terzo articolo si è avviata l‘analisi di uno degli elementi fondamentali di Mavem, il file POM. In particolare sono state presentate le prime due macro componenti: la gestione delle relazioni tra file POM e le impostazioni di build. Per molti si tratta di un elemento di grande importanza, in quanto ne evidenzia un certo stato di maturità e di versatilità , sebbene ciò possa spesso andare a discapito della facilità di apprendimento. A semplificare parzialmente la situazione interviene il fatto che molti valori e sezioni del file POM non sono strettamente necessarie in progetti ordinari. In molti casi, infatti, è sufficiente accettare i valori e il comportamento di default.
Particolarmente apprezzabile è lo sforzo di astrazione che ha portato alla formulazione della struttura del file POM e quindi alla possibilità di incapsulare in un solo file, di carattere dichiarativo, tutti gli aspetti di un progetto. Questo permette di eliminare una situazione caotica, spesso frequente in progetti del passato, costituita dalla presenza di dozzine di file di script da utilizzare per i processi di build, deploy, etc.
Inoltre a semplificare ulteriormente la situazione inerviene il proliferare di tutta una serie di tool e di ambienti grafici che possono essere utilizzati congiuntamente o come livello di astrazione di Maven.
Riferimenti
[1] “Ant, La Formica Operosa che beve Caffè” Mokabyte 112 e 113
[2] Luca Vetti Tagliati, “Java Quality Programming” capitolo 7
[3] Maven
http://Maven.apache.org/
[4] Jelly
http://jakarta.apache.org/commons/jelly/
[5] Vincent Massol – Jason Van Zyl”Better Builds With Maven”, Mergere
http://www.mergere.com/m2book_download.jsp
[6] Luca Vetti Tagliati, “Maven: best practice applicate al processo di build e rilascio di progetti Java. Parte I”, Mokabyte 114, Gennaio 2007
[7] Luca Vetti Tagliati, “Maven: best practice applicate al processo di build e rilascio di progetti Java. Parte II” , Mokabyte 115, Febbraio 2007
[8] http://Maven.apache.org/plugins/Maven-enforcer-plugin/rules/requireOS.html