Gradle 5 JUnit BOM and Spring Boot Incorrect Versions - spring-boot

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

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.

Selenium 4.1 and Spring boot Web driver version issue

I tried to migrate gradle project with spring boot from selenium 3 to selenium 4
(implementation 'org.seleniumhq.selenium:selenium-java:4.1.1')
But chrome, firefox and edge web drivers remains from selenium version 3
(org.seleniumhq.selenium:selenium-chrome-driver:3.141.59).
If I remove spring boot dependency from project, they updates to 4.1.1
Currently using gradle version "7.3.3".
Spring boot "2.6.3".
Spring dependency management "1.0.11.RELEASE".
Any ideas why this happens, I hoped that dependency hell dissapeared with spring boot creation)
Thanks in advance!
I have the same promble, I found in spring-boot-starter-parent, there is a spring-boot-dependencies in ~/.m2/repository/org/springframework/boot/spring-boot-dependencies/2.6.3/spring-boot-dependencies-2.6.3.pom, which declared <selenium.version>3.141.59</selenium.version> in properties, so you can try add
<properties>
<selenium.version>4.1.2</selenium.version>
</properties>
in your project pom.xml.
Spring Boot comes with a set of versions that are know to work together. But you can override them in your build script. In case of Gradle, add to build.gradle:
ext['selenium.version'] = '4.1.2'
It is because Spring Boot comes packed with a set of predefined dependecies that you can find here: https://docs.spring.io/spring-boot/docs/2.6.7/reference/htmlsingle/#appendix.dependency-versions
In this page, you will also find, how to change predefined version. In general, you need to add .version to your dependency name and, in case of multiple word names, you change spaces to dashes (not needed in case of selenium).
So, to change version to most recent, type in build.gradle ext['selenium.version'] = '4.4.0' at 0 indent level. (don't add it, say inside dependencies {})

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.

Locating Amqp in the dependency jars

I am migrating up to springBootVersion = '2.2.10.RELEASE' from 1.x,
and when I compile against it, I have spring-integration-core-5.2.8.RELEASE.jar,
But I cannot locate this.
import org.springframework.integration.dsl.amqp.Amqp;
Documentation says I don't need to declare the dsl package due to duplicate declarations. Am I missing something?
What you don't to show pom file here ? But anyway
In Spring Boot 2.2.x include Spring Integration 5.0 if your pom to have dependency.
org.springframework.integration
spring-integration-java-dsl
Delete it.
Documentation says it has been added to the version 5.0. Put it in pom or any other spring-cloud-starter- Spring Cloud include spring-integration.
spring-cloud-starter-stream-rabbit
Check in link https://github.com/spring-projects/spring-integration/wiki/Spring-Integration-4.3-to-5.0-Migration-Guide
org.springframework.integration.dsl.amqp.Amqp;
Now located in
org.springframework.integration.amqp.dsl.Amqp;

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.

Resources