88

Let's say I have four projects:

  • Project A (has a dependency on B and D)
  • Project B (has a dependency on D)
  • Project C (has a dependency on D)
  • Project D

In this scenario if I run project A, Maven will correctly resolve the dependency to D. If I understand this correctly Maven always takes the dependency with the shortest path. Since D is a direct dependency of A it will be used rather then, the D which is specified within B.

But now assume this structure:

  • Project A (has a dependency on B and C)
  • Project B (has a dependency on D)
  • Project C (has a dependency on D)
  • Project D

In this case the paths to resolving D have the same depth. What happens is that Maven will have a conflict. I know that it is possible to tell Maven that he should exclude dependencies. But my question is how to address such kind of problems. I mean in a real world application you have a lot of dependencies and possibly a lot of conflicts as well.

Is the best practice solution really to exclude stuff or are there other possible solutions to this? I find it very hard to deal with when i suddenly get a ClassNotFound Exception because some versions have changed, which caused Maven to take a different dependency. Of course, knowing this fact makes it a little bit easier to guess that the problem is a dependency conflict.

I'm using maven 2.1-SNAPSHOT.

Evdzhan Mustafa
  • 3,645
  • 1
  • 24
  • 40
kukudas
  • 4,834
  • 5
  • 44
  • 65

5 Answers5

109

The maven way of resolving situations like this is to include a <dependencyManagement> section in your project's root pom, where you specify which version of which library will be used.

EDIT:

<dependencyManagement>
  <dependencies>
    <dependency>
        <groupId>foo</groupId>
        <artifactId>bar</artifactId>
        <version>1.2.3</version>
    </dependency>
   </dependencies>
</dependencyManagement>

Now no matter which version of library foo:bar is requested by a dependency, version 1.2.3 will always be used for this project and all sub-projects.

Reference:

ram
  • 2,275
  • 3
  • 27
  • 38
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 1
    What if version are different of project D, Project B (has a dependency on D1.0) - Can not work with D2.0 Project C (has a dependency on D2.0) - Can not work with D1.0 – Nitul Dec 01 '17 at 12:53
  • 4
    @Nitul then you're basically screwed. Your only option then is to repackage one of the dependencies using something like the maven shade plugin – Sean Patrick Floyd Dec 02 '17 at 01:24
  • 7
    To clarify, version numbers specified in `dependencyManagement` apply to transitive dependencies, and project dependencies without an explicit version. But project dependencies with an explicit version override the version in the `dependencyManagement` section. – pnewhook Dec 22 '17 at 18:28
  • I had to add the following to dependencyManagement com.fasterxml.jackson.core jackson-databind – anand Jan 27 '22 at 21:52
38

Maven can handle both situations without any conflict. Conflicts will exist when two versions of a transitive dependency are required. The ClassNotFoundException you describe results from the app (or a dependency) attempting to use a class not available in the version of the conflicted dependency that actually gets used. There are multiple ways to fix the problem.

  1. Update the versions of the libraries you are using that depend on the conflicted dependency, so that they all depend on the same version version of that dependency
  2. Declare the conflicted dependency as a direct dependency of your project with the version you want to be included (in the example, the one with the missing class included in it)
  3. Specify which version of the conflicted dependency that transitive dependencies should use, via the <dependencyManagement> section of the POM
  4. Explicitly exclude the unwanted versions of the conflicted dependency from being included with the dependencies that rely on them using an <exclusion>
Daniel
  • 10,115
  • 3
  • 44
  • 62
  • This answer helped me make the build error go away. Bu! at the cost of no longer relying on supported, tested configurations? If you use to force one of the modules to use a version of dependency that it did not "want" to, you might expose a latent bug. Why doesn't java world abide by rules of semantic versioning more strictly? Then build tools could use version ranges rather than specific versions and there'd be fewer conflicts to resolve. Maybe? – BobHy Oct 29 '22 at 03:33
28

This is fundamentally not a maven issue, but a java issue. If Project B and Project C needs two incompatible versions of project D, then you can't use them both in Project A.

The Maven way of resolving conflicts like these is unfortunately, as you already know, to choose which ones to exclude.

Using mvn dependency:analyze and mvn dependency:tree helps in finding what conflicts you have.

Buhb
  • 7,088
  • 3
  • 24
  • 38
24

You can enforce consistent dependencies in whole project with rule Dependency Convergence.

 <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-enforcer-plugin</artifactId>
     <version>1.3.1</version>
     <executions>
        <execution>
           <id>enforce</id>
           <configuration>
              <rules>
                 <DependencyConvergence/>
              </rules>
           </configuration>
           <goals>
              <goal>enforce</goal>
           </goals>
        </execution>
     </executions>
  </plugin>
MariuszS
  • 30,646
  • 12
  • 114
  • 155
9

One possible strategy is to specify for main project, what version of D to use (the newest one f.g.). However, if library D is not backward-compatible, you have a problem as stated by kukudas - it's impossible to use both libaries in your project.

In such situation there may be necessary to use either B or C in older version, so that both would depend on compatibile versions of D.

Stepan Vihor
  • 1,069
  • 9
  • 10