What is micronaut-bom? - gradle

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.

Related

Need 2 different versions of same dependency in Gradle

I am working on a spring boot Gradle application that has a dependency on spring-ldap. The issue is, I need to use our firm's custom dependency for ldap operation which has an older spring-ldap version bundled in it: org.springframework.ldap:spring-ldap-core:1.3.0.RELEASE but the spring-boot dependency comes with the newer version: org.springframework.ldap:spring-ldap-core:2.3.3.RELEASE.
By default Gradle promotes to the newer version of the dependency but even If I force to use any one from these dependency versions, it is throwing runtime MethodNotFound exception at a different location as both dependencies have different method names which are being used by both parent dependencies.
It seems that I need both dependencies in order to run the application and use them from different parent dependencies like:
Spring boot autoconfigure -> spring-ldap-core:2.3.3.RELEASE
Firm's dependency -> spring-ldap-core:1.3.2.RELEASE
What is the best available solution for this?

How to exclude a transitive dependency inside a transitive dependency?

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.

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.

Gradle 5 JUnit BOM and Spring Boot Incorrect Versions

I am using Gradle 5's BOM (Bill of Materials) feature. This is how I describe it for my JUnit 5 dependencies:
testImplementation(enforcedPlatform("org.junit:junit-bom:5.4.0")) // JUnit 5 BOM
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.junit.jupiter:junit-jupiter-params")
My assumption is that providing the BOM will resolve the versions of the dependencies to 5.4.0. However, they get resolved to 5.1.1. I am not sure why. (I also request enforcedPlatform() to lock the specified version)
Inspecting JUnit 5's BOM we see that all org.junit.jupiter dependencies are listed with version 5.4.0 (resolving to 5.1.1 in the project) and all org.junit.platform dependencies are listed with version 1.4.0 which resolve correctly in the project.
I am not sure what I am missing and was hoping to get some help here. Thanks!
EDIT:
I used Sormuras response and moved all BOMs at the top of the dependencies {} block but was still not getting version 5.4.0. Then I suspected it might be coming from the Gradle Spring Dependency Management plugin that I use, so when I commented it out, I got version JUnit 5.4.0. How do I disable JUnit coming from the Gradle Spring Dependency Management plugin?
FINALLY:
I decided to use the Spring Boot Dependencies BOM directly and remove the Gradle plugin:
implementation(platform("org.springframework.boot:spring-boot-dependencies:2.0.5.RELEASE"))
I imagine the plugin was created for those version of Gradle before Gradle 5 where you couldn't use a BOM file. Now with the BOM support I can directly include it. This way my version of JUnit is as I have specified it in the enforcedPlatform() block.
I accepted Sam Brannen's answer below because he explains well how the issue occurs and what solves it and I think it's relevant for those who use older versions of Gradle.
How do I disable JUnit coming from the Gradle Spring Dependency Management plugin?
For starters, if you are using the dependency management plugin from Spring, you should not be importing the junit-bom since that results in duplicate (and potentially conflicting) management of those dependencies.
Aside from that, whenever you use the dependency management plugin from Spring and want to override a managed version, you have to do it by overriding the exact name of the version defined in the BOM used by the plugin.
This is documented in Spring Boot for Gradle and for Maven.
For Spring Boot the name of the JUnit Jupiter version is "junit-jupiter.version". You can find the names of all managed versions for Spring Boot 2.1.2 here.
So, in Gradle you would override it as follows.
ext['junit-jupiter.version'] = '5.4.0'.
You can see that I have done exactly that here.
With Maven you would override it as follows.
<properties>
<junit-jupiter.version>5.4.0</junit-jupiter.version>
</properties>
Further background information here: https://docs.spring.io/platform/docs/current/reference/html/getting-started-overriding-versions.html
JUnit 5.4.0 simplified its artifacts, and now delivered a single artifact for Jupiter - org.junit:junit-jupiter. I.e., you should simplify your Gradle file too:
testImplementation(enforcedPlatform("org.junit:junit-bom:5.4.0")) // JUnit 5 BOM
testImplementation("org.junit.jupiter:junit-jupiter")
Ensure to include JUnit's BOM before other BOMs that also refer to JUnit. First BOM wins and locks version of all later artifacts.
See this issue for a similar setup using Maven and Spring Boot: https://github.com/sormuras/junit-platform-maven-plugin/issues/29#issuecomment-456958188

Spring boot: submodule dependency

I have multi-module spring boot application. I have organized it in such way that it contains web module which has #SpringBootApplication class and several other modules which are being imported by web module (e.g. batch-jobs module).
web module contains all dependencies from spring boot:
compile('org.springframework.boot:spring-boot-starter-batch')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-integration')
...
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
testCompile('org.springframework.boot:spring-boot-starter-test')
etc...
I wonder whether I should include all spring-boot-starter dependencies into this modules or it's better to have pure spring dependencies like here:
dependencies {
compile 'org.springframework:spring-core'
compile 'org.springframework:spring-context'
compile 'org.springframework.integration:spring-integration-java-dsl'
compile 'org.springframework.batch:spring-batch-core'
...
testCompile 'org.springframework:spring-test'
testCompile 'org.springframework.integration:spring-integration-test'
}
Those dependencies are taken anyway from upper dependency-management configuration. Which approach is better? Can you please advise here?
I think this post will be flagged as opinion-based but anyway:
My thoughts on this topic are (or were if I look at spring boot) to explicitly name the dependencies you actively use in your code (and the specific modules). But with spring boot you cannot really match the dependency in your modules against the starters in the 'project'. Of course you may know that a starter-web will provide mvc from looking in the dependencies outside your project but I think that it will be hard for others to get into the definitions if the project
grows and maintain them.
Pure speculative: What if a starter gets an update and drops a dependency in favour of another? Just to give an example: LibX provided by vendorA is now switched to vendorB. You will still have a JSON dependency to vendorA in your module config but also vendorB gets in your classpath. If they have the same fully qualified name...(bam)
You could extract several starters like persistence related ones to the persistence module and web to web and so on.

Resources