Un JAR exécutable unique utilisant Spring avec Maven

Spring possède dans certains de ses modules des fichiers :

META-INF/spring.schemas
META-INF/spring.handlers

Ces fichiers sont utilisés par le moteur de configuration de Spring afin de supporter des namespaces, que vous utilisez ensuite dans vos fichiers XML, par exemple :

1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

Alors quand on génère un WAR pas de problème, chaque fichier se retrouve dans un JAR séparé à l’intérieur du WAR, et ils sont tous chargés. Mais le jour où vous devez avoir un JAR exécutable, ça se complique. On ne peut pas avoir un JAR dans un JAR, donc les plugins tels que maven-assembly-plugin éclatent chaque librairie à l’intérieur de votre JAR final.

Conséquence de cela, le fichier META-INF/spring.schemas est mis dans le JAR la première fois qu’assembly tombe dessus, puis est ignoré. Il n’y a donc qu’un seul de ces fichiers dans le JAR final. A l’exécution, ça pète :

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: 
Configuration problem: Unable to locate Spring NamespaceHandler for 
XML schema namespace http://www.springframework.org/schema/aop]

Une solution possible est d’externaliser les librairies. On aurait ainsi un JAR exécutable accompagné d’un dossier lib/ contenant tous les autres JAR.

Une autre solution est de concaténer ces fichiers spring.schemas et spring.handlers, et de garder ainsi un unique JAR exécutable. Pour cela, je vous propose maven-shade-plugin plutôt qu’assembly. Et voilà ce que ça donne :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>1.4</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.excilys.converter.Main</mainClass>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Le but est discutable, un JAR unique n’est pas l’idéal dans beaucoup de situations. Mais si c’est ce que vous voulez, cette solution est la seule que j’ai pu trouver ! :)

VN:R_U [1.9.22_1171]
Rating: 0 (from 0 votes)
Share
Ce contenu a été publié dans Maven, Spring, Trucs & astuces, avec comme mot(s)-clef(s) , , , . Vous pouvez le mettre en favoris avec ce permalien.

2 réponses à Un JAR exécutable unique utilisant Spring avec Maven

  1. maven-assembly-plugin et maven-shade-plugin ont des fonctionnalités qui se recoupent.
    maven-shade-plugin semble effectivement plus pertinent pour réaliser un uber-jar, notamment en permettant de fusionner des fichiers de resources ou en translatant des packages (shade).
    En revanche, maven-assembly-plugin est indispensable si tu souhaites réaliser justement l’inverse : un gros zip prêt à l’emploi avec un répertoire lib contenant tes dépendances, des fichiers de shell, etc…

    VN:R_U [1.9.22_1171]
    Rating: 0 (from 0 votes)
  2. Pour continuer sur ce que dis Stéphane : le maven-assembly-plugin est effectivement très pratique dans le cadre de projets Open Source, lorsque Maven n’est pas l’unique voie de distribution (je pense notamment aux projets Android).

    Pour faire un ubber jar, on utilisera peut-être JarJar en complément de maven-assembly : il permet de modifier les packages des classes incluses, et donc d’éviter tout problème de conflit de version (Exemple ici).

    Le Maven Shade Plugin permet aussi de faire l’équivalent du War Overlay pour un JAR. Intérêt : créer une release custom d’un JAR, en ne gardant sur le SVN que les fichiers modifiés, et donc être mieux à même de suivre les évolutions de version. Exemple ici.

    VN:R_U [1.9.22_1171]
    Rating: 0 (from 0 votes)

Laisser un commentaire