Thématiques principales

dimanche 7 janvier 2018

Maven, préparons des parents

Dans un précédent article [1], nous avions vu les principes généraux de Maven et son utilisation. Nous avions egalement vu le pattern parent/module/projet qui permet d’organiser la structure des projets.

Dans cet article, nous allons nous concentrer sur la partie haute de ce pattern en présentant une proposition de pom parents génériques ainsi que quelques déclinaisons de sous parents pour des sous classes d’applications (dans notre cas pour des application OSGI, des projets de plugins mavens et dans un avenir proche pour des application JEE)



Pour illustrer et expliquer ce pattern, je me permettrai de m'inspirer de mes propres projets de mes pom parents du dépôt Git Hub [2]

Nomenclature 

De façon générale, tous pom doit définir 4 points pour lesquels j’applique la nomenclature suivantes:
  • groupid = org.tc.${FONCTION}
  • artifactid = tc-${FONCTION}[-module]
  • version = [maj].[min].[ft]
  • packaging = pom
ou

  • $[FONCTION] est le rôle du pom courant 
  • [-module] la gestion du cas particulier du pom module
  • [maj] modification majeur du composant ( généralement fonctionnelle)
  • [min] modification mineur du composant ( généralement technique)
  • [ft] modification suite à un correctif 

Le groupId permet a maven de classer le pom, ainsi si l’on transpose cela a un répertoire, cela représente un chemin de répertoires dans lequel on trouvera tous les projets de ce type.
L'artifactId représente l’identifiant unique du pom dans la famille de pom classer sous le groupId.
La version permet de gérer les évolutions dans le temps et les dépendances des composants entre eux selon ces évolutions.

Le packaging permet à maven de connaitre la nature du composant qu’il aura à construire ainsi, un packaging pom est typiquement un pom parent ou module alors que jar, implique que l'exécution du pom produise une application java packager. (On aura aussi par exemple des packaging de type war, ejb, bundle ou ear… [3]

Optionnellement, les poms (et dans mon cas ils seront systématiquement définis) il est possible de donner une description au pom permettant de rentrer un peu plus dans le détail de son rôle et egalement de definir le name qui n’est que le nom simple du pom (“human readable”) tel qu’il sera affiché dans le reator du processus Maven (le resultat d’execution du build) il est donc important d’en choisir un lisible, simple et évident.

On choisira un name comme ci-dessous:
name = ${project.artifactId}-${project.version}

Pom parent


<groupId>org.tc.parent</groupId>
<artifactId>tc-parent</artifactId>
<name>${project.artifactId}-${project.version}</name>
<version>0.7.0-SNAPSHOT</version>
<packaging>pom</packaging>
<description>Pom parent des projets</description>

Le plus important dans le pattern parent/module/projet est, comme pour une hiérarchie de classe, l'élément le plus abstrait, celui de plus haut niveau c’est a dire le pom parent. Le pom parent a pour role de factoriser l’ensemble des informations concernant les composants participant à la construction de toutes les familles d’applications de nos projets.

De plus il participe également  à la définition d’information d’ordre plus générale comme l’identité du mainteneur du pom, les url permettant d'accéder au dépôt git ou au site web du projet. Pour ce genre d’informations je vous invite à consulter le pom en question, nous nous intéresserons aux premières composantes.

Donc pour proposer aux futur projet un environnement de production cohérent, le pom parent va définir le type d’encodage des sources:


<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>


Il peut aussi déclarer ou sera déployée la production produite par maven en définissant le distributionManagement qui poussera les jar et les pom dans par exemple un artifactory ou un nexus:


<distributionManagement>
    <snapshotRepository>
     <id>artifactory</id>
     <name>artifactory-snapshots</name>
     <!-- ${arti-url} defined in conf/settings.xml url d'artifactory-->
     <url>${arti-url}/artifactory/libs-snapshot-local</url>
    </snapshotRepository>
    <repository>
     <id>artifactory</id>
     <name>artifactory-releases</name>
     <!-- ${arti-url} defined in conf/settings.xml  url d'artifactory-->
     <url>${arti-url}/artifactory/libs-release-local</url>
    </repository>
    <site>
     <id>siteweb</id>
     <!-- defined in conf/settings.xml correspond au repertoire local ou sera déposé le contenu du build de la phase site-->
     <url>${local-siteweb-url}</url>
    </site>
