Nei precedenti articoli su Maven (Organizziamoci con Maven – Parte I e Parte II), abbiamo visto le nozioni fondamentali del file POM. In questo articolo vedremo una panoramica di elementi avanzati del POM, fra quelli ritenuti più significativi e rilevanti per la maggior parte dei progetti.

La struttura del file pom.xml è molto ricca e non sempre immediata o intuitiva (tanto che anche la relativa descrizione per motivi di spazio, in Organizziamoci con Maven – Parte I è stata sviluppata limitandoci all’elementare, al necessario e poco altro). Tuttavia l’effettiva ricchezza de POM 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 buona parte dei progetti. Inoltre, una volta preparati i vari POM per il primo progetto, questi si prestano a essere riutilizzati per i successivi, con minime variazioni.

Informazioni generali del progetto

Prima di tutto vediamo alcune impostazioni di carattere generale:

  • name:  permette di specificare il nome “interno” del progetto, utilizzato dal team di sviluppo;
  • description: una breve descrizione del progetto;
  • url: indirizzo del sito dedicato al progetto;
  • inceptionYear: indica l’anno di startup del progetto.

<project>
 <name>javaModule</name>
 <description>Un modulo Java<description>
 <url>http://www.cosenonjaviste.it</url>
 <inceptionYear>2012</inceptionYear>
</project>

  • licenses: permette di indicare una o più licenze con la quale verrà rilasciato il progetto.
    Per ogni singola licenza, è possibile specificare: name, url, comments e distribution. Quest’ultimo è un campo enumerato in cui è possibile impostare uno dei seguenti due valori: repo e manual. Il primo indica che il progetto prevede la possibilità di essere scaricato da un repository di Maven, mentre il secondo indica l’obbligatorietà di un’installazione manuale.

<licenses>
 <license>
  <name>Apache 2</name>
  <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
  <distribution>repo</distribution>
  <comments>A OSS license</comments>
 </license>
</licenses>

  • Organization Informazioni relative all’azienda/organizzazione proprietaria del progetto. In particolare, è possibile impostare il nome dell’organizzazione e l’indirizzo del relativo sito internet.

<project>
 ...
 <organization>
  <name>CoseNonJaviste</name>
  <url>http://www.cosenonjaviste.it</url>
 </organization>
</project>

Properties (proprietà)

Maven mette a disposizione una serie di proprietà, le quali non sono altro che delle variabili del file POM e come tutte le variabili, del resto, permettono di sostituire il riferimento ai valori. La notazione è nel seguente formato:

${<proprieta>}

Le proprietà vengono definite all’interno della sezione <properties>. Supponiamo di voler definire la proprietà org.springframework.version e settarla al valore 3.0.5:

  
<properties>
 <org.springframework.version>3.0.5</org.springframework.version>
</properties>
  

Qualora volessimo, ad esempio, aggiungere più dipendenze relative al framework Spring, così da definire una volta sola la versione di interesse e riutilizzarla in tutto il progetto, potremmo usare la suddetta proprietà org.springframework.version nel seguente modo:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>${org.springframework.version}</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>${org.springframework.version}</version>
  <type>jar</type>
  <scope>compile</scope>
</dependency>

Esistono anche delle proprietà predefinite, eccone alcune tipologie:

  • env.<proprieta>: il prefisso env fa riferimento alle variabili di ambiente, come per esempio il PATH, ${env.PATH};
  • project.<proprieta>: in questo caso il prefisso 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’elemento <project><version> (si veda il capitolo: “Un POM di teoria” del primo articolo);
  • java.<proprieta>: permette di accedere alle proprietà di sistema messe a disposizione dalla API Java. Per esempio, ${java.home};

Build (informazioni relative al processo di build)

