mardi 16 novembre 2010

Industrialisez vos build avec Maven


Maven est un outil de gestion du projet informatique désormais bien installé dans le monde de l'entreprise.
Il peut vous apporter beaucoup dans la maîtrise et l'industrialisation de vos processus de gestion de projet.
Les processus standards sont :
  • l'initialisation
  • la compilation
  • le packaging
  • la documentation
  • le déploiement
  • la livraison
  • ...
Cependant, sa mise en place peut s'avérer difficile : pour éviter certains désagréments et industrialiser vos builds, nous allons voir une bonne pratique facile à mettre en place.

Le coeur de Maven


Maven est constitué d'un coeur très léger : ce coeur ne contient pas les processus qui vont gérer votre projet.
Ces processus sont implémentés dans des plugins qui sont téléchargés à la volée par le coeur de Maven.
Au fil du temps, le coeur va mettre à jour les versions des plugins qui implémentent la gestion du projet (clean, compile,… ).
Cet architecture est à la fois une force et une faiblesse.
Grâce à cette architecture Maven est extrêmement modulaire, les possibilités d'extensions sont infinies, la mise à jour des plugins permet de bénéficier des dernières corrections et évolutions des processus.
D'un autre côté, cette mise à jour automatique peut provoquer une instabilité des builds, tel projet qui était livré avec le plugin release, n'est plus livrable un mois plus tard alors que le pom n'a pas évolué.[1]
En effet, chaque mise à jour vient avec son lot de nouveaux bugs et d'incompatibilités avec les autres plugins. J'ai personnellement expérimenté douloureusement cet état de fait pour la livraison d'un framework JEE dont je suis le responsable technique.
Nous effectuons plusieurs livraisons par an avec Maven et son plugin release. Une fois sur deux, la livraison se faisait dans la douleur.
Parfois, nous fumes contraints de livrer à la main une partie du logiciel.
Les problèmes que nous rencontrions étaient dû à une ou plusieurs montées en versions des plugins utilisés lors du release (compile, package, …) dont les évolutions étaient parfois non négligeables : telle option n'est plus prise en compte, telle option provoque un effet de bord, telle utilisation ne fonctionnait plus, ….
Pour palier à ces problèmes l'équipe Maven a figé un certains nombres de plugins dans une version donnée (à partir de Maven 2.1.X). C'est le super POM [2] de Maven qui contient cette configuration (ici).
Cette stratégie est intéressante mais incomplète : d'une part, elle ne couvre que les plugins cités dans le super POM, d'autre part, elle stoppe la montée en version automatique de ces plugins.
Or ces montées versions peuvent être une réponse à des problèmes que vous n'arrivez pas à résoudre (recours à du ant, ...). En effet, lorsqu'on jette un oeil sur la page JIRA des plugins Maven que le super POM référence on s'aperçoit que ces plugins ont évolué.
Par exemple, le plugin assembly est figé dans sa version 2.2-beta-2 dans le super POM de Maven 2.1.X et 2.2.X. Mais lorsqu'on regarde la page JIRA du plugin on s'aperçoit qu'il y a eu 4 versions supplémentaires. On note aussi que les bugs corrigés peuvent être conséquents, par exemple pour la version 2.2-beta-3 (ici) :
  • MASSEMBLY-242 : transitive dependencies do not get included
  • MASSEMBLY-285 : duplicate files added to the assembly
  • ...
De plus, en cas de changement de version de Maven vos builds ne s'exécuteront pas forcément avec le même arbre de dépendances de plugins : les versions de certains plugins étant fixées dans le super POM alors une nouvelle version de Maven peut apporter un super POM différent.

Industrialiser les builds


Pour bénéficier à plein de la puissance de Maven, il est indispensable de mettre en oeuvre un POM d'entreprise (dit aussi corporate POM).
Ce POM contient alors la configuration pour tous les plugins usuels pour une ou plusieurs versions de Maven (de nombreux plugins fonctionnent à partir de Maven 2.0.6).
Tout POM de projet qu'il soit simple ou multi-module hérite alors de ce POM.
Pour établir ce POM corporate vous avez besoin de la commande :
mvn help:effective-pom

