lundi 16 avril 2012

JAVA - Récupérer la version courante d'un JAR / WAR programmatiquement

Récupérer la version courante d'un JAR

C'est facile, et cela se fait en deux temps : 
  1. Tout d'abord, il s'agit de configurer MAVEN pour que le manifest associé à votre JAR contienne la version de votre librairie. C'est le composant MAVEN ARCHIVER qui doit être paramétré de façon à rajouter l'information attendue dans le fichier manifest de votre JAR. Il suffit de rajouter ce fragment de XML dans votre fichier pom :
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-jar-plugin</artifactid>
    <version>2.3.2</version>
    <configuration>
     <archive>
      <manifest>
       <adddefaultimplementationentries>true</adddefaultimplementationentries>
       <adddefaultspecificationentries>true</adddefaultspecificationentries>
      </manifest>
     </archive>
    </configuration>

   </plugin>
  1. Puis, dans le code, il vous suffit de récupérer l'objet Package associé à l'une des classes du JAR (par exemple LaClasseDuJar ) dont vous voulez récupérer la version ; l'instance de cet objet vous donnera les informations de version que vous cherchez, selon :
Package objPackage = LaClasseDuJar.class.getPackage();
String version = objPackage.getImplementationVersion();

Version d'une application web (ou WAR)

C'est un peu plus compliqué : le coup du Manifest ne marche pas tout le temps. En tous les cas, avec tomcat, je ne suis pas arrivé à faire fonctionner la technique précédente même si celle-ci fonctionne parfaitement pour les JAR utilisés par la webapp. Du coup, j'utilise l'astuce tout bête suivante : dans mon WAR j'inclue un fichier texte contenant la version de l'application.

Génération du fichier texte avec maven

Pour générer le fichier texte avec l'information de version attendue , j'utilise le plugin maven replacer. L'idée consiste à faire remplacer un pattern spécifique par l'information attendue lors de la phase de préparation du war (ici lors de la phase précédant sa génération ). Voici par exemple un extrait d'un fichier pom permettant de faire ce que nous attendons :
<build>
...
<plugin>
       <groupId>com.google.code.maven-replacer-plugin</groupId>
       <artifactId>maven-replacer-plugin</artifactId>
       <version>1.3.9</version>
       <executions>  
       <execution>  
         <!-- le remplacement doit se faire avant que le war soit packagé -->  
         <phase>prepare-package</phase>  
         <goals>  
           <goal>replace</goal>  
         </goals>  
       </execution>  
     </executions>
     <configuration>  
       <includes>
         <include>target/mywebapp/WEB-INF/classes/com/me/config/version.prop</include>
       </includes>  
       <regex>false</regex>  
       <!-- chaîne à remplacer -->  
       <token>PROJECT_VERSION</token>  
       <!-- remplacer par la version du projet -->  
       <value>${project.version}</value>  
     </configuration>
      </plugin>
...
</build>
Pour que cela fonctionne, il faut tout d'abord que vous disposiez d'un fichier version.prop dont le contenu peut être -
version=PROJECT_VERSION
- et devant se trouver par exemple dans le package com.me.config ; puis que vous configuriez ainsi la génération de votre fichier war :
<plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-war-plugin</artifactId>
       <version>2.1.1</version>
       <executions>
       <execution>
          <id>prepare-war</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>war</goal>
          </goals>
       </execution>
   </executions>
       <configuration>
        <useCache>true</useCache>
     </configuration>
      </plugin>
Cette configuration ( en particulier le useCache=true ) signifie à maven de réutiliser les fichiers générés dans target pour constituer le war. Ainsi, le fichier patché par le plugin replacer sera celui inclus dans le package. Si jamais cette technique ne fonctionne pas pour vous (le useCache étant marqué comme expérimental) vous pouvez toujours demander au plugin replacer de patcher le fichier source version.prop : c'est à dire celui se trouvant dans vos sources ( il vous faudra sans doute utiliser une expression régulière plutôt qu'un simple mot à remplacer ).

Lecture du fichier texte

C'est très simple, par exemple à partir d'une classe MainConfig, contenue dans le même package ( com.me.config ) que le fichier version.prop :
InputStream is = MainConfig.class.getResourceAsStream("./version.prop");
if (is!=null) {
  Properties prop = new Properties();
  String version =  null;
  try {
     prop.load(is);
     version = prop.getProperty("version");
  } catch (IOException e1) {
               
  }
}