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"?
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".