Solving the JAR hell problem in Maven

   How many times have you a developed a Java application that requires different (possibly incompatible) versions of the same library ? How many time have seen a NoSuchMethodError or IllegalArgumentException thrown from a library method and wondering - what's wrong with my application ? Although the answer is simple (and not always realized) there is no clear solution to the JAR hell problem. Consider the following simple diagram:

JAR hell

   Clearly if you use Maven 2.0 (or above) you may declare only the dependencies to Library_A and Library_B - the Maven dependency mechanism will autoresolve the dependencies to Library_C by reading the Maven configuration of Library_A and Library_B (autoresolution of transitive dependencies was introduced in Maven 2.0):

<dependencies>
   <dependency>
      <groupId>org.library_A.com</groupId>
      <artifactId>Library_A</artifactId>
      <version>1.0</version>
   </dependency>
  <dependency>
     <groupId>org.library_B.com</groupId>
     <artifactId>Library_B</artifactId>
     <version>1.0</version>
  </dependency>
</dependencies>

   Obviously two different version of Library_C (1.1 and 1.3) will be included on the classpath. Here is when the fun comes (depending on whether the two libraries are compatible). Here are two scenarios when the two versions of Library_C on the classpath will not break the application:

  • if Library_C 1.3 is backward compatible with Library_C 1.1 and Library_C 1.3 is first on the classpath;

  • if Library_C 1.3 is NOT backward compatible with Library_C 1.1 but Library_A 1.0 and Library_B 1.0 use only classes that are compatible in the two versions of Library_C;

   In many cases the above scenarios do not take place and different problems typically occur when running the application (exceptions, of course, may not even be thrown, but your application could observe unexpected behaviour). Typically you should avoid having two different versions of the same library on the classpath. How to detect this ? There are different ways - one is to use a Maven plugin (such as tattletale-maven or maven-duplicate-finder-plugin - see references) that will detect duplicate libraries at build time. But what to do when you hit such an issue ? Here are several options:

  • if possible replace Library_A or Library_B with another library that does not use Library_C at all;

  • if versions of Library_C are backward-compatible that you can move the library that uses version 1.3 above the library that uses version 1.1 so that the greater version of Library_C is loaded;

  • you can try to find a version of the libraries that can be used by both Library_A and Library_B and declare it as a dependency before Library_A and Library_B - this is more of a "trial-and-error" approach;

  • if Library_C is open-source and the corresponding license allows it - you can build your own customized version of the library that allows usage from both Library_A and Library_C and and declare it as a dependency before Library_A and Library_B - this requires additional time to get acquainted with the library;

References

1) Escape from JAR hell
http://praisethesoftware.com/2012/10/17/escape_from_the_jar_hell.html

2) Fight dependency hell in Maven
http://cupofjava.de/blog/2013/02/01/fight-dependency-hell-in-maven/

3) Maven dependency hell
http://olemortenamundsen.wordpress.com/2008/05/22/maven-dependency-hell/

 

 

 

 

 

Share