I have a maven-built Java application, which pulls in a number of libraries. The application is in one git repo (with its own maven build), and each library is in its own git repo (with its own maven build). Also, both the application and some of the libraries depend on guava.
The application's pom.xml specifies guava version 19.0:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
The application also imports another library, called library1. library1 also depends on guava. But library1's pom.xml specifies a later version of guava:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
When the application was built, maven chose the guava version that was specified at the application level. But that caused library1 to fail, because it was expecting a later version of guava.
If we had been using grade and tried to specify this combination of guava versions, I believe gradle would have caused the build to fail, which is probably the right thing to do.
Given that we may have pom.xml files with such a version discrepancy, is there a way to have set up maven so that it would have noticed the discrepancy and either failed the build or displayed a really prominent warning about the problem?
Excluding a Transitive Dependency From an Artifact One way to resolve a version collision is by removing a conflicting transitive dependency from specific artifacts. In our example, we don't want to have the com. google. guava library transitively added from the project-a artifact.
We can use mvn dependency:tree -Dverbose command to print the dependency conflicts. It can help us in determining if there are any incompatibility issues with a JAR.
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.
You can setup a dependencyConvergence enforcer rule in Maven. This rule requires that dependency version numbers converge.
If a project has two dependencies, A and B, both depending on the same artifact, C, this rule will fail the build if A depends on a different version of C than the version of C depended on by B.
The rule can be added like this.
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M2</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
...
</project>
More details can be found here.
To detect all the transitive dependencies you can use the maven-dependency-plugin
:
mvn dependency:tree -Dverbose
It will show the direct and transitive dependencies of your project. The -Dverbose
option shows the conflicts.
[INFO] [dependency:tree]
[INFO] org.apache.maven.plugins:maven-dependency-plugin:maven-plugin:2.0-alpha-5-SNAPSHOT
[INFO] +- org.apache.maven.reporting:maven-reporting-impl:jar:2.0.4:compile
[INFO] | \- commons-validator:commons-validator:jar:1.2.0:compile
[INFO] | \- commons-digester:commons-digester:jar:1.6:compile
[INFO] | \- (commons-collections:commons-collections:jar:2.1:compile - omitted for conflict with 2.0)
[INFO] \- org.apache.maven.doxia:doxia-site-renderer:jar:1.0-alpha-8:compile
[INFO] \- org.codehaus.plexus:plexus-velocity:jar:1.1.3:compile
[INFO] \- commons-collections:commons-collections:jar:2.0:compile
For the selection of the same library with different versions:
Dependency mediation - this determines what version of an artifact will be chosen 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.
"nearest definition" means that the version used will be the closest one to your project in the tree of dependencies. For 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.
So, if one of your libraries does not work with the other versions of guava, that means that your dependencies are not compatible. That should be updated to work with the newer version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With