1

In my maven project, I am using a dependency A which in turn uses another dependency B (Version 1.0). Now, in my project, I also need to use the dependency B directly but a different version (need to use 2.0) than what dependency A is using internally. So to do that I added a maven dependency B version 2.0 in my pom.xml. But this is causing an issue when I am using functionality of dependency A because it is not compatible with dependency B (Version 2.0 - which I have included explicitly in my pom).

How can I include a new version of dependency B in my pom without having the dependency A refer to the new version rather making it refer to 1.0 of dependency A?

Project -> A -> B (v1.0)

Project -> B (v2.0) # this is causing A to refer B's 2.0 instead of 1.0

So what I want is to include the v2.0 of dependency B but still make dependency A to refer v.10 B.

old_soul_on_the_run
  • 279
  • 1
  • 6
  • 15
  • You have to upgrade the project A to use B(v2.0) otherwise that will not work... shading or exclusions will not solve the problem because having all that on class path or shaded will cause other issues. That's typical compatibility issue between versions.. That can never being solved by shading/exclusion etc. Only by upgrading the project the common version (B v2.0)... – khmarbaise Feb 12 '23 at 09:19
  • Maven has opinions. One of them is that a newer version with a given group+artifactid can always replace an older version. – Thorbjørn Ravn Andersen Feb 12 '23 at 10:39
  • @ThorbjørnRavnAndersen I don't think this is just an "opinion", it is more the way Java works, i.e. don't load two classes with the same qualified name (and it is very likely that two versions of the same JAR overlap in class names). – J Fabian Meier Feb 12 '23 at 11:44
  • @JFabianMeier You are probably thinking of having multiple versions of the same jar on the classpath (which can be very confusing and lead to OSGi). I am thinking of the mechanism Maven use to select which version of a dependency should be used to select the jar that is used for the classpath. There are rules to decide which version is "newest" as that is the one used, and to my knowledge Maven always does this without any warnings. This bit us when Guava was updated by a deep dependency and methods had been removed. – Thorbjørn Ravn Andersen Feb 12 '23 at 19:48
  • 1
    @ThorbjørnRavnAndersen Maven always takes the "nearest" version, not the newest (or highest) version, i.e. direct dependencies beat transitive dependencies of first order, those beat the ones of second order etc. This can lead to rather unfortunate results. Usually, I try to fix most of the versions using `` to avoid this. – J Fabian Meier Feb 12 '23 at 20:44
  • @JFabianMeier Am I getting this wrong? My understanding is that if Maven is told to use 1.4 and 1.8 of the same artifact regardless of how deep in the dependencies, it chooses the newest of these (as in highest numbered) when resolving? – Thorbjørn Ravn Andersen Feb 12 '23 at 21:05
  • @ThorbjørnRavnAndersen No. See https://stackoverflow.com/q/34201120/927493 and https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html . – J Fabian Meier Feb 13 '23 at 09:50

2 Answers2

2

You need to use the Maven shade plugin to shade one of the dependencies into a different package. Otherwise you cannot use two versions of the same dependency.

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
0

Maven can't magically fix your dependency issue, there are other technologies, like OSGi, for those kinds of issues. You can also implement your own class loader strategy.

You are hoping that Maven can solve your "classpath hell" problem, it can't.

So beware when there is a change to the major version, there are normally also breaking changes.

What you need to define is an <exclusion/> element in your dependency to ignore A's dependency. This might break A.

  <dependencies>
    <dependency>
      <groupId>Project-A</groupId>
      <artifactId>Artifact-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <exclusions>
        <!-- Exclude A's dependecy on B -->
        <exclusion>
          <groupId>Project-B</groupId>
          <artifactId>Artifact-B</artifactId>
          <!-- could be Version 1.0, but it doesn't matter, we are ignoring it
          <version>1.0</version>
          -->
        </exclusion>
      </exclusions> 
    </dependency>
    <dependency>
      <!-- Include own dependecy on B V 2.0 -->
      <groupId>Project-B</groupId>
      <artifactId>Artifact-B</artifactId>
      <version>2.0</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
Bill Mair
  • 1,073
  • 6
  • 15
  • 1
    How does this solve "_but still make dependency A to refer B 1.0_"? Doesn't this assume that B 2.0 is backwards-compatible with B 1.0? – andrewJames Feb 11 '23 at 22:49
  • I have the same question as andewJames. The above solution will not make Project-A refer to version 1.0 of Project-B and that is something I need because 2.0 of Project-B is not backward compatible with 1.0. – old_soul_on_the_run Feb 11 '23 at 23:35
  • Exclusions don't help at all here. – J Fabian Meier Feb 12 '23 at 07:16
  • Maven can't solve this. Major version changes (breaking changes) can not be solved in maven. This overrides a dependency that an included artifact defines, A needs B1 and my project C needs A and B2, so this excludes A's dependency. To have 2 or more versions of the same API loaded in the same JVM requires delving into programming class loaders or class loader technology e.g. OSGi. But that is a massive change in the application architecture. – Bill Mair Feb 12 '23 at 11:01