De l’art du livrable

Au cours de certaines missions, je suis arrivé à un constat qui était que le processus de génération d’un livrable était souvent délaissé au profit de l’effort de développement de l’application.
C’est vrai qu’il peut être concevable qu’il ne s’agit que de la dernière étape d’un processus de développement, cependant, avec l’introduction de cycles courts (agilité, …), pouvoir fournir rapidement un livrable de qualité au client est primordial.
Dans un post précédent, j’avais parlé de la façon dont j’avais en place maven dans un processus d’usine logicielle. J’y avais également abordé succinctement comment il était possible de générer un livrable.
Ce post présente donc plus précisément comment cela est possible avec maven (je ne reviendrai pas sur le pourquoi car cela me parait évident dans le sens où c’est ce que verra le client final et que, tout le monde le sait, la première impression est très souvent importante – un peu comme lorsque l’on offre un cadeau à quelqu’un, l’emballage a son importance même si, au final, le cadeau est pourri… 😉 – ).

Cahier des charges

L’application de référence sera une application simple de type J2SE (nommée foo) qui devra pouvoir s’exécuter de manière simple en exécutant par exemple un .bat ou un .sh.
Le livrable devra contenir la javadoc de l’application ainsi que son code source.
Il pourra optionnellement venir avec un fichier README qui précisera certaines informations (version, howto, issues, …).
Enfin, il sera supposé que le client ne dispose que d’un JRE sur la machine où sera amenée à être exécutée l’application.

Le livrable devra contenir :

  • dans le répertoire lib, toutes les librairies nécessaires à l’exécution de l’application,
  • dans le répertoire api, la javadoc de notre application,
  • dans le répertoire source, les sources de l’application sous forme de jar,
  • à la racine, le fichier README ainsi que les .bat et .sh nécessaires à l’exécution de l’application.

Mise en œuvre

Méthodologie

Comme vous l’aurez compris, maven 2 sera notre outil pour gérer notre compilation, l’exécution des tests unitaires (qui ne doivent pas être oubliés), et production du livrable.
Pour ce faire, notre application suivra les préconisations maven et le plugin assembly sera utilisé pour la phase de génération du livrable qui sera sous forme de zip. En outre, ce plugin sera associé à la phase maven d’installation de notre application.

Arborescence du projet

foo
|-- src
|---- main
|------ java
|------ resources
|-------- launcher.bat
|-------- launcher.sh
|-------- cp.bat
|-------- README.txt
|------  assembly
|-------- src.xml
|------ test
|-------- java
|--  pom.xml

Fichier pom.xml

<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>
<artifactId>foo</artifactId>
<groupId>com.xxx</groupId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging></p>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.9</version>
<classifier>jdk15</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
</dependency>
...
</dependencies>
<build>
<finalName>foo</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/src.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.6.1</version>
<executions>
<execution>
<goals>
<goal>javadoc</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Fichier src.xml

<assembly>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<scope>compile</scope>
<useProjectArtifact>false</useProjectArtifact>
<useTransitiveDependencies>true</useTransitiveDependencies>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>target/site/apidocs</directory>
<outputDirectory>api</outputDirectory>
<includes>
<include>
**
</include>
</includes>
</fileSet>
</fileSets>
<files> <!--a noter qu'ici, on prefere prendre les fichiers  au cas par cas pour permettre un eventuel filtrage et pour setter les  droits : un fileSets aurait pu être utilisé-->
<file>
<source>src/main/resources/README.txt</source>
<destName>README.txt</destName>
</file>
<file>
<source>target/sources.jar</source>
<destName>sources/sources.jar</destName>
</file>
<file>
<source>src/main/resources/launcher.sh</source>
<destName>launcher.sh</destName>
<fileMode>0755</fileMode>
</file>
<file>
<source>src/main/resources/launcher.bat</source>
<destName>launcher.bat</destName>
</file>
<file>
<source>src/main/resources/cp.bat</source>
<destName>cp.bat</destName>
</file>
</files>
</assembly>

Fichiers launcher.bat et cp.bat

fichier cp.bat :

set CP=%CP%;%1

fichier launcher.bat :

set TEST_HOME=.
set CP=lib
for %%i in (%TEST_HOME%\lib\*.jar) do call cp.bat %%i
java -classpath %CP% monpackage.ClasseMain

Fichier launcher.sh

#!/bin/bash
export TEST_HOME=.
export TEST_CP=.
for i in ${TEST_HOME}/lib/*.jar;
do TEST_CP=$i:${TEST_CP}
done
java -classpath ${TEST_CP}:lib monpackage.ClasseMain

Conclusion

On a donc pu constater que la procédure de production d’un livrable est totalement intégrable avec maven 2 et qu’elle est extrêmement simple et peu consommatrice en temps une fois mise en place.
En outre, cela permet de normaliser le livrable et d’être sûr de ne rien oublier d’une version à une autre… alors pourquoi s’en passer?
Il est à noter que la problématique est identique avec d’autres outils (ant, graddle, easy-ant, …) et qu’il est indispensable de prendre en compte cette phase de développement.

Pour aller plus loin…

Nombre de vue : 50

COMMENTAIRES 5 commentaires

  1. Il est possible de n’avoir qu’un seul fichier batch sous Windows avec quelques astuces que je publierai dans un ticket suivant.
    Mais dans notre cas, je pense que le mieux est de faire générer par Maven le fichier MANIFEST.MF dans notre JAR. Ce qui permet de charger les autres librairies sans avoir à les préciser dans le classpath.

    La configuration du plugin Maven de création du JAR (maven-jar-plugin) permet tout à fait de le faire, en voici un exemple :

    maven-jar-plugin
    2.1

    ${maven.dependency.classpath}

    monpackage.ClasseMain
    true
    true
    ./lib/

    On peut même y mettre la classe contenant la méthode main à exécuter, ce qui simplifiera la commande pour le lancement de notre application. Elle sera du type :
    java -jar lib/foo-1.0-SNAPSHOT.jar

  2. ————————————-
    Il est possible de n’avoir qu’un seul fichier batch sous Windows avec quelques astuces que je publierai dans un ticket suivant.
    Mais dans notre cas, je pense que le mieux est de faire générer par Maven le fichier MANIFEST.MF dans notre JAR. Ce qui permet de charger les autres librairies sans avoir à les préciser dans le classpath.

    La configuration du plugin Maven de création du JAR (maven-jar-plugin) permet tout à fait de le faire, en voici un exemple :
    <plugin>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.1</version>
    <configuration>
    <includes>
    <include>${maven.dependency.classpath}</include>
    </includes>
    <archive>
    <manifest>
    <mainClass>monpackage.ClasseMain</mainClass>
    <addClasspath>true</addClasspath>
    <addExtensions>true</addExtensions>
    <classpathPrefix>./lib/</classpathPrefix>
    </manifest>
    </archive>
    </configuration>
    </plugin>

    On peut même y mettre la classe contenant la méthode main à exécuter, ce qui simplifiera la commande pour le lancement de notre application. Elle sera du type :
    java -jar lib/foo-1.0-SNAPSHOT.jar

  3. Khanh Maudoux dit :

    Cool.
    Merci pour l’info. Je ne connaissais pas cette astuce avec le fichier Manifest 😉
    et pour l’utilisation d’un seul fichier .bat, je n’ai pas réussi à trouver mieux donc je suis preneur.

  4. […] META-INFMANIFEST.MF dans le JAR est la plus propre. Vous trouverez dans le billet de Khanh, L’art du livrable, comment le faire générer par Maven. Pour que cela fonctionne, il faut que les JAR que vous […]

AJOUTER UN COMMENTAIRE