How to exclude a transitive dependency inside a transitive dependency? - maven

I have a maven project "Project-1". This project uses some dependencies, let's consider jackson databind 2.9.8
Another maven project "Project-2" uses "Project-1" as a dependency.
"Project-3" uses "Project-2" as a dependency.
In the "Project-3", I used jackson databind 2.10.0 using the dependency management tag. Additionally, under the dependency tag of "Project-2" inside the pom of "Project-1", I added the exclusion tag of jackson databind.
In the dependency tree of "Project-3", only one version of jackson databind is getting resolved which is 2.10.0
But, In a security scan in the pipeline, it is still reporting some security issue for the jackson databind 2.9.8
I am not sure how come this version is getting resolved even though it is not mentioned in the dependency tree.
I am trying to add the exclusion in the "Project-1" now. But, is there any better way to exclude it in "Project-3"?

It seems like the dependency "jackson databind" is not pulled by Maven, so their is no way to exclude it in Maven.
It may be the dependency of a Maven plugin you run, and you can try to find that out by running Maven with the debug flag -X. Then you might need to remove that plugin.
It may as well be the case that your "scanner" does not only scan the dependencies of the project, but other things as well. Maybe it is a bug in the scanner so that excluded artifacts are also scanned.

Related

What is micronaut-bom?