La sezione <build>, è dedicata alle informazioni relative al processo build. Queste informazioni comprendono tra le altre cose: la struttura del progetto, la directory dei file sorgente, la configurazione dei vari plug-in, e altro ancora…
Come visto in precedenza, molte di queste informazioni sono già impostate di default e pertanto è necessario specificarne i valori solo qualora si intenda sovrascrivere tali impostazioni.
Da notare che l’elemento <build> è utilizzato in due differenti modi dal file POM, questi sono rispettivamente:

  • Project build: si tratta dell’elemento che contiene le informazioni tipicamente utilizzate durante il processo di build vero e proprio;
  • Profile build: si tratta di alterazioni delle suddette informazioni, o di quelle di default, che hanno luogo solo in particolari circostanze detti profili del POM e che vedremo più avanti.

<project>
<!-- "Project Build" -->
  <build>...</build>

  <profiles>
  <profile>
   <!-- "Profile Build" -->
    <build>...</build>
   </profile>
  </profiles>
</project>

Il codice che segue, mostra la struttura comune ad entrambi, denominata baseBuild:

<build>
 <defaultGoal>install</defaultGoal>
 <directory>${basedir}/target</directory>
 <finalName>${artifactId}-${version}</finalName>
 <filters>
   <filter>filters/filter.properties</filter>
 </filters>
 <resources>... <resources>
</build>

Gli elementi contenuti sono:

  • defaultGoal: rappresenta il goal o la frase da eseguire qualora nessuna venga specificata (normalmente viene indicato tramite linea di comando, per esempio: mvn package );
  • directory: specifica la directory dove i file prodotti dal processo di build devono venir memorizzati (la directory di default è la directory target);
  • flnalName: indica come viene nominato il prodotto di Maven (di 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). Per esempio la proprietà miaProprieta=valore contenuta nel file  filter.properties  nella cartella filters;
  • resources: sezione dedicata alle risorse, ovvero quei file che non vengono compilati ma vengono comunque inglobati nel progetto generato.

    Vediamo adesso più in dettaglio come deve essere gestita quest’ultima sezione…

Resources (gestione dei file risorse)

Le risorse sono dei file che, seppur tipicamente non contengano codice sorgente e quindi non devono essere compilati, devono però essere incluse nel file di distribuzione, oppure utilizzate per altri scopi.
Per chiarire cosa siano le risorse in questione, possiamo ad esempio immaginare dei file di mapping per framework quali Mybatis o Hibernate, oppure dei file di configurazione di Spring.