Cette commande produit un POM qui correspond au POM effectif qui est le résultat de toutes les configurations applicables : la hiérarchie de POM (super POM, ...), settings.xml, ...
Exécutée sur plusieurs POM qui sont représentatifs, elle vous permettra d'établir rapidement un premier jet de votre POM corporate.
Extrait de sortie de la commande avec Maven 2.2.1 et la section pluginManagement (issue du super POM):
   <pluginManagement>
     <plugins>
       <plugin>
         <artifactId>maven-antrun-plugin</artifactId>
         <version>1.3</version>
       </plugin>
       <plugin>
         <artifactId>maven-assembly-plugin</artifactId>
         <version>2.2-beta-2</version>
       </plugin>
       <plugin>
         <artifactId>maven-clean-plugin</artifactId>
         <version>2.2</version>
       </plugin>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.0.2</version>
       </plugin>
       <plugin>
         <artifactId>maven-dependency-plugin</artifactId>
         <version>2.0</version>
       </plugin>
       <plugin>
         <artifactId>maven-deploy-plugin</artifactId>
         <version>2.4</version>
       </plugin>
       <plugin>
         <artifactId>maven-ear-plugin</artifactId>
         <version>2.3.1</version>
       </plugin>
       <plugin>
         <artifactId>maven-ejb-plugin</artifactId>
         <version>2.1</version>
       </plugin>
       <plugin>
         <artifactId>maven-install-plugin</artifactId>
         <version>2.2</version>
       </plugin>
       <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <version>2.2</version>
       </plugin>
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
         <version>2.5</version>
       </plugin>
       <plugin>
         <artifactId>maven-plugin-plugin</artifactId>
         <version>2.4.3</version>
       </plugin>
       <plugin>
         <artifactId>maven-rar-plugin</artifactId>
         <version>2.2</version>
       </plugin>
       <plugin>
         <artifactId>maven-release-plugin</artifactId>
         <version>2.0-beta-8</version>
       </plugin>
       <plugin>
         <artifactId>maven-resources-plugin</artifactId>
         <version>2.3</version>
       </plugin>
       <plugin>
         <artifactId>maven-site-plugin</artifactId>
         <version>2.0-beta-7</version>
       </plugin>
       <plugin>
         <artifactId>maven-source-plugin</artifactId>
         <version>2.0.4</version>
       </plugin>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <version>2.4.3</version>
       </plugin>
       <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <version>2.1-alpha-2</version>
       </plugin>
     </plugins>
   </pluginManagement>

