Blog

Résoudre les conflits de dépendances

Lors de nos développements, nous reposons beaucoup sur des projets externes qui nous fournissent énormément de services utiles. Dans un récent projet, nous avons eu besoin de faire fonctionner Neo4j conjointement à ElasticSearch. Jusqu’ici, aucun soucis n’est à déplorer, mais nous avions une exigence particulière : il fallait que l’application puisse démarrer automatiquement un serveur Neo4J ainsi qu’un serveur ElasticSearch sur les postes de développements (ainsi que pour les tests d’intégration).

Problème existant

Les deux outils que nous utilisons se basent sur Apache Lucene pour toute la partie indexation et accès aux données. Mais, et c’est là que le problème se situe, ils n’utilisent pas les mêmes versions de Lucene.

<!-- Extrait du pom d'ElasticSearch -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>4.9.0</version>
    <scope>compile</scope>
</dependency>

<!-- Extrait du pom de Neo4j -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>3.6.2</version>
</dependency>

ElasticSearch utilise la version 4.9.0, alors que Neo4J utilise la version 3.6.2. Ainsi, en fonction du bon vouloir du Classloader qui sera utilisé par l’application, il se peut qu’ElasticSearch ou Neo4J refuse de fonctionner. La difficulté pour comprendre et détecter le problème est qu’il se manifeste souvent par un obscur NoClassDefFoundError ou NoSuchMethodError qui n’est pas des plus explicites (d’autant plus lorsque notre IDE nous montre une version qui contient ledit symbole non trouvé).

Solution de contournement

Le conflit est assez simple à contourner une fois qu’on a compris ce qui se passe. En fait, il y a deux classes portant le même nom dans les classes chargées, par exemple org.lucene.MaClass, l’une effaçant l’autre aux yeux du ClassLoader. Une pratique courante est de renommer (ou relocate) le package de base d’une bibliothèque utilisée et de l’inclure dans le fichier de package du projet, le plugin maven-shade est conçu dans cette optique. Le choix fait est de renommer la dépendance Lucene dans Neo4J pour notre part, ainsi, nous avons forké le projet et configuré le plugin shade pour qu’il inclue le contenu de la dépendance d’Apache Lucene et qu’il fasse le renommage de org.apache.lucene en shaded.org.apache.lucene.

 <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.2</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
                <configuration>
                    <createDependencyReducedPom>true</createDependencyReducedPom>

                    <artifactSet>
                        <includes>
                            <include>org.apache.lucene:*</include>
                        </includes>
                    </artifactSet>
                    <relocations>
                        <relocation>
                            <pattern>org.apache.lucene</pattern>
                            <shadedPattern>shaded.org.apache.lucene</shadedPattern>
                        </relocation>
                    </relocations>
                </configuration>
            </execution>
        </executions>
  </plugin>

Déploiement et nommage

Pour ne pas polluer les dépôts, le numéro de version modifié a été postfixé par -shaded. Le déploiement a été fait sur un dépôt Maven qui est en fait un simple repository Github. Le commit correspondant à cette modification est consultable ici.

comments powered by Disqus

Contact

legos

Code-Troopers

26 bis rue Abraham Bosse
37000 Tours - Fr

contact@code-troopers.com

07 82 28 72 16

Suivez nos actualités