Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting dependency conflicts with Maven

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?

like image 310
Mike W Avatar asked Jul 26 '19 22:07

Mike W


People also ask

How does Maven handle conflicting dependencies?

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.

How do you determine dependency conflict?

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.

How do you resolve conflicts in dependences in POM xml?

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.


Video Answer


2 Answers

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.

like image 132
Abhishek Garg Avatar answered Oct 27 '22 00:10

Abhishek Garg


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.

like image 29
jordiburgos Avatar answered Oct 27 '22 01:10

jordiburgos