forcing the version of a library's transitive Maven dependencies via the library itself

85 Views Asked by At

I'm familiar with how a Maven project such as my-app can force the version of transitive dependencies. Today I stumbled onto a related question: how can a library my-lib force the versions of its transitive dependencies in relation to my-app without directly including the transitive dependencies?

I have a library my-lib, the only purpose of which is to make it easy for other projects to include several transitive dependencies at once—in other words, my-lib is sort of like a bundling mechanism. In my-lib/pom.xml I duly declare com.amazonaws:aws-lambda-java-log4j2:1.5.1 and then use it:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-log4j2</artifactId>
      <version>1.5.1</version>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-log4j2</artifactId>
  </dependency>
</dependencies>

But I want to override the version of Log4j being used, so in my-lib-parent I import a specific Log4j BOM version:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-bom</artifactId>
      <version>2.20.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
<dependencyManagement>

Sure enough, if I do a mvn dependency:tree on my-lib, it shows that Log4j 2.20.0 is being used:

[INFO] +- com.amazonaws:aws-lambda-java-log4j2:jar:1.5.1:compile
[INFO] |  +- org.apache.logging.log4j:log4j-core:jar:2.20.0:compile
[INFO] |  \- org.apache.logging.log4j:log4j-api:jar:2.20.0:compile

So I happily added my-lib as a dependency to my-app. When my code tried to log information, I got a java.lang.NoClassDefFoundError: org/apache/logging/log4j/BridgeAware. It turns out that even though the my-lib project is using org.apache.logging.log4j:log4j-core:jar:2.20.0, this is only in relation to my-lib itself—it doesn't effect the versions of the transitive dependencies my-lib brings in, in terms of my-app, when my-app includes my-lib as a dependency!

Thus if I do a mvn dependency:tree on my-app, it shows that org.apache.logging.log4j:log4j-api:jar:2.17.1 is being used:

[INFO] |  +- com.amazonaws:aws-lambda-java-log4j2:jar:1.5.1:provided
[INFO] |  |  +- org.apache.logging.log4j:log4j-core:jar:2.17.1:provided
[INFO] |  |  \- org.apache.logging.log4j:log4j-api:jar:2.17.1:provided

I guess it makes sense, but frankly I had never thought much about it. I had just assumed that my-app would get the version of transitive dependencies set by dependency management in my-lib.

I note that org.apache.logging.log4j:log4j-slf4j2-impl:jar:2.20.0 is getting the right version in my-app, which also came in to my-lib via the Log4j BOM, but that seems to be because my-lib explicitly includes org.apache.logging.log4j:log4j-slf4j2-impl:jar:2.20.0 as its direct dependency.

So is that the only solution: am I forced to go find and enumerate all the transitive dependencies of com.amazonaws:aws-lambda-java-log4j2 and declare them explicitly in my-lib if I want them to be set to another version when my-lib is included in another project? Is there a simpler approach, similar to importing a BOM, that would allow my-lib to simply say "force all transitive Log4j versions to that of the Log4j BOM when my-lib is included in another project"?

1

There are 1 best solutions below

0
On

I also stumbled about the problem in the past: DependencyManagement is not transitive.

You can avoid the problem by using the flatten Maven Plugin to produce a "consumer pom".