javamavendependency-managementmaven-enforcer-plugin

Solving Maven dependency convergence issues


I use the maven-enforcer-plugin to check for dependency convergence issues. A typical output would be:

[WARNING] Rule 1: org.apache.maven.plugins.enforcer.DependencyConvergence failed 
  with message:
Failed while enforcing releasability the error(s) are [
Dependency convergence error for junit:junit:3.8.1 paths to dependency are:
+-foo:bar:1.0-SNAPSHOT
  +-ca.juliusdavies:not-yet-commons-ssl:0.3.9
    +-commons-httpclient:commons-httpclient:3.0
      +-junit:junit:3.8.1
and
+-foo:bar:1.0-SNAPSHOT
  +-junit:junit:4.11
]

Seeing this message, I would normally "solve" it by excluding the transitive dependency, e.g.

<dependency>
  <groupId>ca.juliusdavies</groupId>
  <artifactId>not-yet-commons-ssl</artifactId>
  <version>0.3.9</version>
  <exclusions>
    <!-- This artifact links to another artifact which stupidly includes 
      junit in compile scope -->
    <exclusion>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </exclusion>
  </exclusions>
</dependency>

I'd like to understand whether this is truly a fix and the risks involved in excluding libraries in this fashion. As I see it:

Are my thoughts correct and is there an alternative way of handling this issue? I'm interested in answers that focus on the general case - I realise the junit example above is a little strange.


Solution

  • We all agree that JUnit should never be set to another scope than test. Generally speaking I don't think either that there is another solution than excluding the unwanted dependency, so we all agree that your are right to do it.

    A SIMPLE CASE :

    As Andreas Krueger says, there may be a risk with versions (I actually encountered it). Let say that the project's dependencies are the following:

    +-foo:bar:1.0-SNAPSHOT
      +-group1:projectA:2.0
         +-group2:projectB:3.8.1
      +-group2:projectB:4.11
    

    Note that it is only a mere simplification of your case. Seeing this dependency tree, you would exclude the dependency projectB given by projectA :

    <dependency>
      <groupId>group1</groupId>
      <artifactId>projectA</artifactId>
      <version>2.0</version>
      <exclusions>
        <exclusion>
          <groupId>group2</groupId>
          <artifactId>projectB</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    

    After packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 3.8.1. Everything would be fine and the project would run without encountering any problem at all.

    Then, a while after, let say that you decide to upgrade to the next version of project A, version 3.0 which adds new great features :

    <dependency>
      <groupId>group1</groupId>
      <artifactId>projectA</artifactId>
      <version>3.0</version>
      <exclusions>
        <exclusion>
          <groupId>group2</groupId>
          <artifactId>projectB</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    

    The problem is that you are not aware yet that projectA version 3.0 also have upgraded its dependency projectB to version 5.0 :

    +-foo:bar:1.0-SNAPSHOT
      +-group1:projectA:3.0
         +-group2:projectB:5.0
      +-group2:projectB:4.11
    

    In that case, the exclusion you would have made a while ago excludes projectB version 5.0.

    However, projectA version 3.0 needs the improvements from project B version 5.0. Because of the exclusion, after packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 5.0. At the moment you use any of projectA's new features, the program wouldn't run correctly.

    WHAT WAS THE SOLUTION ?

    I encountered this problem in a Java-EE project.

    A team developped database services. They packaged it as projectA. Each time they updated the services, they also updated a file listing all their current dependencies and the current versions.

    ProjectA was a dependency for the Java-EE project I was working on. Each time the service-team updated ProjectA, I also checked the versions' updates.

    In fact, there is no harm in excluding a dependency. But each time you update a dependency where an exclusion has been set, You have to check :

    I guess maven exclusions are like kitchen knifes. It's sharp, cuts vegetables with no effort, but requires care when handling it...