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