<build>
   ...
   <resources>
       <resource>
           <targetPath>META-INF/cosenonjaviste</targetPath>
           <filtering>false</filtering>
           <directory>${basedir}/src/main/cosenonjaviste</directory>
           <includes>
               <include>config.xml</include>
           </includes>
           <excludes>
               <exclude>**/*.properties</exclude>
           </excludes>
       </resource>
   </resources>
   <testResources>
       ...
   </testResources>
   ...
</build>

La sezione risorse (resoruces), prevede le seguenti informazioni:

  • resource: specifica i file da includere e relativa allocazione;
  • filtering: si tratta di un elemento booleano che indica se abilitare o meno il filtraggio delle variabili nei file risorse. Infatti allo stesso modo delle proprietà definite nella sezione precedente, è possibile richiamare nelle risorse i riferimenti di proprietà. Di seguito potete vedere un esempio;
  • directory: indica dove si trovano le risorse. L’ubicazione di default è: $basedir/src/main/resources;
  • includes: permette di selezionare alcune risorse, specificando un pattern;
  • excludes: permette di escludere alcune risorse, specificando un pattern. 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;
  • targetPath: specifica la struttura della directory dove ubicare il file delle risorse. Il default è la directory base, tuttavia la convenzione adottata nei file JAR sarebbe quella di includere le risorse a partire dalla directory META-INF;
  • testResources: questa sezione è dedicata alle 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.

Plugins (gestione dei plug-in)

La sezione Project build puo’ contenere a sua volta anche una sezione dedicata alla configurazione dei plug-in che si vogliano utilizzare.

<build>
   ...
 <plugins>
   <plugin>
       <groupId>org.apache.Maven.plugins</groupId>
       <artifactId>Maven-compiler-plugin</artifactId>
       <configuration>
           <source>1.5</source>
           <target>1.5</target>
       </configuration>
   </plugin>
 </plugins>
   ...
</build>

Come si vede dall’esempio è necessario specificare le coordinate del plug-in: groupId, artifactId e version (quest’ultimo se se si necessita di specificare la versione).
Di solito è sufficiente non aggiungere ulteriori informazioni; tuttavia ci sono altri elementi, eccone alcuni:

  • extensions: si tratta di un attributo booleano (di default è false), il cui valore specifica se caricare, o meno, le estensioni del plug-in (vedi esempio);
  • inherited:  un altro attributo booleano, il cui valore indica se eventuali file POM, ereditano da quello corrente la configurazione del presente plug-in;
  • configuration: si tratta di una sezione specifica dei singoli plug-in. In questa sezione è possibile specificare una qualsiasi proprietà che una classe JavaBeans potrebbe richiedere.
    Senza entrare troppo nel dettaglio, ciascun plug-in è composto da uno o più JavaBeans (detti MOJO) e ogniuno di questi implementa un goal del plug-in. I parametri specificati in questa sezione sono i valori utilizzati dallo specifico plug-in;
  • dependencies: simile alla sezione  dependencies del POM. La differenza è che le dipendenze definite in questa sezione non sono al livello di progetto, bensì al livello di plug-in;
  • executions: permette di configurare il goal di un plug-in. Ogni plug-in può definire diversi goal e ciascuno può richiedere una diversa configurazione.

MOJO è un gioco di parole utilizzato per definire i Maven POJO ( POJO sta per Plain Old Java Object), ossia classi Java, simili a JavaBeans con alcune proprietà (vedi anche…).

Il tag <executions> è a sua volta caratterizzato da diversi sottocampi:

  • id: è l’identifcatore del particolare blocco di esecuzione;
  • goals: definisce la lista dei goals specificati dal blocco di esecuzione;
  • phase: rappresenta la phase in cui la lista di goals viene eseguita.
  • inherited: attributo booleano che se impostato a false, fa si che l’esecuzione non venga passata ad eventuali POM che ereditano;
  • configuration: come definito sopra, con l’eccezione che la configurazione è limitata a questo elenco specifico di goals, piuttosto che a tutti i goal del plug-in.

<build>
   ...
 <plugins>
  <plugin>
    <artifactId>maven-myquery-plugin</artifactId>
      <version>1.0</version>
      <executions>
         <execution>
            <id>execution1</id>
            <phase>test</phase>
            <configuration>
              <url>http://www.foo.com/query</url>
              <timeout>15</timeout>
              <options>
                <option>four</option>
                <option>five</option>
                <option>six</option>
              </options>
            </configuration>
            <goals>
              <goal>query</goal>
            </goals>
       </execution>
    </executions>
  </plugin>
 </plugins>
   ...
</build>

Strettamente connessa con la sezione plugin  e la sezione del <pluginManagement>.
La differenza principale è che mentre la prima sezione permette di configurare plug-in per il loro utilizzo all’interno di un particolare progetto, in questa sezione la configurazione è destinata ai progetti che ereditano.
La struttura di pluginManagement e plugin è sostanzialmente equivalente. Tuttavia, le informazioni specificate all’interno di quest’ultima sono destinate a rimanere confinate all’interno di un file POM, mentre quelle specificate in pluginManagement sono visibili a tutti i file POM ereditanti.

Directories

Come abbiamo visto Maven ha una sua caratteristica organizzazione delle cartelle. Tuttavia nella Project build è possibile alterarla e indicare le directory dei file sorgente Java, degli scripts, dei file compilati (.class) e delle classi di test compilate:

<project>
   <build>
       <sourceDirectory>
	 ${basedir}/src/main/java
       </sourceDirectory>
       <scriptSourceDirectory>
         ${basedir}/src/main/scripts
       </scriptSourceDirectory>
       <testSourceDirectory>
         ${basedir}/src/test/java
       </testSourceDirectory>
       <outputDirectory>
         ${basedir}/target/classes
       </outputDirectory>
       <testOutputDirectory>
         ${basedir}/target/test-classes
       </testOutputDirectory>
       ...
   </build>
</project>

I percorsi possono essere specificati sia in termini assoluti, che in modo relativo a partire dalla directory di base ${basedir}.
Ovviamente questa intera sezione può essere omessa qualora si accetti la configurazione di default di Maven.

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 è chiamata site. Questa si avvale delle informazioni incluse nella sezione reporting, che si occupa di definire e configurare i relativi plugin. Ma vedremo questa caratteristica in un prossimo articolo…

Giunti a questo punto, possiamo finalmente uscire dal tag <build>… 🙂

Profiles (profili)

i profiles permettono di variare opportune impostazioni in funzione dell’ambiente di build. Un esempio classico consiste nel far in modo che un progetto, per ovvie necessità di sviluppo, faccia riferimento ad un diverso database (o application server) a seconda dell’ambiente di esecuzione (ambiente di sviluppo o di produzione). Un altro esempio consiste nel fare in modo che il sistema, a seconda della versione del JDK utilizzata, assuma le opportune impostazione.
Gli elementi del profilo, sono stati tutti già esaminati in precedenza, con l’eccezione della sezione attivazione. Vediamo il seguente listato di esempio:

<project>
    …
    <profiles>
        <profile>
            <id>test</id>
            <activation/>
            <properties>
                <tomcat-server>
                   tomcat-test
                </tomcat-server>
                <tomcat-url>
		    http://192.168.1.2:8080/manager
                </tomcat-url>
                <username>root</username>
                <password>password</password>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <activation/>
            <properties>
                <tomcat-server>
                   tomcat-prod
                </tomcat-server>
                <tomcat-url>
                    http://192.168.1.3:8080/manager
                </tomcat-url>
                <username>root</username>
                <password>password</password>
            </properties>
        </profile>
    </profiles>
    …
</project>

L’esempio definisce due profili, uno di test e uno di produzione, i quali sono distinti da due diversi server Apache Tomcat. In generale un profile può essere attivato esplicitamente attraverso la linea di comando. In questo caso è necessario utilizzare il flag -p e il nome del profilo, il tutto da postporre al comando mvn goal|fase.

mvn package -P nome_profilo

Nel caso del nostro esempio, non ci resta che eseguire il target deploy di Maven indicando quale sia il profilo e quindi, il server su cui effettuare il deploy. Potete indicare il profilo eseguendo il seguente comando:

mvn tomcat:deploy -P prod

Activation (attivazione dei profili)

I profili possono essere attivati di default con una configurazione simile alla seguente:

<profiles>
  <profile>
    <id>prod</id>
    <activation>
      <activeByDefault>true</activeByDefault>
      ...
    </activation>
    ...
  </profile>
</profiles>

Tuttavia più in generale un profilo viene attivato quando tutte le condizioni definite nella sezione <activation> sono contemporaneamente soddisfatte.
Vediamo quali condizioni possiamo utilizzare:

  • jdk: questo elemento esegue la verifica della versione del jdk. In particolare, il controllo è soddisfatto qualora il numero di versione del jdk in esecuzione risulti compatibile con quello impostato. Se per esempio si impostasse l’elemento jdk al valore 1.6 e l’ambiente di esecuzione riportasse un valore 1.6.0_07, il controllo risulterebbe soddisfatto.

    <profiles>
      <profile>
        <activation>
          <jdk>1.6</jdk>
        </activation>
        ...
      </profile>
    </profiles>
    

  • os: in questo caso l’obiettivo dell’elemento è verificare il sistema operativo di esecuzione. Gli elementi utilizzabili sono:
    • arch: l’architettura hardware di riferimento;
    • family: famiglia (per la quale i valori ammessi sono dos, mac, netware, os/2, tandem, unix, windows, win9x, z/os, os/400);
    • name: il nome del sistema operativo;
    • vesion: la versione;
    • display: flag per individuare il sistema operativo.

    <profiles>
      <profile>
        <activation>
          <os>
            <name>Windows XP</name>
            <family>Windows</family>
            <arch>x86</arch>
            <version>5.1.2600</version>
          </os>
        </activation>
        ...
      </profile>
    </profiles>
    
  • property: l’esempio seguente attiva il profilo quando la proprietà “environment” viene specificato con il valore “test”.
    <profiles>
      <profile>
        <activation>
         <property>
          <name>environment</name>
          <value>test</value>
         </property>
        </activation>
        ...
      </profile>
    </profiles>
    

  • file: questa attivazione è soddisfatta qualora Maven riesca ad individuare l’esistenza (sezione exists) o l’assenza (sezione missings) di un determinato file.
    <profiles>
      <profile>
        <activation>
         <file>
          <exists>${basedir}/file2.properties</exists>
          <missing>${basedir}/file1.properties</missing>
         </file>
        </activation>
        ...
      </profile>
    </profiles>
    

Per individuare l’insieme, eventualmente vuoto, dei profili attivati in un determinato ambiente, è necessario eseguire il comando:

mvn help:active-profiles

SCM – Software Configuation Management

Quest’ultima sezione è dedicata ai cosiddetti Software Version Control, utilizzati per la gestione dei file sorgenti e comunemente denominati anche SCM (Software Configuration Management). Per la connessione con i sistemi SCM, Maven utilizza un proprio plug-in: Maven SCM. Il quale fornisce un’interfaccia comune per accedere alle diverse implementazioni SCM ( CVS, Subversion, Git, etc.).
Questa interfaccia è basata su una API che segue il seguente formato:

scm:[provider]:[provider_specific]

Nel listato riportato sotto è presente una configurazione a una connessione di esempio ad un server Subversion:

<project>
 …
 <scm>
    <connection>scm:svn:http://url_svn/my-project/trunk/</connection>
    <developerConnection>scm:svn:http://url_svn/my-project/trunk/</developerConnection>
    <tag>trunk</tag>
    <url>http://url_svn/my-project</url>
  </scm>
 …
</project>

Gli elementi SCM sono:

  • connection e developerConnection che permettono di definire le diverse modalità di connessione al sistema di controllo del versionamento attraverso Maven. In particolare:
    • connection: richiede un accesso di sola lettura che permettere a Maven di localizzare e leggere il codice sorgente;
    • developerConnectio: richiede un accesso in lettura/scrittura;
  • tag: specifica l’etichetta (HEAD,trunk,…) dalla quale è possibile accedere al progetto;
  • url: specifica l’indirizzo URL che permette di navigare il repository.

Oltre a quanto visto, occorre aggiungere il plugin scm al nostro POM. Ossia il plugin deputato da Maven alla gestione dell’integrazione del nostro progetto con Subversion:

<build>
...
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-scm-plugin</artifactId>
   <version>1.0-SNAPSHOT</version>
   <configuration>
      <connectionType>developerConnection</connectionType>
   </configuration>
  </plugin>
 </plugins>
...
</build>

A questo punto, ogni qual volta vorremmo aggiornare la nostra versione del progetto all’ultima versione contenuta nel repository, basterà lanciare il comando:

mvn scm:update

Viceversa quando vorremo fare il commit delle ultime modifiche da noi apportate ai sorgenti, eseguiremo il comando:

mvn -Dmessage=Commit_log scm:checkin

Oppure in caso sia necessaria l’autenticazione:

mvn -Dusername=user -Dpassword=pwd -Dmessage=Commit_log scm:checkin

Riferimenti

Per approfondire quanto visto finora, eccovi alcuni link:

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