Vous n'avez plus ensuite qu'à recopier ce fragment dans votre POM corporate sans oublier de monter en version si nécessaire (voir le JIRA), d'ajouter des détails de configuration ou les plugins qui n'y sont pas.
Exemple incomplet d'un POM corporate (voir le dépôt github) pour la version 2.1.0 de Maven :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 
 <modelVersion>4.0.0</modelVersion>
 <groupId>jee.architect.cookbook</groupId>
 <artifactId>corporate-pom</artifactId>
 <packaging>pom</packaging> 
 <version>1.0</version> 
 <name>mon-organisation-base-pom</name> 
 <url>http://maven.apache.org</url>
 <build> 
     <pluginManagement> 
        <plugins> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-clean-plugin</artifactId> 
            <version>2.2</version> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-source-plugin</artifactId> 
            <version>2.0.4</version> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-resources-plugin</artifactId> 
            <version>2.3</version> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-compiler-plugin</artifactId> 
            <version>2.0.2</version> 
            <configuration> 
              <encoding>UTF-8</encoding> 
              <source>1.5</source> 
              <target>1.5</target> 
            </configuration> 
          </plugin> 
          <plugin> 
            <groupId>org.codehaus.mojo</groupId> 
            <artifactId>aspectj-maven-plugin</artifactId> 
            <version>1.1</version> 
            <configuration> 
              <encoding>UTF-8</encoding> 
              <source>1.5</source> 
              <target>1.5</target> 
              <complianceLevel>1.5</complianceLevel> 
            </configuration> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-install-plugin</artifactId> 
            <version>2.2</version> 
          </plugin> 
          <plugin> 
            <groupId>org.codehaus.cargo</groupId> 
            <artifactId>cargo-maven2-plugin</artifactId> 
            <version>1.0.1-SNAPSHOT</version> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-eclipse-plugin</artifactId> 
            <version>2.6</version> 
            <configuration>                 
               <ajdtVersion>none</ajdtVersion> 
               <wtpversion>2.0</wtpversion> 
            </configuration> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-release-plugin</artifactId> 
            <version>2.0-beta-9</version>
            <configuration> 
              <remoteTagging>true</remoteTagging> 
            </configuration> 
          </plugin> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-pmd-plugin</artifactId> 
            <version>2.4</version> 
            <configuration> 
              <targetJdk>1.5</targetJdk> 
              <rulesets> 
                <ruleset>/rulesets/basic.xml</ruleset> 
                <ruleset>/rulesets/basic-jsp.xml</ruleset> 
                <ruleset>/rulesets/braces.xml</ruleset> 
                <ruleset>/rulesets/codesize.xml</ruleset> 
                <ruleset>/rulesets/clone.xml</ruleset> 
                <ruleset>/rulesets/coupling.xml</ruleset> 
                <ruleset>/rulesets/design.xml</ruleset> 
                <ruleset>/rulesets/imports.xml</ruleset>
                <ruleset>/rulesets/migrating_to_15.xml</ruleset> 
                <ruleset>/rulesets/strings.xml</ruleset> 
                <ruleset>/rulesets/unusedcode.xml</ruleset> 
              </rulesets> 
             </configuration> 
          </plugin> 
       </plugins> 
    </pluginManagement> 
  </build> 
</project>

Extrait incomplet d'un POM d'un projet simple qui utilise ce POM de base :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
  <parent>
     <groupId>jee.architect.cookbook</groupId>
     <artifactId>corporate-pom</artifactId>
     <version>1.0</version> 
   </parent>
   <groupId>mon-projet</groupId> 
   <artifactId>mon-projet-web</artifactId> 
   <packaging>war</packaging> 
   <version>1.2-SNAPSHOT</version> 
   <name>mon-projet-web</name>

Les POMs qui utilisent le POM corporate peuvent parfaitement surcharger la configuration, en re-déclarant les plugins concernés dans le POM du projet.

Conclusion


Cette approche offre plusieurs bénéfices :
  • tous les projets qui utilisent une version donnée de Maven se base sur les même processus : même version, même configuration (encoding, JDK, … )
  • les développeurs ne perdent plus de temps à configurer Maven (ou à copier/coller plus ou moins proprement les configurations)
  • les builds sont reproductibles dans le temps [3]
  • la configuration est centralisée : la correction, l'évolution ou la montée en version de la configuration se fait dans un seul POM
Cependant n'oubliez pas que le POM d'entreprise constitue un projet en soit, il faut donc prévoir une charge pour sa mise au point et pour la maintenance corrective ou évolutive (n'oubliez pas si vous utilisez le POM Corporate du billet de mettre à jour les plugins).

Contrat Creative Commons
the jee architect cookbook by Olivier SCHMITT est mis à disposition selon les termes de la licence Creative Commons Paternité - Pas d'Utilisation Commerciale - Pas de Modification 3.0 Unported.

mardi 9 novembre 2010

The jee architect cookbook : mon premier billet !


Tout d'abord bienvenue sur mon blog !

Déjà huit années au poste d'architecte Java/JEE d'un grand compte !

J'éprouve l'envie de partager mon expérience avec d'autres.

Ce blog sera l'occasion de partager :
- des petites ou des grandes recettes autour de Java/JEE,
- de commenter l'actualité,
- ...

J'espère que ces billets vous seront utiles.

Merci de votre visite.
A bientôt.