In questo articolo presentiamo un altro componente fondamentale di Maven: i repository server. Questo meccanismo è stato disegnato per semplificare distribuzione e condivisione dei file.
Introduzione
In questo articolo si prosegue con la presentazione dei componenti principali di Maven. In particolare l’attenzione è focalizzata sul repository server (più semplicemente repository). Si tratta di una sorta di file system strutturato disegnato per memorizzare i manufatti dei progetti. Questi non sono altro che le librerie utilizzate (file JAR), i “distribuibili” prodotti (JAR, WAR, EAR), plug-in vari, e così via. Come illustrato nei primi articoli (cfr. [8]), ogni manufatto è univocamente identificato dalle proprie coordinate ottenute per mezzo della tripla:
- group id
- artifact id
- numero di versione
Sempre negli articoli precedenti, abbiamo anticipato come Maven preveda due tipologie di repository:
- locali
- remoti
I repository appartenenti alla prima categoria sono copie installate tipicamente negli dischi rigidi dei vari computer e svolgono il duplice compito di funzionare da cache, riducendo il numero di download remoti, e di memorizzare e rendere disponibili i manufatti temporanei risultato del processo di build (file non ancora distribuiti in versione stabile, ossia gli snapshot).
I repository remoti, invece, sono nella maggior parte dei casi repository esterni alla propria organizzazione e quindi gestiti da terze parti.
Si ricordi che Maven permette di definire in due file la lista dei repository che si desidera utilizzare:
- POM. In questo caso è necessario impostare le sezioni
… . Poiche’ è una sezione ereditabile, è possibile limitarsi a impostarla nel POM genitore e quindi ereditarla automaticamente in tutti i POM figli. Da notare che i repository di default sono già presenti nel POM standard di maven: il famoso super POM. - settings. In particolare, è necessario impostare l’apposita lista nel file dei settings memorizzato localmente (~/.m2/settings.xml; in macchine Windows, un tipico path è C:Documents and Settings
.m2).
Indipendentemente dalla loro tipologia, i repository, come logico attendersi, mantengono la medesima struttura.
Il repository centrale più famoso, per motivi storici, è sicuramente iBiblio (http://www.ibiblio.org/maven/). In effetti, l’intero meccanismo del repository Maven si deve proprio all’incontro tra il team Maven e quello di iBiblio. Data questa notorietà, è praticamente impossibile utilizzare direttamente il repository iBiblio: le prestazioni sono insoddisfacenti. Lo stesso sito invita a utilizzare gli appositi siti mirror, che però non presentano grosse migliorie in termini di prestazioni.
Struttura tipica dei repository Maven
La figura 1 mostra un esempio tipico di struttura dei repository Maven. Visto quanto riportato poco sopra circa l’efficienza dei repository remoti, è fortemente consigliato alle organizzazioni, soprattuto di dimensioni medio-grandi, di gestire repository “remoti interni” a uso e consumo dei propri team di sviluppo. Ciò fornisce un grande aiuto per sopperire alle inefficienze dei repository globali e dei relativi mirror.
Figura 1 – Esempio tipico di organizzazione dei respository server di Maven.
Ai repository remoti si accede tipicamente attraverso diversi protocolli di rete, tra cui file://, ftp://, http:// etc. Per la precisione, per la gestione ed il deployment dei manufatti, Maven si affida al framework Wagon (http://maven.apache.org/wagon/) che, attualmente, supporta i seguenti protocolli: File, HTTP, HTTP lightweight, FTP, SSH/SCP, WebDAV e SCM (quest’ultimo non è ancora stato rilasciato).
Il flusso tipico dei manufatti prevede un passaggio dai repository centrali via via a quelli locali; come dire dal governo centrale via via a quelli locali. Il repository centrale dell’azienda esegue il download dei manufatti direttamente dai repository centrali “globali”. Questi, come mostrato in figura 1, sono normalmente organizzati in un repository centrale e una serie di mirror distribuiti per le varie regioni geografiche. Il repository centrale dell’azienda, a sua volta, si incarica di mantenere aggiornati i repository locali dei vari progetti che, a loro volta, mantengono aggiornati i repository locali dei computer di “sviluppo”. Questa organizzazione piuttosto gerarchica, non è tuttavia sempre rispettata. In effetti, i manufatti distribuiti dalla Sun, vengono prelevati direttamente dal relativo repository (probabilmente per problemi di licenze). Ciò rende necessario configurare il server proxy come mostrato nel seguente listatino.
true
http
proxy.somewhere.com
8080
proxyuser
somepassword
www.google.com|*.somewhere.com
Questo proxy può essere utilizzato per scaricare manufatti proprietari mantenuti nelle aziende proprietarie (questi non possono essere memorizzati nei repository centrali sempre per questioni di licenze). Da notare che qualora il proxy richieda le credenziali minime (user e password) queste vanno definite (in chiaro!) nel file delle impostazioni ($user.home/.m2/settings.xml), soluzione che probabilmente non è la più sicura in assoluto.
Il flusso tipico dal globale via via verso il locale è invertito qualora si desideri pubblicare i vari manufatti. Lo scenario tipico consiste nell’avere i manufatti prodotti da uno specifico progetto pubblicati a ritroso fino a raggiungere il repository del progetto o anche quello centrale dell’azienda. Qualora invece si dovesse lavorare in progetti open source non sarebbe infrequente che il flusso a ritroso giunga fino alla pubblicazione sul repository centrale globale.
Come riportato negli articoli precedenti, la pubblicazione dei vari manufatti avviene attraverso il plug-in deploy che si occupa, tra l’altro di generare i vari file di checksum.
A questo punto dovrebbe essere chiaro che uno dei grandi vantaggi generati dal meccanismo dei repository consiste nell’avere una singola copia dei vari manufatti, indipendentemente da quanti progetti e sotto-progetti li utilizzino. Questo, tra l’altro, permette di eliminare tutta una serie di problemi legati alla garanzia di consistenza, integrità, etc.
Perche’ il repository è un concetto importante…
Al fine di comprendere appieno l’importanza dei repository, è necessario far mente locale alla situazione tipica degli ambienti che non utilizzano Maven.
In questi casi, il processo standard di condivisione dei manufatti tra i diversi team di sviluppo prevede, nella migliore delle ipotesi, la relativa memorizzazione nel SCM (Software Configuration Management, gestione della configurazione del software, come CVS, Subversion, IBM Rational ClearCase, etc) del progetto, mentre, nei casi più drammatici, è addirittura possibile assistere a un continuo scambio di file JAR via e-mail!
In ogni modo, questa pratica presenta una serie di problemi. Il primo è di ordine concettuale: i manufatti risultanti del processo di compilazione, in quanto sempre riproducibili, non dovrebbero essere memorizzati nel SCM. Oltre a questo problema strettamente di carattere filosofico, ne sono presenti altri decisamente più pratici. Per esempio, la continua memorizzazione dei manufatti nel SCM può dar luogo a una notevole occupazione di spazio su disco.
Poi vi è il problema della versione. Qualora non presente, è naturale avere a che fare con tutta una serie di problemi di allineamento, di controllo della sincronizzazione, etc. A tal proposito, vi dice nulla il seguente scambio di battute? Sviluppatore Uno: “La libreria che avete rilasciato non funziona!”, Sviluppatore Due: “Quale versione stai utilizzando?”, Sviluppatore Uno: “Come si fa vedere la versione?” e via discorrendo…
Qualora invece la versione sia gestita, potrebbe rendersi necessario un continuo aggiornamento delle dipendenze del proprio progetto (è il caso in cui al nome dei vari JAR sia aggiunta l’indicazione della versione) oppure si potrebbe ricadere in un caso simile alla mancanza di gestione della versione, qualora questa sia memorizzata all’interno dei vari file. Inoltre, spesso, la condivisione dei file richiede tutta una serie di passi manuali. Infine, la presenza del repository rende il processo di check out del progetto più veloce: non è necessario eseguire il check-out di file binari di grandi dimensioni. Questi sono scaricati, secondo un meccanismo di lazy loading, una volta soltanto attraverso il repository.
Struttura
Giacche’ il local repository non è altro che una sezione del file system (e quindi una struttura gerarchica di file e directory) organizzata secondo le direttive di Maven, è possibile ispezionarla con qualsiasi tool di esplorazione file, come il Windows Explorer. In figura 2 è riportata una classica struttura di un repository locale.
Figura 2 – Esempio di repository locale.
Da un’attenta analisi di questa struttura risulta difficile comprendere il criterio alla base dell’organizzazione di tale repository… Questo è dovuto al fatto che si è passati da un’organizzazione iniziale (quella di Maven 1.x) spesso denominata legacy, a una più razionale e scalabile (quella di Maven 2); per mantenere la retrocompatibilità, principio sacro per gli ambienti Java, si è deciso di lasciare convivere le due organizzazioni.
Un altro elemento da notare è la presenza di diversi file. In particolare, è presente il file POM, i file relativi al controllo di integrità (checksum hash) relativi sia ai file POM sia ai vari manufatti, etc. Questi servono per verificare/assicurare l’integrità dei vari manufatti dopo il loro trasferimento tra i vari repository (il servizio che si occupa di ciò è denominato “dependency management engine”). In particolare, e molto ma molto brevemente, dato un file o un qualsiasi testo, il processo di hashing si occupa di generare una cifratura di lunghezza molto inferiore a quella del file iniziale, legata al contenuto del file stesso. Pertanto, avendo a disposizione il file originario è sempre possibile eseguire l’algoritmo di hashing e quindi confrontare la cifratura ottenuta con quella originaria. Se le due differiscono, allora si è verificato un errore. Ma può anche accadere di trovarsi nella situazione in cui il file di controllo sia corrotto.
Nella nuova struttura di Maven, questi file (JAR, POM) vengono definiti “primary” per distinguerli dai “secondary” come JavaDoc, file di testo, etc., memorizzati in una struttura a parte.
La nuova struttura di Maven (dalla versione 2) non dovrebbe presentare troppe sorprese al pubblico di programmatori Java, poiche’ ci sono grosse similitudini con la convenzione utilizzata per i package.
In particolare, la genarchia del repository centrale prevede:
- groupId; per esempio: org/hibernate
- tipo di file (jars, javasource, javadoc, licenses, poms); per esempio: org/hibernate/jars
- artifactid; per esempio: org/hibernate/jars/hibernate
- versione; per esempio: org/hibernate/jars/hibernate-3.1.2.jar
Da notare che non tutti i repository rispettano esattamente la stessa struttura. Come già detto, il problema è che attualmente vi è una mescolanza di stili. Questo genera una serie di problemi; per esempio è possibile notare come la struttura dei manufatti Hibernate vari a seconda della release: groupId=hibernate (Hibernate 1-3), groupId=net.sf.hibernate (Hibernate 1-2), groupId=org.hibernate (Hibernate 3).
Problemi con la struttura originaria
La struttura originaria del repository di Maven è stata cambiata in quanto presentava alcuni problemi. I principali sono:
- Ridotta scalabilità. Data l’organizzazione del file, era molto frequente il caso che diversi manufatti andassero a “bloccare” parti comuni. Questo è diventato evidentissimo dall’analisi dei flussi sul repository di iBiblio. In effetti, durante gli orari di maggiore uso, il repository diventava (e ahime’ diviene ancora) praticamente inutilizzabile.
- Layout meno razionale. La struttura portava ad avere moltissimi manufatti memorizzati nella stessa direttory, creando problemi di fruizione.
- Mancanza di distinzione tra manufatti primari (jar e pom) e seondari (javadoc, testi, etc.)
Conclusioni
Questo articolo è stato dedicato alla presentazione del meccanismo dei repository che, come visto, cerca di rispondere a una serie di problemi pratici derivanti dalla necessità di distribuire i vari manufatti, di garantirne l’integrità, e così via.
Il progetto Maven è nato all’interno della comunità open source al fine di risolvere una serie di problemi pratici. Uno di questi era dovuto all’elevato livello di interdipendenza dei vari progetti open source. La soluzione richiedeva la capacità di standardizzare l’ubicazione dei vari file (sorgenti, documentazione, distribuzione, etc.), di fornire una struttura comune per i vari progetti, di permettere il reperimento dei file di distribuzione attraverso un meccanismo di repository condiviso e di far sì che il processo di build diventasse più agevole, più trasparente e meno complesso: tutti questi furono elementi chiave che ne assicurarono il successo fin dalle prime versioni.
Sebbene, il meccanismo dei repository sia basato su una serie di buone idee e principi, allo stato attuale esistono ancora alcuni problemi. I vari repository centrali sono difficilmente utilizzabili per via del sovraccarico a cui sono sottoposti soprattutto durante gli orari di punta. Per superare questo inconveniente è necessario gestire un proprio repository, il che non è sempre possibile in imprese medie e piccole, soprattutto per questioni organizzative; la struttra dei repository è un po’ problematica per via della convivenza della struttura iniziale (Maven 1.x) e di quella delle nuove versioni (Maven 2). Oltre a questi, ci sono altri problemi classici legati allo scarso, e spesso confuso, livello di documentazione, allo sviluppo del sistema non sempre guidato da un chiaro disegno con conseguenti problemi di incongruenze, etc.
Comunque, il meccanismo dei repository è ampiamente riuscito a risolvere la maggior parte dei problemi che affliggevano la distribuzione dei manufatti dei vari progetti e sicuramente rende il processo di build e distribuzione più efficiente, razionale e facile da implementare.
Riferimenti
[1] Giovanni Puliti, “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”, Parti I-V, Mokabyte 114, 115, 117, 118, 119
[7] http://maven.apache.org/plugins/maven-enforcer-plugin/rules/requireOS.html