Reputation: 10331
In my project there is a class implementing an interface. The interface comes from a dependency. I have another dependency that itself has a dependency on a jar that also contains the same interface, except a version with more methods; the two jars containing the same package-interface DON'T have the same groupId or artifactId.
Compilation is failing because the compiler complains that the class in my project is not implementing all the methods. I realized it is because the compiler is taking the interface reference from the wrong jar. My question is, why is maven using the interface from the transitive dependency instead of the one from the jar that I explicitly mention in the project POM? I can see the jar that is used appears earlier in the definition (so i imagine in the classpath as well), but i thought in these cases maven resolved it by using the colliding class/interface from the dependency with the shortest path
Here is part of the dependency tree. Note that this is grepped but it can still be seen that javax.servlet:servlet-api
(the one actually used) is deeper in the tree compared to tomcat:servlet
(the one that should be used)
[builder@ca-rd-build11 proj]$ mvn dependency:tree | grep servlet
[INFO] | +- javax.servlet:servlet-api:jar:2.4:compile
[INFO] +- tomcat:servlet:jar:4.0.6:compile
I'm using maven 3.0.4
Upvotes: 20
Views: 16047
Reputation: 17860
For future visitors (since the title is generic):
In Maven you have two categories of dependencies:
<dependencies>
As per the official Maven documentation the following mediation is applied when multiple versions are encountered as dependencies:
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. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins.
Example: if dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter. You could explicitly add a dependency to D 2.0 in A to force the use of D 2.0.
In the above example, A is the actual project, B and E are external dependencies defined in its pom.xml file, and C and D are transitive dependencies.
Upvotes: 4
Reputation: 299208
why is maven using the interface from the transitive dependency instead of the one from the jar that I explicitly mention in the project POM?
Because, to Maven, the two have nothing to do with each other. Maven doesn't know about class or package names, Maven just knows about groupId and artifactId. Since those are not the same, maven doesn't have any reason to omit the transitive dependency.
And if I am correct, Maven places the dependencies on the classpath in the order they are defined, i.e. the transitive dependencies of dependency a
appear before dependency b
.
Upvotes: 20
Reputation: 4989
When you declare your dependency on the other jar file you can tell it to exclude the transitive dependency on the conflicting jar file:
<dependency>
<groupId>group</groupId>
<artifactId>artifact</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>othergroup</groupId>
<artifactId>ArtifactToExclude</artifactId>
</exclusion>
</exclusions>
</dependency>
Upvotes: 5