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).
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é).
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>
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.