</distributionManagement>


Dans le cas présent, on en a profité également pour définir ou sera déployé le site maven si la phase site est exécutée (variable ${local-siteweb-url}). A noter, que l’on utilise des variables directement dans le pom qui doivent avoir été défini dans le settings. Cette approche rend certe le pom non utilisable directement mais il procure l’avantage de permettre la pré-configuration des builds pour l’environnement dans lequel il va s'exécuter. Dans le settings pour pouvoir exécuter le build, il faudra affecter a ces variables les urls de artifactory et du site web.

On passe ensuite à la définition des outils de reporting a utiliser pour la génération du site web maven du projet.

Dans notre cas, nous employons


<reporting>
    <plugins>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-plugin-plugin</artifactId>
      <version>3.3</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-changelog-plugin</artifactId>
      <version>2.2</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-checkstyle-plugin</artifactId>
      <version>2.10</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-docck-plugin</artifactId>
      <version>1.0</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jxr-plugin</artifactId>
      <version>2.3</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-pmd-plugin</artifactId>
      <version>3.0.1</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-project-info-reports-plugin</artifactId>
      <version>2.6</version>
     </plugin>
     <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-report-plugin</artifactId>
      <version>2.18.1</version>
     </plugin>
    </plugins>
</reporting>


Dont les rôles sont [4] :

  • maven-plugin-plugin : Permet de generer la description d’un plugin maven
  • maven-changelog-plugin : Permet de generer le rapport changelog
  • maven-checkstyle-plugin : Permet de generer le rapport d’analyse de checkstyle (bonne pratique d’ecriture du code)
  • maven-docck-plugin : Permet de vérifier la documentation
  • maven-jxr-plugin : fourni un rapport des references croisées des sources java
  • maven-pmd-plugin : fournir un rapport d’analyse de code du code java
  • maven-project-info-reports-plugin : Permet de consolider dans un rapport les informations complémentaire contenu dans les pom
  • maven-surefire-report-plugin : permet de fournir un rapport d'exécution des tests unitaires

Ensuite par défaut il est possible de déclarer que tous les projets utilisent les même outils de tests. Dans le cas de nos projets nous avons fait le choix d’utiliser JUnit et Mockito


<dependencies>
    <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.10</version>
     <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-all</artifactId>
     <version>1.9.5</version>
     <scope>test</scope>
    </dependency>
</dependencies>


Nous avons traité tous les éléments annexes au build lui même (c’est a dire la documentations et les informations complémentaires générées lors de la construction du site maven) . Maintenant, il convient de définir la configuration des builds eux même et donc la configuration des composants participants à ces builds. Ces éléments sont à déclarer entre les balises <build></build>.

Premier point a ajouter aux builds est la déclaration des répertoires de ressources et de les spécifier filtering. De cette façon, il est possible de déclarer des fichiers de configuration incluant des variables qui seront remplacées et affectées au moment du build par la valeur déclarée dans le pom. Ainsi, en déclarant des profils avec des valeurs différentes, il est possible de réaliser des productions incorporant des configurations differentes. (Nous reviendrons sur ce point plus tard, ce ne sont pas les exemples d’utilisations qui manquerons)


<resources>
 <resource>
  <directory>src/main/resources</directory>
  <filtering>true</filtering>
 </resource>
</resources>


Ensuite, il faut ajouter différents plugins:

Le plugin permettant de spécifier le JDK à employer ainsi que sa version:


<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>3.1</version>
 <configuration>
  <verbose>true</verbose>
  <source>1.8</source>
  <target>1.8</target>
  <testSource>1.8</testSource>
  <testTarget>1.8</testTarget>
  <arg>-XprintRounds -XprintProcessorInfo -Xlint -J-verbose</arg>
  <encoding>UTF-8</encoding>
  <fork>true</fork>
  <!-- defined in conf/settings.xml -->
  <executable>${JAVA_1_8_HOME}\bin\javac</executable>
 </configuration>
</plugin>


Les options -XprintRounds -XprintProcessorInfo -Xlint -J-verbose ont pour but d’augmenter la verbosité pendant la compilation et de fournir plus d’informations sur l’utilisation du processeur d’annotation [5].

