Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven chooses inconsistent version of indirect dependency: why, and how to prevent?

We encountered a situation where Maven chose an inconsistent version of an indirect dependency, and I'd like to understand why, and how to prevent this in the future.

Our pom.xml file had the following dependencies:

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-jpa</artifactId>
  <version>1.1.0.RELEASE</version>
</dependency> 
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>3.1.2.RELEASE</version>
</dependency>

but without a direct dependency on spring-core.

Both depend on spring-core: spring-web 3.1.2.RELEASE depends on spring-core 3.1.2.RELEASE (see its pom-file), and spring-data-jpa 1.1.0.RELEASE depends on an arbitrary 3.x version of spring-core (to be precise [3.0.7.RELEASE,4.0.0.RELEASE), see its pom-file).

Combining both, I would expect Maven to choose spring-core version 3.1.2.RELEASE. However, it does not. Instead, it chooses the highest from the range [3.0.7.RELEASE,4.0.0.RELEASE), which currently is 3.2.0.RELEASE.

(Reproduction scenario: put the above pom.xml file (gist) in its own directory, and run mvn dependency:tree -Dverbose=true: for me the result is this tree (gist). I get the same result for both Maven 2.2.1 on Linux and Maven 3.0.4 on Windows.)

This seems wrong, because it is inconsistent: a version of spring-core which spring-web's pom-file does not allow is used.

(This occurred for us when spring-core 3.2.0.RC1 became available: on the next update it was suddenly selected, and we were lucky that we got a build error because of an incompatible change between spring-core 3.1 and 3.2. But next time we may not be so lucky, and have runtime errors which are very difficult to track down.)

Urghh: I just notice that the order of <dependency> declarations matters: if I put spring-web first, then spring-core 3.1.2.RELEASE is selected. What gives?

Question: How can we make Maven choose consistent versions of indirect dependencies, or at least warn if it makes a choice which goes against a version specified in a pom-file?

Update: I am asking for a general solution here. For this specific case, I know that I can get the correct behavior by adding a dependency in <dependencyManagement>, specifying that I always want spring-core 3.1.2.RELEASE. However, I would like Maven to do the Right Thing (TM) without such specific declarations.

like image 825
MarnixKlooster ReinstateMonica Avatar asked Jan 24 '13 07:01

MarnixKlooster ReinstateMonica


People also ask

How does Maven decide which version of a dependency to use?

Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM.

How do you resolve dependency conflicts in Maven?

Enforcer can help developers solve dependency conflicts in Maven by analyzing all libraries declared in the POM file. The plugin uses a lot of different rules, but we are only interested in one: dependencyConvergence – ensures all dependencies converge to the same version.

Does Maven override dependency version?

By taking advantage of Maven's nearest definition logic, developers can override the version of a dependency by declaring it on the root pom. xml file.

How do I exclude a specific version of a dependency in Maven?

Multiple transitive dependencies can be excluded by using the <exclusion> tag for each of the dependency you want to exclude and placing all these exclusion tags inside the <exclusions> tag in pom. xml. You will need to mention the group id and artifact id of the dependency you wish to exclude in the exclusion tag.


1 Answers

What you expect seems logic but Maven has no chance to do this. It just resolves dependencies without the knowledge that spring-data-jpa might have somethig to do with spring-core.

The dependency resolution works as describe here and is the way you already described:

Dependency mediation - this determines what version of a dependency will be used when multiple versions of an artifact are encountered. Currently, Maven 2.0 only supports using the "nearest definition" which means that it will use the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, until Maven 2.0.8 it was not defined which one would win, but since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.

So I think the only way to prevent this situation is to be aware of it and to explecitly set the version you need in your own pom.

By the way, why do you think "This seems wrong, because it is inconsistent: a version of spring-core is used which spring-web's pom-file does not allow."?

A version specification of <version>x.y</version> is only a recommendation (see 'Note' here) to use this version. If you intend to force to use this version you have to set <version>[x.y]</version>.

like image 70
FrVaBe Avatar answered Nov 15 '22 04:11

FrVaBe