Question

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:

  • The "fix" is normally safe, provided I'm choosing to use the newer version. This relies on the library authors maintaining backwards compatibility.

  • There is typically no impact on the Maven build (since the nearer definition wins), however by excluding the dependency I'm telling Maven that I know about this problem and thus appeasing the maven-enforcer-plugin.

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.

Était-ce utile?

La 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 :

  • if this exclusion still makes sense.
  • if you need to upgrade the version of the excluded dependency in your own project.

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

Autres conseils

If JUnit as an artifact is coming through as a dependency in compile scope, it is a bug of one of your libraries, here: ca.juliusdavies.

JUnit should always be included in test scope. Thus, it is not packed into the produced .jar, .war or .ear file, on successful build.

Generally speaking, there is no harm in excluding already included dependencies, as when library 1 and library 2 share one common dependency.

The only problem, of course, that can occur, is when library 1 and library 2 include different versions of the same dependent artifact. This can cause run-time errors, when the features of the library have changed. Fortunately, this is not often the case, unless the difference in the version numbers is great. In general, it is advisable to include the latest dependency version and exlude the older one. This is most of the time viable.

If not, check wheter there are updates to the first-level dependencies of your project.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top