Comme pour les urls dont nous avons parlé, il faudra ajouter la variable ${JAVA_1_8_HOME} à votre settings permettant à maven de savoir ou se trouve votre installation de Java.

Pour produire la javadoc on ajoutera le plugin suivant avec le paramètre Xdoclint:none afin a rendre le parseur générant la documentation moins rigide [6], (sinon il se peut que vous ne puissiez plus générer votre java doc):


<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <version>2.9</version>
  <executions>
    <execution>
      <id>attach-javadocs</id>
      <goals>
        <goal>jar</goal>
      </goals>
      <configuration>
        <additionalparam>-Xdoclint:none</additionalparam>
      </configuration>
    </execution>
  </executions>
</plugin>


Même combat avec le plugin maven site qui va aussi vous produire la javadoc en plus des reporting que nous avons vu précédemment:


<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-site-plugin</artifactId>
  <version>3.3</version>
  <configuration>
  <reportPlugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-javadoc-plugin</artifactId>
      <configuration>
        <additionalparam>-Xdoclint:none</additionalparam>
      </configuration>
    </plugin>
   </reportPlugins>
   </configuration>
</plugin>


Enfin si vous livrer vos sources sous la forme d’un jar séparé de vos binaires, il faudra ajouter le plugin maven-source-plugin:

Pom parents fils

Voila on a fait le tour des plugins du pom parent principal. De la même manière, on va pouvoir créer d’autres pom parent plus spécialisés héritant de ce pom [2]. Sans entrer dans le détail, le dépôt propose par exemple de façon plus ou moins abouti des pom parent pour la production de bundles OSGI ou des plugins maven ou des ébauches pour la production de projets JEE ou Android.

Pom Module

Une fois ces pom définit, il reste à orchestrer leur production. Pour cela on utilisera un pom module.


<groupId>org.tc.module</groupId>
<artifactId>tc-parent-module</artifactId>
<name>${project.artifactId}-${project.version}</name>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<description>Pom module de tc-project</description>


De façon très simple, un pom module ne définit que la liste des projets à construire.  Dans le cas de notre dépôt ce pom se constitue donc du pom parent principal et des pom fils au sein du balisage xml module:


<modules>
  <module>tc-parent</module>
  <module>tc-osgi-parent</module>
  <module>tc-jee-parent</module>
  <module>tc-maven-plugin-parent</module>
  <module>tc-android-parent</module>
</modules>


A noter que l’on retrouve souvent les pom parents et les pom modules sous la forme d’un seul et même pom. Si cette configuration est souvent la plus simple lorsque l’on a qu’un seul projet java a réaliser (même constitué par plusieurs sous projet), il n’est pas préconisé de procéder ainsi. En effet comme pour le pattern MVC ou l’on retrouve souvent le contrôleur et la vue implémenter dans le même composant, il est important pour éviter des rework de respecter la règles de la séparation des préoccupations. Ainsi, il sera plus simple de modifier la liste des éléments à construire sans avoir a modifier la version du pom parent et par voie de conséquence tous les pom en héritant.

Enfin dernier point à considérer selon les convenances, pour que le build puisse aboutir dans le cas d’utilisation de la phase deploy de maven, il est nécessaire de permettre au pom module d'être uploader dans un repository distant. Il reste alors a votre choix de soit declarer dans le pom un bloc distributionManagement comme nous l’avons fait pour le pom parent mais cela revient à dupliquer du code ou plus simplement, de déclarer le pom module comme fils du pom parent afin de bénéficier du distributionManagement hérité de celui ci.

Pour plus d’information sur la construction des pom je vous invite a consulter la documentation officielle [7]

References:

[1] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/maven-introduction.html
[2] https://github.com/collonville-tom/tc-parent
[3] https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Packaging
[4] http://matthieu-lux.developpez.com/tutoriels/java/maven/?page=site
[5] http://www.javatronic.fr/articles/2014/08/31/how_to_make_sure_javac_is_using_a_specific_annotation_processor.html
[6] http://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html
[7] https://maven.apache.org/guides/introduction/introduction-to-the-pom.html

Aucun commentaire:

Enregistrer un commentaire