Hilikus
Hilikus

Reputation: 10331

Maven dependency order

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

Answers (3)

Voicu
Voicu

Reputation: 17860

For future visitors (since the title is generic):

In Maven you have two categories of dependencies:

  • (external) dependencies: what you define directly in your pom.xml files inside <dependencies>
  • transitive dependencies: the dependencies your pom.xml dependencies require (automatically included, together with their dependencies, and so on)

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

Sean Patrick Floyd
Sean Patrick Floyd

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

Nick Wilson
Nick Wilson

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

Related Questions