I have a parent project contains a dozen child projects, one of the child projects use org.apache.httpcomponents:httpclient:jar:4.3.5
, which depends on org.apache.httpcomponents:httpcore:jar:4.3.2
.
However, the result version of httpcore
is resolved to 4.2.1 instead of 4.3.2.
The following is an extraction of the output when running dependency:tree
with debug option checked in Eclipse:
...
[DEBUG] Using mirror nexus (http://192.168.0.111:8081/nexus/content/groups/public) for apache.snapshots (http://repository.apache.org/snapshots).
[DEBUG] testArtifact: artifact=org.apache.httpcomponents:httpclient:jar:4.3.5:compile
[DEBUG] includeArtifact: artifact=org.apache.httpcomponents:httpclient:jar:4.3.5:compile
[DEBUG] startProcessChildren: artifact=org.apache.httpcomponents:httpclient:jar:4.3.5:compile
[DEBUG] manageArtifactVersion: artifact=org.apache.httpcomponents:httpcore:jar:4.3.2:compile, replacement=org.apache.httpcomponents:httpcore:jar:4.2.1
[DEBUG] Using mirror nexus (http://192.168.0.111:8081/nexus/content/groups/public) for apache.snapshots (http://repository.apache.org/snapshots).
...
It just shows replacement=org.apache.httpcomponents:httpcore:jar:4.2.1
, but it tells nothing about the reason of the replacement. The parent project's pom.xml uses quite a lot dependencies and even though I could try to remove those dependencies one by one and check the result, it would be quite time consuming. Is there any more effective way to debug the artifact replacement?
Here is almost the full log of the dependency:tree
from Eclipse with debug option checked.
From your log, you can find the lines:
[DEBUG] com.company.xyz:xyz-integration-lib:jar:0.0.1-SNAPSHOT
[DEBUG] com.company.xyz:xyz-utils:jar:0.0.1-SNAPSHOT:compile
[DEBUG] commons-codec:commons-codec:jar:1.8:compile
[DEBUG] javax.mail:mail:jar:1.4:provided
[DEBUG] javax.activation:activation:jar:1.1.1:provided (version managed from 1.1 by org.jboss.spec:jboss-javaee-6.0:3.0.2.Final)
[DEBUG] org.apache.commons:commons-lang3:jar:3.3.2:compile
[DEBUG] junit:junit:jar:4.8.2:test
[DEBUG] com.thoughtworks.xstream:xstream:jar:1.4.7:compile
[DEBUG] xmlpull:xmlpull:jar:1.1.3.1:compile
[DEBUG] xpp3:xpp3_min:jar:1.1.4c:compile
[DEBUG] joda-time:joda-time:jar:2.4:compile
[DEBUG] org.assertj:assertj-joda-time:jar:1.1.0:test
[DEBUG] org.assertj:assertj-core:jar:1.3.0:test
[DEBUG] org.apache.httpcomponents:httpclient:jar:4.3.5:compile
[DEBUG] org.apache.httpcomponents:httpcore:jar:4.2.1:compile (version managed from 4.3.2 by org.jboss.as:jboss-as-parent:7.2.0.Final)
[DEBUG] commons-logging:commons-logging:jar:1.1.3:compile
[DEBUG] org.slf4j:slf4j-api:jar:1.7.7:compile
[DEBUG] org.slf4j:slf4j-log4j12:jar:1.7.7:compile
[DEBUG] log4j:log4j:jar:1.2.17:compile
[DEBUG] org.mockito:mockito-all:jar:1.9.5:test
[DEBUG] org.powermock:powermock-module-junit4:jar:1.5.5:test
[DEBUG] org.powermock:powermock-module-junit4-common:jar:1.5.5:test
[DEBUG] org.powermock:powermock-core:jar:1.5.5:test
[DEBUG] org.javassist:javassist:jar:3.18.1-GA:test (version managed from 3.18.2-GA by org.springframework.boot:spring-boot-dependencies:1.1.4.RELEASE)
[DEBUG] org.powermock:powermock-reflect:jar:1.5.5:test
[DEBUG] org.objenesis:objenesis:jar:2.1:test
[DEBUG] org.powermock:powermock-api-mockito:jar:1.5.5:test
[DEBUG] org.powermock:powermock-api-support:jar:1.5.5:test
Where you can see that the javassist
and httpcore
versions are dropped by certain transitive dependencies and javax.activation
version is raised by one.
This happens when more than one of your project dependencies are depending on the same library and have defined dependencies to different versions of that library. This can be annoying, since generally you cannot change how the parent POM or its dependencies are affecting the versions of your transitive dependencies.
The mediation rules from Maven docs are as follows:
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.
"nearest definition" means that the version used will be the closest one to your project in the tree of dependencies, eg. 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
However what you can do is manage the dependency versions yourself. This is called dependency management and as stated by the same docs:
Dependency management - this allows project authors to directly
specify the versions of artifacts to be used when they are encountered
in transitive dependencies or in dependencies where no version has
been specified. In the example in the preceding section a dependency
was directly added to A even though it is not directly used by A.
Instead, A can include D as a dependency in its dependencyManagement
section and directly control which version of D is used when, or if,
it is ever referenced.
Thus, you can just add:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>bar</groupId>
<artifactId>foo</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
into your own POM and this will always override whatever version are being defined for your transitive dependencies through dependency mediation.