When Maven Dependency Plugin Lies

On April 25, 2013, in Java, maven, spring, Uncategorized, by ittaiz@wix.com

Problem:

We had an integration test which creates a spring ClassPathXmlApplicationContext and while doing so the test blew up with a NoSuchMethodError. It turned out that we had conflicting versions of dependencies on Spring artifacts. This in itself is not an unusual problem – such problems are addressed with the maven dependency plugin using the verbose option. However, what do you do when the maven plugin is wrong?

Investigation:

We started to dig in and we found that the AbstractAutowireCapableBeanFactory in its getTypeForFactoryMethod method tries to access the GenericTypeResolver resolveReturnTypeForGeneric method and fails on a java.lang.NoSuchMethodError: org.springframework.core.GenericTypeResolver.resolveReturnTypeForGenericMethod(Ljava/lang/reflect/Method;.

Initial investigation and googling we found that the method was added in 3.2.0 while we’re supposed to be running with 3.1.1.

Further investigation determined that spring-data-mongodb depends on spring framework in range [3.0.7-4) 1 and because maven takes the latest available version given that range 2 it tried to take 3.2.2.
Note that the above changes a bit given a conflict between an explicit version dependency and a range dependency but, IINM, when determining the dependencies of spring mongo there is no conflict.

The problem was further masked by two symptoms:

  1. We have other projects that use this pattern and have no problem- This was explained by the fact that the conflict-resolving mechanism of maven chooses the nearest version it finds by default 3 and since all other projects which need spring-data-mongodb depend on this project they were lucky enough to grab the 3.1.1 version and not 3.2.2
  2. dependency:tree shows it brings 3.1.1 while bringing 3.2.2- Since the stack trace showed other results I wrote a test which checks from which jar each of the above classes comes from and verified that indeed the AbstractAutowireCapableBeanFactory class arrives from spring-beans 3.2.2 and not 3.1.1 as "mvn dependency:tree" showed (a big thanks to http://bit.ly/10zD1iV for the code snippet of finding the jar of a class in runtime).
Maven dependency:tree output showing spring-beans:3.1.1 is used in the artifact

View the code on Gist.

Test which proves spring-beans:3.2.2 is used in the artifact (asserting what the jvm in the error was saying)

View the code on Gist.

The reason spring-core artifact came in 3.1.1 when spring-beans came as 3.2.2 is that our framework explicitly depends on spring-core and this artifact explicitly depends on the framework. This means spring-core 3.1.1 from framework is 2 hops which is shorter than the 3.2.2 from spring-data-mongodb.

Solution: 

Depend on spring-data-mongodb while excluding spring-beans like so:

View the code on Gist.

The Open question mark:

Why dependency:tree (in verbose mode) did not show that it brings spring-beans in 3.2.2 but in 3.1.1 while explicitly specifying that spring-core 3.2.2 was removed due to conflict? I chalk this up to a bug in the dependency plugin.

Update:

Thanks to @Samuel_Langlois we confirmed that the “open question mark” is indeed a Maven Dependency Plugin bug which was resolved in 2.5 and so using 2.7 and forward will show you the correct info.

View the code on Gist.

  1. http://repo1.maven.org/maven2/org/springframework/data/spring-data-mongodb-parent/1.0.1.RELEASE/spring-data-mongodb-parent-1.0.1.RELEASE.pom
  2. http://www.maestrodev.com/better-builds-with-maven/creating-applications-with-maven/resolving-dependency-conflicts-and-using-version-ranges/
  3. http://www.maestrodev.com/better-builds-with-maven/creating-applications-with-maven/resolving-dependency-conflicts-and-using-version-ranges/
Tagged with:  

8 Responses to When Maven Dependency Plugin Lies

  1. nadavdavdav says:

    awesome! great catch :)

  2. Yoav Abrahami says:

    Actually, you should say Maven…

  3. Samuel Langlois says:

    You using an outdated version of the Maven dependency plugin: version 2.1, according to the log.
    Try the following instead :
    mvn org.apache.maven.plugins:maven-dependency-plugin:2.7:tree

    The version 2.5 fixed this bug… but since then, the -Dverbose option is not working any more :-( See http://jira.codehaus.org/browse/MDEP-374

    • ittaiz@wix.com says:

      @Samuel Thanks for the heads up, tried it and it works. I’ll update the post. Do you know by any chance how I can configure maven to run with the latest version (without specifying it in my POMs)? If I’m not mistaken 2.1 was chosen by some default of maven and not by any configuration on my side

    • ittaiz says:

      Thanks for your comment.
      The main point I was trying to make here is that the above tools can be misleading when used with an outdated version

  4. ittaiz says:

    Another small update is that we weren’t sure why Maven was picking the 2.1 version since all explanations said it should pick the latest one when it is not explicitey configured.
    The explanation is that some plugins (like the dependency plugin) are explicitly configured by the super pom by maven.
    To resolve this issue either upgrade to 3.1/3.1.1 or just explicitly configure the plugin with the relevant version.
    more detail can be found here http://stackoverflow.com/questions/16695255/how-does-maven-determine-which-plugin-version-to-use-for-plugins-used-from-the-c

Comment