While I worked on a Micronaut project und tried to solve some problems, like dynamic versioning of dependencies and creating a multi module micronaut project with Gradle. I found in some code examples of the build.gradle this dependency:
annotationProcessor platform("io.micronaut:micronaut-bom:2.0.0")
But I could not find any official documentation referring to it. What does it do? Can it help me with versioning my dependencies?
The Micronaut BOM (Bill Of Material, further reading) declares all the framework dependencies of a specific Micronaut release. In your example it is the BOM of Micronaut release 2.0.0.
By declaring the as a platform dependency you don't have to worry about Micronaut about the versions of supported JARs, since they already are defined in the BOM.
For example if you decide to use the Caffeine Cache with Micronaut you simply declare it as a dependency but omit the version number.
dependencies {
implementation("io.micronaut.cache:micronaut-cache-caffeine")
}
That way you make sure that you are using well tested dependencies with your Micronaut release.
why is it an `annotationProcessor?
An update since the following question came up.
Uuh. Nice. So its a collection of dependencies with the best working versions for my specific micronaut version? Cool. And why is it a annotationProcessor?
annotationProcessor is Gradle related and is called a dependency configuration. When using the java Gradle plugin you have out of the box dependency configurations such as implementation or api.
With a dependency configuration you configure the dependencies for e.g. an annotation processor by using the configuration annotationProcessor. implementation is used for your compile time dependencies. And if you want to expose the dependencies to other modules that use your module you might want to use api instead of implementation.

What's the logic of Gradle dependency resolving

In Gradle 6.7, we have a dependencyManagement.dependencies to set the defaults for the project.
Recently, someone replaced the individual dependency lines for Spring with a dependencySet.
dependencySet(group: 'org.springframework.boot', version: "2.2.11.RELEASE") {
entry 'spring-boot-devtools'
entry 'spring-boot-dependencies'
entry 'spring-boot-devtools'
entry 'spring-boot-starter-aop'
entry 'spring-boot-starter-cache'
entry 'spring-boot-starter-webflux'
...
Now after spotting some CVE alerts, I found out that Gradle resolves spring-boot-starter-cache to 2.2.8 anyway. I am not sure where it's getting that version from: We don't have it in our project, and the deps tree appears as if we asked for it ourselves (it's at level 0).
+--- org.springframework.boot:spring-boot-starter-cache -> 2.2.8.RELEASE
When I add the item explicitly, as we had before for all,
dependency 'org.springframework.boot:spring-boot-starter-cache:2.2.11.RELEASE'
then it ends up being resolved as 2.2.11.
+--- org.springframework.boot:spring-boot-starter-cache -> 2.2.11.RELEASE
In Maven, dependency management is very straighforward, compared to this: You control it using dependency management, and BOMs, and all works, no surprises like this.
So maybe I am missing something in Gradle's logic, even after reading the dependency management guide.
How can I use BOM-like dependencySet to control all entry-es at once? Or do I have wrong assumptions?
In Gradle 6.7, we have a dependencyManagement.dependencies to set the defaults for the project.
Do not confuse Spring's dependency management Gradle plugin with Gradle's native dependency mangaement functionality. Although they achieve the same goal, they do it in very different ways.
I am not sure where it's getting that version from: We don't have it in our project, and the deps tree appears as if we asked for it ourselves (it's at level 0).
You can use the dependencyInsight task to get more information on a specific dependency and why a specific version was chosen.
./gradlew dependencyInsight --dependency org.springframework.boot:spring-boot-starter-cache
See Viewing and debugging dependencies
for more details.
How can I use BOM-like dependencySet to control all entry-es at once? Or do I have wrong assumptions?
The docs for the Spring dependency management plugin are clear what you need to do to achieve that: https://docs.spring.io/dependency-management-plugin/docs/current/reference/html/#dependency-management-configuration-dsl-dependency-sets
If it is not working as you expect, then you need to debug your dependencies as I have linked above.
Also from your examples, my guess is that you have a typical Spring Boot application with the Spring Boot Gradle plugin applied. If so, then the Spring Boot Gradle plugin detects if the Spring dependency management plugin is applied and automatically imports the Spring Boot BOM. So there should not be a need to manage Spring specific dependencies as you are.

what are the maven dependency policies on transient exclusions

I am using Maven 3, and I have a small project with a module that depends on another module of this project. The lower one is basically the JPA entity model and the other one the REST service using it. All of them have spring-boot dependencies
I wanted to use more recent Hibernate and EhCache dependencies and sorted these out in the JPA module, excluding them, and the maven dependency:tree shows everything as I wanted it.
But when I add that module in my other module as dependency, all those excluded dependency versions are back again in my dependency tree.
Why does Maven pick the dependencies deeper in the tree branches (and excluded) before the versions I defined on first level in my module?
I guess you defined the exclusions in the <dependencyManagement> section of the POM of the first module. When another module referenced the first module, these exclusions are gone, right?
What I would suggest:
If you need newer versions of a given dependency, do not use exclusions, but use <dependencyManagement> to define the new version. This overwrites all transitive version definitions.

Maven: How to include a dependency with a specific build profile?

I have a project where I use Spring Boot 1.1.2.RELEASE which uses Spring 4.1.5, and Spring HATEOAS 0.10.0.RELEASE which uses Spring 4.0.9. This causes some dependency problems like the infamous java.lang.ClassNotFoundException: org.springframework.beans.factory.SmartInitializingSingleton.
I dug into the POM of spring-hateoas and found that there are different profiles defined, one of them being spring41 which depends on Spring 4.1.5. Is it possible to select this profile in my <dependency> section, or do I have to exclude the Spring dependencies?
Automatically selecting a profile for a build isn't easy. You can enable it by default in your personal settings.xml but that breaks the build for everyone who doesn't have the same file.
You can't enable a profile in the POM of the project.
With Maven 3.3, you can add the profile to ${maven.projectBasedir}/.mvn/maven.config. Since this file is part of the project, it's easy to share. You can use the Maven Enforcer plugin to make sure everyone uses a Maven version with actually uses the file.
If you can't use 3.3, then your best bet is to exclude the dependencies. If you have a parent POM, then you can use a dependencyManagement element to do it for all POMs in the reactor build.

Dependency conflict resolution with different group id?

Artifacts with conflicting content:
org.javassist:javassist:jar:3.17.1-GA && javassist:javassist:jar:3.12.1.GA
The former comes from Hibernate and the latter from Guava. Both end up in the final war file. The problem is that the classic conflict resolution mechanisms fail because the group id is different. So both the jars end up in the final project deliverable.
I can't just exclude Guava's dependency in the managed dependencies because the project is a multi-module. Some modules use Guava w/o Hibernate. They would miss their javassist dependency. But if I don't the "excess" artifact will pop up in the project's war file.
Is there a way to tell Maven those two dependencies are in fact two different versions of the same code ?
I'd stick with the following solution:
Add explicit dependency on javassist artifact in main pom.xml of your project (so that all of the child modules inherit this dependency).
Exclude javassist both from Hibernate and Guava.
Most important: add a comment indicating why you need javassist dependency :)

Resources