From c9aae1275c8d8449787491dcd9addd1187101b58 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 19 Feb 2026 15:03:33 +0100 Subject: [PATCH 001/376] Next development version (v4.0.4-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0d5e85608101..c444b5fac523 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.0.3-SNAPSHOT +version=4.0.4-SNAPSHOT latestVersion=false spring.build-type=oss From 4b2efca33877e885df906ca3a9891529ba0674f4 Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Sat, 21 Feb 2026 13:02:33 +0700 Subject: [PATCH 002/376] Align message in MyEnvironmentPostProcessor code sample See gh-49282 Signed-off-by: Tran Ngoc Nhan --- .../MyEnvironmentPostProcessor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/application/customizetheenvironmentorapplicationcontext/MyEnvironmentPostProcessor.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/application/customizetheenvironmentorapplicationcontext/MyEnvironmentPostProcessor.kt index c87ca6c0751b..b7009376ec34 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/application/customizetheenvironmentorapplicationcontext/MyEnvironmentPostProcessor.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/application/customizetheenvironmentorapplicationcontext/MyEnvironmentPostProcessor.kt @@ -37,7 +37,7 @@ class MyEnvironmentPostProcessor : EnvironmentPostProcessor { } private fun loadYaml(path: Resource): PropertySource<*> { - Assert.isTrue(path.exists()) { "Resource $path does not exist" } + Assert.isTrue(path.exists()) { "'path' [$path] must exist" } return try { loader.load("custom-resource", path)[0] } catch (ex: IOException) { From f092971c7c5152050e0b0ea55a677cd8538bf8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 23 Feb 2026 12:13:30 +0100 Subject: [PATCH 003/376] Fix link to INSTALL.txt --- .../src/docs/antora/modules/ROOT/pages/installing.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/installing.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/installing.adoc index cd94a510914c..da1f0283478d 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/installing.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/installing.adoc @@ -90,7 +90,7 @@ You can download the Spring CLI distribution from one of the following locations endif::[] -Once downloaded, follow the {url-github-raw}/cli/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt[INSTALL.txt] instructions from the unpacked archive. +Once downloaded, follow the {url-github-raw}/cli/spring-boot-cli/src/main/content/INSTALL.txt[INSTALL.txt] instructions from the unpacked archive. In summary, there is a `spring` script (`spring.bat` for Windows) in a `bin/` directory in the `.zip` file. Alternatively, you can use `java -jar` with the `.jar` file (the script helps you to be sure that the classpath is set correctly). From 55ef2df143e25223c7ab52b04de4b3ed4bf0119e Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Mon, 23 Feb 2026 10:44:00 +0700 Subject: [PATCH 004/376] Update JDK requirement for CLI See gh-49290 Signed-off-by: Tran Ngoc Nhan --- .../spring-boot-cli/src/main/content/INSTALL.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt index c914bcd4ff8d..39f2ab1d2846 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt @@ -7,7 +7,7 @@ in order to complete your installation. Prerequisites ------------- -Spring Boot CLI requires Java JDK v1.8 or above in order to run. Groovy v${groovy.version} +Spring Boot CLI requires Java JDK 17 or above in order to run. Groovy v${groovy.version} is packaged as part of this distribution, and therefore does not need to be installed (any existing Groovy installation is ignored). From 12e5cd4b4593673ef7d032d4404d403c78a73194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 23 Feb 2026 12:44:54 +0100 Subject: [PATCH 005/376] Consistently enable parameters generation with AOT This commit updates the Maven Plugin to retain parameters information in AOT-generated code. This aligns with the Gradle Plugin and allows to consistently use parameter names resolution in generated code. Closes gh-49268 --- .../src/intTest/projects/aot-compiler-arguments/pom.xml | 2 +- .../java/org/springframework/boot/maven/AbstractAotMojo.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-compiler-arguments/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-compiler-arguments/pom.xml index 6b0d97eab914..e4c3ce0e73f3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-compiler-arguments/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-compiler-arguments/pom.xml @@ -22,7 +22,7 @@ process-aot - -verbose --invalid-compiler-arg + -parameters --invalid-compiler-arg diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java index 3adcd5c53616..adb196f31d8a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractAotMojo.java @@ -168,6 +168,7 @@ protected final void compileSourceFiles(URL[] classPath, File sourcesDirectory, args.add(target); } } + args.add("-parameters"); args.addAll(new RunArguments(this.compilerArguments).getArgs()); Iterable compilationUnits = fileManager.getJavaFileObjectsFromPaths(sourceFiles); Errors errors = new Errors(); From 4bc47d0919bb3d10f98717571466f281e249ac7e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 23 Feb 2026 15:23:22 +0000 Subject: [PATCH 006/376] Remove stale mention of Groovy Closes gh-49297 --- .../spring-boot-cli/src/main/content/INSTALL.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt index 39f2ab1d2846..8409968b766f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt +++ b/spring-boot-project/spring-boot-tools/spring-boot-cli/src/main/content/INSTALL.txt @@ -7,9 +7,7 @@ in order to complete your installation. Prerequisites ------------- -Spring Boot CLI requires Java JDK 17 or above in order to run. Groovy v${groovy.version} -is packaged as part of this distribution, and therefore does not need to be installed (any -existing Groovy installation is ignored). +Spring Boot CLI requires Java JDK 17 or above in order to run. The CLI will use whatever JDK it finds on your path, to check that you have an appropriate version you should run: From 147d3d763bfb804fb35a29d6eb4fa0d13a5d26b2 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Mon, 2 Feb 2026 11:36:33 +0800 Subject: [PATCH 007/376] Members declared in final classes should not be protected See gh-49031 Signed-off-by: Yanming Zhou --- .../java/org/springframework/boot/build/bom/BomResolver.java | 2 +- .../boot/autoconfigure/info/ProjectInfoAutoConfiguration.java | 2 +- .../org/springframework/boot/test/util/TestPropertyValues.java | 2 +- .../boot/loader/tools/BuildPropertiesWriter.java | 2 +- .../org/springframework/boot/logging/log4j2/ColorConverter.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java index fe865002f646..f0165d562ba7 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java @@ -199,7 +199,7 @@ private File parentBomFile(Node bom) { private static final class Node { - protected final XPath xpath; + private final XPath xpath; private final org.w3c.dom.Node delegate; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java index 98d99f1bb3ed..d5700e951a91 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.java @@ -73,7 +73,7 @@ public BuildProperties buildProperties() throws Exception { loadFrom(this.properties.getBuild().getLocation(), "build", this.properties.getBuild().getEncoding())); } - protected Properties loadFrom(Resource location, String prefix, Charset encoding) throws IOException { + private Properties loadFrom(Resource location, String prefix, Charset encoding) throws IOException { prefix = prefix.endsWith(".") ? prefix : prefix + "."; Properties source = loadSource(location, encoding); Properties target = new Properties(); diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/util/TestPropertyValues.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/util/TestPropertyValues.java index e0ce12672883..cb17a57aaac4 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/util/TestPropertyValues.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/util/TestPropertyValues.java @@ -308,7 +308,7 @@ public Class getSourceClass() { return this.sourceClass; } - protected String applySuffix(String name) { + private String applySuffix(String name) { return (this.suffix != null) ? name + "-" + this.suffix : name; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java index 9d55be1e0f0a..cb426e93d9f6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java @@ -71,7 +71,7 @@ private void createFileIfNecessary(File file) throws IOException { } } - protected Properties createBuildInfo(ProjectDetails project) { + private Properties createBuildInfo(ProjectDetails project) { Properties properties = CollectionFactory.createSortedProperties(true); addIfHasValue(properties, "build.group", project.getGroup()); addIfHasValue(properties, "build.artifact", project.getArtifact()); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ColorConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ColorConverter.java index bd4cb6ba3116..407e47599624 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ColorConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ColorConverter.java @@ -109,7 +109,7 @@ public void format(LogEvent event, StringBuilder toAppendTo) { } } - protected void appendAnsiString(StringBuilder toAppendTo, String in, AnsiElement element) { + private void appendAnsiString(StringBuilder toAppendTo, String in, AnsiElement element) { toAppendTo.append(AnsiOutput.toString(element, in)); } From 9c509cf5b3e734b0306cd371346879ff9125e3e8 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 23 Feb 2026 18:11:54 +0100 Subject: [PATCH 008/376] Fix nesting support in RouterFunctions descriptions Prior to this commit, descriptions for `RouterFunctions` web routes would not include `nest` predicates and would only consider the last predicate in the chain. This commit ensures that nested predicates are properly chained adn considered in the resulting description that is displayed in Actuator endpoints. Fixes gh-49289 --- ...herHandlersMappingDescriptionProvider.java | 14 +- ...herServletsMappingDescriptionProvider.java | 14 +- ...ndlersMappingDescriptionProviderTests.java | 89 ++++++++++++ ...rvletsMappingDescriptionProviderTests.java | 136 ++++++++++++++++++ 4 files changed, 249 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java index 24ef4f2a3829..02be752baa36 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProvider.java @@ -16,8 +16,10 @@ package org.springframework.boot.actuate.web.mappings.reactive; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -167,20 +169,28 @@ private static final class MappingDescriptionVisitor implements Visitor { private final List descriptions = new ArrayList<>(); + private Deque predicates = new ArrayDeque<>(); + @Override public void startNested(RequestPredicate predicate) { + this.predicates.addLast(predicate); } @Override public void endNested(RequestPredicate predicate) { + this.predicates.removeLast(); } @Override public void route(RequestPredicate predicate, HandlerFunction handlerFunction) { DispatcherHandlerMappingDetails details = new DispatcherHandlerMappingDetails(); details.setHandlerFunction(new HandlerFunctionDescription(handlerFunction)); - this.descriptions.add( - new DispatcherHandlerMappingDescription(predicate.toString(), handlerFunction.toString(), details)); + RequestPredicate reduced = this.predicates.stream() + .reduce(RequestPredicate::and) + .map((all) -> all.and(predicate)) + .orElse(predicate); + this.descriptions + .add(new DispatcherHandlerMappingDescription(reduced.toString(), handlerFunction.toString(), details)); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java index cc96b8e0f717..e9613bd70ea4 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.java @@ -16,8 +16,10 @@ package org.springframework.boot.actuate.web.mappings.servlet; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -235,20 +237,28 @@ private static final class MappingDescriptionVisitor implements Visitor { private final List descriptions = new ArrayList<>(); + private Deque predicates = new ArrayDeque<>(); + @Override public void startNested(RequestPredicate predicate) { + this.predicates.addLast(predicate); } @Override public void endNested(RequestPredicate predicate) { + this.predicates.removeLast(); } @Override public void route(RequestPredicate predicate, HandlerFunction handlerFunction) { DispatcherServletMappingDetails details = new DispatcherServletMappingDetails(); details.setHandlerFunction(new HandlerFunctionDescription(handlerFunction)); - this.descriptions.add( - new DispatcherServletMappingDescription(predicate.toString(), handlerFunction.toString(), details)); + RequestPredicate reduced = this.predicates.stream() + .reduce(RequestPredicate::and) + .map((all) -> all.and(predicate)) + .orElse(predicate); + this.descriptions + .add(new DispatcherServletMappingDescription(reduced.toString(), handlerFunction.toString(), details)); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java index 45fd3fecd410..5e626ba4cd58 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/reactive/DispatcherHandlersMappingDescriptionProviderTests.java @@ -16,19 +16,38 @@ package org.springframework.boot.actuate.web.mappings.reactive; +import java.util.List; +import java.util.Map; + import org.junit.jupiter.api.Test; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.actuate.web.mappings.reactive.DispatcherHandlersMappingDescriptionProvider.DispatcherHandlersMappingDescriptionProviderRuntimeHints; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.config.EnableWebFlux; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; +import static org.springframework.web.reactive.function.server.RequestPredicates.path; /** * Tests for {@link DispatcherHandlersMappingDescriptionProvider}. * * @author Moritz Halbritter + * @author Brian Clozel */ class DispatcherHandlersMappingDescriptionProviderTests { @@ -43,4 +62,74 @@ void shouldRegisterHints() { .accepts(runtimeHints); } + @Test + void shouldDescribeAnnotatedControllers() { + new ReactiveWebApplicationContextRunner().withUserConfiguration(ControllerWebConfiguration.class) + .run((context) -> { + + Map> describedMappings = new DispatcherHandlersMappingDescriptionProvider() + .describeMappings(context); + assertThat(describedMappings).hasSize(1).containsOnlyKeys("webHandler"); + List descriptions = describedMappings.get("webHandler"); + assertThat(descriptions).hasSize(2) + .extracting("predicate") + .containsExactlyInAnyOrder("{POST /api/projects, consumes [application/json]}", + "{GET /api/projects/{id}, produces [application/json]}"); + }); + } + + @Test + void shouldDescribeRouterFunctions() { + new ReactiveWebApplicationContextRunner().withUserConfiguration(RouterConfiguration.class).run((context) -> { + + Map> describedMappings = new DispatcherHandlersMappingDescriptionProvider() + .describeMappings(context); + assertThat(describedMappings).hasSize(1).containsOnlyKeys("webHandler"); + List descriptions = describedMappings.get("webHandler"); + assertThat(descriptions).hasSize(2) + .extracting("predicate") + .containsExactlyInAnyOrder("(/api && (POST && (/projects/ && Content-Type: application/json)))", + "(/api && (GET && (/projects//{id} && Accept: application/json)))"); + }); + } + + @SuppressWarnings("unchecked") + @Configuration(proxyBeanMethods = false) + @EnableWebFlux + static class ControllerWebConfiguration { + + @Controller + @RequestMapping("/api") + static class SampleController { + + @PostMapping(path = "/projects", consumes = MediaType.APPLICATION_JSON_VALUE) + void createProject() { + } + + @GetMapping(path = "/projects/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + void findProject() { + } + + } + + } + + @Configuration(proxyBeanMethods = false) + @EnableWebFlux + static class RouterConfiguration { + + @Bean + RouterFunction routerFunctions() { + return RouterFunctions.route() + .nest(path("/api"), + (builder) -> builder + .POST(path("/projects/").and(contentType(MediaType.APPLICATION_JSON)), + (request) -> ServerResponse.ok().build()) + .GET(path("/projects//{id}").and(accept(MediaType.APPLICATION_JSON)), + (request) -> ServerResponse.ok().build())) + .build(); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProviderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProviderTests.java index 6270080a43cc..3b794548276a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProviderTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProviderTests.java @@ -16,19 +16,51 @@ package org.springframework.boot.actuate.web.mappings.servlet; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import jakarta.servlet.FilterRegistration; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRegistration; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletsMappingDescriptionProvider.DispatcherServletsMappingDescriptionProviderRuntimeHints; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockServletConfig; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.RouterFunctions; +import org.springframework.web.servlet.function.ServerResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.web.servlet.function.RequestPredicates.accept; +import static org.springframework.web.servlet.function.RequestPredicates.contentType; +import static org.springframework.web.servlet.function.RequestPredicates.path; /** * Tests for {@link DispatcherServletsMappingDescriptionProvider}. * * @author Moritz Halbritter + * @author Brian Clozel */ class DispatcherServletsMappingDescriptionProviderTests { @@ -43,4 +75,108 @@ void shouldRegisterHints() { .accepts(runtimeHints); } + @Test + void shouldDescribeAnnotatedControllers() { + Supplier contextSupplier = prepareContextSupplier(); + new WebApplicationContextRunner(contextSupplier).withUserConfiguration(ControllerWebConfiguration.class) + .run((context) -> { + + Map> describedMappings = new DispatcherServletsMappingDescriptionProvider() + .describeMappings(context); + assertThat(describedMappings).hasSize(1).containsOnlyKeys("dispatcherServlet"); + List descriptions = describedMappings.get("dispatcherServlet"); + assertThat(descriptions).hasSize(2) + .extracting("predicate") + .containsExactlyInAnyOrder("{POST [/api/projects], consumes [application/json]}", + "{GET [/api/projects/{id}], produces [application/json]}"); + }); + } + + @Test + void shouldDescribeRouterFunctions() { + Supplier contextSupplier = prepareContextSupplier(); + new WebApplicationContextRunner(contextSupplier).withUserConfiguration(RouterConfiguration.class) + .run((context) -> { + + Map> describedMappings = new DispatcherServletsMappingDescriptionProvider() + .describeMappings(context); + assertThat(describedMappings).hasSize(1).containsOnlyKeys("dispatcherServlet"); + List descriptions = describedMappings.get("dispatcherServlet"); + assertThat(descriptions).hasSize(2) + .extracting("predicate") + .containsExactlyInAnyOrder("(/api && (POST && (/projects/ && Content-Type: application/json)))", + "(/api && (GET && (/projects//{id} && Accept: application/json)))"); + }); + } + + @SuppressWarnings("unchecked") + private Supplier prepareContextSupplier() { + ServletContext servletContext = mock(ServletContext.class); + given(servletContext.getInitParameterNames()).willReturn(Collections.emptyEnumeration()); + given(servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration()); + FilterRegistration filterRegistration = mock(FilterRegistration.class); + given((Map) servletContext.getFilterRegistrations()) + .willReturn(Collections.singletonMap("testFilter", filterRegistration)); + ServletRegistration servletRegistration = mock(ServletRegistration.class); + given((Map) servletContext.getServletRegistrations()) + .willReturn(Collections.singletonMap("testServlet", servletRegistration)); + return () -> { + AnnotationConfigServletWebApplicationContext context = new AnnotationConfigServletWebApplicationContext(); + context.setServletContext(servletContext); + return context; + }; + } + + @Configuration(proxyBeanMethods = false) + @EnableWebMvc + static class ControllerWebConfiguration { + + @Bean + DispatcherServlet dispatcherServlet(WebApplicationContext context) throws ServletException { + DispatcherServlet dispatcherServlet = new DispatcherServlet(context); + dispatcherServlet.init(new MockServletConfig()); + return dispatcherServlet; + } + + @Controller + @RequestMapping("/api") + static class SampleController { + + @PostMapping(path = "/projects", consumes = MediaType.APPLICATION_JSON_VALUE) + void createProject() { + } + + @GetMapping(path = "/projects/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + void findProject() { + } + + } + + } + + @Configuration(proxyBeanMethods = false) + @EnableWebMvc + static class RouterConfiguration { + + @Bean + DispatcherServlet dispatcherServlet(WebApplicationContext context) throws ServletException { + DispatcherServlet dispatcherServlet = new DispatcherServlet(context); + dispatcherServlet.init(new MockServletConfig()); + return dispatcherServlet; + } + + @Bean + RouterFunction routerFunctions() { + return RouterFunctions.route() + .nest(path("/api"), + (builder) -> builder + .POST(path("/projects/").and(contentType(MediaType.APPLICATION_JSON)), + (request) -> ServerResponse.ok().build()) + .GET(path("/projects//{id}").and(accept(MediaType.APPLICATION_JSON)), + (request) -> ServerResponse.ok().build())) + .build(); + } + + } + } From e808d7a7de85bd41079427b76f217f8bf4f19189 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 24 Feb 2026 15:32:50 +0000 Subject: [PATCH 009/376] Align server.tomcat.max-part-count default with Tomcat's default Fixes gh-49311 --- .../tomcat/autoconfigure/TomcatServerProperties.java | 2 +- .../autoconfigure/TomcatServerPropertiesTests.java | 11 +++++++++++ .../TomcatWebServerFactoryCustomizerTests.java | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/module/spring-boot-tomcat/src/main/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerProperties.java b/module/spring-boot-tomcat/src/main/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerProperties.java index 9bfb7e8ae321..90c6e551820b 100644 --- a/module/spring-boot-tomcat/src/main/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerProperties.java +++ b/module/spring-boot-tomcat/src/main/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerProperties.java @@ -88,7 +88,7 @@ public class TomcatServerProperties { * Maximum total number of parts permitted in a multipart/form-data request. Requests * that exceed this limit will be rejected. A value of less than 0 means no limit. */ - private int maxPartCount = 10; + private int maxPartCount = 50; /** * Maximum amount of request body to swallow. diff --git a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerPropertiesTests.java b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerPropertiesTests.java index 4f730df6cefd..e9280187a80c 100644 --- a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerPropertiesTests.java +++ b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatServerPropertiesTests.java @@ -194,6 +194,17 @@ void tomcatMaxHttpFormPostSizeMatchesConnectorDefault() { .isEqualTo(getDefaultConnector().getMaxPostSize()); } + @Test + void tomcatMaxPartCountMatchesConnectorDefault() { + assertThat(this.properties.getMaxPartCount()).isEqualTo(getDefaultConnector().getMaxPartCount()); + } + + @Test + void tomcatMaxPartHeaderSizeMatchesConnectorDefault() { + assertThat(this.properties.getMaxPartHeaderSize().toBytes()) + .isEqualTo(getDefaultConnector().getMaxPartHeaderSize()); + } + @Test void tomcatUriEncodingMatchesConnectorDefault() { assertThat(this.properties.getUriEncoding().name()).isEqualTo(getDefaultConnector().getURIEncoding()); diff --git a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatWebServerFactoryCustomizerTests.java b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatWebServerFactoryCustomizerTests.java index 81ff6af9b513..224908004814 100644 --- a/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatWebServerFactoryCustomizerTests.java +++ b/module/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/autoconfigure/TomcatWebServerFactoryCustomizerTests.java @@ -190,7 +190,7 @@ void customMaxHttpFormPostSize() { @Test void defaultMaxPartCount() { customizeAndRunServer( - (server) -> assertThat(server.getTomcat().getConnector().getMaxPartCount()).isEqualTo(10)); + (server) -> assertThat(server.getTomcat().getConnector().getMaxPartCount()).isEqualTo(50)); } @Test From 9ab01f61bde002213d15d2148ad0a8600ddbe98c Mon Sep 17 00:00:00 2001 From: giyeon95 Date: Tue, 24 Feb 2026 15:04:00 +0900 Subject: [PATCH 010/376] Remove extraneous semicolon from read-timeout value Signed-off-by: giyeon95 See gh-49306 --- .../src/docs/antora/modules/reference/pages/io/rest-client.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc index 944c7b8a00e4..2309a1edbcb8 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc @@ -386,7 +386,7 @@ spring: serviceclient: echo: base-url: "https://echo.zuplo.io" - read-timeout: 2s; + read-timeout: 2s apiversion: default: 1.0.0 insert: From ae7d04e6dc01d2996752ed1c1bbad4310db793f3 Mon Sep 17 00:00:00 2001 From: l2yuPa Date: Sat, 21 Feb 2026 01:03:31 +0900 Subject: [PATCH 011/376] Remove Jetty test idle timeout configuration Signed-off-by: l2yuPa See gh-49278 --- .../jetty/JettyServletWebServerFactoryTests.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java index 53d3706a4220..2d1b928b1a1d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java @@ -98,16 +98,7 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT @Override protected JettyServletWebServerFactory getFactory() { - JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); - factory.addServerCustomizers((server) -> { - for (Connector connector : server.getConnectors()) { - if (connector instanceof ServerConnector serverConnector) { - // TODO Set the shutdown idle timeout in main code? - serverConnector.setShutdownIdleTimeout(10000); - } - } - }); - return factory; + return new JettyServletWebServerFactory(0); } @Override From 5a764167ab6863e9aa1b556a263532dbf19bf825 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Feb 2026 09:53:37 +0000 Subject: [PATCH 012/376] Document applying AOT plugin directly for AOT on the JVM Closes gh-49307 --- .../examples/aot/apply-aot-plugin.gradle | 6 +++ .../examples/aot/apply-aot-plugin.gradle.kts | 6 +++ .../modules/gradle-plugin/pages/aot.adoc | 22 +++++++- .../gradle/docs/AotDocumentationTests.java | 51 +++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle.kts create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/AotDocumentationTests.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle new file mode 100644 index 000000000000..89d257ab1cfc --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle @@ -0,0 +1,6 @@ +plugins { + id 'org.springframework.boot' version '{version-spring-boot}' + id 'java' +} + +apply plugin: 'org.springframework.boot.aot' diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle.kts b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle.kts new file mode 100644 index 000000000000..5a1a0884e264 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/examples/aot/apply-aot-plugin.gradle.kts @@ -0,0 +1,6 @@ +plugins { + id("org.springframework.boot") version "{version-spring-boot}" + java +} + +apply(plugin = "org.springframework.boot.aot") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/aot.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/aot.adoc index ee631e308f74..d1e40fae0ba7 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/aot.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/aot.adoc @@ -4,8 +4,8 @@ Spring AOT is a process that analyzes your code at build-time in order to generate an optimized version of it. It is most often used to help generate GraalVM native images. -The Spring Boot Gradle plugin provides tasks that can be used to perform AOT processing on both application and test code. -The tasks are configured automatically when the {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin] is applied: +The `org.springframework.boot.aot` Gradle plugin provides tasks that can be used to perform AOT processing on both application and test code. +The plugin is applied automatically when the {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin] is applied: [tabs] ====== @@ -23,6 +23,24 @@ include::example$aot/apply-native-image-plugin.gradle.kts[] ---- ====== +If you want to xref:reference:packaging/aot.adoc[run an AOT-processed application on the JVM] rather than as a native image, apply the `org.springframework.boot.aot` plugin directly: + +[tabs] +====== +Groovy:: ++ +[source,groovy,indent=0,subs="verbatim,attributes"] +---- +include::example$aot/apply-aot-plugin.gradle[] +---- +Kotlin:: ++ +[source,kotlin,indent=0,subs="verbatim,attributes"] +---- +include::example$aot/apply-aot-plugin.gradle.kts[] +---- +====== + [[aot.processing-applications]] diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/AotDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/AotDocumentationTests.java new file mode 100644 index 000000000000..01735cb95e56 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/AotDocumentationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.gradle.docs; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.gradle.junit.GradleMultiDslExtension; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for AOT documentation. + * + * @author Andy Wilkinson + */ +@ExtendWith(GradleMultiDslExtension.class) +class AotDocumentationTests { + + GradleBuild gradleBuild; + + @TestTemplate + void applyNativeImagePlugin() { + assertThat(this.gradleBuild.script(Examples.DIR + "aot/apply-native-image-plugin").build("tasks").getOutput()) + .contains("nativeCompile") + .contains("aotClasses"); + } + + @TestTemplate + void applyAotPlugin() { + assertThat(this.gradleBuild.script(Examples.DIR + "aot/apply-aot-plugin").build("tasks").getOutput()) + .contains("aotClasses") + .doesNotContain("nativeCompile"); + } + +} From 20ff2b7ff17be57317b1f0bde9724532c8ea0f33 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Feb 2026 15:12:55 +0000 Subject: [PATCH 013/376] Register reflection hints for HTTP service client property binding Fixes gh-49274 --- .../service/HttpServiceClientProperties.java | 15 +++++++++++++++ .../service/HttpServiceClientPropertiesTests.java | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java index 075f14227e4a..c90b34015771 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java @@ -21,9 +21,14 @@ import org.jspecify.annotations.Nullable; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrar; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.http.client.autoconfigure.HttpClientProperties; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientProperties.HttpServiceClientPropertiesRuntimeHints; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.env.Environment; /** @@ -34,6 +39,7 @@ * @author Phillip Webb * @since 4.0.0 */ +@ImportRuntimeHints(HttpServiceClientPropertiesRuntimeHints.class) public class HttpServiceClientProperties { private final Map properties; @@ -58,4 +64,13 @@ static HttpServiceClientProperties bind(Environment environment) { .orElse(Collections.emptyMap())); } + static class HttpServiceClientPropertiesRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + BindableRuntimeHintsRegistrar.forTypes(HttpClientProperties.class).registerHints(hints, classLoader); + } + + } + } diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java index 8d22c97cd741..2c9322be1c33 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java @@ -22,9 +22,13 @@ import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.http.client.HttpRedirects; +import org.springframework.boot.http.client.autoconfigure.ApiversionProperties; import org.springframework.boot.http.client.autoconfigure.HttpClientProperties; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientProperties.HttpServiceClientPropertiesRuntimeHints; import org.springframework.context.annotation.Configuration; import org.springframework.mock.env.MockEnvironment; @@ -66,6 +70,16 @@ void bindProperties() { assertThat(c3).isNotNull(); assertThat(c3.getBaseUrl()).isEqualTo("https://example.com/phil"); assertThat(properties.get("c4")).isNull(); + + } + + @Test + void registersHintsForBindingOfHttpClientProperties() { + RuntimeHints hints = new RuntimeHints(); + new HttpServiceClientPropertiesRuntimeHints().registerHints(hints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.reflection().onType(HttpClientProperties.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(ApiversionProperties.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(ApiversionProperties.Insert.class)).accepts(hints); } @Configuration From d92d3a958b858edaa630a1d7162c8c346d51046d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 26 Feb 2026 09:36:08 +0000 Subject: [PATCH 014/376] Clarify inferred relationships between registrations and providers Closes gh-49240 --- .../oauth2/client/OAuth2ClientProperties.java | 8 ++-- .../reference/pages/web/spring-security.adoc | 41 ++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java index 1c5d9d30fff4..e707a34f8002 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java @@ -76,9 +76,11 @@ private void validateRegistration(String id, Registration registration) { public static class Registration { /** - * Reference to the OAuth 2.0 provider to use. May reference an element from the - * 'provider' property or used one of the commonly used providers (google, github, - * facebook, okta). + * Reference to the OAuth 2.0 provider to use. May reference one of the common + * providers (google, github, facebook, okta) or the ID of a custom provider + * configured using 'spring.security.oauth2.client.provider.<id>.*' + * properties. When not set, the ID of this registration is used to identify the + * provider. */ private String provider; diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc index 57695d4566c1..e1bfee2420b2 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc @@ -101,6 +101,33 @@ If you have `spring-security-oauth2-client` on your classpath, you can take adva This configuration makes use of the properties under javadoc:org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties[]. The same properties are applicable to both servlet and reactive applications. +Each registration must specify an OAuth 2 provider. +When set, the value of the `spring.security.oauth2.client.registration..provider` property is used to specify the registration's provider. +If the `provider` property is not set, the registration's ID is used instead. +Both approaches are shown in the following example: + +[configprops,yaml] +---- +spring: + security: + oauth2: + client: + registration: + my-client: + client-id: "abcd" + client-secret: "password" + provider: "example" + example: + client-id: "abcd" + client-secret: "password" +---- + +The registrations `my-client` and `example` will both use the provider with ID `example`. +The former will do so due to the value of the `spring.security.oauth2.client.registration.my-client.provider` property. +The latter will do so due to its ID being `example` and there being no `provider` property configured for the registration. + +The specified provider can either be a reference to a provider configured using `spring.security.oauth2.client.provider..*` properties or one of the xref:web/spring-security.adoc#web.security.oauth2.client.common-providers[known common providers]. + You can register multiple OAuth2 clients and providers under the `spring.security.oauth2.client` prefix, as shown in the following example: [configprops,yaml] @@ -150,6 +177,10 @@ spring: user-name-attribute: "name" ---- +In this example, there are three registrations. +In order of declaration, their IDs are `my-login-client`, `my-client-1`, and `my-client-2`. +There is also a single provider with ID `my-oauth-provider`. + For OpenID Connect providers that support https://openid.net/specs/openid-connect-discovery-1_0.html[OpenID Connect discovery], the configuration can be further simplified. The provider needs to be configured with an `issuer-uri` which is the URI that it asserts as its Issuer Identifier. For example, if the `issuer-uri` provided is "https://example.com", then an "OpenID Provider Configuration Request" will be made to "https://example.com/.well-known/openid-configuration". @@ -182,12 +213,12 @@ For production environments, consider using a javadoc:org.springframework.securi [[web.security.oauth2.client.common-providers]] ==== OAuth2 Client Registration for Common Providers -For common OAuth2 and OpenID providers, including Google, Github, Facebook, and Okta, we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta`, respectively). - -If you do not need to customize these providers, you can set the `provider` attribute to the one for which you need to infer defaults. -Also, if the key for the client registration matches a default supported provider, Spring Boot infers that as well. +For common OAuth2 and OpenID providers (Google, Github, Facebook, and Okta), we provide a set of provider defaults. +The IDs of these common provides are `google`, `github`, `facebook`, and `okta`, respectively. -In other words, the two configurations in the following example use the Google provider: +If you do not need to customize these providers, set the registration's `provider` property to the ID of one of the common providers. +Alternatively, you can xref:web/spring-security.adoc#web.security.oauth2.client[use a registration ID that matches the ID of the provider]. +The two configurations in the following example use the common `google` provider: [configprops,yaml] ---- From 917cf57ed0f36fbc391fb2ce98bed431cac6f10d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 06:22:46 +0000 Subject: [PATCH 015/376] Bump minimatch in /antora Bumps and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together. Updates `minimatch` from 3.1.2 to 3.1.3 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v3.1.2...v3.1.3) Updates `minimatch` from 5.1.6 to 5.1.7 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v3.1.2...v3.1.3) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.3 dependency-type: indirect - dependency-name: minimatch dependency-version: 5.1.7 dependency-type: indirect ... See gh-49318 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index 35589355b430..fc8a73ac9de4 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -1997,9 +1997,10 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2424,9 +2425,10 @@ } }, "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, From bbd566c3cfefc4d30c431d26d938b4c941bb4584 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 26 Feb 2026 14:51:41 +0100 Subject: [PATCH 016/376] Add spring-boot-reactor to reactive starters This commit adds the "spring-boot-reactor" module as a dependency to reactive starters. While this was already the case for many starters, this module was missing from GraphQL, RSocket, WebClient and WebFlux. Closes gh-49332 --- starter/spring-boot-starter-graphql/build.gradle | 1 + starter/spring-boot-starter-rsocket/build.gradle | 1 + starter/spring-boot-starter-webclient/build.gradle | 1 + starter/spring-boot-starter-webflux/build.gradle | 1 + 4 files changed, 4 insertions(+) diff --git a/starter/spring-boot-starter-graphql/build.gradle b/starter/spring-boot-starter-graphql/build.gradle index 9368b123a925..47ed0b1d0db6 100644 --- a/starter/spring-boot-starter-graphql/build.gradle +++ b/starter/spring-boot-starter-graphql/build.gradle @@ -24,5 +24,6 @@ dependencies { api(project(":starter:spring-boot-starter")) api(project(":starter:spring-boot-starter-jackson")) + api(project(":module:spring-boot-reactor")) api(project(":module:spring-boot-graphql")) } diff --git a/starter/spring-boot-starter-rsocket/build.gradle b/starter/spring-boot-starter-rsocket/build.gradle index 0a8b46a4c0f6..b6fd22b2960b 100644 --- a/starter/spring-boot-starter-rsocket/build.gradle +++ b/starter/spring-boot-starter-rsocket/build.gradle @@ -25,6 +25,7 @@ dependencies { api(project(":starter:spring-boot-starter-jackson")) api(project(":starter:spring-boot-starter-reactor-netty")) + api(project(":module:spring-boot-reactor")) api(project(":module:spring-boot-rsocket")) api("io.rsocket:rsocket-transport-netty") diff --git a/starter/spring-boot-starter-webclient/build.gradle b/starter/spring-boot-starter-webclient/build.gradle index 6664f90db206..f5c0340486c2 100644 --- a/starter/spring-boot-starter-webclient/build.gradle +++ b/starter/spring-boot-starter-webclient/build.gradle @@ -24,6 +24,7 @@ dependencies { api(project(":starter:spring-boot-starter")) api(project(":starter:spring-boot-starter-jackson")) + api(project(":module:spring-boot-reactor")) api(project(":module:spring-boot-webclient")) api("io.projectreactor.netty:reactor-netty-http") diff --git a/starter/spring-boot-starter-webflux/build.gradle b/starter/spring-boot-starter-webflux/build.gradle index a14375d8dbcf..600681a4bd56 100644 --- a/starter/spring-boot-starter-webflux/build.gradle +++ b/starter/spring-boot-starter-webflux/build.gradle @@ -25,5 +25,6 @@ dependencies { api(project(":starter:spring-boot-starter-jackson")) api(project(":starter:spring-boot-starter-reactor-netty")) + api(project(":module:spring-boot-reactor")) api(project(":module:spring-boot-webflux")) } From 7f7532a0b90d98d7a9758aca36321445bbf5ae8b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 27 Feb 2026 09:34:34 +0000 Subject: [PATCH 017/376] Remove duplicate spring-web dependency Closes gh-49338 --- module/spring-boot-jackson2/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/module/spring-boot-jackson2/build.gradle b/module/spring-boot-jackson2/build.gradle index 507ed5f1b6cb..34abea90be1d 100644 --- a/module/spring-boot-jackson2/build.gradle +++ b/module/spring-boot-jackson2/build.gradle @@ -38,7 +38,6 @@ dependencies { optional(project(":module:spring-boot-jackson")) { exclude(group: "tools.jackson.core", module: "jackson-databind") } - optional("org.springframework:spring-web") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") From 622e1e00442c1feb52b2aa039d01b560fc11061a Mon Sep 17 00:00:00 2001 From: Jay Choi Date: Fri, 27 Feb 2026 13:26:16 +0900 Subject: [PATCH 018/376] Fix @AutoConfigureWebTestClient preventing timeout property binding See gh-49340 Signed-off-by: Jay Choi --- .../reactive/AutoConfigureWebTestClient.java | 3 ++ ...TimeoutSpringBootTestIntegrationTests.java | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/AutoConfigureWebTestClient.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/AutoConfigureWebTestClient.java index d3884b831db9..1ea56bb1a7d9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/AutoConfigureWebTestClient.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/AutoConfigureWebTestClient.java @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; +import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping; import org.springframework.context.ApplicationContext; import org.springframework.test.web.reactive.server.WebTestClient; @@ -35,6 +36,7 @@ * mock requests and responses. At the moment, only WebFlux applications are supported. * * @author Stephane Nicoll + * @author Jay Choi * @since 2.0.0 * @see WebTestClientAutoConfiguration * @see WebTestClient#bindToApplicationContext(ApplicationContext) @@ -52,6 +54,7 @@ * {@link Duration#parse(CharSequence)}). * @return the web client timeout */ + @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE) String timeout() default ""; } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java new file mode 100644 index 000000000000..e69dbeb31910 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2026-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.autoconfigure.web.reactive.webclient; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.reactive.config.EnableWebFlux; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpringBootTest @SpringBootTest} with + * {@link AutoConfigureWebTestClient @AutoConfigureWebTestClient} timeout property + * binding. + * + * @author Jay Choi + */ +@SpringBootTest(properties = { "spring.main.web-application-type=reactive", "spring.test.webtestclient.timeout=30s" }, + classes = ExampleWebFluxApplication.class) +@AutoConfigureWebTestClient +@EnableWebFlux +class WebTestClientTimeoutSpringBootTestIntegrationTests { + + @Autowired + private WebTestClient webClient; + + @Test + void timeoutFromPropertyShouldBeApplied() { + assertThat(this.webClient).hasFieldOrPropertyWithValue("responseTimeout", Duration.ofSeconds(30)); + } + +} From a1b382a66b8b54f087f9c6e56591b401ee226646 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 27 Feb 2026 09:48:59 +0000 Subject: [PATCH 019/376] Polish "Fix @AutoConfigureWebTestClient preventing timeout property binding" See gh-49340 --- ...TimeoutSpringBootTestIntegrationTests.java | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java index e69dbeb31910..8a5bf94be962 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebTestClientTimeoutSpringBootTestIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2026-present the original author or authors. + * Copyright 2012-present the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,14 @@ import java.time.Duration; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.config.EnableWebFlux; import static org.assertj.core.api.Assertions.assertThat; @@ -34,19 +35,52 @@ * binding. * * @author Jay Choi + * @author Andy Wilkinson */ -@SpringBootTest(properties = { "spring.main.web-application-type=reactive", "spring.test.webtestclient.timeout=30s" }, - classes = ExampleWebFluxApplication.class) -@AutoConfigureWebTestClient -@EnableWebFlux +@SpringBootTest(properties = { "spring.main.web-application-type=reactive" }, classes = ExampleWebFluxApplication.class) class WebTestClientTimeoutSpringBootTestIntegrationTests { - @Autowired - private WebTestClient webClient; + @Nested + @AutoConfigureWebTestClient + @TestPropertySource(properties = "spring.test.webtestclient.timeout=30s") + class TimeoutFromProperty { + + @Autowired + private WebTestClient webClient; + + @Test + void timeoutIsApplied() { + assertThat(this.webClient).hasFieldOrPropertyWithValue("responseTimeout", Duration.ofSeconds(30)); + } + + } + + @Nested + @AutoConfigureWebTestClient(timeout = "25s") + class TimeoutFromAnnotation { + + @Autowired + private WebTestClient webClient; + + @Test + void timeoutIsApplied() { + assertThat(this.webClient).hasFieldOrPropertyWithValue("responseTimeout", Duration.ofSeconds(25)); + } + + } + + @Nested + @AutoConfigureWebTestClient + class DefaultTimeout { + + @Autowired + private WebTestClient webClient; + + @Test + void timeoutIsApplied() { + assertThat(this.webClient).hasFieldOrPropertyWithValue("responseTimeout", Duration.ofSeconds(5)); + } - @Test - void timeoutFromPropertyShouldBeApplied() { - assertThat(this.webClient).hasFieldOrPropertyWithValue("responseTimeout", Duration.ofSeconds(30)); } } From 88bd63a0dc9d9dbb29e7f81ff4692d631e7237f1 Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Mon, 2 Mar 2026 09:12:11 +0700 Subject: [PATCH 020/376] Fix devtools configuration example format See gh-49357 Signed-off-by: Tran Ngoc Nhan --- .../antora/modules/reference/pages/using/devtools.adoc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc index d1499cc6a235..f75c9dd16083 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc @@ -253,11 +253,8 @@ Here is an example where some local class files are excluded and some extra libr [source,properties] ---- -restart: - exclude: - companycommonlibs: "/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/" - include: - projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar" +restart.exclude.companycommonlibs="/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/" +include.projectcommon="/mycorp-myproj-[\\w\\d-\\.]+\\.jar" ---- NOTE: All property keys must be unique. From bf2e7ce8dd63a1e0b1ad2774b595456b06198b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 2 Mar 2026 09:27:54 +0100 Subject: [PATCH 021/376] Polish "Fix devtools configuration example format" See gh-49357 --- .../src/docs/antora/modules/reference/pages/using/devtools.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc index f75c9dd16083..b01383af1423 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/using/devtools.adoc @@ -254,7 +254,7 @@ Here is an example where some local class files are excluded and some extra libr [source,properties] ---- restart.exclude.companycommonlibs="/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/" -include.projectcommon="/mycorp-myproj-[\\w\\d-\\.]+\\.jar" +restart.include.projectcommon="/mycorp-myproj-[\\w\\d-\\.]+\\.jar" ---- NOTE: All property keys must be unique. From 90d75b74c5ffc316530ef070bb8e84cb55915cc3 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 2 Mar 2026 10:38:50 -0800 Subject: [PATCH 022/376] Align "noteworthy" issues in release notes with Spring Framework Update the oss changelog generator config which I missed in commit 54ab3c5a617cd4538f454c2c2fd68e26dd7a680a. See gh-49150 --- .../create-github-release/changelog-generator-oss.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/create-github-release/changelog-generator-oss.yml b/.github/actions/create-github-release/changelog-generator-oss.yml index fbaf7173763d..480b7c221cba 100644 --- a/.github/actions/create-github-release/changelog-generator-oss.yml +++ b/.github/actions/create-github-release/changelog-generator-oss.yml @@ -1,12 +1,12 @@ changelog: sections: - - title: ":warning: Noteworthy Changes" + - title: ":warning: Attention Required" labels: - - "status: noteworthy" + - "for: upgrade-attention" summary: mode: "member-comment" config: - prefix: "Noteworthy change:" + prefix: "Attention Required:" - title: ":star: New Features" labels: - "type: enhancement" From f144dfecc7f570c12743e0a375bd0ce648703560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:39:31 +0000 Subject: [PATCH 023/376] Bump actions/upload-artifact from 6 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... See gh-49370 Signed-off-by: dependabot[bot] --- .github/workflows/build-pull-request.yml | 2 +- .github/workflows/verify.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index d3e70557a436..8420a1a829b4 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -18,7 +18,7 @@ jobs: uses: ./.github/actions/print-jvm-thread-dumps - name: Upload Build Reports if: failure() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: build-reports path: '**/build/reports/' diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 4de8cf216a9c..94bad4f57e8c 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -79,7 +79,7 @@ jobs: run: ./gradlew spring-boot-release-verification-tests:test - name: Upload Build Reports on Failure if: failure() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: build-reports path: '**/build/reports/' From b7ca903f367c98791f218f081af95f485f41aad8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:39:36 +0000 Subject: [PATCH 024/376] Bump gradle/actions from 5.0.1 to 5.0.2 Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/f29f5a9d7b09a7c6b29859002d29d24e1674c884...0723195856401067f7a2779048b490ace7a47d7c) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: 5.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... See gh-49371 Signed-off-by: dependabot[bot] --- .github/workflows/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 94bad4f57e8c..644c8b642689 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -59,7 +59,7 @@ jobs: with: stable: true - name: Set Up Gradle - uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 with: cache-read-only: false - name: Configure Gradle Properties From f2179e1d9c3f5f62c8719821ce7f5cbd79d6d9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 3 Mar 2026 08:58:34 +0100 Subject: [PATCH 025/376] Polish "Bump gradle/actions from 5.0.1 to 5.0.2" See gh-49371 --- .github/actions/prepare-gradle-build/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 93a12a6261e3..4bd7099b877e 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -42,12 +42,12 @@ runs: ${{ inputs.java-toolchain == 'true' && '17' || '' }} - name: Set Up Gradle With Read/Write Cache if: ${{ inputs.cache-read-only == 'false' }} - uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 with: cache-read-only: false develocity-access-key: ${{ inputs.develocity-access-key }} - name: Set Up Gradle - uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 with: develocity-access-key: ${{ inputs.develocity-access-key }} develocity-token-expiry: 4 From c1dea9cad74d7d726136b3c6a560a9d407ea5e3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 07:57:25 +0000 Subject: [PATCH 026/376] Bump fast-xml-parser from 4.5.3 to 4.5.4 in /antora Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.5.3 to 4.5.4. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.5.3...v4.5.4) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 4.5.4 dependency-type: indirect ... See gh-49374 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index fc8a73ac9de4..7037cbae437a 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -1310,9 +1310,9 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.4.tgz", + "integrity": "sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==", "funding": [ { "type": "github", @@ -1321,7 +1321,7 @@ ], "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" From 42466e47760f9fb2a38cb8260283096a0f6d8b15 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Mar 2026 08:54:54 +0000 Subject: [PATCH 027/376] Allow Customizer beans to refine auth server config Fixes gh-49367 --- ...izationServerWebSecurityConfiguration.java | 9 +++--- ...onServerWebSecurityConfigurationTests.java | 31 ++++++++++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfiguration.java b/module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfiguration.java index 7f0406541085..8ad67e962df9 100644 --- a/module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfiguration.java +++ b/module/spring-boot-security-oauth2-authorization-server/src/main/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfiguration.java @@ -27,7 +27,6 @@ import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.web.SecurityFilterChain; @@ -50,11 +49,11 @@ class OAuth2AuthorizationServerWebSecurityConfiguration { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) { - OAuth2AuthorizationServerConfigurer authorizationServer = new OAuth2AuthorizationServerConfigurer(); - http.securityMatcher(authorizationServer.getEndpointsMatcher()); - http.with(authorizationServer, withDefaults()); + http.oauth2AuthorizationServer((authorizationServer) -> { + http.securityMatcher(authorizationServer.getEndpointsMatcher()); + authorizationServer.oidc(withDefaults()); + }); http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()); - http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(withDefaults()); http.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(withDefaults())); http.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor( new LoginUrlAuthenticationEntryPoint("/login"), createRequestMatcher())); diff --git a/module/spring-boot-security-oauth2-authorization-server/src/test/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfigurationTests.java b/module/spring-boot-security-oauth2-authorization-server/src/test/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfigurationTests.java index 09f34444dd36..9d7485608f6f 100644 --- a/module/spring-boot-security-oauth2-authorization-server/src/test/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfigurationTests.java +++ b/module/spring-boot-security-oauth2-authorization-server/src/test/java/org/springframework/boot/security/oauth2/server/authorization/autoconfigure/servlet/OAuth2AuthorizationServerWebSecurityConfigurationTests.java @@ -32,7 +32,6 @@ import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository; @@ -97,6 +96,19 @@ void webSecurityConfigurationConfiguresAuthorizationServerWithFormLogin() { }); } + @Test + void httpSecurityCustomizerCanRefineAuthorizationServerConfiguration() { + this.contextRunner.withUserConfiguration(TestOAuth2AuthorizationServerConfiguration.class) + .withPropertyValues(CLIENT_PREFIX + ".foo.registration.client-id=abcd", + CLIENT_PREFIX + ".foo.registration.client-secret=secret", + CLIENT_PREFIX + ".foo.registration.client-authentication-methods=client_secret_basic", + CLIENT_PREFIX + ".foo.registration.authorization-grant-types=client_credentials", + CLIENT_PREFIX + ".foo.registration.scopes=test") + .withUserConfiguration(OAuth2AuthorizationServerCustomizationConfiguration.class) + .run((context) -> assertThat(findFilter(context, OAuth2AuthorizationEndpointFilter.class, 0)) + .hasFieldOrPropertyWithValue("consentPage", "https://example.com/custom-consent-page")); + } + @Test void securityFilterChainsBackOffWhenSecurityFilterChainBeanPresent() { this.contextRunner @@ -165,9 +177,8 @@ static class TestSecurityFilterChainConfiguration { @Bean @Order(1) SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) { - OAuth2AuthorizationServerConfigurer authorizationServer = new OAuth2AuthorizationServerConfigurer(); - http.securityMatcher(authorizationServer.getEndpointsMatcher()) - .with(authorizationServer, Customizer.withDefaults()); + http.oauth2AuthorizationServer( + (authorizationServer) -> http.securityMatcher(authorizationServer.getEndpointsMatcher())); http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()); return http.build(); } @@ -176,6 +187,18 @@ SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) { @Order(2) SecurityFilterChain securityFilterChain(HttpSecurity http) { return http.httpBasic(withDefaults()).build(); + + } + + } + + @Configuration(proxyBeanMethods = false) + static class OAuth2AuthorizationServerCustomizationConfiguration { + + @Bean + Customizer oauth2AuthorizationServiceCustomizer() { + return (http) -> http.oauth2AuthorizationServer((authorizationServer) -> authorizationServer + .authorizationEndpoint((endpoint) -> endpoint.consentPage("https://example.com/custom-consent-page"))); } } From 1a42b5c282fb65dcb637af079a76b9c909d69625 Mon Sep 17 00:00:00 2001 From: Jay Choi Date: Tue, 10 Feb 2026 22:46:22 +0900 Subject: [PATCH 028/376] Restore error page support in WAR deployments See gh-49176 Signed-off-by: Jay Choi --- .../support/ErrorPageFilterConfiguration.java | 7 +++++++ .../SpringBootServletInitializerTests.java | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilterConfiguration.java b/core/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilterConfiguration.java index d980979368dd..c478dbfc6465 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilterConfiguration.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilterConfiguration.java @@ -18,6 +18,7 @@ import jakarta.servlet.DispatcherType; +import org.springframework.boot.web.error.ErrorPageRegistrarBeanPostProcessor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -26,6 +27,7 @@ * Configuration for {@link ErrorPageFilter}. * * @author Andy Wilkinson + * @author Jay Choi */ @Configuration(proxyBeanMethods = false) class ErrorPageFilterConfiguration { @@ -43,4 +45,9 @@ FilterRegistrationBean errorPageFilterRegistration(ErrorPageFil return registration; } + @Bean + static ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor() { + return new ErrorPageRegistrarBeanPostProcessor(); + } + } diff --git a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java index 5c86176f9220..8c2b756ddb02 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java @@ -41,6 +41,7 @@ import org.springframework.boot.context.logging.LoggingApplicationListener; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; +import org.springframework.boot.web.error.ErrorPageRegistrarBeanPostProcessor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -66,6 +67,7 @@ * * @author Phillip Webb * @author Andy Wilkinson + * @author Jay Choi */ @ExtendWith(OutputCaptureExtension.class) class SpringBootServletInitializerTests { @@ -135,6 +137,21 @@ void errorPageFilterRegistrationCanBeDisabled() { } } + @Test + void errorPageRegistrarBeanPostProcessorIsRegistered() { + ServletContext servletContext = mock(ServletContext.class); + given(servletContext.addFilter(any(), any(Filter.class))).willReturn(mock(Dynamic.class)); + given(servletContext.getInitParameterNames()).willReturn(Collections.emptyEnumeration()); + given(servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration()); + try (AbstractApplicationContext context = (AbstractApplicationContext) new WithErrorPageFilter() + .createRootApplicationContext(servletContext)) { + assertThat(context).isNotNull(); + Map beans = context + .getBeansOfType(ErrorPageRegistrarBeanPostProcessor.class); + assertThat(beans).hasSize(1); + } + } + @Test @SuppressWarnings("rawtypes") void errorPageFilterIsRegisteredWithNearHighestPrecedence() { From 2a80a3cac64a7b45a19bc62ec518c696f0a1dfc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 3 Mar 2026 10:31:19 +0100 Subject: [PATCH 029/376] Polish "Restore error page support in WAR deployments" See gh-49176 --- .../SpringBootServletInitializerTests.java | 32 ++++++++----------- .../deployment/AbstractDeploymentTests.java | 8 +++++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java index 8c2b756ddb02..cdb4904908f9 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.springframework.boot.DefaultApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; @@ -132,8 +133,8 @@ void errorPageFilterRegistrationCanBeDisabled() { try (AbstractApplicationContext context = (AbstractApplicationContext) new WithErrorPageFilterNotRegistered() .createRootApplicationContext(this.servletContext)) { assertThat(context).isNotNull(); - Map errorPageFilterBeans = context.getBeansOfType(ErrorPageFilter.class); - assertThat(errorPageFilterBeans).isEmpty(); + assertThat(context.getBeansOfType(ErrorPageFilter.class)).isEmpty(); + assertThat(context.getBeansOfType(ErrorPageRegistrarBeanPostProcessor.class)).isEmpty(); } } @@ -143,12 +144,10 @@ void errorPageRegistrarBeanPostProcessorIsRegistered() { given(servletContext.addFilter(any(), any(Filter.class))).willReturn(mock(Dynamic.class)); given(servletContext.getInitParameterNames()).willReturn(Collections.emptyEnumeration()); given(servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration()); - try (AbstractApplicationContext context = (AbstractApplicationContext) new WithErrorPageFilter() + try (AbstractApplicationContext context = (AbstractApplicationContext) new DefaultSpringBootServletInitializer() .createRootApplicationContext(servletContext)) { assertThat(context).isNotNull(); - Map beans = context - .getBeansOfType(ErrorPageRegistrarBeanPostProcessor.class); - assertThat(beans).hasSize(1); + assertThat(context.getBeansOfType(ErrorPageRegistrarBeanPostProcessor.class)).hasSize(1); } } @@ -159,7 +158,7 @@ void errorPageFilterIsRegisteredWithNearHighestPrecedence() { given(servletContext.addFilter(any(), any(Filter.class))).willReturn(mock(Dynamic.class)); given(servletContext.getInitParameterNames()).willReturn(Collections.emptyEnumeration()); given(servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration()); - try (AbstractApplicationContext context = (AbstractApplicationContext) new WithErrorPageFilter() + try (AbstractApplicationContext context = (AbstractApplicationContext) new DefaultSpringBootServletInitializer() .createRootApplicationContext(servletContext)) { assertThat(context).isNotNull(); Map registrations = context.getBeansOfType(FilterRegistrationBean.class); @@ -177,7 +176,7 @@ void errorPageFilterIsRegisteredForRequestAndAsyncDispatch() { given(servletContext.addFilter(any(), any(Filter.class))).willReturn(mock(Dynamic.class)); given(servletContext.getInitParameterNames()).willReturn(Collections.emptyEnumeration()); given(servletContext.getAttributeNames()).willReturn(Collections.emptyEnumeration()); - try (AbstractApplicationContext context = (AbstractApplicationContext) new WithErrorPageFilter() + try (AbstractApplicationContext context = (AbstractApplicationContext) new DefaultSpringBootServletInitializer() .createRootApplicationContext(servletContext)) { assertThat(context).isNotNull(); Map registrations = context.getBeansOfType(FilterRegistrationBean.class); @@ -190,7 +189,7 @@ void errorPageFilterIsRegisteredForRequestAndAsyncDispatch() { @Test void executableWarThatUsesServletInitializerDoesNotHaveErrorPageFilterConfigured() { - try (ConfigurableApplicationContext context = new SpringApplication(ExecutableWar.class).run()) { + try (ConfigurableApplicationContext context = new SpringApplication(DefaultApplicationArguments.class).run()) { assertThat(context.getBeansOfType(ErrorPageFilter.class)).isEmpty(); } } @@ -340,21 +339,16 @@ protected SpringApplicationBuilder configure(SpringApplicationBuilder applicatio } @Configuration(proxyBeanMethods = false) - static class WithErrorPageFilterNotRegistered extends SpringBootServletInitializer { - - WithErrorPageFilterNotRegistered() { - setRegisterErrorPageFilter(false); - } + static class DefaultSpringBootServletInitializer extends SpringBootServletInitializer { } @Configuration(proxyBeanMethods = false) - static class WithErrorPageFilter extends SpringBootServletInitializer { - - } + static class WithErrorPageFilterNotRegistered extends SpringBootServletInitializer { - @Configuration(proxyBeanMethods = false) - static class ExecutableWar extends SpringBootServletInitializer { + WithErrorPageFilterNotRegistered() { + setRegisterErrorPageFilter(false); + } } diff --git a/system-test/spring-boot-deployment-system-tests/src/systemTest/java/org/springframework/boot/deployment/AbstractDeploymentTests.java b/system-test/spring-boot-deployment-system-tests/src/systemTest/java/org/springframework/boot/deployment/AbstractDeploymentTests.java index f16cbdb4a5e4..47435d7981c8 100644 --- a/system-test/spring-boot-deployment-system-tests/src/systemTest/java/org/springframework/boot/deployment/AbstractDeploymentTests.java +++ b/system-test/spring-boot-deployment-system-tests/src/systemTest/java/org/springframework/boot/deployment/AbstractDeploymentTests.java @@ -56,6 +56,14 @@ void home() { }); } + @Test + void errorPage() { + getDeployedApplication().test((rest) -> { + ResponseEntity response = rest.getForEntity("/does-not-exist", String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + }); + } + @Test void health() { getDeployedApplication().test((rest) -> { From c2cb4f2bb1aa895576833bbaa4e589dc9f5625ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 3 Mar 2026 10:44:00 +0100 Subject: [PATCH 030/376] Fix broken test setup --- .../servlet/support/SpringBootServletInitializerTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java index cdb4904908f9..affd5bb56c44 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/web/servlet/support/SpringBootServletInitializerTests.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; -import org.springframework.boot.DefaultApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; @@ -189,7 +188,8 @@ void errorPageFilterIsRegisteredForRequestAndAsyncDispatch() { @Test void executableWarThatUsesServletInitializerDoesNotHaveErrorPageFilterConfigured() { - try (ConfigurableApplicationContext context = new SpringApplication(DefaultApplicationArguments.class).run()) { + try (ConfigurableApplicationContext context = new SpringApplication(DefaultSpringBootServletInitializer.class) + .run()) { assertThat(context.getBeansOfType(ErrorPageFilter.class)).isEmpty(); } } From 2bb5313bddf7d853eef134e2b9126cc8054eb75e Mon Sep 17 00:00:00 2001 From: itsmevichu Date: Fri, 13 Feb 2026 08:56:38 +0530 Subject: [PATCH 031/376] Add missing SSL support for Docker Compose See gh-49210 Signed-off-by: itsmevichu --- ...nectionDetailsFactoryIntegrationTests.java | 10 ++++ ...nectionDetailsFactoryIntegrationTests.java | 18 ++++-- ...nectionDetailsFactoryIntegrationTests.java | 10 ++++ ...nectionDetailsFactoryIntegrationTests.java | 10 ++++ .../cassandra/cassandra-ssl-compose.yaml | 34 +++++++++++ .../connection/cassandra/client-keystore.p12 | Bin 0 -> 2944 bytes .../cassandra/client-truststore.p12 | Bin 0 -> 1782 bytes .../connection/cassandra/server-keystore.p12 | Bin 0 -> 2928 bytes .../cassandra/server-truststore.p12 | Bin 0 -> 1782 bytes .../service/connection/elasticsearch/ca.crt | 32 +++++++++++ .../connection/elasticsearch/client.crt | 26 +++++++++ .../connection/elasticsearch/client.key | 28 +++++++++ .../elasticsearch-ssl-compose.yaml | 32 +++++++++++ .../connection/elasticsearch/server.crt | 26 +++++++++ .../connection/elasticsearch/server.key | 28 +++++++++ .../compose/service/connection/mongo/ca.crt | 32 +++++++++++ .../service/connection/mongo/client.crt | 26 +++++++++ .../service/connection/mongo/client.key | 28 +++++++++ .../connection/mongo/mongo-ssl-compose.yaml | 28 +++++++++ .../service/connection/mongo/mongo.pem | 54 ++++++++++++++++++ .../compose/service/connection/rabbit/ca.crt | 32 +++++++++++ .../service/connection/rabbit/client.crt | 26 +++++++++ .../service/connection/rabbit/client.key | 28 +++++++++ .../connection/rabbit/rabbit-ssl-compose.yaml | 26 +++++++++ .../service/connection/rabbit/rabbitmq.conf | 8 +++ .../service/connection/rabbit/server.crt | 26 +++++++++ .../service/connection/rabbit/server.key | 28 +++++++++ ...DockerComposeConnectionDetailsFactory.java | 9 +++ ...DockerComposeConnectionDetailsFactory.java | 12 +++- ...DockerComposeConnectionDetailsFactory.java | 9 +++ ...DockerComposeConnectionDetailsFactory.java | 9 +++ 31 files changed, 630 insertions(+), 5 deletions(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/cassandra-ssl-compose.yaml create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/client-keystore.p12 create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/client-truststore.p12 create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-keystore.p12 create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-truststore.p12 create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/ca.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/client.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/client.key create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/elasticsearch-ssl-compose.yaml create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/server.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/server.key create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/ca.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/client.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/client.key create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/mongo-ssl-compose.yaml create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/mongo.pem create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/ca.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.key create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbit-ssl-compose.yaml create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbitmq.conf create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.crt create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.key diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactoryIntegrationTests.java index dc20d1012a9e..2d3aa3505669 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails; import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails.Node; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.testsupport.container.TestImage; import static org.assertj.core.api.Assertions.assertThat; @@ -35,6 +36,15 @@ class CassandraDockerComposeConnectionDetailsFactoryIntegrationTests { @DockerComposeTest(composeFile = "cassandra-compose.yaml", image = TestImage.CASSANDRA) void runCreatesConnectionDetails(CassandraConnectionDetails connectionDetails) { assertConnectionDetails(connectionDetails); + assertThat(connectionDetails.getSslBundle()).isNull(); + } + + @DockerComposeTest(composeFile = "cassandra-ssl-compose.yaml", image = TestImage.CASSANDRA, additionalResources = { + "server-keystore.p12", "server-truststore.p12", "client-keystore.p12", "client-truststore.p12" }) + void runWithSslCreatesConnectionDetails(CassandraConnectionDetails connectionDetails) { + assertConnectionDetails(connectionDetails); + SslBundle sslBundle = connectionDetails.getSslBundle(); + assertThat(sslBundle).isNotNull(); } @DockerComposeTest(composeFile = "cassandra-bitnami-compose.yaml", image = TestImage.BITNAMI_CASSANDRA) diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactoryIntegrationTests.java index 8892ac8c8f74..0d92b1b544f3 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node; import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.testsupport.container.TestImage; import static org.assertj.core.api.Assertions.assertThat; @@ -36,15 +37,24 @@ class ElasticsearchDockerComposeConnectionDetailsFactoryIntegrationTests { @DockerComposeTest(composeFile = "elasticsearch-compose.yaml", image = TestImage.ELASTICSEARCH_8) void runCreatesConnectionDetails(ElasticsearchConnectionDetails connectionDetails) { - assertConnectionDetails(connectionDetails); + assertConnectionDetails(connectionDetails, Protocol.HTTP); + assertThat(connectionDetails.getSslBundle()).isNull(); + } + + @DockerComposeTest(composeFile = "elasticsearch-ssl-compose.yaml", image = TestImage.ELASTICSEARCH_8, + additionalResources = { "ca.crt", "server.crt", "server.key", "client.crt", "client.key" }) + void runWithSslCreatesConnectionDetails(ElasticsearchConnectionDetails connectionDetails) { + assertConnectionDetails(connectionDetails, Protocol.HTTPS); + SslBundle sslBundle = connectionDetails.getSslBundle(); + assertThat(sslBundle).isNotNull(); } @DockerComposeTest(composeFile = "elasticsearch-bitnami-compose.yaml", image = TestImage.BITNAMI_ELASTICSEARCH) void runWithBitnamiImageCreatesConnectionDetails(ElasticsearchConnectionDetails connectionDetails) { - assertConnectionDetails(connectionDetails); + assertConnectionDetails(connectionDetails, Protocol.HTTP); } - private void assertConnectionDetails(ElasticsearchConnectionDetails connectionDetails) { + private void assertConnectionDetails(ElasticsearchConnectionDetails connectionDetails, Protocol expectedProtocol) { assertThat(connectionDetails.getUsername()).isEqualTo("elastic"); assertThat(connectionDetails.getPassword()).isEqualTo("secret"); assertThat(connectionDetails.getPathPrefix()).isNull(); @@ -52,7 +62,7 @@ private void assertConnectionDetails(ElasticsearchConnectionDetails connectionDe Node node = connectionDetails.getNodes().get(0); assertThat(node.hostname()).isNotNull(); assertThat(node.port()).isGreaterThan(0); - assertThat(node.protocol()).isEqualTo(Protocol.HTTP); + assertThat(node.protocol()).isEqualTo(expectedProtocol); assertThat(node.username()).isEqualTo("elastic"); assertThat(node.password()).isEqualTo("secret"); } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactoryIntegrationTests.java index 1bf53e3915f0..ea16fc234ff7 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.boot.testsupport.junit.DisabledOnOs; @@ -39,6 +40,15 @@ class MongoDockerComposeConnectionDetailsFactoryIntegrationTests { @DockerComposeTest(composeFile = "mongo-compose.yaml", image = TestImage.MONGODB) void runCreatesConnectionDetails(MongoConnectionDetails connectionDetails) { assertConnectionDetailsWithDatabase(connectionDetails, "mydatabase"); + assertThat(connectionDetails.getSslBundle()).isNull(); + } + + @DockerComposeTest(composeFile = "mongo-ssl-compose.yaml", image = TestImage.MONGODB, + additionalResources = { "ca.crt", "client.crt", "client.key", "mongo.pem" }) + void runWithSslCreatesConnectionDetails(MongoConnectionDetails connectionDetails) { + assertConnectionDetailsWithDatabase(connectionDetails, "mydatabase"); + SslBundle sslBundle = connectionDetails.getSslBundle(); + assertThat(sslBundle).isNotNull(); } @DisabledOnOs(os = { OS.LINUX, OS.MAC }, architecture = "aarch64", disabledReason = "The image has no ARM support") diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactoryIntegrationTests.java index 45026f8c9509..8706cecd3d75 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -19,6 +19,7 @@ import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails; import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails.Address; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.testsupport.container.TestImage; import static org.assertj.core.api.Assertions.assertThat; @@ -36,6 +37,15 @@ class RabbitDockerComposeConnectionDetailsFactoryIntegrationTests { @DockerComposeTest(composeFile = "rabbit-compose.yaml", image = TestImage.RABBITMQ) void runCreatesConnectionDetails(RabbitConnectionDetails connectionDetails) { assertConnectionDetails(connectionDetails); + assertThat(connectionDetails.getSslBundle()).isNull(); + } + + @DockerComposeTest(composeFile = "rabbit-ssl-compose.yaml", image = TestImage.RABBITMQ, + additionalResources = { "ca.crt", "server.crt", "server.key", "client.crt", "client.key", "rabbitmq.conf" }) + void runWithSslCreatesConnectionDetails(RabbitConnectionDetails connectionDetails) { + assertConnectionDetails(connectionDetails); + SslBundle sslBundle = connectionDetails.getSslBundle(); + assertThat(sslBundle).isNotNull(); } @DockerComposeTest(composeFile = "rabbit-bitnami-compose.yaml", image = TestImage.BITNAMI_RABBITMQ) diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/cassandra-ssl-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/cassandra-ssl-compose.yaml new file mode 100644 index 000000000000..cdc7008dd930 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/cassandra-ssl-compose.yaml @@ -0,0 +1,34 @@ +services: + cassandra: + image: '{imageName}' + ports: + - '9042' + environment: + - 'CASSANDRA_SNITCH=GossipingPropertyFileSnitch' + - 'JVM_OPTS=-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0' + - 'HEAP_NEWSIZE=128M' + - 'MAX_HEAP_SIZE=1024M' + - 'CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch' + - 'CASSANDRA_DC=testdc1' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_ENABLED: true' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_REQUIRE_CLIENT_AUTH: true' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_KEYSTORE: /run/secrets/ssl/server-keystore.p12' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_KEYSTORE_PASSWORD: password' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_TRUSTSTORE: /run/secrets/server-truststore.p12' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_TRUSTSTORE_PASSWORD: password' + - 'CASSANDRA_CLIENT_ENCRYPTION_OPTIONS_STORE_TYPE: PKCS12' + labels: + - 'org.springframework.boot.sslbundle.jks.keystore.type:PKCS12' + - 'org.springframework.boot.sslbundle.jks.keystore.location=client-keystore.p12' + - 'org.springframework.boot.sslbundle.jks.keystore.password=password' + - 'org.springframework.boot.sslbundle.jks.truststore.type=PKCS12' + - 'org.springframework.boot.sslbundle.jks.truststore.location=client-truststore.p12' + - 'org.springframework.boot.sslbundle.jks.truststore.password=password' + secrets: + - server-keystore + - server-truststore +secrets: + server-keystore: + file: ./server-keystore.p12 + server-truststore: + file: ./server-truststore.p12 \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/client-keystore.p12 b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/client-keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..dc56224cf6ddfcd6c0fcc5429c31a2b94958b5a4 GIT binary patch literal 2944 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 YOw>};sfqHceKh$sLByA7y#fL!5J@#n5dZ)H literal 0 HcmV?d00001 diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-keystore.p12 b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..46a94d956b6371a26967e45f411b2bdb51f8810c GIT binary patch literal 2928 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 literal 0 HcmV?d00001 diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-truststore.p12 b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/cassandra/server-truststore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..ce1010ad55defb1535e76e9cd396f1d6b890eade GIT binary patch literal 1782 zcmV7Duzgg_YDCD0ic2gjRb-Qi72`Yw2hW8Bt2LUi<1_>&LNQU*SClCSwATSID2r7n1hW8Bu2?YQ! z9R>+thDZTr0|Wso1P~SIxOV8Qfd;NARa^(sHI9IS1@Iu6jp*Uyvj2j}7Di6RtQ{c-ly|L=p)kL0Gh=E67m`Kn6@XY{|!&)-0k$R@p4N%+-f&lG$i?R;qP zo?oqh#u<>qvO)$nb{d2#F3FKxec%6~j*e89{!@v0M#yiS#j!T|90(CsON|a97sQ&Q zr+nRn+t--#Mdw;cvW#`cCvTxPq4qAb}Cb9S*<)4#VnMD{9z%|RZ{aferh{- z0wO~!MG{ISe=5pJ&yEd+^rZ?xZXdK;+0Q|p3|dC-G2I#nNa0%u@=Y-96p8uP z!Ze_f_`ALQYDJ<#R~;L!x9n|u*89hnb`VSgUdqsf>_5#8$H;ZVa6eb4aOP*(ZBeLK zp)$egfu!rv?`LDKV3=h>aNO-|%_EakVj-Lv<=hh1(+^8?=*y;!ergWYn@$eZ2A^ufW2zS3OEgMU{P z4ZjBJ*4p2B(C6dq>NYO1tFIq~HW-O$*dp?`c5$ir*O>UJSiiNb8Jv<41FcV+Z00(4 zGu@4SLs#2(G=`TUUK-`EYEu8?!8q<#Dd;pekyWEQ<_2uj8v$#`a-9h*M=-wmU+879 zPr=B|Ji_ceU@bisFDL>eGp*<-RSOz2w-`FR@VlLhuowO`72M?q!eV1wxd)@M>XVRs zvva#{9p!mBmn0bM?+=nFUg^m{2K2M9Y0$z>=5_Xj&mAKn<*qKhf(U`o65FsHbo5^& zJlHXgg_19J3V>XU6vLaIN7YusXPS^2+|68dmH|`kP8l&vVEc1$W~&d4ozl{(@W-2a zO5T+p+;Ex|B2jzz4mpfzSCJg6;6PlyY(%#|_%W0G<|2`}AG&H|7$~$~4pGqGUmw@6klA|j1Ae2*!{>i;zgp=-P$;wDs`IrTf#7h29 zL4f8ahk~N+s%?{-4 zJZD}IYboQ)t7fUmw17bX?lC(}QGFfqbTF-apa%uXk2laV_mh4iS;W@*pR!-Ddi(ZA zMK(p+gbrfrBqk(6~tIRgQ=+e>So@zS`@;UkShu^CB-4%|@n zuU8~9s^vbFVX02?3$|c~?)SggLPRQu$^A0tR04#&t==0D7sW>%NC9O71OfpC00bZ;O!bM`nudcTSagP{ + mongod + --auth + --tlsMode requireTLS + --tlsCertificateKeyFile /run/secrets/ssl-cert + --tlsCAFile /run/secrets/ssl-ca + --bind_ip_all + labels: + - 'org.springframework.boot.sslbundle.pem.keystore.certificate=client.crt' + - 'org.springframework.boot.sslbundle.pem.keystore.private-key=client.key' + - 'org.springframework.boot.sslbundle.pem.truststore.certificate=ca.crt' +secrets: + ssl-ca: + file: 'ca.crt' + ssl-cert: + file: 'mongo.pem' \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/mongo.pem b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/mongo.pem new file mode 100644 index 000000000000..f25c216ea3f1 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/mongo/mongo.pem @@ -0,0 +1,54 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCyWXGxJDNNMmU0 +cx9cLZsIhcI+x8KAIGXsVs/PzbyPAmT4RhxeHT5ZMUaSzRbFLBvw1DNrlS3IJQMS +55mP19WPmZYFj3tYK7TVKX/fEKUeZiKxd/LK/bsYp5zs9pI66sKbYkOTIdE26aIl +E5riO143QY4fPDsViwzfvv1O43DXj+C1b54Gsae+CulTt9tS/HjYOLp+0ZhBTAiL +7e4hqpP/+LuS0MxoBAm2QvqFpPNnkk3aoo7lrfj9ZHP8Cm8/DeWkNXxziqhOTcLQ +GD0w/5oOdm2O0sFI30IlbCv6fXVBoyAdOi0Qqw5qEZ2LcnbNjEK8k/QtP6GM3Y71 +pHNIwiwzAgMBAAECgf9REZuCvy2Bi8SoTnjqQuHG5FuA6cPuisuFZr1k88IO+zJQ +uY3WKNs29BV+LcxnoK29W8jQnjqPHXcMfrF5dVWmkrrJdu8JLaGWVHF+uBq8nRb0 +2LvREh5XhZTGzIESNdc/7GIxdouag/8FlzCUYQGuT3v9+wUCiim+4CuIuPvv7ncD +8vANe3Ua5G0mHjVshOiMNpegg45zYlzYpMtUFPs+asLilW6A7UlgC+pLZ1cHUUlU +ZB7KOGT9JdrZpilTidl6LLvDDQK30TSWz8A26SuEAE71DR2VEjLVpjTNS76vlx+c +CrYr/WwpMb0xul+e/uHiNgo+51FiTiJ/IfuGeskCgYEA804CXQM6i5m4/Upps2yG +aTae5xBaYUquZREp5Zb054U6lUAHI41iTMTIwTTvWn5ogNojgi+YjljkzRj2RQ5k +NccBkjBBwwUNVWpBoGeZ73KAdejNB4C4ucGc2kkqEDo4MU5x3IE4JK1Yi1jl9mKb +IR6m3pqb2PCQHjO8sqKNHYkCgYEAu6fH/qUd/XGmCZJWY5K6jg3dISXH16MTO5M+ +jetprkGMMybWKZQa1GedXurPexE48oRlRhkjdQkW6Wcj1Qh6OKp6N2Zx8sY4dLeQ +yVChnMPFE2LK+UlRCKJUZi+rzX415ML6pZg+yW7O2cHpMKv7PlXISw2YDqtboCAi +Y+doqNsCgYBE1yqmBJbZDuqfiCF2KduyA0lcmWzpIEdNw1h2ZIrwwup7dj1O2t8Y +V4lx2TdsBF4vLwli+XKRvCcovMpZaaQC70bLhSnmMxS9uS3OY+HTNTORqQfx+oLJ +1DU8Mf1b0A08LjTbLhijkASAkOuoFehMq66NR3OXIyGz2fGnHYUN+QKBgCC47SL2 +X/hl7PIWVoIef/FtcXXqRKLRiPUGhA3zUwZT38K7rvSpItSPDN4UTAHFywxfEdnb +YFd0Mk6Y8aKgS8+9ynoGnzAaaJXRvKmeKdBQQvlSbNpzcnHy/IylG2xF6dfuOA7Q +MYKmk+Nc8PDPzIveIYMU58MHFn8hm12YaKOpAoGAV1CE8hFkEK9sbRGoKNJkx9nm +CZTv7PybaG/RN4ZrBSwVmnER0FEagA/Tzrlp1pi3sC8ZsC9onSOf6Btq8ZE0zbO1 +vsAm3gTBXcrCJxzw0Wjt8pzEbk3yELm4WE6VDEx4da2jWocdspslpIwdjHnPwsbH +r5O3ZAgigZs/ZtKW/U4= +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIEWjCCAkKgAwIBAgIURBZvq442tp+/K9TZII5Vy/LzVxwwDQYJKoZIhvcNAQEL +BQAwOzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MB4XDTI0MDUwMTE2NTMyNVoXDTM0MDQyOTE2NTMyNVow +LzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDESMBAGA1UEAwwJbG9jYWxob3N0 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsllxsSQzTTJlNHMfXC2b +CIXCPsfCgCBl7FbPz828jwJk+EYcXh0+WTFGks0WxSwb8NQza5UtyCUDEueZj9fV +j5mWBY97WCu01Sl/3xClHmYisXfyyv27GKec7PaSOurCm2JDkyHRNumiJROa4jte +N0GOHzw7FYsM3779TuNw14/gtW+eBrGnvgrpU7fbUvx42Di6ftGYQUwIi+3uIaqT +//i7ktDMaAQJtkL6haTzZ5JN2qKO5a34/WRz/ApvPw3lpDV8c4qoTk3C0Bg9MP+a +DnZtjtLBSN9CJWwr+n11QaMgHTotEKsOahGdi3J2zYxCvJP0LT+hjN2O9aRzSMIs +MwIDAQABo2IwYDALBgNVHQ8EBAMCBaAwEQYJYIZIAYb4QgEBBAQDAgZAMB0GA1Ud +DgQWBBS9XQHGwJZhG0olAGM1UMNuwZ65DzAfBgNVHSMEGDAWgBRVMLDVqPECWaH6 +GruL9E52VcTrPjANBgkqhkiG9w0BAQsFAAOCAgEAhBcqm5UQahn8iFMETXvfLMR6 +OOPijsHQ5lVfhig08s46a9O5eaJ9EYSYyiDnxYvZ4gYVH03f/kPwNLamvGR5KIBQ +R0DltkPPX4a11/vjwlSq1cXAt9r59nY+sNcVXWgIWH7zNodL8lyTpYhqvB2wEQkx +t2/JKZ8A0sGjed4S6I5HofYd7bnBxQZgfZShQ2SdDbzbcyg4SCEb8ghwnsH0KNZo +jJF+20RpK2VMViE6lylLTEMd/PyAdST/NPoqVxyva3QjTrKt+tkkFTsmNVMXcmYC +f1xo1/YFp73FFE63VYFI+Yw+Ajau8sYSo4+YvgFCy+Efhf3h3GFDtaiNod56uX9G +9M/cu8XsFzFP2e/0YWY3XL+v7ESOdc3g7yS4FQZ7Z6YvfAed9hCB25cDECvZXqJG +HSYDR38NHyAPROuCwlEwDyVmWRl9bpwZt+hr9kaTQScIDx+rV/EF3o0GKIwtR7AK +jaPAta0f4/Uu+EuWAcccSRUMtfx5/Jse/6iliBvy7JXmA+Y0PrT7K4uHO7iktdI+ +x8WbfZKfnLVuqw5fneTjC1n48Ltjis/f8DgO7BuWTmLdZXddjqqxzBSukFTBn4Hg +/oSg3XiMywOAVrRCNJehcdTG0u/BqZsrRjcYAJaf5qG/0tMLNsuF9Y53XQQAeezE +etL+7y0mkeQhVF+Kmy4= +-----END CERTIFICATE----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/ca.crt b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/ca.crt new file mode 100644 index 000000000000..beed250b132b --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/ca.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFhjCCA26gAwIBAgIUfIkk29IT9OpbgfjL8oRIPSLjUcAwDQYJKoZIhvcNAQEL +BQAwOzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MB4XDTI0MDUwMTE2NTMyNVoXDTM0MDQyOTE2NTMyNVow +OzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNh +dGUgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAusN2 +KzQQUUxZSiI3ZZuZohFwq2KXSUNPdJ6rgD3/YKNTDSZXKZPO53kYPP0DXf0sm3CH +cyWSWVabyimZYuPWena1MElSL4ZpJ9WwkZoOQ3bPFK1utz6kMOwrgAUcky8H/rIK +j2JEBhkSHUIGr57NjUEwG1ygaSerM8RzWw1PtMq+C8LOu3v94qzE3NDg1QRpyvV9 +OmsLsjISd0ZmAJNi9vmiEH923KnPyiqnQmWKpYicdgQmX1GXylS22jZqAwaOkYGj +X8UdeyvrohkZkM0hn9uaSufQGEW4yKACn3PkjJtzi8drBIyjIi9YcAzBxZB9oVKq +XZMlltgO2fDMmIJi0Ngt0Ci7fCoEMqSocKyDKML6YLr9UWtx4bfsrk+rVO9Q/D/v +8RKgstv7dCf2KWRX3ZJEC0IBHS5gLNq0qqqVcGx3LcSyhdiKJOtSwAnNkHMh+jSQ +xLSlBjcSqTPiGTRK/Rddl+xnU/mBgk7ZBGNrUFaD5McMFjddS7Ih82aHnpQ1gekW +nUGv+Tm/G68h2BvZ5U2q+RfeOCgRW9i/AYW2jgT7IFnfjyUXgBQveauMAchomqFE +VLe95ZgViF6vmH34EKo3w9L5TQiwk/r53YlM7TSOTyDqx66t4zGYDsVMicpKmzi4 +2Rp8EpErARRyREUIKSvWs9O9+uT3+7arNLgHe5ECAwEAAaOBgTB/MB0GA1UdDgQW +BBRVMLDVqPECWaH6GruL9E52VcTrPjAfBgNVHSMEGDAWgBRVMLDVqPECWaH6GruL +9E52VcTrPjAPBgNVHRMBAf8EBTADAQH/MCwGA1UdEQQlMCOCC2V4YW1wbGUuY29t +gglsb2NhbGhvc3SCCTEyNy4wLjAuMTANBgkqhkiG9w0BAQsFAAOCAgEAeSpjCL3j +2GIFBNKr/5amLOYa0kZ6r1dJs+K6xvMsUvsBJ/QQsV5nYDMIoV/NYUd8SyYV4lEj +7LHX5ZbmJrvPk30LGEBG/5Vy2MIATrQrQ14S4nXtEdSnBvTQwPOOaHc+2dTp3YpM +f4ffELKWyispTifx1eqdiUJhURKeQBh+3W7zpyaiN4vJaqEDKGgFQtHA/OyZL2hZ +BpxHB0zpb2iDHV8MeyfOT7HQWUk6p13vdYm6EnyJT8fzWvE+TqYNbqFmB+CLRSXy +R3p1yaeTd4LnVknJ0UBKqEyul3ziHZDhKhBpwdglYOQz4eWjSFhikX9XZ8NaI38Q +QqLZVn0DsH2ztkjrQrUVgK2xn4aUuqoLDk4Hu6h5baUn+f2GLuzx+EXc/i3ikYvw +Y3JyufOgw6nGGFG+/QXEj85XtLPhN7Wm42z2e/BGzi0MLl65sfpEDXvFTA72Yzws +OYaeg/HxeYwUHQgs2fKl/LgV4chntSCvTqfNl6OnQafD/ISJNpx3xWR3HwF+ypFG +UaLE+e1soqEJbzL31U/6pypHLsj8Y8r9hJbZXo2ibnhjFV6fypUAP0rbIzaoWcrJ +T0Sbliz+KQTMzCcubiAi4bI/kZ5FJ4kkaHqUpIWzlx1h2WVJ65ASFDjBWb8eVmB6 +Dyno/RVFR/rUL5091gjGRXhLsi1oUHKdEzU= +-----END CERTIFICATE----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.crt b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.crt new file mode 100644 index 000000000000..811d880fcbd3 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEWjCCAkKgAwIBAgIURBZvq442tp+/K9TZII5Vy/LzVx0wDQYJKoZIhvcNAQEL +BQAwOzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MB4XDTI0MDUwMTE2NTMyNVoXDTM0MDQyOTE2NTMyNVow +LzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDESMBAGA1UEAwwJbG9jYWxob3N0 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvGb7tu0odSuOjeY1lHlh +sRR4PayAvlryjfrrp49hjoVTiL3d/Jo6Po5HlqwJcYuclm0EWQR5Vur/zYJpfUE7 +b8+E9Qwe50+YzfQ2tVFEdq/VfqemrYRGee+pMelOCI90enOKCxfpo6EHbz+WnUP0 +mnD8OAF9QpolSdWAMOGJoPdWX65KQvyMXvQbj9VIHmsx7NCaIOYxjHXB/dI2FmXV ++m4VT6mb8he9dXmgK/ozMq6XIPOAXe0n3dlfMTSEddeNeVwnBpr/n5e0cpwGFhdf +NNu5CI4ecipBhXljJi/4/47M/6hd69HwE05C4zyH4ZDZ2JTfaSKOLV+jYdBUqJP5 +dwIDAQABo2IwYDALBgNVHQ8EBAMCBaAwEQYJYIZIAYb4QgEBBAQDAgeAMB0GA1Ud +DgQWBBRWiWOo9cm2IF/ZlhWLVjifLzYa/DAfBgNVHSMEGDAWgBRVMLDVqPECWaH6 +GruL9E52VcTrPjANBgkqhkiG9w0BAQsFAAOCAgEAA5Wphtu2nBhY+QNOBOwXq4zF +N5qt2IYTLfR7xqpKhhXx9VkIjdPWpcsGuCuMmfPVNvQWE6iK0/jMMqToTj4H6K7e +MN74j0GwwcknT1P42tUzEpg8LKR8VMdhWhyqdniCDNWWuaz1iVSoF0S2i4jFSzH5 +1q3KMKMZ4niK5aJI0fAGa4fCjyuun1Mfg/qGBGwLnqDkIXjeAopZf4Jb64TtzjAs +j9NT6mYbe3E0tw3fHT9ihYdbZDZgSjeCsuq9OiRMVb0DWWmRoLmmOrlN8IJlHV/3 +WyI/ta4Cw5EZ0oaOg0lIyOxXyvElth1xIvh+kdqZSBsU0gNBri6ZIzYbbTh2KTTO +BJHQt9L5naWG27pDrIxBicWXS/MIYonktm3YgCLfuW3kWcVk8bIlNhfcoAYBBgfM +IEYSYEq+bH2IQ+YoWQz3AxjJ8gEuuSUP6R6mYY65FfpjkKgcpGBvw4EIAmqKDtPS +hlLY/F0XVj9KZzrMyH4/vonu+DAb/P7Zmt2fyk/dQO6bAc3ltRmJbJm4VJ2v/T8I +LVu2FtcUYgtLNtkWUPfdb3GSUUgkKlUpWSty31TKSUszJjW1oRykQhEko6o5U3S8 +ptQzXdApsb1lGOqewkubE25tIu2RLiNkKcjFOjJ/lu0vP9k76wWwRVnFLFvfo4lW +pgywiOifs5JbcCt0ZQ0= +-----END CERTIFICATE----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.key b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.key new file mode 100644 index 000000000000..2ae0f49bf4a4 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8Zvu27Sh1K46N +5jWUeWGxFHg9rIC+WvKN+uunj2GOhVOIvd38mjo+jkeWrAlxi5yWbQRZBHlW6v/N +gml9QTtvz4T1DB7nT5jN9Da1UUR2r9V+p6athEZ576kx6U4Ij3R6c4oLF+mjoQdv +P5adQ/SacPw4AX1CmiVJ1YAw4Ymg91ZfrkpC/Ixe9BuP1UgeazHs0Jog5jGMdcH9 +0jYWZdX6bhVPqZvyF711eaAr+jMyrpcg84Bd7Sfd2V8xNIR11415XCcGmv+fl7Ry +nAYWF18027kIjh5yKkGFeWMmL/j/jsz/qF3r0fATTkLjPIfhkNnYlN9pIo4tX6Nh +0FSok/l3AgMBAAECggEABXnBe3MwXAMQENzNypOiXK4VE3XMYkePfdsSK163byOD +w3ZeTgQNfU4g8LJK8/homzO0SQIJAdz2+ZFbpsp4A2W2zJ+1jvN5RuX/8/UcVhmk +tb1IL/LWCvx5/aoYBWkgIA70UfQJa2jDbdM0v5j/Gu9yE7GI14jh6DFC3xGMGV3b +fOwManxf7sDibCI1nGjnFYNGxninRr+tpb+a1KNbVzhett68LrgPmtph6B3HCPAJ +zBigk1Phgb8WHozTXxnLyw9/RdKJ0Ro4PFmtQv0EvCSlytptnF+0nXkqr3f851XS +bUWwYFchIFWPMhPfD5B3niNWCV42/sU/bQlk+BMQAQKBgQD6NvMq8EdYy2Y7fXT5 +FgB4s+7EkLgI2d5LUaCXCFgc6iZtCTQKUXj1rIWeRfGrFVCCe8qV+XIMKt/G5eEi +tn5ifHhktA2A8GK1scj026qHP3bVn0hMaUnkCF1UpDRKPiEO5G/apPtav8PbCNaX +GAimLGw+WZNZuv7+T33bEBeUdwKBgQDAwiidayLXkRkz2deefdDKcXQsB7RHFGGy +vfZPBCGqizxml+6ojJkkDsVUKL1IXFfyK9KpQAI6tezn4oktgu4jAQqkYY7QZobs +RpQx1dR+KxEm7ISDBTq/B1Q9cFKUKVvQQy8N2pnIbCdzb6MTOKLmJqFGTjr+5T8q +F32B5vkDAQKBgDCKfH42AwFc5EZiPlEcTZcdARMtKCa/bXqbKVZjjgR+AFpi0K+3 +womWoI1l8E5KYkYOEe0qaU+m+aaybgy37qjYkNqoe34qJFwvU1b9ToXScBFdRz9b +pbQRU1naSTKl/u/OrUxzeTfPwAU8H7VMOlFSiOVHp2he+J0JetcGtixdAoGBAIJQ +QMj7rxhxHcqyEVUy1b6nKNTDeJs9Kjd+uU/+CQyVCQaK3GvScY2w9rLIv/51f3dX +LRoDDf7HExxJSFgeVgQQJjOvSK+XQMvngzSVzQxm7TeVWpiBJpAS0l6e2xUTSODp +KpyBFsoqZBlkdaj+9xIFN66iILxGG4fHTbBOiDYBAoGBAOZMKjM5N/hGcCmik/6t +p/zBA2pN9O6zwPndITTsdyVWSlVqCZhXlRX47CerAN+/WVCidlh7Vp5Tuy75Wa77 +v16IDLO01txgWNobcLaM4VgFsyLi5JuxK73S18Vb1cKWdHFRF0LH3cUIq20fjpv6 +Odl4vjNOncXMZCLPHQ+bKWaf +-----END PRIVATE KEY----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbit-ssl-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbit-ssl-compose.yaml new file mode 100644 index 000000000000..3f39d29ddbb9 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbit-ssl-compose.yaml @@ -0,0 +1,26 @@ +services: + rabbitmq: + image: '{imageName}' + environment: + - 'RABBITMQ_DEFAULT_USER=myuser' + - 'RABBITMQ_DEFAULT_PASS=secret' + ports: + - '5672' + - '5671' + secrets: + - ssl-ca + - ssl-key + - ssl-cert + volumes: + - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro + labels: + - 'org.springframework.boot.sslbundle.pem.keystore.certificate=client.crt' + - 'org.springframework.boot.sslbundle.pem.keystore.private-key=client.key' + - 'org.springframework.boot.sslbundle.pem.truststore.certificate=ca.crt' +secrets: + ssl-ca: + file: 'ca.crt' + ssl-key: + file: 'server.key' + ssl-cert: + file: 'server.crt' \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbitmq.conf b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbitmq.conf new file mode 100644 index 000000000000..55b842131ca6 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/rabbitmq.conf @@ -0,0 +1,8 @@ +listeners.ssl.default=5671 + +ssl_options.cacertfile=/run/secrets/ssl-ca +ssl_options.certfile=/run/secrets/ssl-cert +ssl_options.keyfile=/run/secrets/ssl-key + +ssl_options.verify=verify_peer +ssl_options.fail_if_no_peer_cert=true diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.crt b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.crt new file mode 100644 index 000000000000..57c66cc78a3b --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEWjCCAkKgAwIBAgIURBZvq442tp+/K9TZII5Vy/LzVxwwDQYJKoZIhvcNAQEL +BQAwOzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDEeMBwGA1UEAwwVQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MB4XDTI0MDUwMTE2NTMyNVoXDTM0MDQyOTE2NTMyNVow +LzEZMBcGA1UECgwQU3ByaW5nIEJvb3QgVGVzdDESMBAGA1UEAwwJbG9jYWxob3N0 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsllxsSQzTTJlNHMfXC2b +CIXCPsfCgCBl7FbPz828jwJk+EYcXh0+WTFGks0WxSwb8NQza5UtyCUDEueZj9fV +j5mWBY97WCu01Sl/3xClHmYisXfyyv27GKec7PaSOurCm2JDkyHRNumiJROa4jte +N0GOHzw7FYsM3779TuNw14/gtW+eBrGnvgrpU7fbUvx42Di6ftGYQUwIi+3uIaqT +//i7ktDMaAQJtkL6haTzZ5JN2qKO5a34/WRz/ApvPw3lpDV8c4qoTk3C0Bg9MP+a +DnZtjtLBSN9CJWwr+n11QaMgHTotEKsOahGdi3J2zYxCvJP0LT+hjN2O9aRzSMIs +MwIDAQABo2IwYDALBgNVHQ8EBAMCBaAwEQYJYIZIAYb4QgEBBAQDAgZAMB0GA1Ud +DgQWBBS9XQHGwJZhG0olAGM1UMNuwZ65DzAfBgNVHSMEGDAWgBRVMLDVqPECWaH6 +GruL9E52VcTrPjANBgkqhkiG9w0BAQsFAAOCAgEAhBcqm5UQahn8iFMETXvfLMR6 +OOPijsHQ5lVfhig08s46a9O5eaJ9EYSYyiDnxYvZ4gYVH03f/kPwNLamvGR5KIBQ +R0DltkPPX4a11/vjwlSq1cXAt9r59nY+sNcVXWgIWH7zNodL8lyTpYhqvB2wEQkx +t2/JKZ8A0sGjed4S6I5HofYd7bnBxQZgfZShQ2SdDbzbcyg4SCEb8ghwnsH0KNZo +jJF+20RpK2VMViE6lylLTEMd/PyAdST/NPoqVxyva3QjTrKt+tkkFTsmNVMXcmYC +f1xo1/YFp73FFE63VYFI+Yw+Ajau8sYSo4+YvgFCy+Efhf3h3GFDtaiNod56uX9G +9M/cu8XsFzFP2e/0YWY3XL+v7ESOdc3g7yS4FQZ7Z6YvfAed9hCB25cDECvZXqJG +HSYDR38NHyAPROuCwlEwDyVmWRl9bpwZt+hr9kaTQScIDx+rV/EF3o0GKIwtR7AK +jaPAta0f4/Uu+EuWAcccSRUMtfx5/Jse/6iliBvy7JXmA+Y0PrT7K4uHO7iktdI+ +x8WbfZKfnLVuqw5fneTjC1n48Ltjis/f8DgO7BuWTmLdZXddjqqxzBSukFTBn4Hg +/oSg3XiMywOAVrRCNJehcdTG0u/BqZsrRjcYAJaf5qG/0tMLNsuF9Y53XQQAeezE +etL+7y0mkeQhVF+Kmy4= +-----END CERTIFICATE----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.key b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.key new file mode 100644 index 000000000000..95e2ef3e8b31 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/rabbit/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCyWXGxJDNNMmU0 +cx9cLZsIhcI+x8KAIGXsVs/PzbyPAmT4RhxeHT5ZMUaSzRbFLBvw1DNrlS3IJQMS +55mP19WPmZYFj3tYK7TVKX/fEKUeZiKxd/LK/bsYp5zs9pI66sKbYkOTIdE26aIl +E5riO143QY4fPDsViwzfvv1O43DXj+C1b54Gsae+CulTt9tS/HjYOLp+0ZhBTAiL +7e4hqpP/+LuS0MxoBAm2QvqFpPNnkk3aoo7lrfj9ZHP8Cm8/DeWkNXxziqhOTcLQ +GD0w/5oOdm2O0sFI30IlbCv6fXVBoyAdOi0Qqw5qEZ2LcnbNjEK8k/QtP6GM3Y71 +pHNIwiwzAgMBAAECgf9REZuCvy2Bi8SoTnjqQuHG5FuA6cPuisuFZr1k88IO+zJQ +uY3WKNs29BV+LcxnoK29W8jQnjqPHXcMfrF5dVWmkrrJdu8JLaGWVHF+uBq8nRb0 +2LvREh5XhZTGzIESNdc/7GIxdouag/8FlzCUYQGuT3v9+wUCiim+4CuIuPvv7ncD +8vANe3Ua5G0mHjVshOiMNpegg45zYlzYpMtUFPs+asLilW6A7UlgC+pLZ1cHUUlU +ZB7KOGT9JdrZpilTidl6LLvDDQK30TSWz8A26SuEAE71DR2VEjLVpjTNS76vlx+c +CrYr/WwpMb0xul+e/uHiNgo+51FiTiJ/IfuGeskCgYEA804CXQM6i5m4/Upps2yG +aTae5xBaYUquZREp5Zb054U6lUAHI41iTMTIwTTvWn5ogNojgi+YjljkzRj2RQ5k +NccBkjBBwwUNVWpBoGeZ73KAdejNB4C4ucGc2kkqEDo4MU5x3IE4JK1Yi1jl9mKb +IR6m3pqb2PCQHjO8sqKNHYkCgYEAu6fH/qUd/XGmCZJWY5K6jg3dISXH16MTO5M+ +jetprkGMMybWKZQa1GedXurPexE48oRlRhkjdQkW6Wcj1Qh6OKp6N2Zx8sY4dLeQ +yVChnMPFE2LK+UlRCKJUZi+rzX415ML6pZg+yW7O2cHpMKv7PlXISw2YDqtboCAi +Y+doqNsCgYBE1yqmBJbZDuqfiCF2KduyA0lcmWzpIEdNw1h2ZIrwwup7dj1O2t8Y +V4lx2TdsBF4vLwli+XKRvCcovMpZaaQC70bLhSnmMxS9uS3OY+HTNTORqQfx+oLJ +1DU8Mf1b0A08LjTbLhijkASAkOuoFehMq66NR3OXIyGz2fGnHYUN+QKBgCC47SL2 +X/hl7PIWVoIef/FtcXXqRKLRiPUGhA3zUwZT38K7rvSpItSPDN4UTAHFywxfEdnb +YFd0Mk6Y8aKgS8+9ynoGnzAaaJXRvKmeKdBQQvlSbNpzcnHy/IylG2xF6dfuOA7Q +MYKmk+Nc8PDPzIveIYMU58MHFn8hm12YaKOpAoGAV1CE8hFkEK9sbRGoKNJkx9nm +CZTv7PybaG/RN4ZrBSwVmnER0FEagA/Tzrlp1pi3sC8ZsC9onSOf6Btq8ZE0zbO1 +vsAm3gTBXcrCJxzw0Wjt8pzEbk3yELm4WE6VDEx4da2jWocdspslpIwdjHnPwsbH +r5O3ZAgigZs/ZtKW/U4= +-----END PRIVATE KEY----- diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactory.java index dd1510ad2bdb..1a9c906d9cf0 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/cassandra/CassandraDockerComposeConnectionDetailsFactory.java @@ -22,6 +22,7 @@ import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; +import org.springframework.boot.ssl.SslBundle; /** * {@link DockerComposeConnectionDetailsFactory} to create @@ -57,11 +58,19 @@ static class CassandraDockerComposeConnectionDetails extends DockerComposeConnec private final String datacenter; + private final SslBundle sslBundle; + CassandraDockerComposeConnectionDetails(RunningService service) { super(service); CassandraEnvironment cassandraEnvironment = new CassandraEnvironment(service.env()); this.contactPoints = List.of(new Node(service.host(), service.ports().get(CASSANDRA_PORT))); this.datacenter = cassandraEnvironment.getDatacenter(); + this.sslBundle = getSslBundle(service); + } + + @Override + public SslBundle getSslBundle() { + return this.sslBundle; } @Override diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactory.java index 5f9f9cb76af6..1ea31da1173c 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/elasticsearch/ElasticsearchDockerComposeConnectionDetailsFactory.java @@ -23,6 +23,7 @@ import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; +import org.springframework.boot.ssl.SslBundle; /** * {@link DockerComposeConnectionDetailsFactory} to create @@ -61,13 +62,22 @@ static class ElasticsearchDockerComposeConnectionDetails extends DockerComposeCo private final List nodes; + private final SslBundle sslBundle; + ElasticsearchDockerComposeConnectionDetails(RunningService service) { super(service); this.environment = new ElasticsearchEnvironment(service.env()); - this.nodes = List.of(new Node(service.host(), service.ports().get(ELASTICSEARCH_PORT), Protocol.HTTP, + this.sslBundle = getSslBundle(service); + Protocol protocol = (this.sslBundle != null) ? Protocol.HTTPS : Protocol.HTTP; + this.nodes = List.of(new Node(service.host(), service.ports().get(ELASTICSEARCH_PORT), protocol, getUsername(), getPassword())); } + @Override + public SslBundle getSslBundle() { + return this.sslBundle; + } + @Override public String getUsername() { return "elastic"; diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactory.java index 6f8b344da121..be85dd5429bd 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/mongo/MongoDockerComposeConnectionDetailsFactory.java @@ -22,6 +22,7 @@ import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; +import org.springframework.boot.ssl.SslBundle; /** * {@link DockerComposeConnectionDetailsFactory} to create {@link MongoConnectionDetails} @@ -56,8 +57,11 @@ static class MongoDockerComposeConnectionDetails extends DockerComposeConnection private final ConnectionString connectionString; + private final SslBundle sslBundle; + MongoDockerComposeConnectionDetails(RunningService service) { super(service); + this.sslBundle = getSslBundle(service); this.connectionString = buildConnectionString(service); } @@ -82,6 +86,11 @@ private ConnectionString buildConnectionString(RunningService service) { return new ConnectionString(builder.toString()); } + @Override + public SslBundle getSslBundle() { + return this.sslBundle; + } + @Override public ConnectionString getConnectionString() { return this.connectionString; diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactory.java index 793a1995545c..e84b872c86fa 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/rabbit/RabbitDockerComposeConnectionDetailsFactory.java @@ -22,6 +22,7 @@ import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; +import org.springframework.boot.ssl.SslBundle; /** * {@link DockerComposeConnectionDetailsFactory} to create {@link RabbitConnectionDetails} @@ -60,9 +61,12 @@ static class RabbitDockerComposeConnectionDetails extends DockerComposeConnectio private final List
addresses; + private final SslBundle sslBundle; + protected RabbitDockerComposeConnectionDetails(RunningService service) { super(service); this.environment = new RabbitEnvironment(service.env()); + this.sslBundle = getSslBundle(service); this.addresses = List.of(new Address(service.host(), service.ports().get(RABBITMQ_PORT))); } @@ -76,6 +80,11 @@ public String getPassword() { return this.environment.getPassword(); } + @Override + public SslBundle getSslBundle() { + return this.sslBundle; + } + @Override public String getVirtualHost() { return "/"; From 9cf13004d75a0cfb90e630356119f63e2795537a Mon Sep 17 00:00:00 2001 From: Fabrice Bibonne Date: Thu, 26 Feb 2026 06:05:39 +0100 Subject: [PATCH 032/376] Allow Log4j2 logging system to be forced by system property Signed-off-by: Fabrice Bibonne See gh-49343 --- .../logging/log4j2/Log4J2LoggingSystem.java | 19 ++++++++++++------- .../boot/logging/LoggingSystemTests.java | 12 ++++++++++++ .../log4j2/Log4J2LoggingSystemTests.java | 1 + .../log4j2/TestLog4J2LoggingSystem.java | 4 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index cea74711abc3..cb7a71ce3976 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -121,11 +121,15 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { /** * Create a new {@link Log4J2LoggingSystem} instance. * @param classLoader the class loader to use. - * @param loggerContext the {@link LoggerContext} to use. + * @throws IllegalArgumentException if the loggerContext instantiated internally is + * not of type org.apache.logging.log4j.core.LoggerContext */ - Log4J2LoggingSystem(ClassLoader classLoader, LoggerContext loggerContext) { + Log4J2LoggingSystem(ClassLoader classLoader) { super(classLoader); - this.loggerContext = loggerContext; + org.apache.logging.log4j.spi.LoggerContext spiLoggerContext = LogManager.getContext(classLoader, false); + Assert.isInstanceOf(LoggerContext.class, spiLoggerContext, + "Log4j2LoggingSystem requires LoggerContext to be of type org.apache.logging.log4j.core.LoggerContext"); + this.loggerContext = (LoggerContext) spiLoggerContext; } @Override @@ -528,10 +532,11 @@ public static class Factory implements LoggingSystemFactory { @Override public @Nullable LoggingSystem getLoggingSystem(ClassLoader classLoader) { if (PRESENT) { - org.apache.logging.log4j.spi.LoggerContext spiLoggerContext = LogManager.getContext(classLoader, false); - Assert.state(spiLoggerContext instanceof LoggerContext, ""); - if (spiLoggerContext instanceof LoggerContext coreLoggerContext) { - return new Log4J2LoggingSystem(classLoader, coreLoggerContext); + try { + return new Log4J2LoggingSystem(classLoader); + } + catch (IllegalStateException ex) { + // Continue } } return null; diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java index cc491eaa3497..def94bc4c3ec 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java @@ -65,6 +65,18 @@ void loggingSystemCanBeDisabled() { assertThat(loggingSystem).isInstanceOf(NoOpLoggingSystem.class); } + @Test + void log4j2CanBeForcedUsingSystemProperty() { + System.setProperty(LoggingSystem.SYSTEM_PROPERTY, Log4J2LoggingSystem.class.getName()); + assertThat(LoggingSystem.get(getClass().getClassLoader())).isInstanceOf(Log4J2LoggingSystem.class); + } + + @Test + void julj2CanBeForcedUsingSystemProperty() { + System.setProperty(LoggingSystem.SYSTEM_PROPERTY, JavaLoggingSystem.class.getName()); + assertThat(LoggingSystem.get(getClass().getClassLoader())).isInstanceOf(JavaLoggingSystem.class); + } + @Test void getLoggerConfigurationIsUnsupported() { assertThatExceptionOfType(UnsupportedOperationException.class) diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index 88025e009d9d..ecb14ddbd980 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -116,6 +116,7 @@ void cleanUp() { this.loggingSystem.getConfiguration().stop(); this.loggingSystem.cleanUp(); PluginRegistry.getInstance().clear(); + LogManager.getFactory().removeContext(this.loggingSystem.getLoggerContext()); } @Test diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java index ca937aa5879b..fe522378a1d6 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/TestLog4J2LoggingSystem.java @@ -16,7 +16,6 @@ package org.springframework.boot.logging.log4j2; -import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.jspecify.annotations.Nullable; @@ -26,7 +25,8 @@ class TestLog4J2LoggingSystem extends Log4J2LoggingSystem { TestLog4J2LoggingSystem(String contextName) { // Tests add resources to the thread context classloader - super(Thread.currentThread().getContextClassLoader(), new LoggerContext(contextName)); + super(Thread.currentThread().getContextClassLoader()); + getLoggerContext().setName(contextName); getLoggerContext().start(); } From 0dd7f6d78d525ea097a20c33e08ab6d3af82689e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Mar 2026 11:15:51 +0000 Subject: [PATCH 033/376] Polish "Allow Log4j2 logging system to be forced by system property" See gh-49343 --- .../boot/logging/log4j2/Log4J2LoggingSystem.java | 7 +++---- .../springframework/boot/logging/LoggingSystemTests.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index cb7a71ce3976..e093a4ce6a36 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -121,14 +121,13 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem { /** * Create a new {@link Log4J2LoggingSystem} instance. * @param classLoader the class loader to use. - * @throws IllegalArgumentException if the loggerContext instantiated internally is - * not of type org.apache.logging.log4j.core.LoggerContext + * @throws IllegalArgumentException if the logger context is not a + * {@link LoggerContext}. */ Log4J2LoggingSystem(ClassLoader classLoader) { super(classLoader); org.apache.logging.log4j.spi.LoggerContext spiLoggerContext = LogManager.getContext(classLoader, false); - Assert.isInstanceOf(LoggerContext.class, spiLoggerContext, - "Log4j2LoggingSystem requires LoggerContext to be of type org.apache.logging.log4j.core.LoggerContext"); + Assert.isInstanceOf(LoggerContext.class, spiLoggerContext); this.loggerContext = (LoggerContext) spiLoggerContext; } diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java index def94bc4c3ec..159ba949d3ea 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemTests.java @@ -72,7 +72,7 @@ void log4j2CanBeForcedUsingSystemProperty() { } @Test - void julj2CanBeForcedUsingSystemProperty() { + void julCanBeForcedUsingSystemProperty() { System.setProperty(LoggingSystem.SYSTEM_PROPERTY, JavaLoggingSystem.class.getName()); assertThat(LoggingSystem.get(getClass().getClassLoader())).isInstanceOf(JavaLoggingSystem.class); } From 2820a77aaf82ea86598e6cc9063d507b95a92c83 Mon Sep 17 00:00:00 2001 From: Chandan Veerabhadrappa Date: Tue, 3 Mar 2026 12:50:56 +0000 Subject: [PATCH 034/376] Fix typo and incorrect Javadoc references in HTTP client builders See gh-49364 Signed-off-by: Chandan Veerabhadrappa --- .../boot/http/client/JettyHttpClientBuilder.java | 12 ++++++------ .../boot/http/client/ReactorHttpClientBuilder.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java index 9fa4a64902a6..440711273c95 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java @@ -64,10 +64,10 @@ private JettyHttpClientBuilder(Consumer customizer, } /** - * Return a new {@link JettyClientHttpRequestFactoryBuilder} that applies additional + * Return a new {@link JettyHttpClientBuilder} that applies additional * customization to the underlying {@link HttpClient}. * @param customizer the customizer to apply - * @return a new {@link JettyClientHttpRequestFactoryBuilder} instance + * @return a new {@link JettyHttpClientBuilder} instance */ public JettyHttpClientBuilder withCustomizer(Consumer customizer) { Assert.notNull(customizer, "'customizer' must not be null"); @@ -76,10 +76,10 @@ public JettyHttpClientBuilder withCustomizer(Consumer customizer) { } /** - * Return a new {@link JettyClientHttpRequestFactoryBuilder} that applies additional + * Return a new {@link JettyHttpClientBuilder} that applies additional * customization to the underlying {@link HttpClientTransport}. * @param httpClientTransportCustomizer the customizer to apply - * @return a new {@link JettyClientHttpRequestFactoryBuilder} instance + * @return a new {@link JettyHttpClientBuilder} instance */ public JettyHttpClientBuilder withHttpClientTransportCustomizer( Consumer httpClientTransportCustomizer) { @@ -90,10 +90,10 @@ public JettyHttpClientBuilder withHttpClientTransportCustomizer( } /** - * Return a new {@link JettyClientHttpRequestFactoryBuilder} that applies additional + * Return a new {@link JettyHttpClientBuilder} that applies additional * customization to the underlying {@link ClientConnector}. * @param clientConnectorCustomizerCustomizer the customizer to apply - * @return a new {@link JettyClientHttpRequestFactoryBuilder} instance + * @return a new {@link JettyHttpClientBuilder} instance */ public JettyHttpClientBuilder withClientConnectorCustomizerCustomizer( Consumer clientConnectorCustomizerCustomizer) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java index 790488575327..3887161f97c7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/ReactorHttpClientBuilder.java @@ -36,7 +36,7 @@ import org.springframework.util.function.ThrowingConsumer; /** - * Builder that can be used to create a Rector Netty {@link HttpClient}. + * Builder that can be used to create a Reactor Netty {@link HttpClient}. * * @author Phillip Webb * @author Andy Wilkinson From ef0a667edc2e237926faf79380dd6f258562e64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 3 Mar 2026 14:10:41 +0100 Subject: [PATCH 035/376] Polish contribution See gh-49364 --- .../boot/http/client/JettyHttpClientBuilder.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java index 440711273c95..6469fac7da0f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/http/client/JettyHttpClientBuilder.java @@ -64,8 +64,8 @@ private JettyHttpClientBuilder(Consumer customizer, } /** - * Return a new {@link JettyHttpClientBuilder} that applies additional - * customization to the underlying {@link HttpClient}. + * Return a new {@link JettyHttpClientBuilder} that applies additional customization + * to the underlying {@link HttpClient}. * @param customizer the customizer to apply * @return a new {@link JettyHttpClientBuilder} instance */ @@ -76,8 +76,8 @@ public JettyHttpClientBuilder withCustomizer(Consumer customizer) { } /** - * Return a new {@link JettyHttpClientBuilder} that applies additional - * customization to the underlying {@link HttpClientTransport}. + * Return a new {@link JettyHttpClientBuilder} that applies additional customization + * to the underlying {@link HttpClientTransport}. * @param httpClientTransportCustomizer the customizer to apply * @return a new {@link JettyHttpClientBuilder} instance */ @@ -90,8 +90,8 @@ public JettyHttpClientBuilder withHttpClientTransportCustomizer( } /** - * Return a new {@link JettyHttpClientBuilder} that applies additional - * customization to the underlying {@link ClientConnector}. + * Return a new {@link JettyHttpClientBuilder} that applies additional customization + * to the underlying {@link ClientConnector}. * @param clientConnectorCustomizerCustomizer the customizer to apply * @return a new {@link JettyHttpClientBuilder} instance */ From e5dc27754ee6a0fe4528ee571fb8d1d932dea419 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Mar 2026 13:32:01 +0000 Subject: [PATCH 036/376] Upgrade to Jackson Bom 3.1.0 The Jackson team have ended support for Jackson 3.0.x. In response to this, this commit upgrades to Jackson 3.1.0. 3.1.x is designated as an LTS release so further Jackson 3.x upgrades should not be necessary in Spring Boot 4.0.x. Closes gh-49383 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c444b5fac523..aaf57c20d9c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ commonsCodecVersion=1.19.0 graalVersion=25 hamcrestVersion=3.0 jackson2Version=2.20.2 -jacksonVersion=3.0.4 +jacksonVersion=3.1.0 javaFormatVersion=0.0.47 junitJupiterVersion=6.0.3 kotlinVersion=2.2.21 From f797b62d47c9598f1082b632771d047831bcd8b9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Mar 2026 14:07:07 +0000 Subject: [PATCH 037/376] Upgrade to Jackson 2 Bom 2.21.1 Closes gh-49389 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index aaf57c20d9c4..3369964b0884 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ checkstyleToolVersion=10.12.4 commonsCodecVersion=1.19.0 graalVersion=25 hamcrestVersion=3.0 -jackson2Version=2.20.2 +jackson2Version=2.21.1 jacksonVersion=3.1.0 javaFormatVersion=0.0.47 junitJupiterVersion=6.0.3 From 018c215e54d61aee9a5336f41b621b865af90342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 4 Mar 2026 08:48:39 +0100 Subject: [PATCH 038/376] Add repository configuration section for SAML support Closes gh-49260 --- .../reference/pages/web/spring-security.adoc | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc index e1bfee2420b2..4c7147515d14 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc @@ -384,6 +384,44 @@ Additional information can be found in the {url-spring-authorization-server-docs +[[web.security.saml2.build]] +=== Build Configuration +SAML 2.0 support builds off of the OpenSAML library that https://shibboleth.atlassian.net/wiki/spaces/DEV/pages/1123844333/Use+of+Maven+Central#Publishing-to-Maven-Central[requires an extra repository] configuration. + + + +[[web.security.saml2.build.maven]] +==== Using Maven +With Maven, you need to add an extra `repository` element to your POM as follows: + +[source,xml,subs="verbatim,attributes"] +---- + + + shibboleth-releases + Shibboleth Releases Repository + https://build.shibboleth.net/maven/releases + + false + + + +---- + + +[[web.security.saml2.build.gradle]] +==== Using Gradle +With Gradle, a repository element should be added to your build script: + +[source,gradle,subs="verbatim,attributes"] +---- +repositories { + maven { url "https://build.shibboleth.net/maven/releases" } +} +---- + + + [[web.security.saml2.relying-party]] === Relying Party From 48cf7ebe6daa773b7734246bd014f89ed03012f7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Mar 2026 11:39:00 +0000 Subject: [PATCH 039/376] Speed up Maven plugin integration tests Closes gh-49396 --- .../build/RepositoryTransformersExtension.java | 16 ---------------- .../spring-boot-maven-plugin/build.gradle | 8 +------- .../springframework/boot/maven/MavenBuild.java | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/RepositoryTransformersExtension.java b/buildSrc/src/main/java/org/springframework/boot/build/RepositoryTransformersExtension.java index 74772a8ef443..3ec3ee08ab70 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/RepositoryTransformersExtension.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/RepositoryTransformersExtension.java @@ -43,8 +43,6 @@ public class RepositoryTransformersExtension { private static final String REPOSITORIES_MARKER = "{spring.mavenRepositories}"; - private static final String PLUGIN_REPOSITORIES_MARKER = "{spring.mavenPluginRepositories}"; - private final Project project; @Inject @@ -80,20 +78,6 @@ private String transformAnt(String line) { return line; } - public Transformer mavenSettings() { - return this::transformMavenSettings; - } - - private String transformMavenSettings(String line) { - if (line.contains(REPOSITORIES_MARKER)) { - return transformMavenRepositories(line, false); - } - if (line.contains(PLUGIN_REPOSITORIES_MARKER)) { - return transformMavenRepositories(line, true); - } - return line; - } - private String transformMavenRepositories(String line, boolean pluginRepository) { return transform(line, (repository, indent) -> mavenRepositoryXml(indent, repository, pluginRepository)); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle index 331ec66ed76d..355c3b9d204e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle @@ -108,18 +108,12 @@ tasks.named("checkCompileClasspathForProhibitedDependencies") { permittedGroups = ["javax.inject"] } -tasks.register("copySettingsXml", Copy) { - from file("src/intTest/projects/settings.xml") - into layout.buildDirectory.dir("generated-resources/settings") - filter(springRepositoryTransformers.mavenSettings()) -} - sourceSets { main { output.dir(layout.buildDirectory.dir("generated/resources/xsd"), builtBy: "xsdResources") } intTest { - output.dir(layout.buildDirectory.dir("generated-resources"), builtBy: ["extractVersionProperties", "copySettingsXml"]) + output.dir(layout.buildDirectory.dir("generated-resources"), builtBy: ["extractVersionProperties"]) } dockerTest { output.dir(layout.buildDirectory.dir("generated-resources"), builtBy: "extractVersionProperties") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/MavenBuild.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/MavenBuild.java index d1baa2720422..559f919477ab 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/MavenBuild.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/MavenBuild.java @@ -160,7 +160,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } }); - String settingsXml = Files.readString(Paths.get("build", "generated-resources", "settings", "settings.xml")) + String settingsXml = Files.readString(Paths.get("src", "intTest", "projects", "settings.xml")) .replace("@localCentralUrl@", new File("build/test-maven-repository").toURI().toURL().toString()) .replace("@localRepositoryPath@", new File("build/local-maven-repository").getAbsolutePath()); Files.writeString(destination.resolve("settings.xml"), settingsXml, StandardOpenOption.CREATE_NEW); From e5325874fbcb6e5ac85f6aefdb2f55f82e3569ab Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Mar 2026 12:47:49 +0000 Subject: [PATCH 040/376] Upgrade to Gradle 9.4.0 Closes gh-49399 --- gradle/wrapper/gradle-wrapper.jar | Bin 46175 -> 48966 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a659d17295f1de7c53e24fdf13ad755c379..d997cfc60f4cff0e7451d19d49a82fa986695d07 100644 GIT binary patch delta 39855 zcmXVXQ+TCK*K{%yXUE#HZQHhO+vc9wwrx+$i8*m5wl%T&&+~r&Ngv!-pWN44UDd0q zdi&(t$mh2PCnV6y+L8_uoB`iaN$a}!Vy7BP$w_57W_S6jHBPo!x>*~H3E@!NHJR5n zxF3}>CVFmQ;Faa4z^^SqupNL0u)AhC`5XDvqE|eW zxDYB9iI_{E3$_gIvlD|{AHj^enK;3z&B%)#(R@Fow?F81U63)Bn1oKuO$0f29&ygL zJVL(^sX6+&1hl4Dgs%DC0U0Cgo0V#?m&-9$knN2@%cv6E$i_opz66&ZXFVUQSt_o% zAt3X+x+`1B(&?H=gM?$C(o3aNMEAX%6UbKAyfDlj{4scw@2;a}sZX%!SpcbPZzYl~ z>@NoDW1zM}tqD?2l4%jOLgJtT#~Iz^TnYGaUaW8s`irY13k|dLDknw)4hH6w+!%zP zoWo3z>|22WGFM$!KvPE74{rt7hs(l?Uk7m+SjozYJG7AZA~TYS$B-k(FqX51pZ2+x zWoDwrCVtHlUaQAS%?>?Zcs`@`M)*S6$a-E5SkXYjm`9L>8EtTzxP%`iXPCgUJhF)LmcO8N zeCq?6sCOM!>?In*g-Nf^!FLX_tD>tdP}Qu&LbWx+5!Z5l7?X!!hk3jRFlKDb!=Jb4 z7y6)re6Y!QE1a;yXoZC*S$_|pT`pA*(6Wwg%;_Q+d*jw;i=|e$DQU=EcB-K+hg9=O z{1{BQsH*V!6t5tw;`ONRF!yo~+cF4p}|xHPE&)@e@Lv4qTL%3}vh4G|Gb$6%Eu zF`@mf2gOj$jYquFnvFCfb9%(9@mOC4N7VWF#;_-4Hr`(ikV(L)V=*hH^P3I<8RXOBnd0%J)*S^v*+L=*srT zh$IKKg?&n5H(Rho@`U^AyL=sN%WY)ZC9U)pfGVfaJpz+_n0|qnri_sF-g>-w^_4A;{;3 z2zTOH6bxZt8k`rB(XAAo>wufzcNZRTJSseFF{MmVV&4XVmKoPC0qRQJG-r9i z#yqN9hrZoA&Zp?DMIJLUtN3A!LZ89wr@`lge7butX>Q;1Yyi18b3#kDs|o$Q-f=a? zS;F_#_D1zk={}uf4ziZ+zjshKO^HC9-@G@n%RhXcLA%&TP#874IHEe;@#u!C3X@nY zaHpT0mAZ-N7)vR8Z|0maGSnM=QxJ8gamH0hLc#sW`>p;KU>wz515s9BDjB0eaqI1( z-&+*wV~o4?ha@KJ;U1zi`2(eKXkxc`NMkKxnz>GSlA0~7IHQ4KQWUPKD<}r@FOC_{ zQIDL`U!eq4@;?!9qWmvk%A6XHbxRY5BPh%#HKP`2>-jhY*TfF#gwLOR~f=$-qCq2V;*bz#LtA+nS@}dcA9S9exiGl z^t`RA_OgVRSg5O!GyJTc)4w-v(m~t)U{2ti*am#Q9`)B^wNC!pE9&ktf6^Cgs(3X9 znK~S~S}nNMh1+T6K>hr}(e9VlKKdt<1`D@~mE;aSB-I=?S;M$lD9`O$<99XzLG2F4 zg8`M+SrA_Cb-Bfo#>)U*nB@lBkUE&<;vN{rnAmuX<|-}ae2*aJG4k@$v%Rc;IM}_v z)wgICOxg ze%Zi6xg$romfi!Wy}i| zT8L+Xa*7}ZVYkJGkOKG>+S57jEDu7AiCi}B5m-HgeIInYmDQX8g6_Liajf_Dx@k^H zg*_C0VY^d-Ta|p6or>0LP}E$ZB{BKT?Up&p1Y|j7746nM)xXv!Tbpbo+eiB_F>?By zkhP*}9ZfjtUYuZUHP^ z>k3^hW#o2WXM~+rrPq9-S8e7APJzY^smW%tJr+s9W{Vi(i`b0pOOfxG`?0-rvo|Fu z#?Do52Z*#pPec0jqtd!y(#T zT|aPAx4<9ST0a)9E5r8l8Y4V0L4;bA_y?{VLNbAme_|R39vQ}m8Ix2Ay0~v%g}07A z86rGJYvG6Be5-4ml(;u`uZMOHPvEiySJ7Jm+^Hu3@33Ko4X$4i= z`nC#q;)J6=<0x<*q_BM)Def2(Xf%!7=adUcN5IX)Yw?1f*V=O+4!h3b)2;N{b>uUxh6KU zFO)rh!~d~HK-z83C*6m5@*(L@qJC@#9TY`${f#|l=ZoRMp7&rBx+gM))6PcXsA0v! z5eQ5U2zyP2%erLHmg=vZbWV&{KE@|FET}xun4QZ+j8GfNg+mtsW-R6kjeuGyVnU=K zBiAQ(?wz7!cz3VX?;-Xic;#aO&xN z-%mu;`sXgYc3{cqb|L1|aGf5UQDzrp1yHOB(HMD^+cpK9SIuM4E5cl5UM~-mybU^`JdHZ6$#~n_V)iQ+PAHacfSa#|SN;k`n%p(7#uf)Q> zlHE8+)PczLFiHEnu~aXa{g_hI94R&V(ZF;Wxh%tFIgmzT8f&bA)>us* zNA*!XoNoV-UPx|T<+mz&aZktvj-_f#meX&88P?CcuJY<%Iz z9~lFd)ITw&2kg3C!vE$_NDd!s8Mn5lu-na9mcBg$=B^ioWX6p8iLP&hule^!6j67i0mYIxNfR>X!CfH?G;y9Tl5)Q+4#bAL!BH~e%- zPkNQrOZIc5s*qXJ;9&h7_s5AJYt*oo2A?tQ*WAM`iaFre%Av|~a>uh&Pzl}s%(oCEd$G1=Km=P=^Tf==pM>*RcAANEI6hw9Vl<3&v zSEdp|TFrt)z!kqdUdibz_*TSj9WEbzlm+6Oym9gQk~vz@*OmO2cWHk$mMEtd*b*r7 z)drx#>)3)0d`ZeHYcf+1exTAWv9*UhjwA1*)%MKl5*IH}epmne{i8njH@p|m(oyy( zD{I8)8qH_SnUA6WFkaH2e4`UtYtt5I_@a_w%%E(o8bb0;@{8i`s?+C zGTz{xBP2eyi~$TfW3N(-R|c))j)dk$yggJDLo-Ur;A@or+w#Fuaqk zx#9j&Vv2ob(sZQpA{>3KU?H*Hf87&w!P(9lj3uA8s_0vlDtUVyIOvgPV@#~%%rVt@ zw6BW$7zKDvf#*ftc& z`H~cLVIoq;Ffl<@kX=47^^aG^#9GFmQE6-w$GApb zd5u1D4@*oJ9mk=`1HaHs?x`)mSd1G??$5*?JEn_`4Ckr-e%Lv8 zcB#IIsb5(CF>u-E29hB(7#I%{7?_gmcZlQ@Vk=OvyPfz5I?DDe+*)JmOOPpev2s!5 zIK)0cqIa_;UB%ily_J+%A|T>dKT_6--1`pFwIsG;*K~n)&@9E%hVLui3^)JrM*gqf zFR%tc@a|xLfAk1%?bH-MF}=Myt7mhS#jC-nv-iRC{I#EKf*^9;PGLcO7a!YiedEhe zeMZothG#o&RMk==LcAw{a;bg2&b7K%WTk+4=gLh#9dDO`(_v0oYCTZ|BCdJ7i!ms{ zB=J|Hn`Nc3mWiQn{&&-{ws!}kD9Sim;8}pt^2HC`x{Ay?Roy54c-d-cnHg{7D5K9z zv@o)c)kswkaHTdvQly_s^g+sDyCjBAbP1%W229JAba?|uqOL*t$|KD^5g3dLKn=Xb z9IW_k?k*)kVn>2Rqj3QejshvLqXQ*1NVJuhKbcUhCA`nKZE_RACNfT&L* zI$YUQJO#8X!-yd3ATPe6yf7LIrHOsIX=b_STgI2a#J8f~@@ll&;%8Kx5|0McAwYlI zNs3D#p)W1q4pJN-#V@~&`C6yx!RKxhy`Cpk?OS$q4dS1IV;hOu-vH(l)%`YjbxgI-26N1|9c;#^ zv+fX)nq-IF#F{VG3bBNiglftne*B||U<63~qoRGb*J2JI7MaAxT6Pdd&(djcek2<= zsBapXlGbq_5`*;^l;cX+-Yulze+duS0ywRjUgkT)#(DTchjKp+>*L;RCt;mZ0$n-k z8u*%CMZ{sj|raK-MZ8XXWWlW)mEyE%K ztogoO4IMeUy1H89tZs(Vig2oUO8UKwC9>3rBxqq_g|@NvW(7NtqQTVfAn$BnHFI4O zZ}Lgk1PBRc%zl^=?B=SeX?x|xi9m0-pMZ}xi`&b{XcL+s=~>u6(+ldBR)}&hKUL9P zVzKOnJ?rBrkSm1gfFcFtn7^rsiJ5L4iyp}T`Y6l7WI}Urs8CuV<`%O12R%B%pvcko(+GnA~)yiUirPXJc=q1P_Rh-`zw_0r9tn*fwW6^V^o z)sML@p8m+~EowB=h?CjA+cr9xRfa$NmNxAalqixbE_s7ZUI!@;K82(r`=l&XyUwfq z!`lnA7>3ylx!48Wlgz>P-lb~w$b6a5+oec>)-d-M;nIHp7nFy0n24)&YO=>S0Z(Yp zO+c<;-(@g9FLsB2vu7RO!0A0{9UTU@frfuP7NgNzHlBvJ+!4@JygLpm{!|eyBtPp4 z3ymxmEb*`x(!{EU%z)C~WOHhb@J zfye(U_Ml~XTl7!d_W$<3ishk^C-c#ef)Ds^SywIDI{mDc9%P1WrBo{1tAiAHb$ zy&0#M4f-qfza8F84nQaWL~S&xNQzG|P>PQy{7o@?vfOk|$I}L{<>eEhVJ~=lJjGym zaWU54Hl1|b@B!8q_oTS?5{Gk{K&8em|M=<&KRlvg^r6cQJO zAu8~Z0eU3i>e=5qqP&$9=w_%xFYB^^LO7LLiRHA^|;S4F6ANMoL=;hZq->= zcSZ^2L)TMD99%?aFwzkZ2$=wMj1ihM{noHe=8-z}K}`R$`FI!B97|x@V}UbVRgO1y z5V37pra5X%7**FZt$6qSDskj3OMr8Dr{wqUpW?%Gj+WaI7IGC{QiQ_?6;BUws?iy9 zr?uCbV7fBv7#rQ!;fPu!Qv?;xMp~V;dS54b?$6MVY(Ljrd4$RVQ^uG=kJ!W`a>&%8 z{N;cW{8i2M^VZ4>D@LN0doB%ye<{pMpKn(ja8DnCG4Kjm?9foo%>}4B#jq zqVJ5aYS;aOeS$JPxW(!)UQWD%y-oS6x&B_=UC=)Wuf_ZRPE9$VPrx&G65;!18!SF# z8JNxYs%6L)e=H6SdCNvIkz)F0yeP*PMcXA6ZE&C~|S^US~Pw2fuW)yo8&XHYgy&QKWjlOsY|OFcq}iu28r z#83E>BRjZsGq~O-)*9))zhWJIa`hY?aJ)2j4|v$nY39=H+-39&s0#Ldiy?@So(>2a zR{k?D8-7N01QN4s>pMqB|38Z$v%);7COMHI81xK@5d)h9j70z{1BQk+E)CK`H@l`b z>1|^8B4&1w`%ov;oh^(Z^jTxcA;Af+EMfV9qa=RBm`SstuEtDq=!)Y%g~~VWxT;-_Q6;X z_oe!AJ3ptQr}_)qdK#%}cRtT*3%K zE>9)EnWh)2ol4C@>6=M89Wntx8XnICocs*JfbX5Y`^LX36EK&NUMp1dkspMN`wbHR&eKLgSS?2O;0?>XODKO444mdhRf z4lUz}Wk$%=Dbhd}WWZ;M!Aq@^tg~dG9u`#FVA5G+iaqaX55onBmg`B8VttXe%0v9! z)2!wlh{C+f#(~QiCyFPbH_hBa85E*3DNR0Nq6T>-KgacFeg|M7G1=f5z2nXf>GusU z{SEjTW2bp5OX~@XR;$;VDvN>Wd}vF{A6jjHT95|&jUMh6r5KbbNfCQ8!vAKi~a{NIp-4h91Q0|o|0oZLW$ z@Xsk_2kB~}X#zJ#At;Bm$P3so&9iJ^0~2Trkh_N?Qoq5XE=n}tGr3AhP_Q~%43ugR z>iJ*l2%MQ3`q@`Q>S)^Mzs(cQZO_d+TC`&XRcq6-9{XA5`}a2entZ>RVRQt~8TmFC zO{qBYMlf97!9ojQ-y+ns*xPg-u2Eyp<;}7#0nwDvj5)ySJL%4vWUf<}(xqs3X*BMC zuVa1ZGCpTAk!bSgk~{Z^&4rin?ifHAg~h^%oP_<2hA z^XcLK@xD}z84HB>%@hXfcUEb{c@_iEY=Nd!7E{wbQNxWsmz@^Fp@MXXZG>J|3pEG; z4I;ee&RgnGmN_mbgc(k3NH63T71RG0PflRE{`iTpJLKlGdx$2cs~ z#8YxgR93!?Pa_MMS#63_z!EY`1#~L?P>D>GPxrHj;_*!73POA4irGJjAPSLK24yNF zjbf$m>Y4l`Sij`np_S{rQk5Ir%`!%c77r8E&Anwc=~E{OCD7bp8)m~882=)R17(F6 zObD&-rkQTf<=k@Axu-{*1E#|&3#Jo+7?(=!T7Vwi##NR!xIJTeU{nR^c*UTl{I`83?m6Z#KF(`VcUkH02b)Y)4W%iXpCZe8&hQ%M_lTq3z3t~J&{mi=D-jX*b}n-W`RIpVQMDh z@!aALf&*Y#s!Ucb!7OQ(|JcqI!&O5v?qFBIfoQtNH(62KRLU$};@N$4wJCH+acP-o zZs3E@s(_cicL$IhaggsA{r;O`X6=&A)PucscLa{3d{<@}Ycbl*4MLX3Oh@q#PTRX? zK_mx>oFh4bh`WCU+K&<-t>f8i4K(g7XeJcjV2~LQp9bd_!fy&>438B;{iOHo=>fL8 zHUH)HOTFOnsSDZ$&-hPcTYIv>=V?%%BV|hoGD%R}-kh{wrM`o>N{)}Jl zdZ1P13p<^gUJY^wDb`)}x$+D9p?1SZ6qB5ZKSBI%SI zHb+Y1-B@PDFQ!I+*?GP@Hh|YfAn1Q4`~gZZo`_87mM9sM6AP&b z*s=0$xQNUsHdW%(JSmxvlMke+Y~=NLf7hFU4ew8I@JXm1Qjk zUp67_=$uQ-Q68@wg+JwRa}lRcv(lfLQ?$;9N_SKYSql6k7Gs-fEuPz}(5lhBn@@Yn zLw!L{&LdsFF=h*OoMv$#-8D&{?UE=Uz|4*kU**U7oC+NytdL1gI|*{M=COpy&=5## zLsvg;tf?Emq)D6lL*AsM1Yj4wA#2B0u%qpgk<*Ovv*T}?YKjXn1&mG=QH>h-CAo-c zge6B-8IRB1uSA(RlBe#`iGt?#I5=}2vb?*rqj(2???JkzS4&!ayf>Os!)x@a5jm;= z*k0(h(r(ELR|oD^azGYV)AC^pruZcBf<{iUv4YooTz)KM&)9zUT;w@P%wWH;2=4C- za4pwrs4_yDSf*iVv3my2=o!1&PwlI!zw^O@V`GI#6269RibKU8ImtT9$r2Gb2KjZ> zGm+LxJ8rVfO*3jTW(W6*`-ui~|w(Bq3D6>lIas>>v|P_BfK!>$rw&JI4Uk zbzAuareUX-UsUrAJrt%odUZL+jz0XeDn`YW21CxGW!{hMoQtEmmF?jP};#B*Pv*R!Z zxW%{;y$)-|J7&}p{gLIy8<6ij4$sJV-}~?hD=MsV*W@~!2_O4HUKhj9>r?>_2vkDz+5pwx|${|ob208d2 zxTyRewhZx#fEE{ZwmaPuL#?aM2QqLKX|i;i#? z%_<@1c$5G+c3(hEYS+BOe`J(aOWT^X0d8FrlZXz5sZNtX-2U}6qyQritVN{(o6MhbCh8Uo{X6V*; zCI+H%>Z8OjPDIkwlLI0f>t{!!{olryPV=7_|HvmpID}GqEU0Ul526k**RV*BhVHA- zC4rtOpUB?O#F+^?>VlXdTs=1DhNTD50kG@Twho=Ex9K};$f)HG_ zo;HdwX};3TWz{*5o71j>mBxT56XUMM$jp&oDKpG^54F4>cN_;a2sO5+9XR+CY+1T& zaf_o~I4A1QI;b!nLleQ|)=@Nqf4LeLBOP{%oHzK0Xg7%H6Gdu6u}n>QUUcdf4Z;gS z9%jHM9cg$^Fvi|W{3>*12;o8%9*|F}w48L4UEx-WmZD!wGRhxyuzveCXk%#j1YmVv zbbdBla;l8+#U4=Pr8y~RBi#xETz|&VQWvEmGdYf#y?aaAJs^|G@7;Xn5>#DX36ILjY`xqFFiDBSK!_ zSmrO)O?FnBtaWU<5)SF0%-@N95E(JkOS}-3HQw0_((7^3pcCz7Db#aH{Ztv}3c{F3 z9`wC};pA~_{8Nv%u8NQ)EV~Zn!|3B1S<9#=Hhz0=pi$PH6;ZSW1w{kSLFw~+8l1n2 z@c5=1c5B!zR?*TZWQ*zVSALXonhlVp=<@*W=WUf%JHU)yNGW5*(%xpj-C2&oI~JClY8V^7KfP>nN+>ti0V+ zaPvJbvYfidk?RUsBie4JyIZz@XzL!k#5pRJ&df8wTc)2yO!#{J`hK&*P+pUvdu3f{!mwdcnK{`y_r%EBVWa}+`47qTjA2|D3teK0ElsnzK2CN+rPqq z9%eLs7SjMK^wSB*F##!MXzvC!C!I7S?FT=JLUg*_2&Eyv8}F;-k6WnaW&a(w{92c; zyE2eo^_d!T>kPz~)8Bf*fAO2}lAtFTqw!Kr@q16OXJb`4uRAoS>1J_n0ViR;L{%XF z%LU-^5ZagUhsGmY9Eh)vIgC!<(4svy*7?;Zc31KO^g|VZa3FEXK{$-d)nwGxzBxrX$%|GWfsvxnAtX8#)L&Fe3H2f)4LMepvhiG7#&o?gx@u~Gf< zcvX1N6sW~u_p}wxi*Qw#pTc;8CqCKVAMRX6L#xWVjc zE4f~S`3&zbKj9!mk;{hL=Lg{@{cFlhaY50yE7rpZZ1CV2BlQG}W{`BgvclA_m2Gw` z47q{A??Iq$doUbf0|1h6f5EK&1^!+H<#!qQ_0I%_hJiw`vm${61Jn3F>M@f34;m4Z z73!El=F0sJ3qr{L>tyc9Bh7`S8~!%MotQ-k%F#51a0+TLQ4`)hd0gu?%W2DT704gR z0Y6+7VG!}Sua)~&X!iODEIhY-?=0Bf?v~rGzz}bgb{3|lvQNW_(rkn|VB@~C!#{pc zwG8F>Ip2ZM#78_L%R+|F%$?4l=Bfg(Y01C^%9Gx=5~P}EN*1rcjW6~hNghXAN?Z8# z(6k1G+RzJ&=OWLxkyW$FX6Y=McV-+ZhmJ=oGZvZL*~ba#+aal!6=!TF4ovQrD{fAS zERD$3@aH2GmE$02=lWoH^<3GH;k9AzXi7GY*VT-NpmkWgamq zxBv6<{lD_9mQ5b!{v$Su|I_+ukdTsT#4$jkF6L(D4sO=QcCHMjcE+x*>S~Z+|F(gF z#j0<*qN$^QZBm?4SpV=-q9Ig|ky?w_7>=eDz$iuQjt-g1)wsFylMJfBZiElIuG2d2_}13!Do&dKc9H z@wOaxB@rFfIS{MjMpl(p99dzbVVhOAl4VU+Z4sHgvB#r%mV=m{;-jL!cP7)LTq`L# z5oK^3X;qt4L(@`1;g`c`pd^FEkW|OsZEEOn!UKCID{~95?@*otOw&(QB)FyOx(|@N zT+gl+?wUo`OI&&P1K+)yj4SgIkoy$H5Bmy+697LVbv#u`;N zVAC|KaCIN>z47DhjXZc6Td%SI9Q=Og2O%mV)K2IOG*S@wvu-uhpzyj*7ii#bb(*yC zx-H<&@t~L7*@cl4ppH((zG)DH=rKXru1T>A6Kr;qRaY@|nz(Xc20aM2HJ~i`>SQ+> z`aO$XUHlkTfvLUz(8ZNe%I`GAZhM4R;C`P>G~V7~idPN$3_on4@na3Yzt~IhN509) zx-ZY%>^*ARzsM(>&J@#uI4GvD?R#*o$XEb?NTCH?-XsN>l&kg>xh93KfGRp59U0z&mBmzI?36&Oxw zhgbj?xh5uxdXCV|@^vhJIG}(NC=X4l>XE_G-i$jy5K}+YE&Pcey zExBLQ5&itH3SngF0tjFF17{oNLA?L)oDIED*(|}cvXhRFwu--aQQ@$~M*jHJrp1_6 zJXaB$O@u6ED?{{{Cgo$NK!~&pIN-USDZyTzWbwSVRp&paO*`w`5JQ79N7EnJEsuoc z!a`YO!j)3mFR)&L*>Na^Tog$;cUKmz!3JlIff}6f$zK2-2m<@aYUV}6>IoEeDZB=T z@5Lj_@QEByMx-N!&#h~)jVn=2kLdzs$NCF*OwdL_BVF>{`QBlHLES(CzZfwzLWuAz zF5Gf)G_3qR6|B7C`h?XW$t}4M=+m9sIJaaxmc5n85i9hDza1(%q%kCv2TPS5C+fjP+^*LHjt|vjQfB z*`RBRAhu&aR&Sm*wC51(E+f8k3DX;Icg%rhQhy=^sFx<@tKp+uD7yVMyPcfqZL=*) z$ud6>OJc+2mN_l1lU2-1DFDvL1J%^*(l|3@!-NwJD|&~2FWVzqp+`IpKH(FE57CbF z!ih(S&?tM)UG}>9ai|%Yd^f4jQ$462$mG1%*7TL_bIS38lw3@edk9l6^@{m7bAdqL z=>u8`;U6-}zzQU<|C_1K{*Tyj#f?CJDpr*CgMnyhFkw+;@e6`?23hR(e)e2%~Xk=5DYaZ}`sSzP$cjump=ohVk3j-md$Fw8pYUx&XTr)Q-Ct z#P!!wMz&l9?QsE-*+Dw_cO;T83(`Kpuw7Ksm@kW8A91D_Hc7SIz)6DLbPKS)o=>kb93KaYu#6aDV#>|P)TfdSc2PB3 zEHV{eey)!ipL%}`r?S{n!vcF1i^fx<1zLQcSEIf>jFoj*RN5#&6Vbe+RJy44kzsgx zFr`n0k0Lh-Zlm4-4_*xi;}0$f_t&Ak=KZD?foPasbJIr^@y-{vFBQBTzq&++<+s!` z!Fxyl=L~vNDA#Y6XfE=3w)wFP8tGqUZyBR6L4La>^D|3)bS{C0w-yqOXI0NF&C{dv zTCU1F(_aYqoNgU4aCId&Y_b zqBo6j1L>*9xS<^&!#Ye6A&&i4p-5EId%sY3*qIJ-wng%gxK!1wnXE_y{dMa`$Zd zU8az`#zNr^UbR7_&BZ&5cLGjfo43l=J;R#j4mueY~^Wdyr9a#Vj4H>+79(ew9F^8y)U zfVzm9)Q|CBdB!bP zHJ+OvP6<^mr?H}ndMAbak1>lO5i+x?v=90Bg!f`^)8EKz!Q3^oo^mboGN1M{Up`j% zDZ!?VLwCEnJeO?^vGE-oU}sp;5Snc1fMwf+TnzDe+q6&qvd9E5nxJc?S(Es1^CrsQ zwM>`cBQEJ(g<4Ed9vw5#=8}2Ny{d;A?vd@ne-A$$E;=DX_zeU^Rd-k8D8+WXI0{8k zLeQhH*Y;M2byiVD_s^A?plT0C1F7qH>WnJh0`(ieJ9HHN#J}zrf=H$PY(0M6;Bgjr z^S+Q^JkE#g#gAaJ;{h3y@u5^mv6^wdBxveguBNt3mobrIkOD~S9M?&VGVFUPgjls} zSYvb+zhz6Nj14cNd^u9ME$#{vg~btue>p*5oQeZ#gkSWW_$Xf^cD;7#VKF#?DxrH} zan5G!6&Z`nQF2glWo}kpl0Mw{JR>EZ8N`-75lc~C=;5^dXQ1E)V9LOmjkD>23hwwQ z(`S|ZviG8@bBxHt3%;~HTNDDmcX#zJ*AdyJ7tfZjfZ$C%W*Z50eN-~wETOAW>s$pj zRHE_4P(fc3TpZ!5c*yA>mc3f5;8JR+xLFbFF;{dLg8s&wj!$**3A#O}!Fv<~-3$c- z!91soC^WUL0VI%6(*#h39lW89ZBe|+Fd-rgiMj(w8rti}_l%uJ`=84KSl?W`R^i|O z9$XyT_*WE$na}$;qhq<@^()6hkn}9j-fI9yqzGNlc?dUBvVjy?_i7G9A8|0K5XoYi z(v|4mWZd4#D%WDXN!b_Rl_V5a-C|9A^C4iWrH{w)AgAj^#IjXH#8MBYJElZG6^fgn zcW8+d=-zS5OHe$cjNtC9qm^Y#4Z9~JXeNK;VyUfi-IwW+DgV#LdXI;?_Ya&K3zrF` ziWC>Pmj!Nfq;d~u3SL9?0AcR(i@gncxM$Llx{ny0u6vk=@|TV`BqoYeXhzhhG{92t zBP~m*{QCxjK!B9{^d8w-g^V(4S4efF{;-dUE}M)mSUUA7cF9*z_o$rs12zjyikr`# z;@L1IM4akqoO0&f&=y&~gX4Vl;{P*$P%Wlf_crFD{pm0*x*B@47dR<6 zJBPr(1kY@pgXj4LCfUEVDw4o!jfCvt&~r(opbX#SaC4|wmYe5M&Q;D`F6;Kim7w9T z@9h!RVVskbO&yv(iPoHzOX(X6e#HebSGXF;XPL}+vaD~cp!*J3l-$>T z3x5R7DD_~Cmol0FNe7E1;1=o2p$1^s~UgDkj$b3M(I$)vBt?c-{$CbkmJ6+}fhH z20e!9LZ`g3GKESCpRA=CF#1JG3b}0cGccXem79Uw(8P)pRq+;Q#94Hh>XvQXe&mkq zSKWE`zfi4;D3Z@$aF_h9cjxTly`IoE;Oq&UktgUK{{RYDdxAJy6}v>!dFq`G^6+nV zEN;u9t1(*Mu^bX4dVdJXUFGF?Kv;%XGa(Ug*S$)nZNCeMeL?3(DzwK? zL{YY4+a;`y2&7)rkBF#wz<7a2{EuD^;G;oM{~l8b|6eFERf!R#3G0RX2jw%L)Ye>F z+KwBR3oB~ecrtAmMWmqvHF>awUc`(tqC|dqeho9xvuNi-AuPPk|5}*2W%+n*w5$1{rq+`IFX5 zjr#Uly#-xuhX5z?cvXj#&KXy^V{Mj>FT--yxy(SWm%tek;)~r60K|D|dVulS(vG`M_4MTb6oNSE0 z&xn#L9N)J;npM7ktR((G7o|VySCZR98h|^F0D-e|6Q1(L1(TU}#ZJ>~P;yg0JLl7C zPgQn;P9bD?>)OT6HSe&y#2jk? zZkP5h48Vt~e=1aBLjVEHkzbbxwEZ7YSFlN7*-YlRDBI%4W^@GL$85Q4X8?0CPkwa^ zEFt3i(*t=^qxStn>+|*?5tmLnRVaWey!I`J3Bh3WCBHdw{?{KRU!of z<+OqxfhtBS&gzwAsJ6@a^;Muj?+TZ~{Yfn+-K-!Zu;_$>ZFxo@tCh{`OrlLHt8pr18=;(PT3U#De8>reXFgWXplR$= z`!ZV5e<0Hj11xBB2W>mol9NI2wKUU*{Dd0fl&pP>!hkG2tENeuY13o~SI@?NT*Hbh z^;_i|Tqn>n6WS*OP}ZMUur4)Bs@?86Ug^gTcoi$#xML@YzJ}MBrP;+CVg$-yJ7KA# z@O5~-AFst5SZ38!YGN7)G){tiIn~u}=sHi&e}&XEq4v9OVIhAD{cUPj<z@DOvY;`Ik^O)sjO<;EKq-fo!0jnd$eemn(a%e-I}fTt4W@U74{b9 zLiPkh;F0njigJ_~G*VksoiVXibQ#8;d~RlZPY~=G%4sid(%o`q*~Y1}?P?|y=fy^_ zf4v*G`tdH@HqVRO1u6-r3=i2d1utcEe_nSY72Q<)pqlsMeL*&6?oghY0e$>6A=|kFrn}bD)O@(|tI=Hlr*-9D~z3 z?_yoeM0dDL+f6Mck;(Q?!6yhS-ldyae;AAE1$zI7Dt8i>OndEq5})$pPJCKm^$Xg; z&C<_GnS-VBH~oGJ?jlf&u5e4mVaB4!*s59<`?Qn~1@>o?x7m zNarmOc|qA!l;`BsSpu8kaf2a-$ zzT{p`rNsd}BGZ30t*GhE3ja?s>=@S5q!;$HayBpVaNJyv5wg0P_IQB zLtA=!wuXH8#w5`R5&4$1``g^mmY`#Koi5nl#rLWhxbG998#L9_%uo@cKNP4tX}h7| z$JDz)`oo8x2xLPO>uAVeZyi$ge^6Stv?N=OP;%Tk@?J|7Z-NkoLYti(Lgg9R658s# zhNPG!lPHuQKX$yuhoAAf;-e#gpUYD|hF>r`(gMRwU+oy+!!OxK6i?*ClL0*79`rZ# zx??xFzbo~S4qD08)~-?T2i_(O-9|mhhm|QoQeIZvRV#|Kbl{)xXFvXkf4>MUcfpW0 zqRBydZ`<@TE1znn+FhD?{1n~R+p}pm+t)>1Q`Q&PQS0CFbQS)Ff4Gg$h9O(NOvc-> zX+#=#vf2C>o{?~QR^Zf=S*+kVONr(XJ>w1d!iJq2rmY3fW6Y1|_+&!(gvRxKj1+Gg z+2Y63*<42J$Y%4lY(3nLe_vEgsvRfqz$H?J$1i4yO8($X`9tRfd8Td54$T@bcmYu* zi_9_MFCEWOwBEAhBg)V>nkJh85nw^+D3;QYCV8!)UOr!P+>T9E@DPIm0`i4dc3hEMSQws@r#U1^0HR$6V& ze`DFFPw*kLTVNy3^ z7G;2VcoemX&S9KVz|s+%F3{C9f<}Sca2`J*0{0`DNOX_jEP(>n#zt_SV6pXy?gN<9 z>`-KPha=4eT(slB*n{DNR4YUie_P-gLl6}TY8Ad;@f^Ymf1(Q7#%PPj<&xq*m|9g# zg88_(Xy6$%SQ@w@oY=K%80(vkpuPDBHjZL*qO)ljF9{z(*U}@16>!-h$iFIVL%b+` z3n}TAi$>9#kQxfOyi;@)u(P{>-4_4r9;3&QTbN z;8o#a*!MX~e`fQcoTV3QoH2+6&bSbD&bS!MoH2ycopB}3az@t$0f;e@^oT-UjeG?b zO^h=Ff@4$oFg6DFj^Nq~`nATPu6L+os2Rl#3CS78tB>N1@|+cpS}!V=Jc~J^ncsd? zU`IIfipbF_NgO+&zrD3%IwswSX@~ z_))+YV^UA6ClY*+d)!Z$bIqYTPwW6f)cKV}thiOHM?~aSV^4}!&w;VWBM-rIh$}7+ zesy;Ne_y{HYa_J2y;E+~75wHfzH=BqI0k?4M_dji_|sNTxT%h@yf^r`yK@0gM1sHS zbe1iaVv*g!U%PVdg02GyM-Jn+$8fQn4*s5#NAXw5x(oj-;NJxyiYuE(#Vmq9+%zn_ z1)=a9%?07(P!O{Zjfy#mS}|`}1n(P**vGioI4OUyAWm+RWf7^|Fh&i^r)HcK23T*w>`5(E)~;Cv!$ zC$;1WfSU+`TPb}PtHYyAiYEw{r-%sb$BaDR(T973m7 ze=KnD$a8l(ZTv{SqJq~@^I9*xoy9Y{wo9t@!&Z-s5?`5#bA z2M9B)4G&NY0012p002-+0|XQR2nYxOldU8Wl7SbK-C8YwyZu11qM-Q2s!$TP8>3=_ z!~~_lLk*<0CO$Q{yVLE`{mR|l8e-&!_%DnJ8cqBG{wU+LXpG{6FZa%znKN@{?)~=t z^H%^5uq^QI__$enV|1lGpwKZk47+En8Fm!Jo-b1`3e6yLh;cS-^+F=$g)XB*QVI8B zyjHzmt(guDjkh|4K%o_7%BCI9CxMknxt6P>h7 zFncJ6((+~KTKnBYvQrJy0t?&qovn7`MQ69UwcV(HciOFbv$MDVye?2~{ARS$k+R1E z`ljuBp_e`p$W>Nf3e5kV^fdE)hm?kr!1U%gw}f*j7BGYJ0{M)kRr{<>$Av#swT_aM z0u2`hiY}!GD&l$4BZ1}0StYAyp%O0PashLg=fuC zf-PY23uaz@#B90z2@5BbBX^v`X57gxG`dC>(eI9tz=t@WJx`*}v_t?~hLaxPYmE_wDvReU%yN z4Y^z{r7q-5>ZWdu#m+QN)lE*!Jz2s)+^jGtU6Fs@guV`PS)dIxlWnPLY?T>zTxJW* z7gs#%(|>=_TgxC+sLoiDD~%)a#+6J5@_}zLPv__JROK|tw+RRV(}$+_nr@6G0jG^G zlhR{uDS7tTw&au5uYCGbw`knawI2VDVOPN68V5`)x-z-T)}*@__65ZBLb~sGVRU@* z$Y320Vi-fPWda9d1rg^Rh<*T2O9u!+{qJ}90000ild*ywlLK8hf6ZEXd{ouF|NYJ^ zcXBg8NC+@2GD47SlL#te5HVp5BmoIahef=Zxk*N5iL(UaLe*-mt=nsDD{A|!wM}d7 zW^oct743rB+EriezP#>>-B+vTeb2dfl9^-z`rbc}Pr|+ToZs(ve%tvi=j2PTJ@y0< zoh#nUbobGtJ62t_f4IvC9WvwL#Z8Mt-HYoNhZ3>ANYqG267fJR5jHWNG^3`GGBMd} zqynK{Gju4GiKP}dbsN!?S--fiClE9G0uf2$ysni-c;)$kO|Ht}cW0te45WIEz;b+= z@t#QBG?S5d4@UdVWD09xd{x6a4XXlSvw!h59%3fFGm%M#f6R@MsL527NcJ@LB#m&? zY&@Ja`ufad<0kdF$NFkFB5{qJOl6lF{YGQdi1##Z>$=Fvox8brY2`h-PeadnMFBV~p%$w+#jaU#rWFL`O2 zPNg)R>5Nmue`-|5Gz|-_gR(4%nHEf1Vtf|F%c(-AnKX-O?o?13&1NbE*|tPT854@h z5sjPa#$7wwKxi)cbeco+n7sKj8ZBUQr4ze$v`#{61=<<3NT-G5FGOqAXfaa>*6f6j z#30739BRI{y;Ma@by`Aa!7AM_u7|1%tY*P!RLkTxf3L{E$CxUs+a{WIbHr{#1mQ~Bh1jaGuCbi(q; zF}(mpjsSZVT~JErQxmu;;$|9MnDYiT+>ub8w%+XCn8?J#8;)%+spg67)gJ3G7w^1O7y}KizBkf4A&z z_g9+@Jq`ZA`q+S+T@xGVH=-G{2HWA?SRrhtLdl4&pYmdE@Lsx0@_8&5wbkm)$)quW zh&=V!-e&R?QdRshMtvh zUxL5JjDao_D<#w0Y!5G*Jwg0A`if3Z(N~#7AmE{|GX+j7NOL#Xwd0XS-;^8R_3Hcu zot~%vf{cN{zDw5}sPoW^fA~ONLMfH<(sv{`b@W{%g;b_1WxID}b!*W${eAj@g#IC7 zZX#YF?cUcJ{7);YMKI5DSoX*C6REQQW?J#a@iqDxqM6OEv~qJ25}sZCI(RAM;urKw zoqkTg0=4S3sTy0KYZ_`j^c$!&5)Ye4wspg2puAQu{f=Iey86BJf92Mx)cHpV@+Y(; ziFmUe#+h1*dCnW<_Am5T$?e~eAQZQfS;gx=5WT997i1!bJFSnT0efgdl{kH z#t0mc2(RS20mV;q4%03xU(;z+rq0q(0@X+)p4w^-c+q5`e14Dx)0~N-v}7XDFfuQr zrQ(2x-8#EuY2%g^e^opT%%b8?L1wj=OIQa9E=BxEC#*>?PeTcVL9|KJQ5_&G=G5!u zGWsGk!!woEp~k)_iaak@DDyIUA9oa;WV%;HgH|uk<~gtu&xMSMct^sn3%oo}YWOLh zkKM26A&c5s z@nd{e2`}Ykx!$G_K;s&nYh{4tH6E^?B9KW3=LV^lMkey`a%ihBGqDP^Bju@U-CQ{3 zbNF28H0L3GS`y|LoP0jhlIp@%Vv53$W%BdG&-zi=7rJm29k_pyp`Q%l6R5u`04bR*?;=isa2O za9~qLF0B=)v0p^Rj7G+8>(#X;O&Us1#D`(!)oPH*dJq6@5B;E zmK9#!$-7G6iMz4cavR>uZ<4$H0S?M2nA#BQlZ)-ce=g%%MoZ#MMXtpDx)j?80|zH% zmpo|<34vB*QC@+7vZu$0s<1ZR>M-KOe2Y~-lD9vWiKZji$bPH9YVdHk&ZZ12i)^TH z!c6&POV?}kn|>ocV1WV>oy@W+JIh@#%x2i7Es;2sfu;^27_Q&2v3Xb9&V!qFG_P;l zaBx@We})|gH*ag-;N=(!SdMbsIw8qveu6yT zf8HS@elw%1SzJU4`|x0cIx9d*<99)18MK!b4L1{YXo>wEo$uuLVogg5rlQ9j_EPI? ze@P81yz?=>y9DUyaOM|5T8~~dnlQo|zpuEb7Ne>$nx5%#GkrLbJhU?sGZQj6Gt$`y z`2G^UkI~l50k8d#Vsg-{tDZvEVr>t9h(E0J`x$M|it1ugTW+$t2yUyTypKxs2g?YN zX-?FLb%l+p!h@x%vzcxyN_&FwRu?;de>w$Ar%?CmV#XiK0=vEZasGr(F8<^UH=_+( zJicxu-k&&RHnu5A+Re1lZG^zvfW{9aFvP|On4ZfI3^pDxdJ|zQGo`Amz*8jEO@%0r z0seQB){>{jt(iQ#&WJ`kBeLk^l8pJzI7%8hqQot=&sdnIJ^6O4}78;-~>u`6TsebXl#;qx>6tPC&ciMi3k&%xoN zMk?KEHAi0ls#P?84b#xoH&8L8e~fN(R}xA1j44ji$4EcVFUUZFW_DUS(cHPNwKZ4m zzo-tc`P;|=?d#9;@ON`3rDGQu?Pe-v^qA`-J*F&izi(w|Wt6zQ7+F4bhAvJ6{QQuA zr1KB>$4stWJ2wVac^Dn42V`3Y(lUz9E=F@-iM7-A)hxdjh1DYG1V=UjyWokv@ej zNR0`$#uS`zSYv4L=9x!Af6+`T(ywmYnnNL|u-%A5izso{Ch#Q)LgYm}`yu zn9g38$V9^`4uz5?Jj&mv4#fT89JIQ&kZQGJmq(y)^~8;MLKX+A)!pJ13&k0z2E`&5 z$$v9iE_M*V@MNz4hqiVgMI>UDA=D+3K%cpA>_#ipYsBMbG^Mn<&ic^AS-Ja`Ng!?D zM-$adB6-*&YIU(xf3|J9RF(zCbY^wljao7KP+mYZ09Bw9)zZlUNmNFYsqo}Hkd})T zx>zR8VOsrva6?VVc2%AJt&1j7<|XoAJvuPH`LVj1$X&yT^TjG%tP~d%^lUqOVYRR( zRwELmqNdp=H}@6^zD8W6iwnitT(e$yv7?D*K!)I%Ua^jzf0f?09$K(3*1cjQZP!JO z*d&YNNS8;nqA)Gu!7YhI8k^ndlQ~cwl%eLr#@VWiHW@WaqKE}jcKB~i;ZBMhF{zcb zOceVj+*^tcu}wPY_S`X$eGROfz75$&>Tid5$G^0Cv1}(#+wiv z$9na=8F^wpe`#-7Q{ZK<*r$u2*zYC7db?E0vaj&wdJ1f7Ghe2QPGKPXARoxhWf^Va z>99451w$e%Er-ojnUc5g@T?>00(R$BPraV#5xo*!CPrAS!9FO68ku;g*Gx88rHize zM;wwC0;U~dmY$~D%*C9Th)X>rJmj(N1g$!c>EhE|e^^=s@<}GmZh1RlSBjvW6e*ob zMY`bZun;6NM^1G+dY(D}MTa<6&C)za@f#WhSD#v`NZ zG);9|Wp|f3ZThz~@5pO9^E01)p)1~u;A{6$^2*C2u9JUFQRG}V?_g5A1zA_zz|`o6 zPhg?2fB&!%Ndrhlu;J!C?GU zMBD8ad*Pi;Qe!``(xI?F%0QD;Rg+87g;WX-1YRvot?TX9nA{ zw5+@)OO3~XK+v~Eleuy^Lx5>%2M`;Jsr$=aK(D^uN!L5$E z&hp*0!?bsZ_MO-&$7_e^vJ-?#g{D)G4$yq6qH0=8Lfk3;WQm-k_!Jtg(P#;=Mr%g_ ze`tL-6OED%Tsei;*+2lq0r74{O)?MH#e56ib@{gnmS~y}Lh3}$hidC`JcsbxUEW)M zd6wcsbVZiZ)=%3A^#}Lw?--&Z&PV8K*W*+d3_8k>b~?+i?aa~*<#mtH+jFD0VDvUQ zx+gbs2S(m0M}p;d0!2bl(Wwe;;gej?e?az;XIWmOe2=pB|#)Ba{s`xdJ}t z5Iy=RonUHm``nMx(@e+sS)WV3f0^k?kZ#hl^tEIB5uaB64P}a%BlJ9QCF-{ZN1wy^ zx3l!UW8?#x1_S=cryb1FPqXyvCfDHTLzw@qns1QvWoxqZhm{hr5}<#!Kr3C&f6LU{ zkFxZ4iF6o9|5QkRiR2sy^=a;Lu^MfVBrUv;@m3bFX*ZQfs1gNrqt7+MuAr~vU8Q7r)Al9|7*v6f38Z8^D-%FrANuy)4=Kn9G@(*z2Gqffw6 zR~N7=i4VSJOwE}Mu~wpFd4YUC$LEx6EgGQ*gB?TcFTW$pOOA7Omg`_Vmt||(B;RtD zc2{s9%V!5yYWEU!gU=ONUb$y*^m%+#YCgB4Qj>zXotH^7yAN8kk4Vq1f2-hCL%e#J zo10v6$zb51&o#vBv%IN-TeI9|t#FdO`1HAl`I0?8XR!Pz#=zH}uY!<1*(5X^zjWz8qN&fil9tAekd<1}nH{hp0)Lb%fs^e{2ub9_I(J)-ZqM z;1GYT-si4+j7Nw*l@~1QJ1h9{T(m?qQ!$Zmrv;;QKWSDBR6qS1-LKJ88hxJV6)khH?Jw;&wCc&%l9HmV~fPS6>8b!b?nTiI>`SqkvHE;b$pgB_jAtYM> zXP%1FQ7R?(*fd#_e{y(!-mpdwstM41l^P{?|D=UdCEPhm+oe8qnKLFKa3|5304&AO zt5jo6T+E{s%2zbsAX!y;=OUS3)VoSICuunn4T@a+zXVeaU^R+=Slf2K^A$}U}9Be<%Uk-L4?W%1%ER) zr~BMZ+8|9E3tn1%5LAZwTUq{2lc$2eH_Sg#8?}NFMt_;*-;VH02(r$V*lK^O^kB>U zwX7=3f46tx5dQ=FPp$4fXzj!%O-3xwaef(u5KL5>f7E@>rV`XDK8(B~N5maIS5ry7 zj0locy`*%UN5_cC$StXO=+qk3GF zjEGW13gCGHwe>?{dRADqRB)?IBWXLmND--9k`S}TurVEM&x$#B({d~9Osmg|d5ST= z3?ve_fA(O7Sdbs1WHjM+?id#SS>nuCg;;W-h%HhWDL{=Sf577U5z!WG9}?~Oz9iUwlFI6zaNb9H zy<sdh|b{tt$^5>6?@tdH5UdEG>653tN^=R!=k%3D=x1P(X8mhY$;-D z`I^oOaRr7mV-+dm>#99jadf;;ZFAHD?Akgz_Dy=fOWW|jY;wEX{ zf06=S*VfyL8pHDGu!)s7fcTDa&@q6LDF9T>Tp@0+9TM+6fdJn}{f>8tTWNr9QqNoI z9{J=K`G?{HB#W2$uj=_Szbc=CMTvTr2(PHYbGn$Rp0mXw^;{xq)U!owav-paP2v&- z-zj#>r-L1(>N(9(rk>@FD)n6ESSz1)e~S7k%^gMM?a}yz44!-+D)d~qmHFaj(qExj zEYnJH7!~kerMQQNRCbz<%rOO=0#PA(HKKPO5RHN0#lvnpiA*L%`A`z%X4yt4k_%+U z0+lt0^`ca!4|`&k%!hJ96H7I*43nCuapq<(M%0%W%kaAt#6-&|M#eB|au`d;e=t1A z7i7`0;bqG*f&TdNyJQPw@%4&g_FvQ~^Q(J|hy=F?&6K^4J(?#$9XT;QHX&`H1SA_s zDa$W$Cl1LjOWdl`UOz3Qc}RPUkoKy;@GEB2C497Ec3A?>vy?cIVk zh3f9`zjzOxUSb}GZ$8AI=7;_VP)i30OlKHPf*1e*?J|?0Bpj3Db1{Dld|PD||9?r_ zdz)sjmTt=!qm&K0u4%_$Wdsq$DA9Is~PQvmWHx*90ahvODJ7HTHo0|hxCL9~E zV;5wy$xMBu&q`$MruxDDaMBtKJHlgiZ>tq=J(jfTHO2FN*+ha1nE@+&6j3|X@1$%y z?WFp-y4_A^D2wZBnvZT?6OP;4>)&faDFnLQY&vFda1yq{VmE)?-_oD9;t9KDN7@=3 zw9_r^sf=eO5=)OVP^K_1?z?G1SjQq zYZcNB6ZM`7E2=jW%eSoK@^gZihw4g{qc(^Ds^n`y5W)OcD2Q2@Enf!*F$Z(y>ktKh zgPg0up#d1EQz)bB>A!;-mUm2!A*~CR8ew3m!mNJVJKKMfK<1-0w|KB@dnv-T1k7d26=Ka3!_<>wb0YzgH&80-0()iH=Zqs zB8#K2N~9f4Xv}4Z= z;zX>K-IIT)u9FciL9EL!ouV*@#;)tlxQVQ1pKW;qL9EYPcdEjo=~KeMX}pkDEM{kz zkt>;#{S7l_(3@E?!{Ma`*d~RBzH7(Z0yrIKC>;3~4;eU<+U5yQcawC$S(1>QID0~w z=(;H5*+~N%={Y;idtG}#?X#(+M_p|zNewn(b0vSea1QTypXDU7Y5Pq2!RlwqR8N&K z??6AT`mWT73h9 zbbo)JE5+OPVgm|?PMOxlQY6-LK10j7MV zogDNo>fi~+qUZ@tDQk4ZyYZd?-i7y)G{F@SPp8dmSiWU)&3GT)FY-RXOEPKCzz2(= z)U4N~)0UQL;6njiCPl<=#p9D=S*T!gC9i+LhlTD+CeTC$4SbZrbUd3eaG8PgCz#M) zSf_Fy!^f*|6|Sb0Z`?Os%DQ?+YDYf6i9iq**nb6tPyPUxenG>c<=mTc(;2z}U;4sVT zc)-Y@g+s=vJ7e}>{?6T*??3rcJeq&E<8H1sXY}PWaW9dy&4Rt1)uw*>wo|-JL3{`I z3zzTG8%3>7$@cZxX*<5rwsht82J7aVbeY7p#UDl z4;0EbZ`u%EW8y~&jpKwRJf`hxj|8wEKbDeq;8+rQcwNf%>iVQ|)$vXZ z)UlE==YPXXGexEsQ_a9{8L5obXKzlkkS=MMRO2Q`=^6Y!fZyTSNwY+;Xv{cEJSR8r zj|!^U#GmO7Iw|9(B2@A(()WLCuh5=?_^Y_**Z3P%b2H5;PB|w2&apvKF6~l(k2Um& zw=~R9@;~r$fPL_v#hRZlV{#+tzJDwDHg_H9h$VYG`5(MmiC6GniuT+NcL#e9Ulik_ zOR1+6{Xe`Oz=as2Av>H@+})8e72gOZ$7|1WQY`5Qms-&_V5Ph43$uTADyFN7@~bkQ zSLO6iuahbS(Nu=Q!tqmdi3~W!2~kx_Rt@kKW2!0^vSU}THq|T|FU{9VxhaSG>YJ

SdO`o?U^bCPz6Ehh!k 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


Xdyd*p!l1Y@nCA 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f78a6af837..dbc3ce4a040f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index adff685a0348..0262dcbd52b4 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From d5f088a61bc6f406b65a4e9d2b29e19fb646bfd4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Mar 2026 15:02:15 +0000 Subject: [PATCH 041/376] Allow more time for Docker Compose-managed containers to be healthy When running with Elasticsearch 9, the recently added test that checks connectivity using SSL is flaky on CI. When the test fails, it does so because Docker Compose reports that the container is unhealthy when running docker compose up. One possibility is that this is due to the container taking too long to reach a healthy state. This commit attempts to confirm that theory and hopefully address the problem by configuring a wait timeout of 120 seconds for the up command. See gh-49385 --- .../service/connection/test/DockerComposeTestExtension.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/spring-boot-docker-compose/src/testFixtures/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java b/core/spring-boot-docker-compose/src/testFixtures/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java index 52c83def0d83..6d97671042df 100644 --- a/core/spring-boot-docker-compose/src/testFixtures/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java +++ b/core/spring-boot-docker-compose/src/testFixtures/java/org/springframework/boot/docker/compose/service/connection/test/DockerComposeTestExtension.java @@ -121,6 +121,8 @@ private SpringApplication prepareApplication(Path composeFile) { properties.put("spring.docker.compose.skip.in-tests", "false"); properties.put("spring.docker.compose.file", composeFile); properties.put("spring.docker.compose.stop.command", "down"); + properties.put("spring.docker.compose.start.arguments[0]", "--wait-timeout"); + properties.put("spring.docker.compose.start.arguments[1]", "120"); application.setDefaultProperties(properties); return application; } From 10bafa4d64e662c7d42872de54ab0ca0d3f1294a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:04:24 +0100 Subject: [PATCH 042/376] Align test schema from changes in Spring Batch See gh-49402 --- .../autoconfigure/batch/custom-schema.sql | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/batch/custom-schema.sql b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/batch/custom-schema.sql index 2181b1132579..50b1d5d64870 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/batch/custom-schema.sql +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/batch/custom-schema.sql @@ -10,26 +10,22 @@ CREATE TABLE PREFIX_JOB_EXECUTION ( JOB_EXECUTION_ID BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, VERSION BIGINT, JOB_INSTANCE_ID BIGINT NOT NULL, - CREATE_TIME TIMESTAMP NOT NULL, - START_TIME TIMESTAMP DEFAULT NULL, - END_TIME TIMESTAMP DEFAULT NULL, + CREATE_TIME TIMESTAMP(9) NOT NULL, + START_TIME TIMESTAMP(9) DEFAULT NULL, + END_TIME TIMESTAMP(9) DEFAULT NULL, STATUS VARCHAR(10), EXIT_CODE VARCHAR(2500), EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED TIMESTAMP, - JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL, + LAST_UPDATED TIMESTAMP(9), constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) references PREFIX_JOB_INSTANCE(JOB_INSTANCE_ID) ) ; CREATE TABLE PREFIX_JOB_EXECUTION_PARAMS ( JOB_EXECUTION_ID BIGINT NOT NULL, - TYPE_CD VARCHAR(6) NOT NULL, - KEY_NAME VARCHAR(100) NOT NULL, - STRING_VAL VARCHAR(250), - DATE_VAL TIMESTAMP DEFAULT NULL, - LONG_VAL BIGINT, - DOUBLE_VAL DOUBLE PRECISION, + PARAMETER_NAME VARCHAR(100) NOT NULL, + PARAMETER_TYPE VARCHAR(100) NOT NULL, + PARAMETER_VALUE VARCHAR(2500), IDENTIFYING CHAR(1) NOT NULL, constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) references PREFIX_JOB_EXECUTION(JOB_EXECUTION_ID) @@ -40,8 +36,9 @@ CREATE TABLE PREFIX_STEP_EXECUTION ( VERSION BIGINT NOT NULL, STEP_NAME VARCHAR(100) NOT NULL, JOB_EXECUTION_ID BIGINT NOT NULL, - START_TIME TIMESTAMP NOT NULL, - END_TIME TIMESTAMP DEFAULT NULL, + CREATE_TIME TIMESTAMP(9) NOT NULL, + START_TIME TIMESTAMP(9) DEFAULT NULL, + END_TIME TIMESTAMP(9) DEFAULT NULL, STATUS VARCHAR(10), COMMIT_COUNT BIGINT, READ_COUNT BIGINT, @@ -53,7 +50,7 @@ CREATE TABLE PREFIX_STEP_EXECUTION ( ROLLBACK_COUNT BIGINT, EXIT_CODE VARCHAR(2500), EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED TIMESTAMP, + LAST_UPDATED TIMESTAMP(9), constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) references PREFIX_JOB_EXECUTION(JOB_EXECUTION_ID) ) ; @@ -74,12 +71,6 @@ CREATE TABLE PREFIX_JOB_EXECUTION_CONTEXT ( references PREFIX_JOB_EXECUTION(JOB_EXECUTION_ID) ) ; -CREATE TABLE PREFIX_STEP_EXECUTION_SEQ ( - ID BIGINT GENERATED BY DEFAULT AS IDENTITY -); -CREATE TABLE PREFIX_JOB_EXECUTION_SEQ ( - ID BIGINT GENERATED BY DEFAULT AS IDENTITY -); -CREATE TABLE PREFIX_JOB_SEQ ( - ID BIGINT GENERATED BY DEFAULT AS IDENTITY -); +CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ; +CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ; +CREATE SEQUENCE BATCH_JOB_SEQ; From bb65863891b40ef09aa76f1d807888511e4adad3 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 5 Mar 2026 10:45:02 +0000 Subject: [PATCH 043/376] Fail if using @DisabledIfDockerUnavailable without Testcontainers Closes gh-49425 --- .../container/DisabledIfDockerUnavailableCondition.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/DisabledIfDockerUnavailableCondition.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/DisabledIfDockerUnavailableCondition.java index c29318870da4..246fb46e9c75 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/DisabledIfDockerUnavailableCondition.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/DisabledIfDockerUnavailableCondition.java @@ -21,6 +21,9 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.testcontainers.DockerClientFactory; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + /** * An {@link ExecutionCondition} that disables execution if Docker is unavailable. * @@ -30,12 +33,17 @@ */ class DisabledIfDockerUnavailableCondition implements ExecutionCondition { + private static final boolean TESTCONTAINERS_PRESENT = ClassUtils.isPresent("org.testcontainers.DockerClientFactory", + DisabledIfDockerUnavailableCondition.class.getClassLoader()); + private static final String SILENCE_PROPERTY = "visibleassertions.silence"; private static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled("Docker available"); @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + Assert.isTrue(TESTCONTAINERS_PRESENT, "Testcontainers is required when using @DisabledIfDockerUnavailable." + + " Add a dependency on org.testcontainers:testcontainers"); String originalSilenceValue = System.getProperty(SILENCE_PROPERTY); try { DockerClientFactory.instance().client(); From 2870d943d008e38b38e277450a5108d4e2f460b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:37:45 +0100 Subject: [PATCH 044/376] Start building against Micrometer 1.16.4 snapshots See gh-49413 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 39e6169d61e9..75e2908bbad6 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1540,7 +1540,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.16.3") { + library("Micrometer", "1.16.4-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [ From daa2a3ed9e0dcb55d45a2c5cdd559394c8d752b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:37:49 +0100 Subject: [PATCH 045/376] Start building against Micrometer Tracing 1.6.4 snapshots See gh-49414 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 75e2908bbad6..ac04a9b08f75 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1563,7 +1563,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.6.3") { + library("Micrometer Tracing", "1.6.4-SNAPSHOT") { considerSnapshots() group("io.micrometer") { bom("micrometer-tracing-bom") From 09210994a673a5bb8739d0f4ed7bbf8f88bad924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:37:53 +0100 Subject: [PATCH 046/376] Start building against Reactor Bom 2025.0.4 snapshots See gh-49415 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index ac04a9b08f75..7c28d3e3b1c8 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1958,7 +1958,7 @@ bom { ] } } - library("Reactor Bom", "2025.0.3") { + library("Reactor Bom", "2025.0.4-SNAPSHOT") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From 0b5639be47610cbf77e0f93107fec4fb26c2fa0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:37:57 +0100 Subject: [PATCH 047/376] Start building against Spring Batch 6.0.3 snapshots See gh-49416 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 7c28d3e3b1c8..7553efa6c20d 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2391,7 +2391,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-amqp/releases/tag/v{version}") } } - library("Spring Batch", "6.0.2") { + library("Spring Batch", "6.0.3-SNAPSHOT") { considerSnapshots() group("org.springframework.batch") { bom("spring-batch-bom") From efb97cf7f65c744b85349d8ac30b42e1841b91f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:38:02 +0100 Subject: [PATCH 048/376] Start building against Spring Data Bom 2025.1.4 snapshots See gh-49417 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 7553efa6c20d..2407da22f15c 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2406,7 +2406,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2025.1.3") { + library("Spring Data Bom", "2025.1.4-SNAPSHOT") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 9e2fd4e896c1a617be10456c95d18b52d8fa052f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:31:43 +0100 Subject: [PATCH 049/376] Start building against Micrometer 1.15.10 snapshots See gh-49403 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 9c57ec19a53c..21a4f70ab305 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1589,7 +1589,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.15.9") { + library("Micrometer", "1.15.10-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [ From 3572794ef8a85a6e74da86b61cd060b35d6f6420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:31:47 +0100 Subject: [PATCH 050/376] Start building against Micrometer Tracing 1.5.10 snapshots See gh-49404 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 21a4f70ab305..db9417a6d048 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1611,7 +1611,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.5.9") { + library("Micrometer Tracing", "1.5.10-SNAPSHOT") { considerSnapshots() group("io.micrometer") { bom("micrometer-tracing-bom") From 3c3d3761d6eb67573bbec5b656a5b8c15c4171d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:31:52 +0100 Subject: [PATCH 051/376] Start building against Reactor Bom 2024.0.16 snapshots See gh-49405 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index db9417a6d048..a22ca997146a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2014,7 +2014,7 @@ bom { ] } } - library("Reactor Bom", "2024.0.15") { + library("Reactor Bom", "2024.0.16-SNAPSHOT") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From d30d84b67e1b8d64b6849b8a6b8411a00dc754be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:31:56 +0100 Subject: [PATCH 052/376] Start building against Spring Batch 5.2.5 snapshots See gh-49406 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index a22ca997146a..c6fad988d245 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2249,7 +2249,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-authorization-server/releases/tag/{version}") } } - library("Spring Batch", "5.2.4") { + library("Spring Batch", "5.2.5-SNAPSHOT") { considerSnapshots() group("org.springframework.batch") { bom("spring-batch-bom") From 64544fe71ce15349f7e556926229034e42439f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:32:00 +0100 Subject: [PATCH 053/376] Start building against Spring Data Bom 2025.0.10 snapshots See gh-49407 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index c6fad988d245..e784555e72dd 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2264,7 +2264,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2025.0.9") { + library("Spring Data Bom", "2025.0.10-SNAPSHOT") { prohibit { versionRange "[2025.1.0-M1,)" because "it exceeds our baseline" From 7c802e62d34240b4fa1523edc76568ed64e4b122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:32:04 +0100 Subject: [PATCH 054/376] Start building against Spring Framework 6.2.17 snapshots See gh-49408 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 840ee87cafa4..166d6b650e67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ mavenVersion=3.9.10 mockitoVersion=5.17.0 nativeBuildToolsVersion=0.10.6 snakeYamlVersion=2.4 -springFrameworkVersion=6.2.16 +springFrameworkVersion=6.2.17-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.52 From 99341101a7bcc8d1f3466d9dd5efc8735f644508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:32:08 +0100 Subject: [PATCH 055/376] Start building against Spring Kafka 3.3.14 snapshots See gh-49409 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index e784555e72dd..ee6503b6eb91 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2356,7 +2356,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.3.13") { + library("Spring Kafka", "3.3.14-SNAPSHOT") { considerSnapshots() group("org.springframework.kafka") { modules = [ From a5ba8ac37cab35da8e50bcb60c14fd8cedb56b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:32:13 +0100 Subject: [PATCH 056/376] Start building against Spring Pulsar 1.2.16 snapshots See gh-49410 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index ee6503b6eb91..b9b547ba845e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2394,7 +2394,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.2.15") { + library("Spring Pulsar", "1.2.16-SNAPSHOT") { considerSnapshots() group("org.springframework.pulsar") { bom("spring-pulsar-bom") From 90eb23a144dfdecc910240ba062a739ab6b92879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:38:10 +0100 Subject: [PATCH 057/376] Start building against Spring Kafka 4.0.4 snapshots See gh-49419 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 2407da22f15c..3bf5076f06a4 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2486,7 +2486,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "4.0.3") { + library("Spring Kafka", "4.0.4-SNAPSHOT") { considerSnapshots() group("org.springframework.kafka") { modules = [ From eda41a3088d3bfe9096d5dce190d6d27bc75edc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:38:14 +0100 Subject: [PATCH 058/376] Start building against Spring Pulsar 2.0.4 snapshots See gh-49420 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 3bf5076f06a4..dbe2180e6221 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2524,7 +2524,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "2.0.3") { + library("Spring Pulsar", "2.0.4-SNAPSHOT") { considerSnapshots() group("org.springframework.pulsar") { bom("spring-pulsar-bom") From d6ab2e77acc0bb2a80ddae4e9efc5720d1b96a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 10:10:41 +0100 Subject: [PATCH 059/376] Upgrade to Elasticsearch Client 9.2.6 Closes gh-49421 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index dbe2180e6221..68ba9a50cf25 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -355,7 +355,7 @@ bom { releaseNotes("https://github.com/ehcache/ehcache3/releases/tag/v{version}") } } - library("Elasticsearch Client", "9.2.5") { + library("Elasticsearch Client", "9.2.6") { prohibit { contains "-alpha" contains "-beta" From 3cb34f6344d10908dd60fbfd2e9611a5919fdd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 10:10:46 +0100 Subject: [PATCH 060/376] Upgrade to MongoDB 5.6.4 Closes gh-49422 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 68ba9a50cf25..5a2641274cc8 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1585,7 +1585,7 @@ bom { releaseNotes("https://github.com/mockito/mockito/releases/tag/v{version}") } } - library("MongoDB", "5.6.3") { + library("MongoDB", "5.6.4") { alignWith { version { of "org.mongodb:mongodb-driver-core" From 17b4d97e7b2ef15b11cd3f0ece6939a26d457db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 16:05:49 +0100 Subject: [PATCH 061/376] Harmonize tutorial with JDK, Maven and Gradle recent versions Closes gh-49411 --- .../pages/first-application/index.adoc | 103 +++++++----------- 1 file changed, 42 insertions(+), 61 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/tutorial/pages/first-application/index.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/tutorial/pages/first-application/index.adoc index d76fbf67d7f3..2a039f0355f5 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/tutorial/pages/first-application/index.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/tutorial/pages/first-application/index.adoc @@ -24,9 +24,9 @@ Before we begin, open a terminal and run the following commands to ensure that y [source,shell] ---- $ java -version -openjdk version "17.0.16" 2025-07-15 LTS -OpenJDK Runtime Environment (build 17.0.16+12-LTS) -OpenJDK 64-Bit Server VM (build 17.0.16+12-LTS, mixed mode, sharing) +openjdk version "17.0.18" 2026-01-20 LTS +OpenJDK Runtime Environment (build 17.0.18+10-LTS) +OpenJDK 64-Bit Server VM (build 17.0.18+10-LTS, mixed mode, sharing) ---- NOTE: This sample needs to be created in its own directory. @@ -42,9 +42,9 @@ If you want to use Maven, ensure that you have Maven installed: [source,shell] ---- $ mvn -v -Apache Maven 3.9.11 (3e54c93a704957b63ee3494413a2b544fd3d825b) +Apache Maven 3.9.12 (848fbb4bf2d427b72bdb2471c22fced7ebd9a7a1) Maven home: /Users/developer/.sdkman/candidates/maven/current -Java version: 17.0.16, vendor: BellSoft, runtime: /Users/developer/.sdkman/candidates/java/17.0.16-librca +Java version: 17.0.18, vendor: BellSoft, runtime: /Users/developer/.sdkman/candidates/java/17.0.18-librca ---- @@ -59,18 +59,18 @@ If you want to use Gradle, ensure that you have Gradle installed: $ gradle --version ------------------------------------------------------------ -Gradle 8.14.3 +Gradle 8.14.4 ------------------------------------------------------------ -Build time: 2025-07-04 13:15:44 UTC -Revision: e5ee1df3d88b8ca3a8074787a94f373e3090e1db +Build time: 2026-01-23 16:30:23 UTC +Revision: ad5ff774b4b0e9a8a0cf1a14ca70d7230003c3ad Kotlin: 2.0.21 -Groovy: 3.0.24 +Groovy: 3.0.25 Ant: Apache Ant(TM) version 1.10.15 compiled on August 25 2024 -Launcher JVM: 17.0.16 (BellSoft 17.0.16+12-LTS) -Daemon JVM: /Users/developer/.sdkman/candidates/java/17.0.16-librca (no JDK specified, using current Java home) -OS: Mac OS X 15.7.1 aarch64 +Launcher JVM: 17.0.18 (BellSoft 17.0.18+10-LTS) +Daemon JVM: /Users/developer/.sdkman/candidates/java/17.0.18-librca (no JDK specified, using current Java home) +OS: Mac OS X 26.3 aarch64 ---- @@ -88,57 +88,37 @@ Open your favorite text editor and add the following: 4.0.0 - - com.example - myproject - 0.0.1-SNAPSHOT - - + org.springframework.boot spring-boot-starter-parent {version-spring-boot} + com.example + myproject + 0.0.1-SNAPSHOT -ifeval::["{build-and-artifact-release-type}" == "opensource-milestone"] - - - - spring-milestones - https://repo.spring.io/milestone - - - - - spring-milestones - https://repo.spring.io/milestone - - -endif::[] ifeval::["{build-and-artifact-release-type}" == "opensource-snapshot"] - - - - spring-snapshots - https://repo.spring.io/snapshot - true - - - spring-milestones - https://repo.spring.io/milestone - - - - - spring-snapshots - https://repo.spring.io/snapshot - - - spring-milestones - https://repo.spring.io/milestone - - + + + + spring-snapshots + https://repo.spring.io/snapshot + + true + + + + + + spring-snapshots + https://repo.spring.io/snapshot + + true + + + endif::[] ---- @@ -175,21 +155,22 @@ Open your favorite text editor and add the following: plugins { id 'java' id 'org.springframework.boot' version '{version-spring-boot}' + id 'io.spring.dependency-management' version '{version-dependency-management-plugin}' } -apply plugin: 'io.spring.dependency-management' - group = 'com.example' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '17' + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories { mavenCentral() ifeval::["{artifact-release-type}" != "release"] - maven { url 'https://repo.spring.io/milestone' } + // you don't need this if you are using a release version maven { url 'https://repo.spring.io/snapshot' } endif::[] } @@ -516,7 +497,7 @@ $ java -jar target/myproject-0.0.1-SNAPSHOT.jar ....... . . . ....... . . . (log output here) ....... . . . -........ Started MyApplication in 0.999 seconds (process running for 1.253) +........ Started MyApplication in 0.497 seconds (process running for 0.651) ---- As before, to exit the application, press `ctrl-c`. @@ -561,7 +542,7 @@ $ java -jar build/libs/myproject-0.0.1-SNAPSHOT.jar ....... . . . ....... . . . (log output here) ....... . . . -........ Started MyApplication in 0.999 seconds (process running for 1.253) +........ Started MyApplication in 0.484 seconds (process running for 0.642) ---- As before, to exit the application, press `ctrl-c`. From 851a8f330df5ad29b435a8f93cea492cd7b68102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 16:24:03 +0100 Subject: [PATCH 062/376] Upgrade to Neo4j Java Driver 6.0.3 Closes gh-49431 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 5a2641274cc8..643125c27f59 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1656,7 +1656,7 @@ bom { ] } } - library("Neo4j Java Driver", "6.0.2") { + library("Neo4j Java Driver", "6.0.3") { alignWith { version { of "org.neo4j.driver:neo4j-java-driver" From 66554ec811bb88242cc9155eaeb79d949560e8d0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 5 Mar 2026 18:23:16 +0000 Subject: [PATCH 063/376] Make snapshot deps available to Maven Plugin integration tests See gh-49396 --- .../boot/build/MavenRepositoryPlugin.java | 39 +++++++++++++---- .../build/mavenplugin/MavenPluginPlugin.java | 43 ++++++++++--------- .../spring-boot-maven-plugin/build.gradle | 1 + 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java index aacc84476324..6c031b1c8bd1 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java @@ -23,8 +23,11 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.attributes.Category; import org.gradle.api.plugins.JavaLibraryPlugin; import org.gradle.api.plugins.JavaPlatformPlugin; import org.gradle.api.plugins.JavaPlugin; @@ -77,19 +80,20 @@ private void setUpProjectRepository(Project project, Task publishTask, File repo DependencySet target = projectRepository.getDependencies(); project.getPlugins() .withType(JavaPlugin.class) - .all((javaPlugin) -> addMavenRepositoryDependencies(project, JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, - target)); + .all((javaPlugin) -> addMavenRepositoryProjectDependencies(project, + JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, target)); project.getPlugins() .withType(JavaLibraryPlugin.class) - .all((javaLibraryPlugin) -> addMavenRepositoryDependencies(project, JavaPlugin.API_CONFIGURATION_NAME, - target)); - project.getPlugins() - .withType(JavaPlatformPlugin.class) - .all((javaPlugin) -> addMavenRepositoryDependencies(project, JavaPlatformPlugin.API_CONFIGURATION_NAME, - target)); + .all((javaLibraryPlugin) -> addMavenRepositoryProjectDependencies(project, + JavaPlugin.API_CONFIGURATION_NAME, target)); + project.getPlugins().withType(JavaPlatformPlugin.class).all((javaPlugin) -> { + addMavenRepositoryProjectDependencies(project, JavaPlatformPlugin.API_CONFIGURATION_NAME, target); + addMavenRepositoryPlatformDependencies(project, JavaPlatformPlugin.API_CONFIGURATION_NAME, target); + }); } - private void addMavenRepositoryDependencies(Project project, String sourceConfigurationName, DependencySet target) { + private void addMavenRepositoryProjectDependencies(Project project, String sourceConfigurationName, + DependencySet target) { project.getConfigurations() .getByName(sourceConfigurationName) .getDependencies() @@ -103,6 +107,23 @@ private void addMavenRepositoryDependencies(Project project, String sourceConfig }); } + private void addMavenRepositoryPlatformDependencies(Project project, String sourceConfigurationName, + DependencySet target) { + project.getConfigurations() + .getByName(sourceConfigurationName) + .getDependencies() + .withType(ModuleDependency.class) + .matching((dependency) -> { + Category category = dependency.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE); + return Category.REGULAR_PLATFORM.equals(category.getName()); + }) + .all((dependency) -> { + Dependency pom = project.getDependencies() + .create(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion()); + target.add(pom); + }); + } + private static final class CleanAction implements Action { private final File location; diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java index a5b1f4ae2554..79d9780ac13e 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java @@ -161,22 +161,25 @@ private void setPackaging(MavenPublication mavenPublication) { } private void addPopulateIntTestMavenRepositoryTask(Project project) { - Configuration runtimeClasspathWithMetadata = project.getConfigurations().create("runtimeClasspathWithMetadata"); - runtimeClasspathWithMetadata - .extendsFrom(project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)); - runtimeClasspathWithMetadata.attributes((attributes) -> attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, - project.getObjects().named(DocsType.class, "maven-repository"))); - TaskProvider runtimeClasspathMavenRepository = project.getTasks() - .register("runtimeClasspathMavenRepository", RuntimeClasspathMavenRepository.class, - (task) -> task.getOutputDir() - .set(project.getLayout().getBuildDirectory().dir("runtime-classpath-repository"))); + Configuration repositoryContents = project.getConfigurations().create("repositoryContents"); + repositoryContents.extendsFrom( + project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME), + project.getConfigurations().getByName("mavenRepository")); + repositoryContents.setCanBeConsumed(false); + TaskProvider populateMavenRepository = project.getTasks() + .register("populateResolvedDependenciesMavenRepository", ResolvedConfigurationMavenRepository.class, + (task) -> { + task.setConfiguration(repositoryContents); + task.getOutputDir() + .set(project.getLayout().getBuildDirectory().dir("resolved-dependencies-maven-repository")); + }); project.getDependencies() .components((components) -> components.all(MavenRepositoryComponentMetadataRule.class)); TaskProvider populateRepository = project.getTasks() .register("populateTestMavenRepository", Sync.class, (task) -> { task.setDestinationDir( project.getLayout().getBuildDirectory().dir("test-maven-repository").get().getAsFile()); - task.with(copyIntTestMavenRepositoryFiles(project, runtimeClasspathMavenRepository)); + task.with(copyIntTestMavenRepositoryFiles(project, populateMavenRepository)); task.dependsOn( project.getTasks().getByName(MavenRepositoryPlugin.PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME)); }); @@ -189,7 +192,7 @@ private void addPopulateIntTestMavenRepositoryTask(Project project) { } private CopySpec copyIntTestMavenRepositoryFiles(Project project, - TaskProvider runtimeClasspathMavenRepository) { + TaskProvider runtimeClasspathMavenRepository) { CopySpec copySpec = project.copySpec(); copySpec.from(project.getConfigurations().getByName(MavenRepositoryPlugin.MAVEN_REPOSITORY_CONFIGURATION_NAME)); copySpec.from(project.getLayout().getBuildDirectory().dir("maven-repository")); @@ -418,25 +421,25 @@ private void configureVariant(ComponentMetadataContext context, VariantMetadata } - public abstract static class RuntimeClasspathMavenRepository extends DefaultTask { + public abstract static class ResolvedConfigurationMavenRepository extends DefaultTask { - private final Configuration runtimeClasspath; - - public RuntimeClasspathMavenRepository() { - this.runtimeClasspath = getProject().getConfigurations().getByName("runtimeClasspathWithMetadata"); - } + private Configuration configuration; @OutputDirectory public abstract DirectoryProperty getOutputDir(); @Classpath - public Configuration getRuntimeClasspath() { - return this.runtimeClasspath; + public Configuration getConfiguration() { + return this.configuration; + } + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; } @TaskAction public void createRepository() { - for (ResolvedArtifactResult result : this.runtimeClasspath.getIncoming().getArtifacts()) { + for (ResolvedArtifactResult result : this.configuration.getIncoming().getArtifacts()) { if (result.getId().getComponentIdentifier() instanceof ModuleComponentIdentifier identifier) { String fileName = result.getFile() .getName() diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle index 355c3b9d204e..e31a3ca2ca75 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle @@ -95,6 +95,7 @@ dependencies { mavenRepository(project(path: ":spring-boot-project:spring-boot-devtools", configuration: "mavenRepository")) mavenRepository(project(path: ":spring-boot-project:spring-boot-docker-compose", configuration: "mavenRepository")) mavenRepository(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-parent", configuration: "mavenRepository")) + mavenRepository("org.springframework:spring-test") versionProperties(project(path: ":spring-boot-project:spring-boot-dependencies", configuration: "resolvedBom")) } From 3aa714572bd76b47c4e11ba109ccd908aee7238e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Mar 2026 07:47:59 +0000 Subject: [PATCH 064/376] Fix attribute matching when populating int test Maven repository See gh-49396 --- .../boot/build/mavenplugin/MavenPluginPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java index 79d9780ac13e..c54e5e112741 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java @@ -165,6 +165,8 @@ private void addPopulateIntTestMavenRepositoryTask(Project project) { repositoryContents.extendsFrom( project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME), project.getConfigurations().getByName("mavenRepository")); + repositoryContents.attributes((attributes) -> attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, + project.getObjects().named(DocsType.class, "maven-repository"))); repositoryContents.setCanBeConsumed(false); TaskProvider populateMavenRepository = project.getTasks() .register("populateResolvedDependenciesMavenRepository", ResolvedConfigurationMavenRepository.class, From 9c93ff4b78b4440a126935c4b28d64ce6d3f73b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 5 Mar 2026 09:38:06 +0100 Subject: [PATCH 065/376] Start building against Spring Framework 7.0.6 snapshots See gh-49418 --- .../logback/SpringBootJoranConfigurator.java | 6 ++-- ...backConfigurationAotContributionTests.java | 30 +++++++------------ .../advanced/customhints/MyRuntimeHints.java | 6 ++-- gradle.properties | 2 +- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java index e53d63235db1..ac1d5cc0bd3c 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java @@ -56,7 +56,7 @@ import org.springframework.aot.generate.GeneratedFiles.Kind; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.SerializationHints; +import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.TypeReference; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; @@ -187,8 +187,8 @@ private void writeTo(GenerationContext generationContext) { .handleFile(Kind.RESOURCE, MODEL_RESOURCE_LOCATION, new RequireNewOrMatchingContentFileHandler(serializedModel)); generationContext.getRuntimeHints().resources().registerPattern(MODEL_RESOURCE_LOCATION); - SerializationHints serializationHints = generationContext.getRuntimeHints().serialization(); - serializationTypes(this.model).forEach(serializationHints::registerType); + ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection(); + serializationTypes(this.model).forEach(reflectionHints::registerJavaSerialization); reflectionTypes(this.model).forEach((type) -> generationContext.getRuntimeHints() .reflection() .registerType(TypeReference.of(type), MemberCategory.INVOKE_PUBLIC_METHODS, diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java index 81b18c77131d..0e521989db0b 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackConfigurationAotContributionTests.java @@ -26,7 +26,6 @@ import java.util.Properties; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; @@ -50,10 +49,9 @@ import org.springframework.aot.generate.GeneratedFiles.Kind; import org.springframework.aot.generate.InMemoryGeneratedFiles; -import org.springframework.aot.hint.JavaSerializationHint; import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.SerializationHints; import org.springframework.aot.hint.TypeHint; import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; @@ -89,12 +87,11 @@ void contributionOfBasicModel() { InMemoryGeneratedFiles generatedFiles = generationContext.getGeneratedFiles(); assertThat(generatedFiles).has(resource("META-INF/spring/logback-model")); assertThat(generatedFiles).has(resource("META-INF/spring/logback-pattern-rules")); - SerializationHints serializationHints = generationContext.getRuntimeHints().serialization(); - assertThat(serializationHints.javaSerializationHints() - .map(JavaSerializationHint::getType) - .map(TypeReference::getName)) - .containsExactlyInAnyOrder(namesOf(Model.class, ArrayList.class, Boolean.class, Integer.class)); - assertThat(generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); + ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection(); + assertThat(reflectionHints.typeHints().map(TypeHint::getType)).containsExactlyInAnyOrderElementsOf( + TypeReference.listOf(Model.class, ArrayList.class, Boolean.class, Integer.class)); + assertThat(reflectionHints.typeHints().map(TypeHint::hasJavaSerialization).distinct()).singleElement() + .isEqualTo(true); InputStreamSource generatedFile = generatedFiles.getGeneratedFile(Kind.RESOURCE, "META-INF/spring/logback-pattern-rules"); assertThat(generatedFile).isNotNull(); @@ -111,12 +108,11 @@ void contributionOfBasicModelThatMatchesExistingModel() { InMemoryGeneratedFiles generatedFiles = generationContext.getGeneratedFiles(); assertThat(generatedFiles).has(resource("META-INF/spring/logback-model")); assertThat(generatedFiles).has(resource("META-INF/spring/logback-pattern-rules")); - SerializationHints serializationHints = generationContext.getRuntimeHints().serialization(); - assertThat(serializationHints.javaSerializationHints() - .map(JavaSerializationHint::getType) - .map(TypeReference::getName)) - .containsExactlyInAnyOrder(namesOf(Model.class, ArrayList.class, Boolean.class, Integer.class)); - assertThat(generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); + ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection(); + assertThat(reflectionHints.typeHints().map(TypeHint::getType)).containsExactlyInAnyOrderElementsOf( + TypeReference.listOf(Model.class, ArrayList.class, Boolean.class, Integer.class)); + assertThat(reflectionHints.typeHints().map(TypeHint::hasJavaSerialization).distinct()).singleElement() + .isEqualTo(true); InputStreamSource generatedFile = generatedFiles.getGeneratedFile(Kind.RESOURCE, "META-INF/spring/logback-pattern-rules"); assertThat(generatedFile).isNotNull(); @@ -330,10 +326,6 @@ private void applyContribution(Model model, Consumer contextCusto contribution.applyTo(generationContext, mock(BeanFactoryInitializationCode.class)); } - private String[] namesOf(Class... types) { - return Stream.of(types).map(Class::getName).toArray(String[]::new); - } - private void withSystemProperty(String name, String value, Runnable action) { System.setProperty(name, value); try { diff --git a/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/packaging/nativeimage/advanced/customhints/MyRuntimeHints.java b/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/packaging/nativeimage/advanced/customhints/MyRuntimeHints.java index 0e4ef3bb2682..b9b98a31ff21 100644 --- a/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/packaging/nativeimage/advanced/customhints/MyRuntimeHints.java +++ b/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/packaging/nativeimage/advanced/customhints/MyRuntimeHints.java @@ -31,12 +31,12 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) { Method method = ReflectionUtils.findMethod(MyClass.class, "sayHello", String.class); hints.reflection().registerMethod(method, ExecutableMode.INVOKE); + // Register type for java serialization + hints.reflection().registerJavaSerialization(MySerializableClass.class); + // Register resources hints.resources().registerPattern("my-resource.txt"); - // Register serialization - hints.serialization().registerType(MySerializableClass.class); - // Register proxy hints.proxies().registerJdkProxy(MyInterface.class); } diff --git a/gradle.properties b/gradle.properties index 3369964b0884..79d2102888ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ mockitoVersion=5.20.0 nativeBuildToolsVersion=0.11.4 nullabilityPluginVersion=0.0.11 snakeYamlVersion=2.5 -springFrameworkVersion=7.0.5 +springFrameworkVersion=7.0.6-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=11.0.18 From 04a4f415a9158c2cc00cd4d282ba330bd7937527 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Mar 2026 14:10:01 +0000 Subject: [PATCH 066/376] Avoid conflict caused by Micrometer Tracing's bom Micrometer Tracing's bom isn't a pure bill of materials for the Micrometer Tracing project as it also imports Micrometer's bom. As Boot itself also imports Micrometer's bom this can lead to conflicts when the versions do not all align. For example, at the time of writing, Boot is using 1.5.10-SNAPSHOT of Micrometer Tracing and trying to use 1.15.10-SNAPSHOT of Micrometer. This results in some conflicts as the Micrometer Tracing bom imports 1.15.9 of the Micrometer bom. Closes gh-49456 --- .../boot/build/bom/BomExtension.java | 28 ++++++++++++-- .../boot/build/bom/CheckBom.java | 12 ++++-- .../boot/build/bom/Library.java | 38 +++++++++++++++---- .../antora/AntoraAsciidocAttributesTests.java | 3 +- .../boot/build/bom/LibraryTests.java | 5 ++- .../spring-boot-dependencies/build.gradle | 17 ++++++++- 6 files changed, 83 insertions(+), 20 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java index d787decf1a09..3d4dc4b6d6c9 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.function.Predicate; import javax.inject.Inject; @@ -40,6 +41,7 @@ import org.springframework.boot.build.bom.BomExtension.LibraryHandler.AlignWithHandler.PropertyHandler; import org.springframework.boot.build.bom.BomExtension.LibraryHandler.AlignWithHandler.VersionHandler; +import org.springframework.boot.build.bom.Library.BomAlignment; import org.springframework.boot.build.bom.Library.DependencyVersionAlignment; import org.springframework.boot.build.bom.Library.Exclusion; import org.springframework.boot.build.bom.Library.Group; @@ -51,6 +53,7 @@ import org.springframework.boot.build.bom.Library.PomPropertyVersionAlignment; import org.springframework.boot.build.bom.Library.ProhibitedVersion; import org.springframework.boot.build.bom.Library.VersionAlignment; +import org.springframework.boot.build.bom.ResolvedBom.Id; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.boot.build.properties.BuildProperties; import org.springframework.util.PropertyPlaceholderHelper; @@ -112,8 +115,8 @@ public void library(String name, String version, Action action) LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version)); addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups, libraryHandler.upgradePolicy, libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, - versionAlignment(libraryHandler), libraryHandler.alignWith.dependencyManagementDeclaredIn, - libraryHandler.linkRootName, libraryHandler.links)); + versionAlignment(libraryHandler), libraryHandler.alignWith.bomAlignment, libraryHandler.linkRootName, + libraryHandler.links)); } private VersionAlignment versionAlignment(LibraryHandler libraryHandler) { @@ -404,7 +407,7 @@ public static class AlignWithHandler { private PropertyHandler property; - private String dependencyManagementDeclaredIn; + private BomAlignment bomAlignment; public void version(Action action) { this.version = new VersionHandler(); @@ -417,7 +420,14 @@ public void property(Action action) { } public void dependencyManagementDeclaredIn(String bomCoordinates) { - this.dependencyManagementDeclaredIn = bomCoordinates; + this.bomAlignment = new BomAlignment(bomCoordinates, (id) -> false); + } + + public void dependencyManagementDeclaredIn(String bomCoordinates, + Action action) { + DependencyManagementDeclaredInHandler handler = new DependencyManagementDeclaredInHandler(); + action.execute(handler); + this.bomAlignment = new BomAlignment(bomCoordinates, handler.exclusions); } public static class VersionHandler { @@ -464,6 +474,16 @@ public void managedBy(String managedBy) { } + public static class DependencyManagementDeclaredInHandler { + + private Predicate exclusions = (id) -> false; + + public void excluding(Predicate exclusion) { + this.exclusions = this.exclusions.or(exclusion); + } + + } + } } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java index b55ff0ae7f37..8abb6e5d9347 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java @@ -25,6 +25,7 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.stream.Collectors; import javax.inject.Inject; @@ -45,6 +46,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.VerificationException; +import org.springframework.boot.build.bom.Library.BomAlignment; import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.ImportedBom; import org.springframework.boot.build.bom.Library.Module; @@ -291,20 +293,22 @@ private CheckDependencyManagementAlignment(Provider resolvedBom, @Override public List check(Library library, ResolvedLibrary resolvedLibrary) { List errors = new ArrayList<>(); - String alignsWithBom = library.getAlignsWithBom(); + BomAlignment alignsWithBom = library.getAlignsWithBom(); if (alignsWithBom != null) { Bom mavenBom = this.bomResolver - .resolveMavenBom(alignsWithBom + ":" + library.getVersion().getVersion()); - checkDependencyManagementAlignment(resolvedLibrary, mavenBom, errors); + .resolveMavenBom(alignsWithBom.getCoordinates() + ":" + library.getVersion().getVersion()); + checkDependencyManagementAlignment(resolvedLibrary, mavenBom, errors, alignsWithBom::exclude); } return errors; } - private void checkDependencyManagementAlignment(ResolvedLibrary library, Bom mavenBom, List errors) { + private void checkDependencyManagementAlignment(ResolvedLibrary library, Bom mavenBom, List errors, + Predicate excluded) { List managedByLibrary = library.managedDependencies(); List managedByBom = managedDependenciesOf(mavenBom); List missing = new ArrayList<>(managedByBom); + missing.removeIf(excluded); missing.removeAll(managedByLibrary); List unexpected = new ArrayList<>(managedByLibrary); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java index fdd16c25460f..fe83fd9d0b2c 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java @@ -30,6 +30,7 @@ import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -48,6 +49,7 @@ import org.gradle.api.artifacts.result.ResolutionResult; import org.w3c.dom.Document; +import org.springframework.boot.build.bom.ResolvedBom.Id; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; /** @@ -76,7 +78,7 @@ public class Library { private final VersionAlignment versionAlignment; - private final String alignsWithBom; + private final BomAlignment bomAlignment; private final String linkRootName; @@ -95,15 +97,14 @@ public class Library { * @param prohibitedVersions version of the library that are prohibited * @param considerSnapshots whether to consider snapshots * @param versionAlignment version alignment, if any, for the library - * @param alignsWithBom the coordinates of the bom, if any, that this library should - * align with + * @param bomAlignment the bom, if any, that this library should align with * @param linkRootName the root name to use when generating link variable or * {@code null} to generate one based on the library {@code name} * @param links a list of HTTP links relevant to the library */ public Library(String name, String calendarName, LibraryVersion version, List groups, UpgradePolicy upgradePolicy, List prohibitedVersions, boolean considerSnapshots, - VersionAlignment versionAlignment, String alignsWithBom, String linkRootName, + VersionAlignment versionAlignment, BomAlignment bomAlignment, String linkRootName, Map> links) { this.name = name; this.calendarName = (calendarName != null) ? calendarName : name; @@ -115,7 +116,7 @@ public Library(String name, String calendarName, LibraryVersion version, List(links)) : Collections.emptyMap(); } @@ -164,8 +165,8 @@ public String getLinkRootName() { return this.linkRootName; } - public String getAlignsWithBom() { - return this.alignsWithBom; + public BomAlignment getAlignsWithBom() { + return this.bomAlignment; } public Map> getLinks() { @@ -193,7 +194,7 @@ public String getNameAndVersion() { public Library withVersion(LibraryVersion version) { return new Library(this.name, this.calendarName, version, this.groups, this.upgradePolicy, - this.prohibitedVersions, this.considerSnapshots, this.versionAlignment, this.alignsWithBom, + this.prohibitedVersions, this.considerSnapshots, this.versionAlignment, this.bomAlignment, this.linkRootName, this.links); } @@ -439,6 +440,27 @@ default Configuration alignmentConfiguration(Project project, Collection excluding; + + public BomAlignment(String bomCoordinates, Predicate excluding) { + this.coordinates = bomCoordinates; + this.excluding = excluding; + } + + public String getCoordinates() { + return this.coordinates; + } + + public boolean exclude(Id id) { + return this.excluding.test(id); + } + + } + /** * Version alignment for a library based on a dependency of another module. */ diff --git a/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java b/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java index 51b89c5b88f7..dff2df1c2110 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.build.bom.Library; +import org.springframework.boot.build.bom.Library.BomAlignment; import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.LibraryVersion; import org.springframework.boot.build.bom.Library.Link; @@ -235,7 +236,7 @@ private Library mockLibrary(Map> links) { List prohibitedVersion = Collections.emptyList(); boolean considerSnapshots = false; VersionAlignment versionAlignment = null; - String alignsWithBom = null; + BomAlignment alignsWithBom = null; String linkRootName = null; Library library = new Library(name, calendarName, version, groups, null, prohibitedVersion, considerSnapshots, versionAlignment, alignsWithBom, linkRootName, links); diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/LibraryTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/LibraryTests.java index 857501959923..4d35d610a764 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/bom/LibraryTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/LibraryTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; +import org.springframework.boot.build.bom.Library.BomAlignment; import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.LibraryVersion; import org.springframework.boot.build.bom.Library.Link; @@ -47,7 +48,7 @@ void getLinkRootNameWhenNoneSpecified() { List prohibitedVersion = Collections.emptyList(); boolean considerSnapshots = false; VersionAlignment versionAlignment = null; - String alignsWithBom = null; + BomAlignment alignsWithBom = null; String linkRootName = null; Map> links = Collections.emptyMap(); Library library = new Library(name, calendarName, version, groups, null, prohibitedVersion, considerSnapshots, @@ -64,7 +65,7 @@ void getLinkRootNameWhenSpecified() { List prohibitedVersion = Collections.emptyList(); boolean considerSnapshots = false; VersionAlignment versionAlignment = null; - String alignsWithBom = null; + BomAlignment alignsWithBom = null; String linkRootName = "spring-data"; Map> links = Collections.emptyMap(); Library library = new Library(name, calendarName, version, groups, null, prohibitedVersion, considerSnapshots, diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index b9b547ba845e..192bbbc3acf3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1613,8 +1613,23 @@ bom { } library("Micrometer Tracing", "1.5.10-SNAPSHOT") { considerSnapshots() + alignWith { + dependencyManagementDeclaredIn("io.micrometer:micrometer-tracing-bom") { + excluding { id -> + !id.artifactId().startsWith("micrometer-tracing") && !id.artifactId().equals("docs") + } + } + } group("io.micrometer") { - bom("micrometer-tracing-bom") + modules = [ + "docs", + "micrometer-tracing", + "micrometer-tracing-bridge-brave", + "micrometer-tracing-bridge-otel", + "micrometer-tracing-integration-test", + "micrometer-tracing-reporter-wavefront", + "micrometer-tracing-test" + ] } links { site("https://micrometer.io") From 7d21d7234d031dbee12e36af98f60a8ecc196996 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Mar 2026 15:10:05 +0000 Subject: [PATCH 067/376] Test Gradle plugin against 9.4.0 Closes gh-49467 --- .../testsupport/gradle/testkit/GradleVersions.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java index 9114f0b8e944..0d8d2dc75942 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java @@ -34,21 +34,21 @@ private GradleVersions() { public static List allCompatible() { if (isJavaVersion(JavaVersion.VERSION_25)) { - return Arrays.asList("9.0.0", "9.3.1"); + return Arrays.asList("9.0.0", "9.4.0"); } if (isJavaVersion(JavaVersion.VERSION_24)) { - return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.3.1"); + return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); } if (isJavaVersion(JavaVersion.VERSION_23)) { - return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.3.1"); + return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); } if (isJavaVersion(JavaVersion.VERSION_22)) { - return Arrays.asList("8.8", GradleVersion.current().getVersion(), "9.0.0", "9.3.1"); + return Arrays.asList("8.8", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); } if (isJavaVersion(JavaVersion.VERSION_21)) { - return Arrays.asList("8.5", GradleVersion.current().getVersion(), "9.0.0", "9.3.1"); + return Arrays.asList("8.5", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); } - return Arrays.asList("7.6.6", "8.4", GradleVersion.current().getVersion(), "9.0.0", "9.3.1"); + return Arrays.asList("7.6.6", "8.4", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); } public static String minimumCompatible() { From 94f029d5cc72ceac07a7875af8b6b9f526f2d826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:23 +0100 Subject: [PATCH 068/376] Upgrade to Hibernate 6.6.44.Final Closes gh-49457 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 192bbbc3acf3..eff91adabcd3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -557,7 +557,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "6.6.42.Final") { + library("Hibernate", "6.6.44.Final") { prohibit { versionRange "[7.0.0.Alpha1,)" because "it exceeds our Jakarta EE 10 baseline" From adfbb2da0dc0c0db4121db6e660c36298ca07287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:28 +0100 Subject: [PATCH 069/376] Upgrade to Jakarta XML WS 4.0.3 Closes gh-49458 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index eff91adabcd3..47f98b27c334 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -952,7 +952,7 @@ bom { releaseNotes("https://github.com/jakartaee/saaj-api/releases/tag/{version}") } } - library("Jakarta XML WS", "4.0.2") { + library("Jakarta XML WS", "4.0.3") { group("jakarta.xml.ws") { modules = [ "jakarta.xml.ws-api" From 41c7d09e7e178b5fb52edff5aeab16e21d8f615e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:32 +0100 Subject: [PATCH 070/376] Upgrade to Jetty 12.0.33 Closes gh-49459 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 47f98b27c334..f319bfe8c84e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1072,7 +1072,7 @@ bom { ] } } - library("Jetty", "12.0.32") { + library("Jetty", "12.0.33") { prohibit { contains ".alpha" because "we don't want alpha dependencies" From 928fb89d0a4b62356bb4f776e6943703a5706960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:36 +0100 Subject: [PATCH 071/376] Upgrade to Kafka 3.9.2 Closes gh-49460 --- spring-boot-project/spring-boot-autoconfigure/build.gradle | 4 +++- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index 307701024775..ac4f381747e6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -236,7 +236,9 @@ dependencies { optional("org.springframework.session:spring-session-hazelcast") optional("org.springframework.session:spring-session-jdbc") optional("org.springframework.amqp:spring-rabbit") - optional("org.springframework.amqp:spring-rabbit-stream") + optional("org.springframework.amqp:spring-rabbit-stream") { + exclude group: "org.lz4", module: "lz4-java" + } optional("org.springframework.kafka:spring-kafka") optional("org.springframework.ws:spring-ws-core") { exclude group: "com.sun.mail", module: "jakarta.mail" diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index f319bfe8c84e..12cf461f77a4 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1190,7 +1190,7 @@ bom { releaseNotes("https://junit.org/junit5/docs/{version}/release-notes") } } - library("Kafka", "3.9.1") { + library("Kafka", "3.9.2") { group("org.apache.kafka") { modules = [ "connect", From 9fd3e26044bf81af2b208db4d0ab6efadbd5e185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:40 +0100 Subject: [PATCH 072/376] Upgrade to Maven Failsafe Plugin 3.5.5 Closes gh-49461 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 12cf461f77a4..644f0dc403a7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1466,7 +1466,7 @@ bom { releaseNotes("https://github.com/apache/maven-enforcer/releases/tag/enforcer-{version}") } } - library("Maven Failsafe Plugin", "3.5.4") { + library("Maven Failsafe Plugin", "3.5.5") { group("org.apache.maven.plugins") { plugins = [ "maven-failsafe-plugin" From 6b1a7bd1ff025a2fbff0869356caeb48b559315c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:45 +0100 Subject: [PATCH 073/376] Upgrade to Maven Shade Plugin 3.6.2 Closes gh-49462 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 644f0dc403a7..46152f60d7b1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1545,7 +1545,7 @@ bom { releaseNotes("https://github.com/apache/maven-resources-plugin/releases/tag/maven-resources-plugin-{version}") } } - library("Maven Shade Plugin", "3.6.1") { + library("Maven Shade Plugin", "3.6.2") { group("org.apache.maven.plugins") { plugins = [ "maven-shade-plugin" From f0a53d44171a7e0e43bcb85f34e436ea209d7a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:49 +0100 Subject: [PATCH 074/376] Upgrade to Maven Surefire Plugin 3.5.5 Closes gh-49463 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 46152f60d7b1..883c6de93f46 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1569,7 +1569,7 @@ bom { releaseNotes("https://github.com/apache/maven-source-plugin/releases/tag/maven-source-plugin-{version}") } } - library("Maven Surefire Plugin", "3.5.4") { + library("Maven Surefire Plugin", "3.5.5") { group("org.apache.maven.plugins") { plugins = [ "maven-surefire-plugin" From 013389c9b309dccfeb9992d9575b86b547f6b332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 14:49:53 +0100 Subject: [PATCH 075/376] Upgrade to Pulsar 4.0.9 Closes gh-49464 --- .../spring-boot-autoconfigure/build.gradle | 12 +++--------- .../spring-boot-dependencies/build.gradle | 2 +- spring-boot-project/spring-boot-docs/build.gradle | 8 ++------ .../spring-boot-starter-pulsar-reactive/build.gradle | 4 +--- .../spring-boot-starter-pulsar/build.gradle | 4 +--- .../spring-boot-testcontainers/build.gradle | 4 +--- 6 files changed, 9 insertions(+), 25 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index ac4f381747e6..e863a9dc0083 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -53,9 +53,7 @@ dependencies { dockerTestImplementation("org.testcontainers:junit-jupiter") dockerTestImplementation("org.testcontainers:mongodb") dockerTestImplementation("org.testcontainers:neo4j") - dockerTestImplementation("org.testcontainers:pulsar") { - exclude group: "commons-logging", module: "commons-logging" - } + dockerTestImplementation("org.testcontainers:pulsar") dockerTestImplementation("org.testcontainers:testcontainers") optional("co.elastic.clients:elasticsearch-java") { @@ -209,12 +207,8 @@ dependencies { optional("org.springframework.data:spring-data-redis") optional("org.springframework.graphql:spring-graphql") optional("org.springframework.hateoas:spring-hateoas") - optional("org.springframework.pulsar:spring-pulsar") { - exclude group: "commons-logging", module: "commons-logging" - } - optional("org.springframework.pulsar:spring-pulsar-reactive") { - exclude group: "commons-logging", module: "commons-logging" - } + optional("org.springframework.pulsar:spring-pulsar") + optional("org.springframework.pulsar:spring-pulsar-reactive") optional("org.springframework.security:spring-security-acl") optional("org.springframework.security:spring-security-config") optional("org.springframework.security:spring-security-data") diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 883c6de93f46..13ded8f8cb1c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1864,7 +1864,7 @@ bom { releaseNotes("https://github.com/prometheus/client_java/releases/tag/parent-{version}") } } - library("Pulsar", "4.0.8") { + library("Pulsar", "4.0.9") { group("org.apache.pulsar") { bom("pulsar-bom") } diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 1be5d7dbdad1..fd0736c58379 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -157,12 +157,8 @@ dependencies { implementation("org.springframework.kafka:spring-kafka-test") { exclude group: "commons-logging", module: "commons-logging" } - implementation("org.springframework.pulsar:spring-pulsar") { - exclude group: "commons-logging", module: "commons-logging" - } - implementation("org.springframework.pulsar:spring-pulsar-reactive") { - exclude group: "commons-logging", module: "commons-logging" - } + implementation("org.springframework.pulsar:spring-pulsar") + implementation("org.springframework.pulsar:spring-pulsar-reactive") implementation("org.springframework.restdocs:spring-restdocs-mockmvc") implementation("org.springframework.restdocs:spring-restdocs-restassured") implementation("org.springframework.restdocs:spring-restdocs-webtestclient") diff --git a/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar-reactive/build.gradle b/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar-reactive/build.gradle index 0a71a42c112c..672670780c8f 100644 --- a/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar-reactive/build.gradle +++ b/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar-reactive/build.gradle @@ -22,9 +22,7 @@ description = "Starter for using Spring for Apache Pulsar Reactive" dependencies { api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter")) - api("org.springframework.pulsar:spring-pulsar-reactive") { - exclude group: "commons-logging", module: "commons-logging" - } + api("org.springframework.pulsar:spring-pulsar-reactive") } checkRuntimeClasspathForConflicts { diff --git a/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar/build.gradle b/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar/build.gradle index 69505deadb69..f9e8a8952764 100644 --- a/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar/build.gradle +++ b/spring-boot-project/spring-boot-starters/spring-boot-starter-pulsar/build.gradle @@ -22,9 +22,7 @@ description = "Starter for using Spring for Apache Pulsar" dependencies { api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter")) - api("org.springframework.pulsar:spring-pulsar") { - exclude group: "commons-logging", module: "commons-logging" - } + api("org.springframework.pulsar:spring-pulsar") } checkRuntimeClasspathForConflicts { diff --git a/spring-boot-project/spring-boot-testcontainers/build.gradle b/spring-boot-project/spring-boot-testcontainers/build.gradle index c7861bb46789..d249ffdc4187 100644 --- a/spring-boot-project/spring-boot-testcontainers/build.gradle +++ b/spring-boot-project/spring-boot-testcontainers/build.gradle @@ -65,9 +65,7 @@ dependencies { dockerTestImplementation("org.springframework.data:spring-data-redis") dockerTestImplementation("org.springframework.kafka:spring-kafka") dockerTestImplementation("org.springframework.ldap:spring-ldap-core") - dockerTestImplementation("org.springframework.pulsar:spring-pulsar") { - exclude group: "commons-logging", module: "commons-logging" - } + dockerTestImplementation("org.springframework.pulsar:spring-pulsar") dockerTestImplementation("org.testcontainers:junit-jupiter") dockerTestRuntimeOnly("com.oracle.database.r2dbc:oracle-r2dbc") From 1ea07eeb29e670274a3dd046a885eb8411afbe1e Mon Sep 17 00:00:00 2001 From: qnnn Date: Wed, 25 Feb 2026 20:57:47 +0800 Subject: [PATCH 076/376] Fix inconsistent ordering of config imports Update `ConfigDataEnvironment` so `spring.config.import` properties defined in environment or system properties are ordered correctly. See gh-49324 Signed-off-by: qnnn --- .../context/config/ConfigDataEnvironment.java | 14 +++++++++- .../ConfigDataEnvironmentContributor.java | 27 +++++++++++++++++++ ...ConfigDataEnvironmentContributorTests.java | 16 +++++++++++ ...ironmentPostProcessorIntegrationTests.java | 20 ++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index 77abf8d2d9d8..c7aeae88d427 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -17,6 +17,7 @@ package org.springframework.boot.context.config; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -57,6 +58,7 @@ * * @author Phillip Webb * @author Madhura Bhave + * @author Nan Chiu */ class ConfigDataEnvironment { @@ -197,7 +199,8 @@ ConfigDataEnvironmentContributors getContributors() { private List getInitialImportContributors(Binder binder) { List initialContributors = new ArrayList<>(); - addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); + addInitialImportPropertyContributors(initialContributors, + bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); addInitialImportContributors(initialContributors, bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS)); addInitialImportContributors(initialContributors, @@ -209,6 +212,15 @@ private ConfigDataLocation[] bindLocations(Binder binder, String propertyName, C return binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(other); } + private void addInitialImportPropertyContributors(List initialContributors, + ConfigDataLocation[] locations) { + List initialPropertiesContributors = new ArrayList<>(); + addInitialImportContributors(initialPropertiesContributors, locations); + initialContributors.add(ConfigDataEnvironmentContributor.ofInitialImportProperty( + initialPropertiesContributors, this.environment.getConversionService(), Arrays.asList(locations)) + ); + } + private void addInitialImportContributors(List initialContributors, ConfigDataLocation[] locations) { for (int i = locations.length - 1; i >= 0; i--) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java index 3c85a5b9ed9b..0e1af5e8ef71 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java @@ -52,6 +52,7 @@ * * @author Phillip Webb * @author Madhura Bhave + * @author Nan Chiu */ class ConfigDataEnvironmentContributor implements Iterable { @@ -404,6 +405,27 @@ static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initi null, null, conversionService); } + /** + * Factory method to create a {@link Kind#INITIAL_IMPORT_PROPERTY initial import property} + * contributor. This contributor is a container that wraps initial imports from + * {@code spring.config.import} property, allowing profile-specific imports to take + * precedence over the imported locations. + * @param contributors the contributors created from the import locations + * @param conversionService the conversion service to use + * @param locations the original import locations from the property + * @return a new {@link ConfigDataEnvironmentContributor} instance + */ + static ConfigDataEnvironmentContributor ofInitialImportProperty( + List contributors, + ConversionService conversionService, + List locations) { + Map> children = new LinkedHashMap<>(); + ConfigDataProperties properties = new ConfigDataProperties(locations, null); + children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors)); + return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT_PROPERTY, null, null, false, null, null, properties, null, children, + conversionService); + } + /** * Factory method to create a contributor that wraps an {@link Kind#EXISTING existing} * property source. The contributor provides access to existing properties, but @@ -477,6 +499,11 @@ enum Kind { */ INITIAL_IMPORT, + /** + * A container contributor that wraps initial imports from spring.config.import property. + */ + INITIAL_IMPORT_PROPERTY, + /** * An existing property source that contributes properties but no imports. */ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java index 998f7f285982..d940f86ba310 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java @@ -44,6 +44,7 @@ * @author Phillip Webb * @author Madhura Bhave * @author Scott Frederick + * @author Nan Chiu */ class ConfigDataEnvironmentContributorTests { @@ -298,6 +299,21 @@ void ofInitialImportCreatedInitialImportContributor() { assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).isEmpty(); } + @Test + void ofInitialImportPropertyCreatedInitialImportPropertyContributor() { + ConfigDataEnvironmentContributor innerContributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION, + this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImportProperty( + Collections.singletonList(innerContributor), this.conversionService, Arrays.asList(TEST_LOCATION)); + assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT_PROPERTY); + assertThat(contributor.getResource()).isNull(); + assertThat(contributor.getImports()).containsExactly(TEST_LOCATION); + assertThat(contributor.isActive(this.activationContext)).isTrue(); + assertThat(contributor.getPropertySource()).isNull(); + assertThat(contributor.getConfigurationPropertySource()).isNull(); + assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).containsExactly(innerContributor); + } + @Test void ofExistingCreatesExistingContributor() { MockPropertySource propertySource = new MockPropertySource(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java index 586f09080a11..2eabfbf72f66 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java @@ -78,6 +78,7 @@ * * @author Madhura Bhave * @author Phillip Webb + * @author Nan Chiu */ class ConfigDataEnvironmentPostProcessorIntegrationTests { @@ -330,6 +331,25 @@ void runWhenHasActiveProfilesFromMultipleAdditionalLocationsWithOneSwitchedOffLo assertThat(property).isEqualTo("frommyprofilepropertiesfile"); } + @Test + @WithResource(name = "testproperties-1.properties", content = """ + my.property=fromtestproperties-1.properties + """) + @WithResource(name = "testproperties-1-myprofile.properties", content = """ + my.property=fromtestproperties-1-myprofile.properties + """) + @WithResource(name = "testproperties-2.properties", content = """ + my.property=fromtestproperties-2.properties + """) + void runWhenHasImportPropertyWithProfileSpecificFileTakesPrecedence() { + ConfigurableApplicationContext context = this.application.run( + "--spring.config.import=classpath:testproperties-1.properties,classpath:testproperties-2.properties", + "--spring.profiles.active=myprofile"); + ConfigurableEnvironment environment = context.getEnvironment(); + String property = environment.getProperty("my.property"); + assertThat(property).isEqualTo("fromtestproperties-1-myprofile.properties"); + } + @Test void runWhenHasLocalFileLoadsWithLocalFileTakingPrecedenceOverClasspath() throws Exception { File localFile = new File(new File("."), "application.properties"); From b35a19c8876a9f99a449ec66b3d42a035ad03027 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 6 Mar 2026 17:46:43 +0100 Subject: [PATCH 077/376] Disable cloud features when platform is NONE Prior to this commit, applying the "spring.main.cloud-platform=none" property would apply this choice to the relevant auto-configuration conditions, but the `CloudPlatform` enum itself would still signal that cloud features like header forwarding is enabled. This commit ensures that "NONE" disables such features. Fixes gh-49478 --- .../java/org/springframework/boot/cloud/CloudPlatform.java | 4 ++++ .../org/springframework/boot/cloud/CloudPlatformTests.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java index eec32739851a..e63d67b6927f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java @@ -47,6 +47,10 @@ public boolean isDetected(Environment environment) { return false; } + @Override + public boolean isUsingForwardHeaders() { + return false; + } }, /** diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java index f794be42a122..a942fca55e1a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java @@ -243,6 +243,7 @@ void isEnforcedWhenBinderPropertyIsMissingReturnsFalse() { assertThat(CloudPlatform.KUBERNETES.isEnforced(binder)).isFalse(); } + @Test void isActiveWhenNoCloudPlatformIsEnforcedAndHasKubernetesServiceHostAndKubernetesServicePort() { Map envVars = new HashMap<>(); envVars.put("EXAMPLE_SERVICE_HOST", "---"); @@ -253,6 +254,11 @@ void isActiveWhenNoCloudPlatformIsEnforcedAndHasKubernetesServiceHostAndKubernet .containsExactly(CloudPlatform.NONE); } + @Test + void nonePlatformShouldDisableCloudPlatformFeatures() { + assertThat(CloudPlatform.NONE.isUsingForwardHeaders()).isFalse(); + } + private Environment getEnvironmentWithEnvVariables(Map environmentVariables) { MockEnvironment environment = new MockEnvironment(); PropertySource propertySource = new SystemEnvironmentPropertySource( From 900d4bc68bc0f1030272dfaa9f3217d2fd24e208 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Mar 2026 18:08:33 +0000 Subject: [PATCH 078/376] Make Elasticsearch SSL healthcheck less aggressive See gh-49210 --- .../connection/elasticsearch/elasticsearch-ssl-compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/elasticsearch-ssl-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/elasticsearch-ssl-compose.yaml index 38171106e72c..4decf017931a 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/elasticsearch-ssl-compose.yaml +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/elasticsearch/elasticsearch-ssl-compose.yaml @@ -20,6 +20,8 @@ services: -u elastic:secret https://localhost:9200/_cluster/health || exit 1"] interval: 5s retries: 5 + start_period: 60s + timeout: 10s labels: - 'org.springframework.boot.sslbundle.pem.keystore.certificate=client.crt' - 'org.springframework.boot.sslbundle.pem.keystore.private-key=client.key' From 646db448ae938161279783a2d5d0bcaf297e7389 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 6 Mar 2026 08:57:36 -0500 Subject: [PATCH 079/376] Polish 'Fix inconsistent ordering of config imports' See gh-49324 --- .../context/config/ConfigDataEnvironment.java | 26 +++++---------- .../ConfigDataEnvironmentContributor.java | 33 ++----------------- ...ConfigDataEnvironmentContributorTests.java | 27 ++++----------- ...onfigDataEnvironmentContributorsTests.java | 28 ++++++++-------- .../config/ConfigDataEnvironmentTests.java | 2 +- 5 files changed, 34 insertions(+), 82 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index c7aeae88d427..47d400302b1d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -17,7 +17,6 @@ package org.springframework.boot.context.config; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -199,8 +198,7 @@ ConfigDataEnvironmentContributors getContributors() { private List getInitialImportContributors(Binder binder) { List initialContributors = new ArrayList<>(); - addInitialImportPropertyContributors(initialContributors, - bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); + addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); addInitialImportContributors(initialContributors, bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS)); addInitialImportContributors(initialContributors, @@ -212,27 +210,21 @@ private ConfigDataLocation[] bindLocations(Binder binder, String propertyName, C return binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(other); } - private void addInitialImportPropertyContributors(List initialContributors, + private void addInitialImportContributors(List initialContributors, ConfigDataLocation[] locations) { - List initialPropertiesContributors = new ArrayList<>(); - addInitialImportContributors(initialPropertiesContributors, locations); - initialContributors.add(ConfigDataEnvironmentContributor.ofInitialImportProperty( - initialPropertiesContributors, this.environment.getConversionService(), Arrays.asList(locations)) - ); + addInitialImportContributors(initialContributors, List.of(locations)); } private void addInitialImportContributors(List initialContributors, - ConfigDataLocation[] locations) { - for (int i = locations.length - 1; i >= 0; i--) { - initialContributors.add(createInitialImportContributor(locations[i])); + List locations) { + if (!locations.isEmpty()) { + this.logger.trace(LogMessage.format("Adding initial config data import from locations %s", locations)); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImports(locations, + this.environment.getConversionService()); + initialContributors.add(contributor); } } - private ConfigDataEnvironmentContributor createInitialImportContributor(ConfigDataLocation location) { - this.logger.trace(LogMessage.format("Adding initial config data import from location '%s'", location)); - return ConfigDataEnvironmentContributor.ofInitialImport(location, this.environment.getConversionService()); - } - /** * Process all contributions and apply any newly imported property sources to the * {@link Environment}. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java index 0e1af5e8ef71..1b83d1610697 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java @@ -393,39 +393,17 @@ static ConfigDataEnvironmentContributor of(List initialImports, ConversionService conversionService) { - List imports = Collections.singletonList(initialImport); - ConfigDataProperties properties = new ConfigDataProperties(imports, null); + ConfigDataProperties properties = new ConfigDataProperties(initialImports, null); return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, false, null, null, properties, null, null, conversionService); } - /** - * Factory method to create a {@link Kind#INITIAL_IMPORT_PROPERTY initial import property} - * contributor. This contributor is a container that wraps initial imports from - * {@code spring.config.import} property, allowing profile-specific imports to take - * precedence over the imported locations. - * @param contributors the contributors created from the import locations - * @param conversionService the conversion service to use - * @param locations the original import locations from the property - * @return a new {@link ConfigDataEnvironmentContributor} instance - */ - static ConfigDataEnvironmentContributor ofInitialImportProperty( - List contributors, - ConversionService conversionService, - List locations) { - Map> children = new LinkedHashMap<>(); - ConfigDataProperties properties = new ConfigDataProperties(locations, null); - children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors)); - return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT_PROPERTY, null, null, false, null, null, properties, null, children, - conversionService); - } - /** * Factory method to create a contributor that wraps an {@link Kind#EXISTING existing} * property source. The contributor provides access to existing properties, but @@ -499,11 +477,6 @@ enum Kind { */ INITIAL_IMPORT, - /** - * A container contributor that wraps initial imports from spring.config.import property. - */ - INITIAL_IMPORT_PROPERTY, - /** * An existing property source that contributes properties but no imports. */ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java index d940f86ba310..34d454f20f04 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java @@ -50,6 +50,8 @@ class ConfigDataEnvironmentContributorTests { private static final ConfigDataLocation TEST_LOCATION = ConfigDataLocation.of("test"); + private static final List TEST_LOCATIONS = List.of(TEST_LOCATION); + private final ConfigDataActivationContext activationContext = new ConfigDataActivationContext( CloudPlatform.KUBERNETES, null); @@ -57,14 +59,14 @@ class ConfigDataEnvironmentContributorTests { @Test void getKindReturnsKind() { - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION, + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImports(TEST_LOCATIONS, this.conversionService); assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT); } @Test void isActiveWhenPropertiesIsNullReturnsTrue() { - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION, + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImports(TEST_LOCATIONS, this.conversionService); assertThat(contributor.isActive(null)).isTrue(); } @@ -287,33 +289,18 @@ void ofCreatesRootContributor() { } @Test - void ofInitialImportCreatedInitialImportContributor() { - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION, + void ofInitialImportsCreatedInitialImportContributor() { + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImports(TEST_LOCATIONS, this.conversionService); assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT); assertThat(contributor.getResource()).isNull(); - assertThat(contributor.getImports()).containsExactly(TEST_LOCATION); + assertThat(contributor.getImports()).isEqualTo(TEST_LOCATIONS); assertThat(contributor.isActive(this.activationContext)).isTrue(); assertThat(contributor.getPropertySource()).isNull(); assertThat(contributor.getConfigurationPropertySource()).isNull(); assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).isEmpty(); } - @Test - void ofInitialImportPropertyCreatedInitialImportPropertyContributor() { - ConfigDataEnvironmentContributor innerContributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION, - this.conversionService); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImportProperty( - Collections.singletonList(innerContributor), this.conversionService, Arrays.asList(TEST_LOCATION)); - assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT_PROPERTY); - assertThat(contributor.getResource()).isNull(); - assertThat(contributor.getImports()).containsExactly(TEST_LOCATION); - assertThat(contributor.isActive(this.activationContext)).isTrue(); - assertThat(contributor.getPropertySource()).isNull(); - assertThat(contributor.getConfigurationPropertySource()).isNull(); - assertThat(contributor.getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION)).containsExactly(innerContributor); - } - @Test void ofExistingCreatesExistingContributor() { MockPropertySource propertySource = new MockPropertySource(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java index 3dd1855a969d..dd4db331b555 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java @@ -91,8 +91,8 @@ binder, new DefaultResourceLoader(getClass().getClassLoader()), @Test void createCreatesWithInitialContributors() { - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, List.of(contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -123,8 +123,8 @@ void withProcessedImportsResolvesAndLoads() { new ConfigData(List.of(propertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) .willReturn(imported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, List.of(contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -155,8 +155,8 @@ void withProcessedImportsResolvesAndLoadsChainedImports() { new ConfigData(List.of(secondPropertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) .willReturn(secondImported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, List.of(contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -184,8 +184,8 @@ void withProcessedImportsProvidesLocationResolverContextWithAccessToBinder() { new ConfigData(List.of(propertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) .willReturn(imported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -215,8 +215,8 @@ void withProcessedImportsProvidesLocationResolverContextWithAccessToParent() { new ConfigData(List.of(secondPropertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) .willReturn(secondImported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, List.of(contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -243,8 +243,8 @@ void withProcessedImportsProvidesLocationResolverContextWithAccessToBootstrapReg new ConfigData(List.of(propertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) .willReturn(imported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); @@ -269,8 +269,8 @@ void withProcessedImportsProvidesLoaderContextWithAccessToBootstrapRegistry() { new ConfigData(List.of(propertySource))); given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) .willReturn(imported); - ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1, - this.conversionService); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor + .ofInitialImports(List.of(LOCATION_1), this.conversionService); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService, ConfigDataEnvironmentUpdateListener.NONE); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java index 0e23beec4a7b..bb0b3501f685 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java @@ -144,7 +144,7 @@ void createCreatesInitialImportContributorsInCorrectOrder() { .map(ConfigDataEnvironmentContributor::getImports) .map(Object::toString) .toArray(); - assertThat(imports).containsExactly("[i2]", "[i1]", "[a2]", "[a1]", "[l2]", "[l1]"); + assertThat(imports).containsExactly("[i1, i2]", "[a1, a2]", "[l1, l2]"); } @Test From 6b7953e6f80fc5f7e16cba65c8b4c20c0a29e572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:13 +0100 Subject: [PATCH 080/376] Upgrade to Hibernate 7.2.6.Final Closes gh-49468 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 1241abb8efa3..bf60c188d4b4 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -563,7 +563,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "7.2.4.Final") { + library("Hibernate", "7.2.6.Final") { group("org.hibernate.orm") { modules = [ "hibernate-agroal", From 3f4f3ccd2b461a95ce7869978ed38fe9225c9f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:17 +0100 Subject: [PATCH 081/376] Upgrade to Jakarta XML WS 4.0.3 Closes gh-49469 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index bf60c188d4b4..135de21806dd 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -926,7 +926,7 @@ bom { releaseNotes("https://github.com/jakartaee/saaj-api/releases/tag/{version}") } } - library("Jakarta XML WS", "4.0.2") { + library("Jakarta XML WS", "4.0.3") { group("jakarta.xml.ws") { modules = [ "jakarta.xml.ws-api" From 3c852d1d18f90268b8aa091b97374a7f5a24e045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:21 +0100 Subject: [PATCH 082/376] Upgrade to Jetty 12.1.7 Closes gh-49470 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 135de21806dd..1ba1cbd8b237 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1042,7 +1042,7 @@ bom { ] } } - library("Jetty", "12.1.6") { + library("Jetty", "12.1.7") { prohibit { contains ".alpha" because "we don't want alpha dependencies" From 7686bd1960c86517852c09b14429306c76888777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:25 +0100 Subject: [PATCH 083/376] Upgrade to Liquibase 5.0.2 Closes gh-49471 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 1ba1cbd8b237..908d2ee77f5e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1267,7 +1267,7 @@ bom { releaseNotes("https://github.com/redis/lettuce/releases/tag/{version}") } } - library("Liquibase", "5.0.1") { + library("Liquibase", "5.0.2") { group("org.liquibase") { modules = [ "liquibase-cdi", From 1785203653453c9f5331ff3db9bea380798bc97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:29 +0100 Subject: [PATCH 084/376] Upgrade to Maven Failsafe Plugin 3.5.5 Closes gh-49472 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 908d2ee77f5e..a2f62f620e4d 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1417,7 +1417,7 @@ bom { releaseNotes("https://github.com/apache/maven-enforcer/releases/tag/enforcer-{version}") } } - library("Maven Failsafe Plugin", "3.5.4") { + library("Maven Failsafe Plugin", "3.5.5") { group("org.apache.maven.plugins") { plugins = [ "maven-failsafe-plugin" From b988a52a91ce19efac6334e9b7bdedb79c17ff86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:33 +0100 Subject: [PATCH 085/376] Upgrade to Maven Shade Plugin 3.6.2 Closes gh-49473 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index a2f62f620e4d..4cc39d6d09d5 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1496,7 +1496,7 @@ bom { releaseNotes("https://github.com/apache/maven-resources-plugin/releases/tag/maven-resources-plugin-{version}") } } - library("Maven Shade Plugin", "3.6.1") { + library("Maven Shade Plugin", "3.6.2") { group("org.apache.maven.plugins") { plugins = [ "maven-shade-plugin" From d1714fd202e7efc3079b1a9a46351d24b37cfc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:37 +0100 Subject: [PATCH 086/376] Upgrade to Maven Surefire Plugin 3.5.5 Closes gh-49474 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 4cc39d6d09d5..349b3abd3ac5 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1520,7 +1520,7 @@ bom { releaseNotes("https://github.com/apache/maven-source-plugin/releases/tag/maven-source-plugin-{version}") } } - library("Maven Surefire Plugin", "3.5.4") { + library("Maven Surefire Plugin", "3.5.5") { group("org.apache.maven.plugins") { plugins = [ "maven-surefire-plugin" From e3e167bf9b9fca9c15ce9c29aea89dfbe3988859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:41 +0100 Subject: [PATCH 087/376] Upgrade to Native Build Tools Plugin 0.11.5 Closes gh-49475 --- ...rary-and-framework-list-schema-v1.0.0.json | 100 +++++++++++++++++ .../metadata-library-index-schema-v1.0.0.json | 106 ++++++++++++++++++ .../metadata-root-index-schema-v1.0.0.json | 67 +++++++++++ gradle.properties | 2 +- 4 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/library-and-framework-list-schema-v1.0.0.json create mode 100644 build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-library-index-schema-v1.0.0.json create mode 100644 build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-root-index-schema-v1.0.0.json diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/library-and-framework-list-schema-v1.0.0.json b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/library-and-framework-list-schema-v1.0.0.json new file mode 100644 index 000000000000..0c6861ec0f85 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/library-and-framework-list-schema-v1.0.0.json @@ -0,0 +1,100 @@ +{ + "$id": "https://raw.githubusercontent.com/oracle/graalvm-reachability-metadata/master/metadata/schemas/library-and-framework-support-schema-v1.0.0.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "default": [], + "examples": [ + { + "artifact": "io.netty:netty5-parent", + "details": [ + { + "minimum_version": "4.1", + "metadata_locations": [ + "https://github.com/netty/netty/tree/main/common/src/main/resources/META-INF/native-image" + ], + "tests_locations": [ + "https://github.com/netty/netty/actions" + ], + "test_level": "fully-tested" + } + ] + } + ], + "items": { + "properties": { + "artifact": { + "default": "", + "description": "The artifact name in the groupId:artifactId format", + "pattern": "^[a-zA-Z0-9._-]+:[a-zA-Z0-9._*-]+$", + "title": "The name of the artifact", + "type": "string" + }, + "description": { + "default": "", + "title": "Short description of the library or framework", + "type": "string" + }, + "details": { + "default": [], + "items": { + "default": {}, + "properties": { + "maximal_version": { + "default": "", + "description": "Maximum version for which this entry applies. If not defined, it is assumed to be supported for all versions starting from the minimum_version", + "title": "Maximal version for which this entry applies", + "type": "string" + }, + "metadata_locations": { + "default": [], + "items": { + "default": "", + "type": "string" + }, + "title": "Web URLs of provided metadata", + "type": "array" + }, + "minimum_version": { + "default": "", + "title": "Minimal version for which this entry applies", + "type": "string" + }, + "test_level": { + "default": "Untested", + "enum": [ + "untested", + "community-tested", + "fully-tested" + ], + "title": "Testing level of reachability metadata for the library or framework", + "type": "string" + }, + "tests_locations": { + "default": [], + "items": { + "default": "", + "type": "string" + }, + "title": "Web URLs to tests (sources, CI dashboards/configurations, ...)", + "type": "array" + } + }, + "required": [ + "minimum_version", + "metadata_locations", + "tests_locations", + "test_level" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "artifact", + "details" + ], + "type": "object" + }, + "title": "Schema for the 'library-and-framework-list.json'", + "type": "array" +} diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-library-index-schema-v1.0.0.json b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-library-index-schema-v1.0.0.json new file mode 100644 index 000000000000..2461661166eb --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-library-index-schema-v1.0.0.json @@ -0,0 +1,106 @@ +{ + "$id": "https://raw.githubusercontent.com/oracle/graalvm-reachability-metadata/master/metadata/schemas/metadata-library-index-schema-v1.0.0.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Schema for metadata///index.json. Each entry describes a metadata bundle for a range of library versions.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "module", + "metadata-version", + "tested-versions" + ], + "properties": { + "module": { + "$ref": "#/$defs/moduleCoordinate", + "description": "Maven coordinates in the form ':'." + }, + "metadata-version": { + "type": "string", + "minLength": 1, + "description": "Subdirectory name where the metadata files for this entry reside, e.g. '7.1.0.Final'." + }, + "tested-versions": { + "type": "array", + "description": "Explicitly tested upstream library versions that this metadata is known to support.", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "test-version": { + "type": "string", + "minLength": 1, + "description": "Overrides 'metadata-version' for use in test directories. This allows one metadata entry to share tests defined for a different version's metadata directory." + }, + "latest": { + "type": "boolean", + "description": "Marks this entry as the latest/default metadata for currently supported versions." + }, + "default-for": { + "type": "string", + "description": "Java regular expression describing the version range for which this entry should be used by default (e.g. '7\\\\.1\\\\..*')." + }, + "override": { + "type": "boolean", + "description": "When true, expresses the intent to exclude outdated built-in metadata shipped with Native Image for the matched versions." + }, + "skipped-versions": { + "type": "array", + "description": "Versions explicitly excluded from support, each with a reason.", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ "version", "reason" ], + "properties": { + "version": { + "type": "string", + "minLength": 1 + }, + "reason": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "$defs": { + "moduleCoordinate": { + "type": "string", + "pattern": "^[^:]+:[^:]+$" + } + }, + "examples": [ + [ + { + "metadata-version": "0.0.1", + "module": "org.example:library", + "tested-versions": [ "0.0.1", "0.0.2" ], + "default-for": "0\\.0\\..*", + "test-version": "0.0.1" + }, + { + "latest": true, + "metadata-version": "1.0.0", + "module": "org.example:library", + "tested-versions": [ "1.0.0", "1.1.0" , "1.2.0"] + }, + { + "metadata-version": "1.19.0", + "module": "io.opentelemetry:opentelemetry-sdk-trace", + "tested-versions": [ "1.19.0" ], + "override": true, + "skipped-versions": [ + { "version": "1.34.0", "reason": "Dependency io.opentelemetry:opentelemetry-api:1.34.0 provides reflect-config.json which does not parse." } + ] + } + ] + ] +} diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-root-index-schema-v1.0.0.json b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-root-index-schema-v1.0.0.json new file mode 100644 index 000000000000..7087aaa1d29c --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/reachability-metadata-repository/schemas/metadata-root-index-schema-v1.0.0.json @@ -0,0 +1,67 @@ +{ + "$id": "https://raw.githubusercontent.com/oracle/graalvm-reachability-metadata/master/metadata/schemas/metadata-root-index-schema-v1.0.0.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Schema for metadata/index.json. This file lists modules known to the repository and optionally the directory where their metadata is stored, the allowed package prefixes for metadata, and inter-module requirements. See docs/CONTRIBUTING.md (Metadata structure).", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "$ref": "#/$defs/moduleCoordinate", + "description": "Maven coordinates for the module in the form ':'." + }, + "directory": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\s].*$", + "description": "Repository-relative path under 'metadata/' containing this module's metadata (e.g. 'org.example/library'). If omitted, the entry may reference requirements only." + }, + "allowed-packages": { + "type": "array", + "description": "List of package (or fully-qualified name) prefixes considered valid sources of metadata entries for this module. Used to filter-in relevant JSON entries.", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "requires": { + "type": "array", + "description": "Optional list of module coordinates this module depends on. Each item is ':'.", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/$defs/moduleCoordinate" + } + } + } + }, + "$defs": { + "moduleCoordinate": { + "type": "string", + "pattern": "^[^:]+:[^:]+$" + } + }, + "examples": [ + [ + { + "directory": "org.example/library", + "module": "org.example:library" + }, + { + "allowed-packages": ["org.package.name"], + "module": "org.example:dependant-library", + "requires": ["org.example:library"] + }, + { + "allowed-packages" : [ "org.hibernate", "jakarta" ], + "directory" : "org.hibernate.orm/hibernate-envers", + "module" : "org.hibernate.orm:hibernate-envers", + "requires" : [ "org.hibernate.orm:hibernate-core" ] + } + ] + ] +} diff --git a/gradle.properties b/gradle.properties index 79d2102888ce..24ce10e33790 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ junitJupiterVersion=6.0.3 kotlinVersion=2.2.21 mavenVersion=3.9.10 mockitoVersion=5.20.0 -nativeBuildToolsVersion=0.11.4 +nativeBuildToolsVersion=0.11.5 nullabilityPluginVersion=0.0.11 snakeYamlVersion=2.5 springFrameworkVersion=7.0.6-SNAPSHOT From 1d206eb4487339e1727d8ebfb935a2dbc6b578da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 6 Mar 2026 16:29:46 +0100 Subject: [PATCH 088/376] Upgrade to Pulsar 4.1.3 Closes gh-49476 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 349b3abd3ac5..43b27122482e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1817,7 +1817,7 @@ bom { releaseNotes("https://github.com/prometheus/client_java/releases/tag/parent-{version}") } } - library("Pulsar", "4.1.2") { + library("Pulsar", "4.1.3") { group("org.apache.pulsar") { bom("pulsar-bom") } From 0df3bb76c8f52edc78143ab546610afece368349 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 9 Mar 2026 09:36:40 +0100 Subject: [PATCH 089/376] Upgrade to Maven 3.9.13 Closes gh-49455 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 166d6b650e67..f4f79e71638c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ jacksonVersion=2.19.4 javaFormatVersion=0.0.47 junitJupiterVersion=5.12.2 kotlinVersion=1.9.25 -mavenVersion=3.9.10 +mavenVersion=3.9.13 mockitoVersion=5.17.0 nativeBuildToolsVersion=0.10.6 snakeYamlVersion=2.4 From 7a6d92cbff421953901078a423717d345eaf7354 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 9 Mar 2026 09:41:11 +0100 Subject: [PATCH 090/376] Document security considerations for forwarded headers This commit highlights that while "forwarded headers" support is enabled automatically for cloud platforms, we generally assume that apps are behind trusted HTTP proxies. If this is not the case, app developers should disable this feature if they choose to expose the application to direct Internet traffic. Closes gh-49507 --- .../antora/modules/how-to/pages/deployment/cloud.adoc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/deployment/cloud.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/deployment/cloud.adoc index 605d2e9ffe6a..b6b093a5ecfc 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/deployment/cloud.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/deployment/cloud.adoc @@ -18,6 +18,15 @@ In this section, we look at what it takes to get the xref:tutorial:first-applica +[[howto.deployment.cloud.security]] +== Security considerations + +Support for xref:how-to:webserver.adoc#howto.webserver.use-behind-a-proxy-server[forwarded headers] should only be enabled when the application receives traffic from a trusted HTTP proxy, and is only directly accessible from a trusted network. +When running on a cloud platform, forward headers are enabled automatically. +This builds on the assumption that the platform will ensure that individual application instances will only be directly accessible from a trusted network. +If that is not the case for your specific platform, set `spring.forward-headers-strategy` to `none`. + + [[howto.deployment.cloud.cloud-foundry]] == Cloud Foundry From 2eb2f8594a9132447f0ec3c9ccb2f98cc5b493b4 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 9 Mar 2026 09:47:27 +0100 Subject: [PATCH 091/376] Use mavenVersion Gradle property in the spring-boot-maven-plugin tests Closes gh-49508 --- .../spring-boot-tools/spring-boot-maven-plugin/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle index e31a3ca2ca75..bc89024be804 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/build.gradle @@ -141,7 +141,8 @@ tasks.register("xsdResources", Sync) { } prepareMavenBinaries { - versions = [ "3.9.9", "3.6.3" ] + versions.add(providers.gradleProperty('mavenVersion')) + versions.add("3.6.3") } tasks.named("documentPluginGoals") { From 3e9632998e03d69dbaf67f0de330e488395697cb Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Mar 2026 08:53:07 +0000 Subject: [PATCH 092/376] Clarify required input state to trigger Quartz job Closes gh-49506 --- .../autoconfigure/quartz/QuartzEndpointDocumentationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointDocumentationTests.java index edc3cb529dbb..b1726ac4d1e6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointDocumentationTests.java @@ -402,7 +402,7 @@ void quartzTriggerJob() throws Exception { .uri("/actuator/quartz/jobs/samples/jobOne")) .hasStatusOk() .apply(document("quartz/trigger-job", preprocessRequest(), preprocessResponse(prettyPrint()), - requestFields(fieldWithPath("state").description("The desired state of the job.")), + requestFields(fieldWithPath("state").description("Desired state of the job. Must be `running`.")), responseFields(fieldWithPath("group").description("Name of the group."), fieldWithPath("name").description("Name of the job."), fieldWithPath("className").description("Fully qualified name of the job implementation."), From 29b82310d7a8b3bbc124c21e4ae8c09e819f6ed5 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sun, 8 Mar 2026 17:04:49 +0100 Subject: [PATCH 093/376] Fix typo in Javadoc for SpringBootTestAnnotation See gh-49505 --- .../boot/test/context/SpringBootTestAnnotation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java index 2e27af3bd62e..63f44501a81a 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java @@ -28,7 +28,7 @@ /** * {@link ContextCustomizer} to track attributes of - * {@link SpringBootTest @SptringBootTest} that are taken into account when evaluating a + * {@link SpringBootTest @SpringBootTest} that are taken into account when evaluating a * {@link MergedContextConfiguration} to determine if a context can be shared between * tests. * From 85b0e3bebf49a65add6994fd07e5b03af3f4b23f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Mar 2026 08:57:03 +0000 Subject: [PATCH 094/376] Polish "Fix typo in Javadoc for SpringBootTestAnnotation" See gh-49505 --- .../boot/test/context/SpringBootTestAnnotation.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java index 63f44501a81a..2aa5147dd6b4 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestAnnotation.java @@ -27,10 +27,9 @@ import org.springframework.test.context.TestContextAnnotationUtils; /** - * {@link ContextCustomizer} to track attributes of - * {@link SpringBootTest @SpringBootTest} that are taken into account when evaluating a - * {@link MergedContextConfiguration} to determine if a context can be shared between - * tests. + * {@link ContextCustomizer} to track attributes of {@link SpringBootTest @SpringBootTest} + * that are taken into account when evaluating a {@link MergedContextConfiguration} to + * determine if a context can be shared between tests. * * @author Phillip Webb * @author Madhura Bhave From eb439875266592d62c40ec45640014b69e2d965f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Mar 2026 10:15:30 +0000 Subject: [PATCH 095/376] Improve test assertions for bean container enablement Closes gh-49519 --- .../HibernateJpaAutoConfigurationTests.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index 204e632492db..5b15d3933e06 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -471,6 +471,21 @@ void hibernatePropertiesCustomizerTakesPrecedenceOverStrategyInstancesAndNamingS }); } + @Test + void beanContainerIsConfiguredByDefault() { + contextRunner().run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getJpaPropertyMap()) + .containsKey(ManagedBeanSettings.BEAN_CONTAINER)); + } + + @Test + void hibernatePropertiesCustomizerCanDisableBeanContainer() { + contextRunner().withUserConfiguration(DisableBeanContainerConfiguration.class) + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getJpaPropertyMap()) + .doesNotContainKey(ManagedBeanSettings.BEAN_CONTAINER)); + } + @Test @WithResource(name = "city.sql", content = "INSERT INTO CITY (ID, NAME, STATE, COUNTRY, MAP) values (2000, 'Washington', 'DC', 'US', 'Google')") @@ -487,12 +502,6 @@ void eventListenerCanBeRegisteredAsBeans() { }); } - @Test - void hibernatePropertiesCustomizerCanDisableBeanContainer() { - contextRunner().withUserConfiguration(DisableBeanContainerConfiguration.class) - .run((context) -> assertThat(context).doesNotHaveBean(City.class)); - } - @Test void vendorPropertiesWithEmbeddedDatabaseAndNoDdlProperty() { contextRunner().run(vendorProperties((vendorProperties) -> { From e487a6b4990099d63a1e8118a95964a2461954fc Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 9 Mar 2026 11:25:39 +0100 Subject: [PATCH 096/376] Improve EndpointRequest matcher documentation Prior to this commit, `EndpointRequest` exposed factory methods for creating security matchers in the context of Actuator endpoints. This is using the popular pattern matching approach for security matchers. Such matchers are not as focused as method-level security and will match the endpoint path itself (`"actuator/endpoint"`) as well as everything beneath it (`"actuator/endpoint/**"`). This commit improves the Javadoc and reference documentation to make this behavior more explicit. Closes gh-49520 --- .../reference/pages/actuator/endpoints.adoc | 3 +++ .../actuate/web/reactive/EndpointRequest.java | 19 ++++++++++--------- .../actuate/web/servlet/EndpointRequest.java | 18 +++++++++--------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc index 31f7874b26e4..c231b03da80f 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/endpoints.adoc @@ -248,6 +248,9 @@ The preceding example uses `EndpointRequest.toAnyEndpoint()` to match a request Several other matcher methods are also available on javadoc:org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest[]. See the xref:api:rest/actuator/index.adoc[API documentation] for details. +NOTE: When matching for Actuator endpoints, `EndpointRequest.to("endpoint")` will consider the endpoint root and all its subpaths, +effectively matching `"/actuator/endpoint/**"` even if the endpoint does not declare nested routes. + If you deploy applications behind a firewall, you may prefer that all your actuator endpoints can be accessed without requiring authentication. You can do so by changing the configprop:management.endpoints.web.exposure.include[] property, as follows: diff --git a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java index 46601f6cccc6..6cf0f6b90275 100644 --- a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java +++ b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java @@ -69,11 +69,12 @@ private EndpointRequest() { } /** - * Returns a matcher that includes all {@link Endpoint actuator endpoints}. It also - * includes the links endpoint which is present at the base path of the actuator - * endpoints. The {@link EndpointServerWebExchangeMatcher#excluding(Class...) - * excluding} method can be used to further remove specific endpoints if required. For - * example:
+	 * Returns a matcher that includes all {@link Endpoint actuator endpoints} and
+	 * everything beneath them. It also includes the links endpoint which is present at
+	 * the base path of the actuator endpoints. The
+	 * {@link EndpointServerWebExchangeMatcher#excluding(Class...) excluding} method can
+	 * be used to further remove specific endpoints if required. For example:
+	 * 
 	 * EndpointRequest.toAnyEndpoint().excluding(ShutdownEndpoint.class)
 	 * 
* @return the configured {@link ServerWebExchangeMatcher} @@ -83,8 +84,8 @@ public static EndpointServerWebExchangeMatcher toAnyEndpoint() { } /** - * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}. - * For example:
+	 * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}
+	 * everything beneath them. For example: 
 	 * EndpointRequest.to(ShutdownEndpoint.class, HealthEndpoint.class)
 	 * 
* @param endpoints the endpoints to include @@ -95,8 +96,8 @@ public static EndpointServerWebExchangeMatcher to(Class... endpoints) { } /** - * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}. - * For example:
+	 * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}
+	 * everything beneath them. For example: 
 	 * EndpointRequest.to("shutdown", "health")
 	 * 
* @param endpoints the endpoints to include diff --git a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java index 3f6f50a03f08..b78109e3598e 100644 --- a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java +++ b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java @@ -67,11 +67,11 @@ private EndpointRequest() { } /** - * Returns a matcher that includes all {@link Endpoint actuator endpoints}. It also - * includes the links endpoint which is present at the base path of the actuator - * endpoints. The {@link EndpointRequestMatcher#excluding(Class...) excluding} method - * can be used to further remove specific endpoints if required. For example: - *
+	 * Returns a matcher that includes all {@link Endpoint actuator endpoints} and
+	 * everything beneath them. It also includes the links endpoint which is present at
+	 * the base path of the actuator endpoints. The
+	 * {@link EndpointRequestMatcher#excluding(Class...) excluding} method can be used to
+	 * further remove specific endpoints if required. For example: 
 	 * EndpointRequest.toAnyEndpoint().excluding(ShutdownEndpoint.class)
 	 * 
* @return the configured {@link RequestMatcher} @@ -81,8 +81,8 @@ public static EndpointRequestMatcher toAnyEndpoint() { } /** - * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}. - * For example:
+	 * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}
+	 * and everything beneath them. For example: 
 	 * EndpointRequest.to(ShutdownEndpoint.class, HealthEndpoint.class)
 	 * 
* @param endpoints the endpoints to include @@ -93,8 +93,8 @@ public static EndpointRequestMatcher to(Class... endpoints) { } /** - * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}. - * For example:
+	 * Returns a matcher that includes the specified {@link Endpoint actuator endpoints}
+	 * and everything beneath them. For example: 
 	 * EndpointRequest.to("shutdown", "health")
 	 * 
* @param endpoints the endpoints to include From 1e09be7f1cf3add6f99f60c85700dc8e53bc7070 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Mar 2026 11:50:17 +0000 Subject: [PATCH 097/376] Adding missing starter for Spring REST Docs Closes gh-48289 --- .../spring-boot-dependencies/build.gradle | 1 + settings.gradle | 1 + .../spring-boot-starter-restdocs/build.gradle | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 starter/spring-boot-starter-restdocs/build.gradle diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 43b27122482e..9fc293bfcb85 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2245,6 +2245,7 @@ bom { "spring-boot-starter-reactor-netty", "spring-boot-starter-restclient", "spring-boot-starter-restclient-test", + "spring-boot-starter-restdocs", "spring-boot-starter-rsocket", "spring-boot-starter-rsocket-test", "spring-boot-starter-security", diff --git a/settings.gradle b/settings.gradle index e3fe924fd049..f243098a9dea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -324,6 +324,7 @@ include "starter:spring-boot-starter-r2dbc-test" include "starter:spring-boot-starter-reactor-netty" include "starter:spring-boot-starter-restclient" include "starter:spring-boot-starter-restclient-test" +include "starter:spring-boot-starter-restdocs" include "starter:spring-boot-starter-rsocket" include "starter:spring-boot-starter-rsocket-test" include "starter:spring-boot-starter-security" diff --git a/starter/spring-boot-starter-restdocs/build.gradle b/starter/spring-boot-starter-restdocs/build.gradle new file mode 100644 index 000000000000..be16ac99ccb7 --- /dev/null +++ b/starter/spring-boot-starter-restdocs/build.gradle @@ -0,0 +1,25 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id "org.springframework.boot.starter" +} + +description = "Starter for using Spring REST Docs" + +dependencies { + api(project(":module:spring-boot-restdocs")) +} From c9145e69d9da54046617a18808af1ad3c11bfd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Sat, 7 Mar 2026 09:07:56 +0100 Subject: [PATCH 098/376] Refresh JDK versions used in integration tests Closes gh-49412 --- .../spring-boot-launch-script-tests/build.gradle | 2 +- .../spring-boot-loader-tests/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle index d6b836594f98..f623067d1d5a 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle @@ -22,7 +22,7 @@ plugins { description = "Spring Boot Launch Script Integration Tests" -def jdkVersion = "17.0.11+10" +def jdkVersion = "17.0.18+10" def jdkArch = "aarch64".equalsIgnoreCase(System.getProperty("os.arch")) ? "aarch64" : "amd64" configurations { diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/build.gradle index 9d23f50ab49a..8ee90b80d231 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/build.gradle @@ -22,7 +22,7 @@ plugins { description = "Spring Boot Loader Integration Tests" -def oracleJdkVersion = "17.0.8" +def oracleJdkVersion = "17.0.12" def oracleJdkArch = "aarch64".equalsIgnoreCase(System.getProperty("os.arch")) ? "aarch64" : "x64" configurations { From 57747989e36285d4618ec36c54d976e22caf1769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Mar 2026 17:20:35 +0100 Subject: [PATCH 099/376] Start building against Spring Security 6.5.9 snapshots See gh-49527 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 13ded8f8cb1c..8a586fc44478 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2452,7 +2452,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-retry/releases/tag/v{version}") } } - library("Spring Security", "6.5.8") { + library("Spring Security", "6.5.9-SNAPSHOT") { considerSnapshots() group("org.springframework.security") { bom("spring-security-bom") From c49ebaa970fca7e062a28948321baad449fc1fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Mar 2026 17:20:39 +0100 Subject: [PATCH 100/376] Start building against Spring WS 4.1.3 snapshots See gh-49528 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 8a586fc44478..8c3b42899026 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2490,7 +2490,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-session/releases/tag/{version}") } } - library("Spring WS", "4.1.2") { + library("Spring WS", "4.1.3-SNAPSHOT") { considerSnapshots() group("org.springframework.ws") { bom("spring-ws-bom") From 2bbb1efbb2ed6a8cdba3476cf8a78fc9b3664b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Mar 2026 17:22:08 +0100 Subject: [PATCH 101/376] Start building against Spring Integration 7.0.4 snapshots See gh-49529 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 9fc293bfcb85..a4fc11eb677f 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2487,7 +2487,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "7.0.3") { + library("Spring Integration", "7.0.4-SNAPSHOT") { considerSnapshots() group("org.springframework.integration") { bom("spring-integration-bom") From 4aeefa899aafe68e972ce429570b183573c2b702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Mar 2026 17:22:12 +0100 Subject: [PATCH 102/376] Start building against Spring Security 7.0.4 snapshots See gh-49530 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index a4fc11eb677f..ccaa925ec75b 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2570,7 +2570,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-restdocs/releases/tag/v{version}") } } - library("Spring Security", "7.0.3") { + library("Spring Security", "7.0.4-SNAPSHOT") { considerSnapshots() group("org.springframework.security") { bom("spring-security-bom") From 686327c736621bae117c09b15daed1957cedce05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Mar 2026 17:22:17 +0100 Subject: [PATCH 103/376] Start building against Spring WS 5.0.1 snapshots See gh-49531 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index ccaa925ec75b..dcd9b6cd2afb 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2608,7 +2608,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-session/releases/tag/{version}") } } - library("Spring WS", "5.0.0") { + library("Spring WS", "5.0.1-SNAPSHOT") { considerSnapshots() group("org.springframework.ws") { bom("spring-ws-bom") From 7306fd04de912d02dbef8f1edda20bba889653e8 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sun, 8 Mar 2026 16:56:54 +0100 Subject: [PATCH 104/376] Use MergedContextConfiguration.hasResources() Simplify the code by using MergedContextConfiguration.hasResources(). See gh-49504 --- .../boot/test/context/SpringBootContextLoader.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java index afc3baf925b7..16071b726685 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java @@ -153,9 +153,7 @@ private ApplicationContext loadContext(MergedContextConfiguration mergedConfig, } private void assertHasClassesOrLocations(MergedContextConfiguration mergedConfig) { - boolean hasClasses = !ObjectUtils.isEmpty(mergedConfig.getClasses()); - boolean hasLocations = !ObjectUtils.isEmpty(mergedConfig.getLocations()); - Assert.state(hasClasses || hasLocations, + Assert.state(mergedConfig.hasResources(), () -> "No configuration classes or locations found in @SpringApplicationConfiguration. " + "For default configuration detection to work you need Spring 4.0.3 or better (found " + SpringVersion.getVersion() + ")."); From 7280d248e4604bd1ddc398f09a52216033545a16 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Mar 2026 17:26:27 +0000 Subject: [PATCH 105/376] Modernize assertion message for no classes or locations Closes gh-49518 --- .../boot/test/context/SpringBootContextLoader.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java index 16071b726685..7e759701a434 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java @@ -51,7 +51,6 @@ import org.springframework.core.KotlinDetector; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; -import org.springframework.core.SpringVersion; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.Order; @@ -154,9 +153,7 @@ private ApplicationContext loadContext(MergedContextConfiguration mergedConfig, private void assertHasClassesOrLocations(MergedContextConfiguration mergedConfig) { Assert.state(mergedConfig.hasResources(), - () -> "No configuration classes or locations found in @SpringApplicationConfiguration. " - + "For default configuration detection to work you need Spring 4.0.3 or better (found " - + SpringVersion.getVersion() + ")."); + () -> "No configuration classes or locations found. Check your test's configuration."); } private Method getMainMethod(MergedContextConfiguration mergedConfig, UseMainMethod useMainMethod) { From 9077605ed0dbb679b1145c1e2f5733c5b3263bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:24:33 +0100 Subject: [PATCH 106/376] Upgrade to DB2 JDBC 12.1.4.0 Closes gh-49544 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 8c3b42899026..244d2f9610b2 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -302,7 +302,7 @@ bom { releaseNotes("https://github.com/CycloneDX/cyclonedx-maven-plugin/releases/tag/cyclonedx-maven-plugin-{version}") } } - library("DB2 JDBC", "12.1.3.0") { + library("DB2 JDBC", "12.1.4.0") { group("com.ibm.db2") { modules = [ "jcc" From 0b185b127e037d6716eaddc4d66fbf3052c4942e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:24:33 +0100 Subject: [PATCH 107/376] Upgrade to Micrometer 1.15.10 Closes gh-49403 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 244d2f9610b2..9cc6867f3275 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1589,7 +1589,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.15.10-SNAPSHOT") { + library("Micrometer", "1.15.10") { considerSnapshots() group("io.micrometer") { modules = [ From 79f82043d0bcd564961311054d69fa08d07b86ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:24:33 +0100 Subject: [PATCH 108/376] Upgrade to Micrometer Tracing 1.5.10 Closes gh-49404 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 9cc6867f3275..562dd12adb99 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1611,7 +1611,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.5.10-SNAPSHOT") { + library("Micrometer Tracing", "1.5.10") { considerSnapshots() alignWith { dependencyManagementDeclaredIn("io.micrometer:micrometer-tracing-bom") { From f66615bd9b1403c7ff8d90eaff77924a17a5a7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:24:33 +0100 Subject: [PATCH 109/376] Upgrade to Reactor Bom 2024.0.16 Closes gh-49405 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 562dd12adb99..3d5074507bff 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2029,7 +2029,7 @@ bom { ] } } - library("Reactor Bom", "2024.0.16-SNAPSHOT") { + library("Reactor Bom", "2024.0.16") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From c8adaa8527d859c036f4dc75c22c2338f50cb494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:27:46 +0100 Subject: [PATCH 110/376] Upgrade to Commons Logging 1.3.6 Closes gh-49545 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index dcd9b6cd2afb..323d944d9c97 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -244,7 +244,7 @@ bom { releaseNotes("https://commons.apache.org/proper/commons-lang/changes.html#a{version}") } } - library("Commons Logging", "1.3.5") { + library("Commons Logging", "1.3.6") { group("commons-logging") { modules = [ "commons-logging" From ac8cf34584847c626ba97ac7d2111a6d37ff0f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:27:50 +0100 Subject: [PATCH 111/376] Upgrade to DB2 JDBC 12.1.4.0 Closes gh-49546 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 323d944d9c97..b0343cbd6f6c 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -301,7 +301,7 @@ bom { releaseNotes("https://github.com/CycloneDX/cyclonedx-maven-plugin/releases/tag/cyclonedx-maven-plugin-{version}") } } - library("DB2 JDBC", "12.1.3.0") { + library("DB2 JDBC", "12.1.4.0") { group("com.ibm.db2") { modules = [ "jcc" From bcee294f88ae6e19d016b4d40fb724dc55081f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:27:50 +0100 Subject: [PATCH 112/376] Upgrade to Micrometer 1.16.4 Closes gh-49413 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index b0343cbd6f6c..5296e0494650 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1540,7 +1540,7 @@ bom { releaseNotes("https://github.com/apache/maven-war-plugin/releases/tag/maven-war-plugin-{version}") } } - library("Micrometer", "1.16.4-SNAPSHOT") { + library("Micrometer", "1.16.4") { considerSnapshots() group("io.micrometer") { modules = [ From 153878b86f9bce361d86b81d78b9ac42c0a0d51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:27:50 +0100 Subject: [PATCH 113/376] Upgrade to Micrometer Tracing 1.6.4 Closes gh-49414 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 5296e0494650..b2370c08bb3e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1563,7 +1563,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.6.4-SNAPSHOT") { + library("Micrometer Tracing", "1.6.4") { considerSnapshots() alignWith { dependencyManagementDeclaredIn("io.micrometer:micrometer-tracing-bom") { From 39440038aeb63622799b6e813e7080442009204a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Mar 2026 14:27:51 +0100 Subject: [PATCH 114/376] Upgrade to Reactor Bom 2025.0.4 Closes gh-49415 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index b2370c08bb3e..8bb3c851c031 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1973,7 +1973,7 @@ bom { ] } } - library("Reactor Bom", "2025.0.4-SNAPSHOT") { + library("Reactor Bom", "2025.0.4") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From 0e9840e7dee563f342f39c4a89cc715231d4d9d7 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 11 Mar 2026 08:34:06 +0100 Subject: [PATCH 115/376] Provide advance warning of the deprecation and forthcoming removal of OpenTelemetry's ZipkinSpanExporter Closes gh-49453 --- .../antora/modules/reference/pages/actuator/tracing.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc index 83bdcdb36f55..ed48a82021c9 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc @@ -12,7 +12,7 @@ TIP: To learn more about Micrometer Tracing capabilities, see its {url-micromete Spring Boot ships auto-configuration for the following tracers: -* https://opentelemetry.io/[OpenTelemetry] with https://zipkin.io/[Zipkin] or https://opentelemetry.io/docs/reference/specification/protocol/[OTLP]. +* https://opentelemetry.io/[OpenTelemetry] with https://opentelemetry.io/docs/reference/specification/protocol/[OTLP]. * https://github.com/openzipkin/brave[OpenZipkin Brave] with https://zipkin.io/[Zipkin]. @@ -128,6 +128,10 @@ The customizers take precedence over anything applied by the auto-configuration. [[actuator.micrometer-tracing.tracer-implementations.otel-zipkin]] === OpenTelemetry With Zipkin +WARNING: OpenTelemetry has https://opentelemetry.io/docs/specs/otel/trace/sdk_exporters/zipkin/[deprecated their Zipkin support]. +The auto-configuration for it will be removed in Spring Boot 4.2. +Either switch to Brave or consider using https://github.com/openzipkin-contrib/zipkin-otel[the Zipkin OTel module] for ingesting OTLP directly. + Tracing with OpenTelemetry and reporting to Zipkin requires the following dependencies: * `org.springframework.boot:spring-boot-micrometer-tracing-opentelemetry` - Spring Boot's support for Micrometer Tracing over OpenTelemetry. From 2b6559ccd62d23259e8f769df7e5451383ba51ab Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 11 Mar 2026 10:30:34 +0100 Subject: [PATCH 116/376] Deprecate ZipkinWithOpenTelemetryTracingAutoConfiguration See gh-49453 --- .../zipkin/ZipkinWithOpenTelemetryTracingAutoConfiguration.java | 2 ++ .../ZipkinWithOpenTelemetryTracingAutoConfigurationTests.java | 1 + 2 files changed, 3 insertions(+) diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfiguration.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfiguration.java index e89c1e8967c3..9b4269659d40 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfiguration.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfiguration.java @@ -40,9 +40,11 @@ * @author Wick Dynex * @author Phillip Webb * @since 4.0.0 + * @deprecated since 4.0.4 for removal in 4.2.0 */ @AutoConfiguration(afterName = "org.springframework.boot.zipkin.autoconfigure.ZipkinAutoConfiguration") @ConditionalOnClass({ ZipkinSpanExporter.class, Span.class }) +@Deprecated(since = "4.0.4", forRemoval = true) public final class ZipkinWithOpenTelemetryTracingAutoConfiguration { @Bean diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfigurationTests.java index fce94731ce7e..b9333d5288bf 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/zipkin/ZipkinWithOpenTelemetryTracingAutoConfigurationTests.java @@ -38,6 +38,7 @@ * * @author Moritz Halbritter */ +@SuppressWarnings("removal") class ZipkinWithOpenTelemetryTracingAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() From 99aff08477bd983b51ce8ce616c13b96efe6ed0c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Mar 2026 11:23:32 +0000 Subject: [PATCH 117/376] List all supported colors when describing color-coded log output Closes gh-49561 --- .../modules/reference/pages/features/logging.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/logging.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/logging.adoc index 70e244379b7d..3fb9f30555c0 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/logging.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/logging.adoc @@ -121,12 +121,22 @@ For example, to make the text yellow, use the following setting: The following colors and styles are supported: +* `black` * `blue` +* `bright_black` +* `bright_blue` +* `bright_cyan` +* `bright_green` +* `bright_magenta` +* `bright_red` +* `bright_white` +* `bright_yellow` * `cyan` * `faint` * `green` * `magenta` * `red` +* `white` * `yellow` From 496386328b16156d80f758d73be38ed8a2c36f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Mar 2026 15:24:19 +0100 Subject: [PATCH 118/376] Upgrade to Lombok 1.18.44 Closes gh-49574 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3d5074507bff..84f818e7c018 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1361,7 +1361,7 @@ bom { javadoc("https://logback.qos.ch/apidocs/ch.qos.logback.core", "ch.qos.logback") } } - library("Lombok", "1.18.42") { + library("Lombok", "1.18.44") { group("org.projectlombok") { modules = [ "lombok" From 03856fe0e2d0dee51e4c0493b8d6771e0fe687b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Mar 2026 15:34:56 +0100 Subject: [PATCH 119/376] Upgrade to Lombok 1.18.44 Closes gh-49575 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 8bb3c851c031..cb9f1b1f35e3 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1312,7 +1312,7 @@ bom { javadoc("https://logback.qos.ch/apidocs/ch.qos.logback.core", "ch.qos.logback") } } - library("Lombok", "1.18.42") { + library("Lombok", "1.18.44") { group("org.projectlombok") { modules = [ "lombok" From ffb36cb5ed7eaf6b24306a897cd228906a2c8308 Mon Sep 17 00:00:00 2001 From: MJY Date: Fri, 13 Mar 2026 17:20:55 +0900 Subject: [PATCH 120/376] Add failure analysis for missing auto-configured MailSender Signed-off-by: MJY See gh-49582 --- .../NoSuchMailSenderBeanFailureAnalyzer.java | 77 +++++++++++++++++ .../main/resources/META-INF/spring.factories | 3 + ...uchMailSenderBeanFailureAnalyzerTests.java | 82 +++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java create mode 100644 module/spring-boot-mail/src/main/resources/META-INF/spring.factories create mode 100644 module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java diff --git a/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java b/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java new file mode 100644 index 000000000000..985e9daf8dbf --- /dev/null +++ b/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.mail.autoconfigure; + +import org.jspecify.annotations.Nullable; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; +import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.mail.MailSender; + +/** + * An {@link AbstractFailureAnalyzer} that improves missing {@link MailSender} guidance + * when mail auto-configuration is present but not activated. + */ +class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer + implements Ordered { + + private static final String MAIL_HOST_PROPERTY = "spring.mail.host"; + + private static final String MAIL_JNDI_NAME_PROPERTY = "spring.mail.jndi-name"; + + private final @Nullable Environment environment; + + NoSuchMailSenderBeanFailureAnalyzer(@Nullable Environment environment) { + this.environment = environment; + } + + @Override + protected @Nullable FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) { + if (!isMissingMailSenderBean(cause) || hasMailConfigurationProperty()) { + return null; + } + String description = "A MailSender bean could not be found because Spring Boot mail auto-configuration " + + "did not match. Neither '" + MAIL_HOST_PROPERTY + "' nor '" + MAIL_JNDI_NAME_PROPERTY + + "' is configured."; + String action = "Consider configuring '" + MAIL_HOST_PROPERTY + "' or '" + MAIL_JNDI_NAME_PROPERTY + + "' to enable auto-configuration. If you want to use a custom mail sender, define a MailSender " + + "bean in your configuration."; + return new FailureAnalysis(description, action, cause); + } + + private boolean isMissingMailSenderBean(NoSuchBeanDefinitionException cause) { + Class beanType = cause.getBeanType(); + if (beanType == null && cause.getResolvableType() != null) { + beanType = cause.getResolvableType().resolve(); + } + return (beanType != null) && MailSender.class.isAssignableFrom(beanType); + } + + private boolean hasMailConfigurationProperty() { + return this.environment != null && (this.environment.containsProperty(MAIL_HOST_PROPERTY) + || this.environment.containsProperty(MAIL_JNDI_NAME_PROPERTY)); + } + + @Override + public int getOrder() { + return 0; + } + +} diff --git a/module/spring-boot-mail/src/main/resources/META-INF/spring.factories b/module/spring-boot-mail/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..bfd0e1dd1086 --- /dev/null +++ b/module/spring-boot-mail/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Failure Analyzers +org.springframework.boot.diagnostics.FailureAnalyzer=\ +org.springframework.boot.mail.autoconfigure.NoSuchMailSenderBeanFailureAnalyzer diff --git a/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java b/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java new file mode 100644 index 000000000000..b3b03121fb16 --- /dev/null +++ b/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.mail.autoconfigure; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.core.env.Environment; +import org.springframework.mail.MailSender; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link NoSuchMailSenderBeanFailureAnalyzer}. + */ +class NoSuchMailSenderBeanFailureAnalyzerTests { + + @Test + void analyzeWhenNotNoSuchBeanDefinitionExceptionShouldReturnNull() { + assertThat(new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new Exception())).isNull(); + } + + @Test + void analyzeWhenNoSuchBeanDefinitionExceptionForDifferentTypeShouldReturnNull() { + assertThat( + new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new NoSuchBeanDefinitionException(String.class))) + .isNull(); + } + + @Test + void analyzeWhenMailHostPropertyIsConfiguredShouldReturnNull() { + Environment environment = new MockEnvironment().withProperty("spring.mail.host", "smtp.example.org"); + assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment) + .analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull(); + } + + @Test + void analyzeWhenMailJndiNamePropertyIsConfiguredShouldReturnNull() { + Environment environment = new MockEnvironment().withProperty("spring.mail.jndi-name", "mail/Session"); + assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment) + .analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull(); + } + + @Test + void analyzeWhenMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() { + FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment()) + .analyze(new NoSuchBeanDefinitionException(MailSender.class)); + assertThat(analysis).isNotNull(); + assertThat(analysis.getDescription()) + .contains("A MailSender bean could not be found") + .contains("spring.mail.host") + .contains("spring.mail.jndi-name"); + assertThat(analysis.getAction()) + .contains("spring.mail.host") + .contains("spring.mail.jndi-name") + .contains("MailSender bean"); + } + + @Test + void analyzeWhenJavaMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() { + assertThat(new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment()) + .analyze(new NoSuchBeanDefinitionException(JavaMailSender.class))).isNotNull(); + } + +} From f0aff3aa3c5ef7c51549823b5b8afba9a159f8db Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 12:02:32 +0000 Subject: [PATCH 121/376] Polish "Add failure analysis for missing auto-configured MailSender" See gh-49582 Signed-off-by: Andy Wilkinson --- .../NoSuchMailSenderBeanFailureAnalyzer.java | 49 ++++++++--- ...uchMailSenderBeanFailureAnalyzerTests.java | 82 ++++++++++--------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java b/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java index 985e9daf8dbf..7997fc332c80 100644 --- a/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java +++ b/module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java @@ -16,18 +16,27 @@ package org.springframework.boot.mail.autoconfigure; +import java.util.Map; + import org.jspecify.annotations.Nullable; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome; +import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.boot.mail.autoconfigure.MailSenderAutoConfiguration.MailSenderCondition; import org.springframework.core.Ordered; -import org.springframework.core.env.Environment; import org.springframework.mail.MailSender; /** * An {@link AbstractFailureAnalyzer} that improves missing {@link MailSender} guidance - * when mail auto-configuration is present but not activated. + * when {@link MailSenderAutoConfiguration} is present but did not match. + * + * @author MJY (answndud) + * @author Andy Wilkinson */ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer implements Ordered { @@ -36,18 +45,22 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer conditionAndOutcomesBySource = conditionEvaluationReport + .getConditionAndOutcomesBySource(); + ConditionAndOutcomes conditionAndOutcomes = conditionAndOutcomesBySource + .get(MailSenderAutoConfiguration.class.getName()); + if (conditionAndOutcomes != null) { + return conditionAndOutcomes.stream() + .filter((candidate) -> candidate.getCondition() instanceof MailSenderCondition) + .findFirst() + .orElse(null); + } + } + return null; + } + private boolean isMissingMailSenderBean(NoSuchBeanDefinitionException cause) { Class beanType = cause.getBeanType(); if (beanType == null && cause.getResolvableType() != null) { @@ -64,11 +94,6 @@ private boolean isMissingMailSenderBean(NoSuchBeanDefinitionException cause) { return (beanType != null) && MailSender.class.isAssignableFrom(beanType); } - private boolean hasMailConfigurationProperty() { - return this.environment != null && (this.environment.containsProperty(MAIL_HOST_PROPERTY) - || this.environment.containsProperty(MAIL_JNDI_NAME_PROPERTY)); - } - @Override public int getOrder() { return 0; diff --git a/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java b/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java index b3b03121fb16..7c719ac1a47f 100644 --- a/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java +++ b/module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java @@ -18,65 +18,73 @@ import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.diagnostics.FailureAnalysis; -import org.springframework.core.env.Environment; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.mail.MailSender; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatException; /** * Tests for {@link NoSuchMailSenderBeanFailureAnalyzer}. + * + * @author MJY (answndud) + * @author Andy Wilkinson */ class NoSuchMailSenderBeanFailureAnalyzerTests { @Test void analyzeWhenNotNoSuchBeanDefinitionExceptionShouldReturnNull() { - assertThat(new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new Exception())).isNull(); + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class)) + .run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory) + .analyze(new Exception()); + assertThat(analysis).isNull(); + }); } @Test void analyzeWhenNoSuchBeanDefinitionExceptionForDifferentTypeShouldReturnNull() { - assertThat( - new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new NoSuchBeanDefinitionException(String.class))) - .isNull(); - } - - @Test - void analyzeWhenMailHostPropertyIsConfiguredShouldReturnNull() { - Environment environment = new MockEnvironment().withProperty("spring.mail.host", "smtp.example.org"); - assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment) - .analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull(); - } - - @Test - void analyzeWhenMailJndiNamePropertyIsConfiguredShouldReturnNull() { - Environment environment = new MockEnvironment().withProperty("spring.mail.jndi-name", "mail/Session"); - assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment) - .analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull(); + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class)) + .run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + assertThatException().isThrownBy(() -> context.getBean(String.class)).satisfies((ex) -> { + FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex); + assertThat(analysis).isNull(); + }); + }); } @Test - void analyzeWhenMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() { - FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment()) - .analyze(new NoSuchBeanDefinitionException(MailSender.class)); - assertThat(analysis).isNotNull(); - assertThat(analysis.getDescription()) - .contains("A MailSender bean could not be found") - .contains("spring.mail.host") - .contains("spring.mail.jndi-name"); - assertThat(analysis.getAction()) - .contains("spring.mail.host") - .contains("spring.mail.jndi-name") - .contains("MailSender bean"); + void analyzeWithoutMailSenderAutoConfigurationShouldReturnNull() { + new ApplicationContextRunner().run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + assertThatException().isThrownBy(() -> context.getBean(MailSender.class)).satisfies((ex) -> { + FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex); + assertThat(analysis).isNull(); + }); + }); } @Test - void analyzeWhenJavaMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() { - assertThat(new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment()) - .analyze(new NoSuchBeanDefinitionException(JavaMailSender.class))).isNotNull(); + void analyzeWhenMailSenderBeanIsMissingAndMailSenderConditionDidNotMatchShouldProvideGuidance() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class)) + .run((context) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + assertThatException().isThrownBy(() -> context.getBean(MailSender.class)).satisfies((ex) -> { + FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex); + assertThat(analysis).isNotNull(); + assertThat(analysis.getDescription()).contains("A MailSender bean could not be found") + .contains("spring.mail.host") + .contains("spring.mail.jndi-name"); + assertThat(analysis.getAction()).contains("spring.mail.host") + .contains("spring.mail.jndi-name") + .contains("MailSender bean"); + }); + }); } } From a40bad0e0d39518e3b18221e438598e3445ca31d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:06:42 +0000 Subject: [PATCH 122/376] Upgrade to Spring Data Bom 2025.0.10 Closes gh-49407 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 84f818e7c018..b833de0a678e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2279,7 +2279,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2025.0.10-SNAPSHOT") { + library("Spring Data Bom", "2025.0.10") { prohibit { versionRange "[2025.1.0-M1,)" because "it exceeds our baseline" From c7292b5b8c3a78b50e474555be9d3dcdce5db0cc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:06:43 +0000 Subject: [PATCH 123/376] Upgrade to Spring Framework 6.2.17 Closes gh-49408 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f4f79e71638c..5c02537d8b47 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ mavenVersion=3.9.13 mockitoVersion=5.17.0 nativeBuildToolsVersion=0.10.6 snakeYamlVersion=2.4 -springFrameworkVersion=6.2.17-SNAPSHOT +springFrameworkVersion=6.2.17 springFramework60xVersion=6.0.23 tomcatVersion=10.1.52 From 722408a05a4afa45514c22a76b2370944f45e3d9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:06:47 +0000 Subject: [PATCH 124/376] Upgrade to Spring HATEOAS 2.5.2 Closes gh-49586 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index b833de0a678e..bffbff7467a8 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2335,7 +2335,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-graphql/releases/tag/v{version}") } } - library("Spring HATEOAS", "2.5.1") { + library("Spring HATEOAS", "2.5.2") { prohibit { versionRange "[3.0.0-M1,)" because "it exceeds our baseline" From 8120f66b073528895d3a5fab9054eef38b90994e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:09:01 +0000 Subject: [PATCH 125/376] Upgrade to Spring Data Bom 2025.1.4 Closes gh-49417 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index cb9f1b1f35e3..084cbc5b5202 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2422,7 +2422,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2025.1.4-SNAPSHOT") { + library("Spring Data Bom", "2025.1.4") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From ae4ac0ec270dce4139a9564eadcccc185e4b10aa Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:09:01 +0000 Subject: [PATCH 126/376] Upgrade to Spring Framework 7.0.6 Closes gh-49418 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4a8ea36032f1..bcc1e1d0b8c1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ mockitoVersion=5.20.0 nativeBuildToolsVersion=0.11.5 nullabilityPluginVersion=0.0.11 snakeYamlVersion=2.5 -springFrameworkVersion=7.0.6-SNAPSHOT +springFrameworkVersion=7.0.6 springFramework60xVersion=6.0.23 tomcatVersion=11.0.18 From c088d33765e1f3e50f501e546ce7f5907f3bb44a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 13:09:06 +0000 Subject: [PATCH 127/376] Upgrade to Spring HATEOAS 3.0.3 Closes gh-49587 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 084cbc5b5202..5b11ec499ba8 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2470,7 +2470,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-graphql/releases/tag/v{version}") } } - library("Spring HATEOAS", "3.0.2") { + library("Spring HATEOAS", "3.0.3") { considerSnapshots() group("org.springframework.hateoas") { modules = [ From c366ddaca0af4bb05a2cd261271479ac7c5699f8 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 13 Mar 2026 16:45:43 +0100 Subject: [PATCH 128/376] Avoid duplicate RSocket endpoint for WebSocket Prior to this commit, choosing a "websocket" transport for your RSocket endpoint with `spring.rsocket.server.port` would not only expose the endpoint on that specific port, but also on the main server. This commit refines the auto-configuration condition to only add a route on the main server if the separate port is not chosen. Fixes gh-49592 --- .../rsocket/RSocketServerAutoConfiguration.java | 2 +- .../rsocket/RSocketServerAutoConfigurationTests.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java index 76201e6cc7f5..72bddffbc5fc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfiguration.java @@ -148,7 +148,7 @@ static class IsReactiveWebApplication { } - @ConditionalOnProperty(name = "spring.rsocket.server.port", matchIfMissing = true) + @ConditionalOnProperty(name = "spring.rsocket.server.port", havingValue = "false", matchIfMissing = true) static class HasNoPortConfigured { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfigurationTests.java index 8af00918d227..04ae67608d96 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfigurationTests.java @@ -84,6 +84,17 @@ void shouldCreateDefaultBeansForReactiveWebApp() { .run((context) -> assertThat(context).hasSingleBean(RSocketWebSocketNettyRouteProvider.class)); } + @Test + void shouldNotCreateWebSocketRouteProviderWhenDedicatedPortIsSet() { + reactiveWebContextRunner() + .withPropertyValues("spring.rsocket.server.port=0", "spring.rsocket.server.transport=websocket", + "spring.rsocket.server.mapping-path=/rsocket") + .run((context) -> { + assertThat(context).hasSingleBean(RSocketServerFactory.class); + assertThat(context).doesNotHaveBean(RSocketWebSocketNettyRouteProvider.class); + }); + } + @Test void shouldCreateDefaultBeansForRSocketServerWhenPortIsSet() { reactiveWebContextRunner().withPropertyValues("spring.rsocket.server.port=0") From 57e768a654b41d490137fd81f8db6c1ff0e53c1a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 15:22:59 +0000 Subject: [PATCH 129/376] Trim trailing slashes from tcp:// Docker hosts Fixes gh-49055 --- .../configuration/ResolvedDockerHost.java | 12 +++++++-- .../ResolvedDockerHostTests.java | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java index 59637051bd20..3687d6fc69d0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHost.java @@ -58,9 +58,17 @@ public class ResolvedDockerHost extends DockerHost { public String getAddress() { String address = super.getAddress(); if (address == null) { - address = getDefaultAddress(); + return getDefaultAddress(); } - return address.startsWith(UNIX_SOCKET_PREFIX) ? address.substring(UNIX_SOCKET_PREFIX.length()) : address; + if (address.startsWith(UNIX_SOCKET_PREFIX)) { + return address.substring(UNIX_SOCKET_PREFIX.length()); + } + if (address.startsWith("tcp://")) { + while (address.endsWith("/")) { + address = address.substring(0, address.length() - 1); + } + } + return address; } public boolean isRemote() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHostTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHostTests.java index ad130b73d176..334692bdd43a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHostTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/configuration/ResolvedDockerHostTests.java @@ -140,6 +140,17 @@ void resolveWhenDockerHostAddressIsTcpReturnsAddress() { assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); } + @Test + void resolveWhenDockerHostAddressIsTcpWithTrailingReturnsAddress() { + ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, + new DockerConnectionConfiguration.Host("tcp://192.168.99.100:2376/", true, "/cert-path")); + assertThat(dockerHost.isLocalFileReference()).isFalse(); + assertThat(dockerHost.isRemote()).isTrue(); + assertThat(dockerHost.getAddress()).isEqualTo("tcp://192.168.99.100:2376"); + assertThat(dockerHost.isSecure()).isTrue(); + assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); + } + @Test void resolveWhenEnvironmentAddressIsLocalReturnsAddress(@TempDir Path tempDir) throws IOException { String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); @@ -180,6 +191,20 @@ void resolveWhenEnvironmentAddressIsTcpReturnsAddress() { assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); } + @Test + void resolveWhenEnvironmentAddressIsTcpWithTrailingSlashReturnsAddress() { + this.environment.put("DOCKER_HOST", "tcp://192.168.99.100:2376/"); + this.environment.put("DOCKER_TLS_VERIFY", "1"); + this.environment.put("DOCKER_CERT_PATH", "/cert-path"); + ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, + new DockerConnectionConfiguration.Host("tcp://1.1.1.1")); + assertThat(dockerHost.isLocalFileReference()).isFalse(); + assertThat(dockerHost.isRemote()).isTrue(); + assertThat(dockerHost.getAddress()).isEqualTo("tcp://192.168.99.100:2376"); + assertThat(dockerHost.isSecure()).isTrue(); + assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); + } + @Test void resolveWithDockerHostContextReturnsAddress() throws Exception { this.environment.put("DOCKER_CONFIG", pathToResource("with-default-context/config.json")); From 76290f787104f5b2b2e8b71896bed39d3e159633 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Mar 2026 16:38:01 +0000 Subject: [PATCH 130/376] Remove unused org.springframework.boot.docker-test plugin Closes gh-49598 --- module/spring-boot-data-couchbase/build.gradle | 1 - module/spring-boot-data-r2dbc/build.gradle | 1 - module/spring-boot-micrometer-tracing-brave/build.gradle | 5 ----- 3 files changed, 7 deletions(-) diff --git a/module/spring-boot-data-couchbase/build.gradle b/module/spring-boot-data-couchbase/build.gradle index f2f16ecd2b5d..bc31450a42fd 100644 --- a/module/spring-boot-data-couchbase/build.gradle +++ b/module/spring-boot-data-couchbase/build.gradle @@ -19,7 +19,6 @@ plugins { id "org.springframework.boot.auto-configuration" id "org.springframework.boot.configuration-properties" id "org.springframework.boot.deployed" - id "org.springframework.boot.docker-test" id "org.springframework.boot.optional-dependencies" } diff --git a/module/spring-boot-data-r2dbc/build.gradle b/module/spring-boot-data-r2dbc/build.gradle index 86219fe502cc..3a99b0c9f986 100644 --- a/module/spring-boot-data-r2dbc/build.gradle +++ b/module/spring-boot-data-r2dbc/build.gradle @@ -19,7 +19,6 @@ plugins { id "org.springframework.boot.auto-configuration" id "org.springframework.boot.configuration-properties" id "org.springframework.boot.deployed" - id "org.springframework.boot.docker-test" id "org.springframework.boot.optional-dependencies" } diff --git a/module/spring-boot-micrometer-tracing-brave/build.gradle b/module/spring-boot-micrometer-tracing-brave/build.gradle index 147a619c6834..1e9e94299761 100644 --- a/module/spring-boot-micrometer-tracing-brave/build.gradle +++ b/module/spring-boot-micrometer-tracing-brave/build.gradle @@ -19,7 +19,6 @@ plugins { id "org.springframework.boot.auto-configuration" id "org.springframework.boot.configuration-properties" id "org.springframework.boot.deployed" - id "org.springframework.boot.docker-test" id "org.springframework.boot.optional-dependencies" } @@ -49,7 +48,3 @@ dependencies { tasks.named("compileTestJava") { options.nullability.checking = "tests" } - -tasks.named("compileDockerTestJava") { - options.nullability.checking = "tests" -} From 766e11dc28fd60a35ce9e27d391512d1ee2a63bf Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 16 Mar 2026 08:44:42 +0100 Subject: [PATCH 131/376] Document support for Java 26 Closes gh-49604 --- .../src/docs/antora/modules/ROOT/pages/system-requirements.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/system-requirements.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/system-requirements.adoc index 04cfa25b4943..f235d388159d 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/system-requirements.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/ROOT/pages/system-requirements.adoc @@ -1,7 +1,7 @@ [[getting-started.system-requirements]] = System Requirements -Spring Boot {version-spring-boot} requires at least https://www.java.com[Java 17] and is compatible with versions up to and including Java 25. +Spring Boot {version-spring-boot} requires at least https://www.java.com[Java 17] and is compatible with versions up to and including Java 26. {url-spring-framework-docs}/[Spring Framework {version-spring-framework}] or above is also required. Explicit build support is provided for the following build tools: From c75807e3952dda4d064f32afe32de03a281a4239 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 11:25:54 +0000 Subject: [PATCH 132/376] Upgrade to Glassfish JAXB 4.0.7 Closes gh-49607 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 5b11ec499ba8..ff0ae294bf31 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -436,7 +436,7 @@ bom { releaseNotes("https://github.com/git-commit-id/git-commit-id-maven-plugin/releases/tag/v{version}") } } - library("Glassfish JAXB", "4.0.6") { + library("Glassfish JAXB", "4.0.7") { alignWith { dependencyManagementDeclaredIn("org.glassfish.jaxb:jaxb-bom") { excluding { candidate -> candidate.classifier().equals("sources") } From 257975a0daf7f976bb47ccfabda30f8e741017c0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 11:25:59 +0000 Subject: [PATCH 133/376] Upgrade to Hibernate 7.2.7.Final Closes gh-49608 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index ff0ae294bf31..8857427cc40e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -563,7 +563,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "7.2.6.Final") { + library("Hibernate", "7.2.7.Final") { group("org.hibernate.orm") { modules = [ "hibernate-agroal", From 756722e57fd42b83635bdb0757b5514757dc8281 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 11:26:04 +0000 Subject: [PATCH 134/376] Upgrade to Jakarta XML Bind 4.0.5 Closes gh-49609 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 8857427cc40e..dc01191deaee 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -899,7 +899,7 @@ bom { .formatted(version.major(), version.minor()), "jakarta.ws.rs") } } - library("Jakarta XML Bind", "4.0.4") { + library("Jakarta XML Bind", "4.0.5") { alignWith { version { from "org.glassfish.jaxb:jaxb-core" From 9b0ad127dbe410a9a48f78cc52cb7776d09fdd25 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 13:14:49 +0000 Subject: [PATCH 135/376] Upgrade to Glassfish JAXB 4.0.7 Closes gh-49615 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index bffbff7467a8..efd39e96a4d5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -448,7 +448,7 @@ bom { releaseNotes("https://github.com/git-commit-id/git-commit-id-maven-plugin/releases/tag/v{version}") } } - library("Glassfish JAXB", "4.0.6") { + library("Glassfish JAXB", "4.0.7") { group("org.glassfish.jaxb") { bom("jaxb-bom") { permit("com.sun.istack:istack-commons-runtime") From e1cc908dcdcad86e7f16e23dbe533bb9a567461d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 13:14:53 +0000 Subject: [PATCH 136/376] Upgrade to Jakarta XML Bind 4.0.5 Closes gh-49616 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index efd39e96a4d5..4d835c734dc5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -923,7 +923,7 @@ bom { .formatted(version.major(), version.minor()), "jakarta.ws.rs") } } - library("Jakarta XML Bind", "4.0.4") { + library("Jakarta XML Bind", "4.0.5") { alignWith { version { of "jakarta.xml.bind:jakarta.xml.bind-api" From ab7d6a9d29b992620a2c8cc34ddbcbe2e37a2e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Mar 2026 15:44:15 +0100 Subject: [PATCH 137/376] Adapt assertion to different error message --- .../tasks/bundling/BootBuildImageIntegrationTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/dockerTest/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/dockerTest/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java index db3268d4cb59..19745c5a506c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/dockerTest/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/dockerTest/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java @@ -517,8 +517,9 @@ void failsWhenCachesAreConfiguredTwice() throws IOException { void failsWithIncompatiblePlatform() throws IOException { writeMainClass(); BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage"); - assertThat(result.getOutput()).contains( - "Image platform mismatch detected. The configured platform 'linux/arm64' is not supported by the image 'ghcr.io/spring-io/spring-boot-cnb-test-builder:0.0.3-amd64'. Requested platform 'linux/arm64' but got 'linux/amd64'"); + assertThat(result.getOutput()).containsAnyOf( + "Image platform mismatch detected. The configured platform 'linux/arm64' is not supported by the image 'ghcr.io/spring-io/spring-boot-cnb-test-builder:0.0.3-amd64'. Requested platform 'linux/arm64' but got 'linux/amd64'", + "image with reference ghcr.io/spring-io/spring-boot-cnb-test-builder:0.0.3-amd64 was found but its platform (linux/amd64) does not match the specified platform (linux/arm64)"); } private void writeMainClass() throws IOException { From 79f91eac561cdae9b1965a4e4945730314df771f Mon Sep 17 00:00:00 2001 From: bbbbooo Date: Wed, 11 Feb 2026 21:46:52 +0900 Subject: [PATCH 138/376] Fix EndpointRequest links matching for separate management port When management.endpoints.web.base-path is empty and management runs on a different port, EndpointRequest.toLinks() and toAnyEndpoint() do not match the links endpoint consistently. Derive the links path based on the management port type on both servlet and reactive sides, and add regression tests for each implementation. See gh-49591 Signed-off-by: bbbbooo --- .../security/reactive/EndpointRequest.java | 25 +++++++++--- .../security/servlet/EndpointRequest.java | 31 +++++++++++---- .../reactive/EndpointRequestTests.java | 39 ++++++++++++++++++- .../servlet/EndpointRequestTests.java | 39 ++++++++++++++++++- 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java index 6702142749b7..dcaad8d55646 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java @@ -228,6 +228,13 @@ private boolean hasImplicitServerNamespace(ApplicationContext applicationContext && applicationContext.getParent() == null; } + protected final String getLinksPath(String basePath) { + if (StringUtils.hasText(basePath)) { + return basePath; + } + return (this.managementPortType == ManagementPortType.DIFFERENT) ? "/" : null; + } + protected final String toString(List endpoints, String emptyValue) { return (!endpoints.isEmpty()) ? endpoints.stream() .map(this::getEndpointId) @@ -326,7 +333,8 @@ protected ServerWebExchangeMatcher createDelegate(PathMappedEndpoints endpoints) streamPaths(this.includes, endpoints).forEach(paths::add); streamPaths(this.excludes, endpoints).forEach(paths::remove); List delegateMatchers = getDelegateMatchers(paths, this.httpMethod); - if (this.includeLinks && StringUtils.hasText(endpoints.getBasePath())) { + String linksPath = getLinksPath(endpoints.getBasePath()); + if (this.includeLinks && linksPath != null) { delegateMatchers.add(new LinksServerWebExchangeMatcher()); } if (delegateMatchers.isEmpty()) { @@ -362,10 +370,17 @@ private LinksServerWebExchangeMatcher() { @Override protected ServerWebExchangeMatcher createDelegate(WebEndpointProperties properties) { - if (StringUtils.hasText(properties.getBasePath())) { - return new OrServerWebExchangeMatcher( - new PathPatternParserServerWebExchangeMatcher(properties.getBasePath()), - new PathPatternParserServerWebExchangeMatcher(properties.getBasePath() + "/")); + String linksPath = getLinksPath(properties.getBasePath()); + if (linksPath != null) { + List linksMatchers = new ArrayList<>(); + linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath)); + if ("/".equals(linksPath)) { + linksMatchers.add(new PathPatternParserServerWebExchangeMatcher("")); + } + else { + linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath + "/")); + } + return new OrServerWebExchangeMatcher(linksMatchers); } return EMPTY_MATCHER; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index 772ed58be3b9..e8a16bd185a1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -225,13 +225,27 @@ protected final List getDelegateMatchers(RequestMatcherFactory r } protected List getLinksMatchers(RequestMatcherFactory requestMatcherFactory, - RequestMatcherProvider matcherProvider, String basePath) { + RequestMatcherProvider matcherProvider, String linksPath) { List linksMatchers = new ArrayList<>(); - linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, basePath)); - linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, basePath, "/")); + linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath)); + if (!"/".equals(linksPath)) { + linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath, "/")); + } return linksMatchers; } + protected String getLinksPath(WebApplicationContext context, String basePath) { + if (StringUtils.hasText(basePath)) { + return basePath; + } + ManagementPortType managementPortType = this.managementPortType; + if (managementPortType == null) { + managementPortType = ManagementPortType.get(context.getEnvironment()); + this.managementPortType = managementPortType; + } + return (managementPortType == ManagementPortType.DIFFERENT) ? "/" : null; + } + protected RequestMatcherProvider getRequestMatcherProvider(WebApplicationContext context) { try { return getRequestMatcherProviderBean(context); @@ -359,8 +373,9 @@ protected RequestMatcher createDelegate(WebApplicationContext context, List delegateMatchers = getDelegateMatchers(requestMatcherFactory, matcherProvider, paths, this.httpMethod); String basePath = endpoints.getBasePath(); - if (this.includeLinks && StringUtils.hasText(basePath)) { - delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory, matcherProvider, basePath)); + String linksPath = getLinksPath(context, basePath); + if (this.includeLinks && linksPath != null) { + delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory, matcherProvider, linksPath)); } if (delegateMatchers.isEmpty()) { return EMPTY_MATCHER; @@ -393,10 +408,10 @@ public static final class LinksRequestMatcher extends AbstractRequestMatcher { protected RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory) { WebEndpointProperties properties = context.getBean(WebEndpointProperties.class); - String basePath = properties.getBasePath(); - if (StringUtils.hasText(basePath)) { + String linksPath = getLinksPath(context, properties.getBasePath()); + if (linksPath != null) { return new OrRequestMatcher( - getLinksMatchers(requestMatcherFactory, getRequestMatcherProvider(context), basePath)); + getLinksMatchers(requestMatcherFactory, getRequestMatcherProvider(context), linksPath)); } return EMPTY_MATCHER; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java index 721e576da967..c1fbc54c65e0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.assertj.core.api.AssertDelegateTarget; import org.junit.jupiter.api.Test; @@ -35,6 +36,7 @@ import org.springframework.boot.web.context.WebServerApplicationContext; import org.springframework.boot.web.server.WebServer; import org.springframework.context.support.StaticApplicationContext; +import org.springframework.core.env.MapPropertySource; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -91,6 +93,15 @@ void toAnyEndpointWhenBasePathIsEmptyShouldNotMatchLinks() { assertMatcher.matches("/bar"); } + @Test + void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), + WebServerNamespace.MANAGEMENT, true); + assertMatcher.matches("/"); + assertMatcher.matches("/foo"); + } + @Test void toAnyEndpointShouldNotMatchOtherPath() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); @@ -143,6 +154,15 @@ void toLinksWhenBasePathEmptyShouldNotMatch() { assertMatcher.doesNotMatch("/"); } + @Test + void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { + ServerWebExchangeMatcher matcher = EndpointRequest.toLinks(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), + WebServerNamespace.MANAGEMENT, true); + assertMatcher.matches("/"); + assertMatcher.doesNotMatch("/foo"); + } + @Test void excludeByClassShouldNotMatchExcluded() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint() @@ -325,10 +345,25 @@ private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, PathMappedEndpoints pathMappedEndpoints, WebServerNamespace namespace) { + return assertMatcher(matcher, pathMappedEndpoints, namespace, false); + } + + private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, + PathMappedEndpoints pathMappedEndpoints, WebServerNamespace namespace, boolean managementPortDifferent) { StaticApplicationContext context = new StaticApplicationContext(); if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); + if (managementPortDifferent) { + context = new NamedStaticWebApplicationContext(namespace); + } + else { + NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); + context.setParent(parentContext); + } + } + if (managementPortDifferent) { + context.getEnvironment() + .getPropertySources() + .addFirst(new MapPropertySource("test", Map.of("management.server.port", 0))); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 0a16d3cade70..6e1c21029e91 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import org.assertj.core.api.AssertDelegateTarget; @@ -36,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.web.context.WebServerApplicationContext; import org.springframework.boot.web.server.WebServer; +import org.springframework.core.env.MapPropertySource; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; @@ -91,6 +93,15 @@ void toAnyEndpointWhenBasePathIsEmptyShouldNotMatchLinks() { assertMatcher.matches("/bar"); } + @Test + void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { + RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, + WebServerNamespace.MANAGEMENT, true); + assertMatcher.matches("/"); + assertMatcher.matches("/foo"); + } + @Test void toAnyEndpointShouldNotMatchOtherPath() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); @@ -150,6 +161,15 @@ void toLinksWhenBasePathEmptyShouldNotMatch() { assertMatcher.doesNotMatch("/"); } + @Test + void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { + RequestMatcher matcher = EndpointRequest.toLinks(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, + WebServerNamespace.MANAGEMENT, true); + assertMatcher.matches("/"); + assertMatcher.doesNotMatch("/foo"); + } + @Test void excludeByClassShouldNotMatchExcluded() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint().excluding(FooEndpoint.class, BazServletEndpoint.class); @@ -350,10 +370,25 @@ private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEnd private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints, RequestMatcherProvider matcherProvider, WebServerNamespace namespace) { + return assertMatcher(matcher, pathMappedEndpoints, matcherProvider, namespace, false); + } + + private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints, + RequestMatcherProvider matcherProvider, WebServerNamespace namespace, boolean managementPortDifferent) { StaticWebApplicationContext context = new StaticWebApplicationContext(); if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); + if (managementPortDifferent) { + context = new NamedStaticWebApplicationContext(namespace); + } + else { + NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); + context.setParent(parentContext); + } + } + if (managementPortDifferent) { + context.getEnvironment() + .getPropertySources() + .addFirst(new MapPropertySource("test", Map.of("management.server.port", 0))); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { From a148983e7fe8ae58064390e7d64f3fa70f38e878 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Mar 2026 16:53:54 +0000 Subject: [PATCH 139/376] Polish "Fix EndpointRequest links matching for separate management port" See gh-49591 Signed-off-by: Andy Wilkinson --- .../security/reactive/EndpointRequest.java | 5 +- .../security/servlet/EndpointRequest.java | 2 +- .../reactive/EndpointRequestTests.java | 30 +++----- .../servlet/EndpointRequestTests.java | 30 +++----- ...ractSampleActuatorCustomSecurityTests.java | 74 ++++++++---------- .../CustomServletPathSampleActuatorTests.java | 4 +- ...asePathSampleActuatorApplicationTests.java | 77 +++++++++++++++++++ ...sePathSampleActuatorApplicationTests.java} | 12 +-- ...CustomServletPathSampleActuatorTests.java} | 10 +-- ...ctuatorCustomSecurityApplicationTests.java | 9 +-- 10 files changed, 146 insertions(+), 107 deletions(-) create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java rename spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/{ManagementPortAndPathSampleActuatorApplicationTests.java => ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java} (85%) rename spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/{ManagementPortCustomServletPathSampleActuatorTests.java => ManagementServerWithCustomServletPathSampleActuatorTests.java} (86%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java index dcaad8d55646..5e09360669bb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java @@ -374,10 +374,7 @@ protected ServerWebExchangeMatcher createDelegate(WebEndpointProperties properti if (linksPath != null) { List linksMatchers = new ArrayList<>(); linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath)); - if ("/".equals(linksPath)) { - linksMatchers.add(new PathPatternParserServerWebExchangeMatcher("")); - } - else { + if (!linksPath.endsWith("/")) { linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath + "/")); } return new OrServerWebExchangeMatcher(linksMatchers); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index e8a16bd185a1..f0e5f28734e0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -228,7 +228,7 @@ protected List getLinksMatchers(RequestMatcherFactory requestMat RequestMatcherProvider matcherProvider, String linksPath) { List linksMatchers = new ArrayList<>(); linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath)); - if (!"/".equals(linksPath)) { + if (!linksPath.endsWith("/")) { linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath, "/")); } return linksMatchers; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java index c1fbc54c65e0..151ea82b28c5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import org.assertj.core.api.AssertDelegateTarget; import org.junit.jupiter.api.Test; @@ -33,10 +32,10 @@ import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.context.WebServerApplicationContext; import org.springframework.boot.web.server.WebServer; import org.springframework.context.support.StaticApplicationContext; -import org.springframework.core.env.MapPropertySource; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -97,7 +96,7 @@ void toAnyEndpointWhenBasePathIsEmptyShouldNotMatchLinks() { void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), - WebServerNamespace.MANAGEMENT, true); + WebServerNamespace.MANAGEMENT); assertMatcher.matches("/"); assertMatcher.matches("/foo"); } @@ -158,7 +157,7 @@ void toLinksWhenBasePathEmptyShouldNotMatch() { void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { ServerWebExchangeMatcher matcher = EndpointRequest.toLinks(); RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), - WebServerNamespace.MANAGEMENT, true); + WebServerNamespace.MANAGEMENT); assertMatcher.matches("/"); assertMatcher.doesNotMatch("/foo"); } @@ -345,25 +344,14 @@ private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, PathMappedEndpoints pathMappedEndpoints, WebServerNamespace namespace) { - return assertMatcher(matcher, pathMappedEndpoints, namespace, false); - } - - private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, - PathMappedEndpoints pathMappedEndpoints, WebServerNamespace namespace, boolean managementPortDifferent) { - StaticApplicationContext context = new StaticApplicationContext(); + StaticWebApplicationContext context; if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - if (managementPortDifferent) { - context = new NamedStaticWebApplicationContext(namespace); - } - else { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); - } + context = new NamedStaticWebApplicationContext(namespace); + context.setParent(new StaticWebApplicationContext()); + TestPropertyValues.of("management.server.port=0").applyTo(context); } - if (managementPortDifferent) { - context.getEnvironment() - .getPropertySources() - .addFirst(new MapPropertySource("test", Map.of("management.server.port", 0))); + else { + context = new StaticWebApplicationContext(); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 6e1c21029e91..7a7620f2564b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import org.assertj.core.api.AssertDelegateTarget; @@ -35,9 +34,9 @@ import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.context.WebServerApplicationContext; import org.springframework.boot.web.server.WebServer; -import org.springframework.core.env.MapPropertySource; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; @@ -97,7 +96,7 @@ void toAnyEndpointWhenBasePathIsEmptyShouldNotMatchLinks() { void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, - WebServerNamespace.MANAGEMENT, true); + WebServerNamespace.MANAGEMENT); assertMatcher.matches("/"); assertMatcher.matches("/foo"); } @@ -165,7 +164,7 @@ void toLinksWhenBasePathEmptyShouldNotMatch() { void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { RequestMatcher matcher = EndpointRequest.toLinks(); RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, - WebServerNamespace.MANAGEMENT, true); + WebServerNamespace.MANAGEMENT); assertMatcher.matches("/"); assertMatcher.doesNotMatch("/foo"); } @@ -370,25 +369,14 @@ private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEnd private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints, RequestMatcherProvider matcherProvider, WebServerNamespace namespace) { - return assertMatcher(matcher, pathMappedEndpoints, matcherProvider, namespace, false); - } - - private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints, - RequestMatcherProvider matcherProvider, WebServerNamespace namespace, boolean managementPortDifferent) { - StaticWebApplicationContext context = new StaticWebApplicationContext(); + StaticWebApplicationContext context; if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - if (managementPortDifferent) { - context = new NamedStaticWebApplicationContext(namespace); - } - else { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); - } + context = new NamedStaticWebApplicationContext(namespace); + context.setParent(new StaticWebApplicationContext()); + TestPropertyValues.of("management.server.port=0").applyTo(context); } - if (managementPortDifferent) { - context.getEnvironment() - .getPropertySources() - .addFirst(new MapPropertySource("test", Map.of("management.server.port", 0))); + else { + context = new StaticWebApplicationContext(); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java index bc9f315fd367..7abb0283fe8d 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java @@ -37,7 +37,7 @@ abstract class AbstractSampleActuatorCustomSecurityTests { abstract String getPath(); - abstract String getManagementPath(); + abstract String getActuatorPath(); abstract Environment getEnvironment(); @@ -58,119 +58,110 @@ void testInsecureStaticResources() { @Test void actuatorInsecureEndpoint() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health", - String.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/health", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).contains("\"status\":\"UP\""); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health/diskSpace", String.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/health/diskSpace", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).contains("\"status\":\"UP\""); } @Test void actuatorLinksWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator", Object.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorLinksWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorLinksWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator", - Object.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - adminRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + adminRestTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void actuatorSecureEndpointWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorSecureEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorSecureEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env/", Object.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env/", Object.class); // EndpointRequest matches the trailing slash but MVC doesn't assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - entity = adminRestTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void secureServletEndpointWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void secureServletEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void secureServletEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void actuatorCustomMvcSecureEndpointWithAnonymous() { - ResponseEntity entity = restTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorCustomMvcSecureEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorCustomMvcSecureEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("test"); assertThat(entity.getHeaders().getFirst("echo")).isEqualTo("test"); @@ -178,8 +169,7 @@ void actuatorCustomMvcSecureEndpointWithAuthorizedUser() { @Test void actuatorExcludedFromEndpointRequestMatcher() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/mappings", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/mappings", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java index ebc95a8a2bfe..7dda1f9e907a 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java @@ -42,8 +42,8 @@ String getPath() { } @Override - String getManagementPath() { - return "http://localhost:" + this.port + "/example"; + String getActuatorPath() { + return "http://localhost:" + this.port + "/example/actuator"; } @Override diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java new file mode 100644 index 000000000000..636a59291ab2 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.actuator.customsecurity; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for a separate management server with custom base path and web + * endpoints base path. + * + * @author Dave Syer + * @author Madhura Bhave + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", + "management.server.base-path=/management", "management.endpoints.web.base-path=/" }) +class ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests + extends AbstractSampleActuatorCustomSecurityTests { + + @LocalServerPort + private int port; + + @LocalManagementPort + private int managementPort; + + @Autowired + private Environment environment; + + @Test + void testMissing() { + ResponseEntity entity = new TestRestTemplate("admin", "admin") + .getForEntity(getActuatorPath() + "/missing", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(entity.getBody()).contains("\"status\":404"); + } + + @Override + String getPath() { + return "http://localhost:" + this.port; + } + + @Override + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/management"; + } + + @Override + Environment getEnvironment() { + return this.environment; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java similarity index 85% rename from spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java rename to spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java index aa08feef9ee2..353902eedbcc 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java @@ -31,15 +31,15 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for separate management and main service ports with custom management - * context path. + * Integration tests for a separate management server with a custom base path. * * @author Dave Syer * @author Madhura Bhave */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", "management.server.base-path=/management" }) -class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSampleActuatorCustomSecurityTests { +class ManagementServerWithCustomBasePathSampleActuatorApplicationTests + extends AbstractSampleActuatorCustomSecurityTests { @LocalServerPort private int port; @@ -53,7 +53,7 @@ class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSample @Test void testMissing() { ResponseEntity entity = new TestRestTemplate("admin", "admin") - .getForEntity("http://localhost:" + this.managementPort + "/management/actuator/missing", String.class); + .getForEntity(getActuatorPath() + "/missing", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); assertThat(entity.getBody()).contains("\"status\":404"); } @@ -64,8 +64,8 @@ String getPath() { } @Override - String getManagementPath() { - return "http://localhost:" + this.managementPort + "/management"; + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/management/actuator"; } @Override diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java similarity index 86% rename from spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java rename to spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java index da042e709958..2fb5aea4684d 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java @@ -30,14 +30,14 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for separate management and main service ports with custom dispatcher - * servlet path. + * Integration tests for a separate management server with a custom dispatcher servlet + * path. * * @author Madhura Bhave */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", "spring.mvc.servlet.path=/example" }) -class ManagementPortCustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests { +class ManagementServerWithCustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests { @LocalServerPort private int port; @@ -61,8 +61,8 @@ String getPath() { } @Override - String getManagementPath() { - return "http://localhost:" + this.managementPort; + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/actuator"; } @Override diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java index 7227fa42028e..4be29ed6ceee 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java @@ -52,8 +52,8 @@ String getPath() { } @Override - String getManagementPath() { - return "http://localhost:" + this.port; + String getActuatorPath() { + return "http://localhost:" + this.port + "/actuator"; } @Override @@ -72,10 +72,9 @@ void testInsecureApplicationPath() { @Test void mvcMatchersCanBeUsedToSecureActuators() { - ResponseEntity entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans", - Object.class); + ResponseEntity entity = beansRestTemplate().getForEntity(getActuatorPath() + "/beans", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans/", Object.class); + entity = beansRestTemplate().getForEntity(getActuatorPath() + "/beans/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } From 49e5d5bc5b448608e118639dab0fd768828dd0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Mar 2026 21:09:04 +0100 Subject: [PATCH 140/376] Upgrade to Spring Security 7.0.4 Closes gh-49530 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index dc01191deaee..0ac1d4611770 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2570,7 +2570,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-restdocs/releases/tag/v{version}") } } - library("Spring Security", "7.0.4-SNAPSHOT") { + library("Spring Security", "7.0.4") { considerSnapshots() group("org.springframework.security") { bom("spring-security-bom") From d04c42d315dda46450ae359e5c3c49a8dd0f49ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Mar 2026 21:10:33 +0100 Subject: [PATCH 141/376] Upgrade to Spring Security 6.5.9 Closes gh-49527 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 4d835c734dc5..3339629161b3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2452,7 +2452,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-retry/releases/tag/v{version}") } } - library("Spring Security", "6.5.9-SNAPSHOT") { + library("Spring Security", "6.5.9") { considerSnapshots() group("org.springframework.security") { bom("spring-security-bom") From f5830536d4c3baf541376c974850fe49c103eaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Mar 2026 09:28:38 +0100 Subject: [PATCH 142/376] Upgrade to Spring Kafka 3.3.14 Closes gh-49409 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3339629161b3..5cd8962de3bf 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2371,7 +2371,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.3.14-SNAPSHOT") { + library("Spring Kafka", "3.3.14") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 1754358a854a98702daf2b24dd10530199fed901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Mar 2026 09:28:38 +0100 Subject: [PATCH 143/376] Upgrade to Spring Pulsar 1.2.16 Closes gh-49410 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 5cd8962de3bf..c809c83c7e40 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2409,7 +2409,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.2.16-SNAPSHOT") { + library("Spring Pulsar", "1.2.16") { considerSnapshots() group("org.springframework.pulsar") { bom("spring-pulsar-bom") From 04eba1fe65cbcd153be0691eda0afa85c5bef558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Mar 2026 09:28:53 +0100 Subject: [PATCH 144/376] Upgrade to Spring Kafka 4.0.4 Closes gh-49419 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 0ac1d4611770..90f8730749d8 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2502,7 +2502,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "4.0.4-SNAPSHOT") { + library("Spring Kafka", "4.0.4") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 8769104df6b099f5f3a00aa2f799a2b0ca553ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Mar 2026 09:28:54 +0100 Subject: [PATCH 145/376] Upgrade to Spring Pulsar 2.0.4 Closes gh-49420 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 90f8730749d8..f9ac290693e4 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2540,7 +2540,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "2.0.4-SNAPSHOT") { + library("Spring Pulsar", "2.0.4") { considerSnapshots() group("org.springframework.pulsar") { bom("spring-pulsar-bom") From 6dab9105affc85541354c74ae7150c8433083abf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 17 Mar 2026 09:11:14 +0000 Subject: [PATCH 146/376] Roll back JAX-B upgrades and prohibit 4.0.7 of the RI See gh-49615 See gh-49616 --- spring-boot-project/spring-boot-dependencies/build.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index c809c83c7e40..758c8a8a4d57 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -448,7 +448,11 @@ bom { releaseNotes("https://github.com/git-commit-id/git-commit-id-maven-plugin/releases/tag/v{version}") } } - library("Glassfish JAXB", "4.0.7") { + library("Glassfish JAXB", "4.0.6") { + prohibit { + versionRange "[4.0.7]" + because "it contains a regression (https://github.com/spring-projects/spring-boot/issues/49607#issuecomment-4072451614)" + } group("org.glassfish.jaxb") { bom("jaxb-bom") { permit("com.sun.istack:istack-commons-runtime") @@ -923,7 +927,7 @@ bom { .formatted(version.major(), version.minor()), "jakarta.ws.rs") } } - library("Jakarta XML Bind", "4.0.5") { + library("Jakarta XML Bind", "4.0.4") { alignWith { version { of "jakarta.xml.bind:jakarta.xml.bind-api" From 73083c2b51d77365297b19c59ae424964446a33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Mar 2026 16:00:21 +0100 Subject: [PATCH 147/376] Upgrade to Kafka 4.1.2 See also https://github.com/yawkat/lz4-java/wiki/Gradle-and-org.lz4:lz4%E2%80%90java:1.8.1 Closes gh-49627 --- documentation/spring-boot-docs/build.gradle | 13 ++++++++++--- platform/spring-boot-dependencies/build.gradle | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/documentation/spring-boot-docs/build.gradle b/documentation/spring-boot-docs/build.gradle index e95c95d2bd2a..449baac2faeb 100644 --- a/documentation/spring-boot-docs/build.gradle +++ b/documentation/spring-boot-docs/build.gradle @@ -371,9 +371,16 @@ configurations { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) } extendsFrom configurations.javadoc - resolutionStrategy.eachDependency { - if (it.requested.group == 'org.opensaml') { - it.useVersion '4.0.1' + resolutionStrategy { + capabilitiesResolution { + withCapability("org.lz4:lz4-java") { + select(candidates.find { ((ModuleComponentIdentifier) it.id).group == "at.yawk.lz4" }) + } + } + eachDependency { + if (it.requested.group == 'org.opensaml') { + it.useVersion '4.0.1' + } } } } diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index dbd8dd7959db..e9dac9ebc9f9 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1172,7 +1172,7 @@ bom { releaseNotes("https://junit.org/junit5/docs/{version}/release-notes") } } - library("Kafka", "4.1.1") { + library("Kafka", "4.1.2") { group("org.apache.kafka") { modules = [ "connect", From 5ee4ffe567322a87fde3f08b54dabc4a670c886a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:20 +0100 Subject: [PATCH 148/376] Upgrade to JBoss Logging 3.6.3.Final Closes gh-49630 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 758c8a8a4d57..52db6d1c6b0e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1017,7 +1017,7 @@ bom { .formatted(version.toString().replace(".java11", ""))) } } - library("JBoss Logging", "3.6.2.Final") { + library("JBoss Logging", "3.6.3.Final") { group("org.jboss.logging") { modules = [ "jboss-logging" From a5db799f8aec7b7ce60b2b3610b310dcc44d7a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:24 +0100 Subject: [PATCH 149/376] Upgrade to Spring Integration 6.5.8 Closes gh-49631 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 52db6d1c6b0e..60d83632b796 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2360,7 +2360,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "6.5.7") { + library("Spring Integration", "6.5.8") { considerSnapshots() group("org.springframework.integration") { bom("spring-integration-bom") From 691583437e0814b6fd5aea257e2cf5e0919be8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:24 +0100 Subject: [PATCH 150/376] Upgrade to Spring WS 4.1.3 Closes gh-49528 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 60d83632b796..e0d7b302367b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2494,7 +2494,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-session/releases/tag/{version}") } } - library("Spring WS", "4.1.3-SNAPSHOT") { + library("Spring WS", "4.1.3") { considerSnapshots() group("org.springframework.ws") { bom("spring-ws-bom") From dab797eadf6cb50a0e298fd811de3f822beb9898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:29 +0100 Subject: [PATCH 151/376] Upgrade to JBoss Logging 3.6.3.Final Closes gh-49632 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index e9dac9ebc9f9..16556916d112 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -991,7 +991,7 @@ bom { .formatted(version.toString().replace(".java11", ""))) } } - library("JBoss Logging", "3.6.2.Final") { + library("JBoss Logging", "3.6.3.Final") { group("org.jboss.logging") { modules = [ "jboss-logging" From 91ddcff68f0ddc91add89b667bafdd1dedcfae12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:29 +0100 Subject: [PATCH 152/376] Upgrade to Spring Integration 7.0.4 Closes gh-49529 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 16556916d112..348cfe4db632 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2491,7 +2491,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "7.0.4-SNAPSHOT") { + library("Spring Integration", "7.0.4") { considerSnapshots() group("org.springframework.integration") { bom("spring-integration-bom") From f3d20bbecf4c7e925fa718451ff9a33e7b21de7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 07:50:29 +0100 Subject: [PATCH 153/376] Upgrade to Spring WS 5.0.1 Closes gh-49531 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 348cfe4db632..12f87c5ac812 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2612,7 +2612,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-session/releases/tag/{version}") } } - library("Spring WS", "5.0.1-SNAPSHOT") { + library("Spring WS", "5.0.1") { considerSnapshots() group("org.springframework.ws") { bom("spring-ws-bom") From 9bc3768814759f490d937949fbfbbd205c0f6628 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:14:37 +0000 Subject: [PATCH 154/376] Bump @springio/asciidoctor-extensions in /antora Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.17 to 1.0.0-alpha.18. - [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc) - [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.17...v1.0.0-alpha.18) --- updated-dependencies: - dependency-name: "@springio/asciidoctor-extensions" dependency-version: 1.0.0-alpha.18 dependency-type: direct:production update-type: version-update:semver-patch ... See gh-49622 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 10 +++++----- antora/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index 7037cbae437a..76cf383a22fe 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -12,7 +12,7 @@ "@springio/antora-extensions": "1.14.7", "@springio/antora-xref-extension": "1.0.0-alpha.5", "@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.10", - "@springio/asciidoctor-extensions": "1.0.0-alpha.17" + "@springio/asciidoctor-extensions": "1.0.0-alpha.18" } }, "node_modules/@antora/asciidoc-loader": { @@ -511,15 +511,15 @@ } }, "node_modules/@springio/asciidoctor-extensions": { - "version": "1.0.0-alpha.17", - "resolved": "https://registry.npmjs.org/@springio/asciidoctor-extensions/-/asciidoctor-extensions-1.0.0-alpha.17.tgz", - "integrity": "sha512-mvVEKZNdGQu1+raOF+sy1DKWZrq1bB0dM4ZVlIIFV+jJ/mengXByq7YQk63nMOFsue6fGlgb3nQUte8EbvoQAw==", + "version": "1.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@springio/asciidoctor-extensions/-/asciidoctor-extensions-1.0.0-alpha.18.tgz", + "integrity": "sha512-deR4Dsl7GXKrmP4f1Xk2UHVrZ21Cn9sGbcxgwmpZbdvVAD4E8Gnmckns4Awmre6K6dJkaqCK9j4yoQQ9NGyN3g==", "license": "ASL-2.0", "dependencies": { "js-yaml": "~4.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.0.0" } }, "node_modules/abort-controller": { diff --git a/antora/package.json b/antora/package.json index a998ede3455b..38327b93e9b1 100644 --- a/antora/package.json +++ b/antora/package.json @@ -10,7 +10,7 @@ "@springio/antora-xref-extension": "1.0.0-alpha.5", "@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.10", "@asciidoctor/tabs": "1.0.0-beta.6", - "@springio/asciidoctor-extensions": "1.0.0-alpha.17" + "@springio/asciidoctor-extensions": "1.0.0-alpha.18" }, "config": { "ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip" From 07f4f432815778b5cf367471677ee6c5a8058f9b Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 18 Mar 2026 14:18:28 +0100 Subject: [PATCH 155/376] Test against Java 26 GA Closes gh-49642 --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df301a460259..7a4b176fc913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,6 @@ jobs: toolchain: false - version: 26 toolchain: true - early-access: true exclude: - os: name: Linux From 01fbede2b27237616e215fe0df7c294ae47bdd73 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 18 Mar 2026 16:26:50 +0100 Subject: [PATCH 156/376] Handle all requests in CloudFoundry mapping Prior to this commit, the Servlet and Reactive CloudFoundry Actuator Handler mapping would only handle requests to actual Actuator endpoints. Here, our original intent has always been to reserve this URL namespace for the CloudFoundry use case and not delegate to other handler mappings. This commit ensures that requests under this path should be processed by known endpoints, or result in a "HTTP 403 Forbidden" response. Fixes gh-49645 --- ...dFoundryWebFluxEndpointHandlerMapping.java | 6 +++ ...CloudFoundryActuatorAutoConfiguration.java | 25 ++---------- ...CloudFoundryActuatorAutoConfiguration.java | 16 ++------ ...undryWebEndpointServletHandlerMapping.java | 6 +++ ...oundryWebFluxEndpointIntegrationTests.java | 6 +++ ...FoundryActuatorAutoConfigurationTests.java | 2 +- ...FoundryActuatorAutoConfigurationTests.java | 4 +- ...FoundryMvcWebEndpointIntegrationTests.java | 11 ++++++ ...AbstractWebFluxEndpointHandlerMapping.java | 39 +++++++++++++++++++ .../AbstractWebMvcEndpointHandlerMapping.java | 31 +++++++++++++++ 10 files changed, 109 insertions(+), 37 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java index c8643f797da5..50833eb8fce3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java @@ -77,6 +77,12 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH this.securityInterceptor = securityInterceptor; } + @Override + protected void initHandlerMethods() { + super.initHandlerMethods(); + registerCatchAllMapping(HttpStatus.FORBIDDEN); + } + @Override protected ReactiveWebOperation wrapReactiveWebOperation(ExposableWebEndpoint endpoint, WebOperation operation, ReactiveWebOperation reactiveWebOperation) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java index e81ee8992711..508fd828819c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; @@ -36,7 +35,6 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; -import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; import org.springframework.boot.actuate.info.GitInfoContributor; @@ -64,7 +62,6 @@ import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; -import org.springframework.util.function.SingletonSupplier; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.WebFilter; @@ -159,20 +156,15 @@ private CorsConfiguration getCorsConfiguration() { static class IgnoredPathsSecurityConfiguration { @Bean - static WebFilterChainPostProcessor webFilterChainPostProcessor( - ObjectProvider handlerMapping) { - return new WebFilterChainPostProcessor(handlerMapping); + static WebFilterChainPostProcessor webFilterChainPostProcessor() { + return new WebFilterChainPostProcessor(); } } static class WebFilterChainPostProcessor implements BeanPostProcessor { - private final Supplier pathMappedEndpoints; - - WebFilterChainPostProcessor(ObjectProvider handlerMapping) { - this.pathMappedEndpoints = SingletonSupplier - .of(() -> new PathMappedEndpoints(BASE_PATH, () -> handlerMapping.getObject().getAllEndpoints())); + WebFilterChainPostProcessor() { } @Override @@ -184,9 +176,8 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw } private WebFilterChainProxy postProcess(WebFilterChainProxy existing) { - List paths = getPaths(this.pathMappedEndpoints.get()); ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers - .pathMatchers(paths.toArray(new String[] {})); + .pathMatchers(BASE_PATH + "/**"); WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange); MatcherSecurityWebFilterChain ignoredRequestFilterChain = new MatcherSecurityWebFilterChain( cloudFoundryRequestMatcher, Collections.singletonList(noOpFilter)); @@ -195,14 +186,6 @@ private WebFilterChainProxy postProcess(WebFilterChainProxy existing) { return new WebFilterChainProxy(ignoredRequestFilterChain, allRequestsFilterChain); } - private static List getPaths(PathMappedEndpoints pathMappedEndpoints) { - List paths = new ArrayList<>(); - pathMappedEndpoints.getAllPaths().forEach((path) -> paths.add(path + "/**")); - paths.add(BASE_PATH); - paths.add(BASE_PATH + "/"); - return paths; - } - } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java index e8a01f0121fb..0b2ce6c0c5a9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java @@ -33,7 +33,6 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; -import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.info.GitInfoContributor; @@ -65,7 +64,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; -import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.servlet.DispatcherServlet; @@ -172,22 +170,16 @@ public static class IgnoredCloudFoundryPathsWebSecurityConfiguration { @Bean @Order(FILTER_CHAIN_ORDER) - SecurityFilterChain cloudFoundrySecurityFilterChain(HttpSecurity http, - CloudFoundryWebEndpointServletHandlerMapping handlerMapping) throws Exception { - RequestMatcher cloudFoundryRequest = getRequestMatcher(handlerMapping); + SecurityFilterChain cloudFoundrySecurityFilterChain(HttpSecurity http) throws Exception { + RequestMatcher cloudFoundryRequest = getRequestMatcher(); http.csrf((csrf) -> csrf.ignoringRequestMatchers(cloudFoundryRequest)); http.securityMatchers((matches) -> matches.requestMatchers(cloudFoundryRequest)) .authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll()); return http.build(); } - private RequestMatcher getRequestMatcher(CloudFoundryWebEndpointServletHandlerMapping handlerMapping) { - PathMappedEndpoints endpoints = new PathMappedEndpoints(BASE_PATH, handlerMapping::getAllEndpoints); - List matchers = new ArrayList<>(); - endpoints.getAllPaths().forEach((path) -> matchers.add(pathMatcher(path + "/**"))); - matchers.add(pathMatcher(BASE_PATH)); - matchers.add(pathMatcher(BASE_PATH + "/")); - return new OrRequestMatcher(matchers); + private RequestMatcher getRequestMatcher() { + return pathMatcher(BASE_PATH + "/**"); } private PathPatternRequestMatcher pathMatcher(String path) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java index f9aeed9a374f..0d885784c00b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java @@ -80,6 +80,12 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin this.allEndpoints = allEndpoints; } + @Override + protected void initHandlerMethods() { + super.initHandlerMethods(); + registerCatchAllMapping(HttpStatus.FORBIDDEN); + } + @Override protected ServletWebOperation wrapServletWebOperation(ExposableWebEndpoint endpoint, WebOperation operation, ServletWebOperation servletWebOperation) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java index 2f2aaa9bed6c..ed973e4c3aa0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java @@ -210,6 +210,12 @@ void linksToOtherEndpointsWithRestrictedAccess() { .doesNotExist())); } + @Test + void unknownEndpointsAreForbidden() { + this.contextRunner.run(withWebTestClient( + (client) -> client.get().uri("/cfApplication/unknown").exchange().expectStatus().isForbidden())); + } + private ContextConsumer withWebTestClient( Consumer clientConsumer) { return (context) -> { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java index 3a96362756c4..92484f7ae96a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java @@ -192,7 +192,7 @@ void cloudFoundryPathsIgnoredBySpringSecurity() { assertThat(cfBaseWithTrailingSlashRequestMatches).isTrue(); assertThat(cfRequestMatches).isTrue(); assertThat(cfRequestWithAdditionalPathMatches).isTrue(); - assertThat(otherCfRequestMatches).isFalse(); + assertThat(otherCfRequestMatches).isTrue(); assertThat(otherRequestMatches).isFalse(); otherRequestMatches = filters.get(1) .matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build())) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java index b98b9f17203b..39e90f0daa2c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java @@ -188,9 +188,7 @@ void cloudFoundryPathsPermittedBySpringSecurity() { testCloudFoundrySecurity(request, BASE_PATH + "/", chain); testCloudFoundrySecurity(request, BASE_PATH + "/test", chain); testCloudFoundrySecurity(request, BASE_PATH + "/test/a", chain); - request.setServletPath(BASE_PATH + "/other-path"); - request.setRequestURI(BASE_PATH + "/other-path"); - assertThat(chain.matches(request)).isFalse(); + testCloudFoundrySecurity(request, BASE_PATH + "/other-path", chain); request.setServletPath("/some-other-path"); request.setRequestURI("/some-other-path"); assertThat(chain.matches(request)).isFalse(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java index 9ea193654c07..fda667710948 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java @@ -199,6 +199,17 @@ void linksToOtherEndpointsWithRestrictedAccess() { .doesNotExist()); } + @Test + void unknownEndpointsAreForbidden() { + load(TestEndpointConfiguration.class, + (client) -> client.get() + .uri("/cfApplication/unknown") + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus() + .isForbidden()); + } + private void load(Class configuration, Consumer clientConsumer) { BiConsumer consumer = (context, client) -> clientConsumer.accept(client); new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 603421386f8e..4d9da5856994 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -56,6 +56,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; @@ -104,6 +105,9 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi private final Method handleReadMethod = ReflectionUtils.findMethod(ReadOperationHandler.class, "handle", ServerWebExchange.class); + private final Method handleCatchAllMethod = ReflectionUtils.findMethod(CatchAllHandler.class, "handle", + ServerWebExchange.class); + private final boolean shouldRegisterLinksMapping; /** @@ -177,6 +181,17 @@ protected ReactiveWebOperation wrapReactiveWebOperation(ExposableWebEndpoint end return reactiveWebOperation; } + /** + * Register a "catch all" handler for the rest of actuator namespace, ensuring that + * all requests are handled by this handler mapping. + * @param responseStatus the response status to use for handled requests + */ + protected void registerCatchAllMapping(HttpStatus responseStatus) { + String subPath = this.endpointMapping.createSubPath("/**"); + registerMapping(RequestMappingInfo.paths(subPath).build(), new CatchAllHandler(responseStatus), + this.handleCatchAllMethod); + } + private RequestMappingInfo createRequestMappingInfo(WebOperation operation) { WebOperationRequestPredicate predicate = operation.getRequestPredicate(); String path = this.endpointMapping.createSubPath(predicate.getPath()); @@ -483,6 +498,30 @@ public String toString() { } + /** + * Catch-all handler that always replies with a fixed HTTP status. + */ + private static final class CatchAllHandler { + + private final HttpStatus responseStatus; + + CatchAllHandler(HttpStatus responseStatus) { + this.responseStatus = responseStatus; + } + + Mono handle(ServerWebExchange exchange) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(this.responseStatus); + return response.setComplete(); + } + + @Override + public String toString() { + return "Actuator catch-all endpoint"; + } + + } + private static class WebFluxEndpointHandlerMethod extends HandlerMethod { WebFluxEndpointHandlerMethod(Object bean, Method method) { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index 719de6b50261..66cd1eef84f8 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -103,6 +103,9 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin private final Method handleMethod = ReflectionUtils.findMethod(OperationHandler.class, "handle", HttpServletRequest.class, Map.class); + private final Method catchAllMethod = ReflectionUtils.findMethod(CatchAllHandler.class, "handle", + HttpServletResponse.class); + private RequestMappingInfo.BuilderConfiguration builderConfig = new RequestMappingInfo.BuilderConfiguration(); /** @@ -195,6 +198,17 @@ protected ServletWebOperation wrapServletWebOperation(ExposableWebEndpoint endpo return servletWebOperation; } + /** + * Register a "catch all" handler for the rest of actuator namespace, ensuring that + * all requests are handled by this handler mapping. + * @param responseStatus the response status to use for handled requests + */ + protected void registerCatchAllMapping(HttpStatus responseStatus) { + String subPath = this.endpointMapping.createSubPath("/**"); + registerMapping(RequestMappingInfo.paths(subPath).options(this.builderConfig).build(), + new CatchAllHandler(responseStatus), this.catchAllMethod); + } + private RequestMappingInfo createRequestMappingInfo(WebOperationRequestPredicate predicate, String path) { String subPath = this.endpointMapping.createSubPath(path); List paths = new ArrayList<>(); @@ -441,6 +455,23 @@ public String toString() { } + /** + * Catch-all handler that always replies with a fixed HTTP status. + */ + private static final class CatchAllHandler { + + private final HttpStatus responseStatus; + + CatchAllHandler(HttpStatus responseStatus) { + this.responseStatus = responseStatus; + } + + void handle(HttpServletResponse response) { + response.setStatus(this.responseStatus.value()); + } + + } + /** * {@link HandlerMethod} subclass for endpoint information logging. */ From 1f2ea4a6db03eb1d666a174a525f2bff658b8296 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 18 Mar 2026 17:35:22 +0100 Subject: [PATCH 157/376] Revisit EndpointRequest matcher for additional paths Prior to this commit, the `EndpointRequest` request matcher generator would create many, complex matchers for the case of health groups exposed on additional paths. This commit revisits the matcher generation and simplifies their matching to avoid conflicts with other matchers. Fixes gh-49648 --- .../autoconfigure/security/reactive/EndpointRequest.java | 6 +++--- .../autoconfigure/security/servlet/EndpointRequest.java | 7 +++---- .../security/reactive/EndpointRequestTests.java | 1 + .../security/servlet/EndpointRequestTests.java | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java index 5e09360669bb..f2bbd90ca23c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java @@ -441,12 +441,12 @@ protected boolean ignoreApplicationContext(ApplicationContext applicationContext @Override protected ServerWebExchangeMatcher createDelegate(PathMappedEndpoints endpoints) { - Set paths = this.endpoints.stream() + List delegateMatchers = this.endpoints.stream() .filter(Objects::nonNull) .map(this::getEndpointId) .flatMap((endpointId) -> streamAdditionalPaths(endpoints, endpointId)) - .collect(Collectors.toCollection(LinkedHashSet::new)); - List delegateMatchers = getDelegateMatchers(paths, this.httpMethod); + .map(PathPatternParserServerWebExchangeMatcher::new) + .collect(Collectors.toCollection(ArrayList::new)); return (!CollectionUtils.isEmpty(delegateMatchers)) ? new OrServerWebExchangeMatcher(delegateMatchers) : EMPTY_MATCHER; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index f0e5f28734e0..8c564b05697d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -475,13 +475,12 @@ protected RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory) { PathMappedEndpoints endpoints = context.getBean(PathMappedEndpoints.class); RequestMatcherProvider matcherProvider = getRequestMatcherProvider(context); - Set paths = this.endpoints.stream() + List delegateMatchers = this.endpoints.stream() .filter(Objects::nonNull) .map(this::getEndpointId) .flatMap((endpointId) -> streamAdditionalPaths(endpoints, endpointId)) - .collect(Collectors.toCollection(LinkedHashSet::new)); - List delegateMatchers = getDelegateMatchers(requestMatcherFactory, matcherProvider, paths, - this.httpMethod); + .map((path) -> requestMatcherFactory.antPath(matcherProvider, this.httpMethod, path)) + .collect(Collectors.toCollection(ArrayList::new)); return (!CollectionUtils.isEmpty(delegateMatchers)) ? new OrRequestMatcher(delegateMatchers) : EMPTY_MATCHER; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java index 151ea82b28c5..a7321f58adbc 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java @@ -315,6 +315,7 @@ void toAdditionalPathsWithEndpointClassShouldNotMatchOtherPaths() { FooEndpoint.class); RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.doesNotMatch("/additional/foo"); assertMatcher.doesNotMatch("/foo"); assertMatcher.doesNotMatch("/bar"); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 7a7620f2564b..8e3fb1153869 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -321,6 +321,7 @@ void toAdditionalPathsWithEndpointClassShouldNotMatchOtherPaths() { FooEndpoint.class); RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.doesNotMatch("/additional/foo"); assertMatcher.doesNotMatch("/foo"); assertMatcher.doesNotMatch("/bar"); } From 273942749ccb4a230fc935d20a8d59cba641e027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 18 Mar 2026 18:16:39 +0100 Subject: [PATCH 158/376] Upgrade to Spring Batch 6.0.3 Closes gh-49416 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 12f87c5ac812..40528a1ebc6e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2411,7 +2411,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-amqp/releases/tag/v{version}") } } - library("Spring Batch", "6.0.3-SNAPSHOT") { + library("Spring Batch", "6.0.3") { considerSnapshots() group("org.springframework.batch") { bom("spring-batch-bom") From dd54841c4a961efe28f197537ad0afd115027e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Mar 2026 07:40:12 +0100 Subject: [PATCH 159/376] Upgrade to Spring Batch 5.2.5 Closes gh-49406 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index e0d7b302367b..9e9e89920a7c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2268,7 +2268,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-authorization-server/releases/tag/{version}") } } - library("Spring Batch", "5.2.5-SNAPSHOT") { + library("Spring Batch", "5.2.5") { considerSnapshots() group("org.springframework.batch") { bom("spring-batch-bom") From 7151419752494508e15005d95a521c2bf4cbf656 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 19 Mar 2026 09:25:03 +0100 Subject: [PATCH 160/376] Upgrade to Testcontainers 2.0.4 Closes gh-49655 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 40528a1ebc6e..df06f9d58bf9 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -2638,7 +2638,7 @@ bom { releaseNotes("https://github.com/xerial/sqlite-jdbc/releases/tag/{version}") } } - library("Testcontainers", "2.0.3") { + library("Testcontainers", "2.0.4") { group("org.testcontainers") { bom("testcontainers-bom") } From 6620dea347a522d160d725a6a51e4ca3a54e1142 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 19 Mar 2026 09:25:44 +0100 Subject: [PATCH 161/376] Polishing See gh-49645 --- .../web/reactive/AbstractWebFluxEndpointHandlerMapping.java | 3 ++- .../web/servlet/AbstractWebMvcEndpointHandlerMapping.java | 3 ++- .../reactive/AbstractWebFluxEndpointHandlerMappingTests.java | 4 ++++ .../servlet/AbstractWebMvcEndpointHandlerMappingTests.java | 4 ++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 4d9da5856994..84044f23b8db 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -509,6 +509,7 @@ private static final class CatchAllHandler { this.responseStatus = responseStatus; } + @Reflective Mono handle(ServerWebExchange exchange) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(this.responseStatus); @@ -577,7 +578,7 @@ static class AbstractWebFluxEndpointHandlerMappingRuntimeHints implements Runtim @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { this.reflectiveRegistrar.registerRuntimeHints(hints, WriteOperationHandler.class, - ReadOperationHandler.class); + ReadOperationHandler.class, CatchAllHandler.class); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index 66cd1eef84f8..c6cecfe2ebe3 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -466,6 +466,7 @@ private static final class CatchAllHandler { this.responseStatus = responseStatus; } + @Reflective void handle(HttpServletResponse response) { response.setStatus(this.responseStatus.value()); } @@ -531,7 +532,7 @@ static class AbstractWebMvcEndpointHandlerMappingRuntimeHints implements Runtime @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - this.reflectiveRegistrar.registerRuntimeHints(hints, OperationHandler.class); + this.reflectiveRegistrar.registerRuntimeHints(hints, OperationHandler.class, CatchAllHandler.class); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMappingTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMappingTests.java index 9264a463b444..ad094ff19646 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMappingTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMappingTests.java @@ -45,6 +45,10 @@ void shouldRegisterHints() { .onType(TypeReference .of("org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping.ReadOperationHandler"))) .accepts(runtimeHints); + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference + .of("org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping.CatchAllHandler"))) + .accepts(runtimeHints); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMappingTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMappingTests.java index d986b6b2c1a8..9b85dcafb5ec 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMappingTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMappingTests.java @@ -40,6 +40,10 @@ void shouldRegisterHints() { .onType(TypeReference .of("org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler"))) .accepts(runtimeHints); + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference + .of("org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.CatchAllHandler"))) + .accepts(runtimeHints); } } From 3ebd1475088cbadf1355cffcbf6e3945af6d8376 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 19 Mar 2026 11:50:57 +0100 Subject: [PATCH 162/376] Next development version (v3.5.13-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5c02537d8b47..0ebffc19bf29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.5.12-SNAPSHOT +version=3.5.13-SNAPSHOT latestVersion=false spring.build-type=oss From 501d736a7b1de4990244def7b886a15e35072965 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 19 Mar 2026 11:55:50 +0100 Subject: [PATCH 163/376] Next development version (v4.0.5-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bcc1e1d0b8c1..8aa818750fbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.0.4-SNAPSHOT +version=4.0.5-SNAPSHOT latestVersion=false spring.build-type=oss From 7524b32f82ecc1e09ef8a8ffa29aaea3f0c379d5 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Mar 2026 18:03:13 +0000 Subject: [PATCH 164/376] Upgrade to Gradle 9.4.1 Closes gh-49665 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dbc3ce4a040f..c61a118f7ddb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0262dcbd52b4..739907dfd159 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 4cd1b02696c2bce11e6741f4c3c45f0cb3a43c17 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Mar 2026 18:06:12 +0000 Subject: [PATCH 165/376] Test Gradle plugin against Gradle 9.4.1 Closes gh-49667 --- .../testsupport/gradle/testkit/GradleVersions.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java index 0d8d2dc75942..98ffc5e4b3d2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java @@ -34,21 +34,21 @@ private GradleVersions() { public static List allCompatible() { if (isJavaVersion(JavaVersion.VERSION_25)) { - return Arrays.asList("9.0.0", "9.4.0"); + return Arrays.asList("9.0.0", "9.4.1"); } if (isJavaVersion(JavaVersion.VERSION_24)) { - return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); + return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.1"); } if (isJavaVersion(JavaVersion.VERSION_23)) { - return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); + return Arrays.asList(GradleVersion.current().getVersion(), "9.0.0", "9.4.1"); } if (isJavaVersion(JavaVersion.VERSION_22)) { - return Arrays.asList("8.8", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); + return Arrays.asList("8.8", GradleVersion.current().getVersion(), "9.0.0", "9.4.1"); } if (isJavaVersion(JavaVersion.VERSION_21)) { - return Arrays.asList("8.5", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); + return Arrays.asList("8.5", GradleVersion.current().getVersion(), "9.0.0", "9.4.1"); } - return Arrays.asList("7.6.6", "8.4", GradleVersion.current().getVersion(), "9.0.0", "9.4.0"); + return Arrays.asList("7.6.6", "8.4", GradleVersion.current().getVersion(), "9.0.0", "9.4.1"); } public static String minimumCompatible() { From 7f0136f8c6f79b64ddfe1abc8aec3813bfdf9668 Mon Sep 17 00:00:00 2001 From: Joowon-Seo Date: Fri, 20 Mar 2026 01:12:35 +0900 Subject: [PATCH 166/376] Add boundary tests for JsonValueWriter max nesting depth Verify behavior at the maxNestingDepth boundary. Signed-off-by: Joowon-Seo See gh-49662 --- .../boot/json/JsonValueWriterTests.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java index 10ebb94ce103..d38db8d98895 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java @@ -296,6 +296,38 @@ void illegalStateExceptionShouldBeThrownWhenIterableExceededNestingDepth() { "JSON nesting depth (129) exceeds maximum depth of 128 (current path: [0][0][0][0][0][0][0][0][0][0][0][0]"); } + @Test + void shouldAllowStartingObjectWhenCurrentDepthIsMaxDepth() { + StringBuilder out = new StringBuilder(); + JsonValueWriter writer = new JsonValueWriter(out, 2); + + writer.start(Series.OBJECT); + writer.start(Series.OBJECT); + writer.start(Series.OBJECT); + + writer.end(Series.OBJECT); + writer.end(Series.OBJECT); + writer.end(Series.OBJECT); + + assertThat(out).hasToString("{{{}}}"); + } + + @Test + void shouldAllowStartingArrayWhenCurrentDepthIsMaxDepth() { + StringBuilder out = new StringBuilder(); + JsonValueWriter writer = new JsonValueWriter(out, 2); + + writer.start(Series.ARRAY); + writer.start(Series.ARRAY); + writer.start(Series.ARRAY); + + writer.end(Series.ARRAY); + writer.end(Series.ARRAY); + writer.end(Series.ARRAY); + + assertThat(out).hasToString("[[[]]]"); + } + private String write(V value) { return doWrite((valueWriter) -> valueWriter.write(value)); } From 371746826e3a3e4ccb57eef500fb4316568790f2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Mar 2026 18:26:28 +0000 Subject: [PATCH 167/376] Polish "Add boundary tests for JsonValueWriter max nesting depth" See gh-49662 --- .../org/springframework/boot/json/JsonValueWriterTests.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java index d38db8d98895..1488e6ea3665 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java @@ -300,15 +300,12 @@ void illegalStateExceptionShouldBeThrownWhenIterableExceededNestingDepth() { void shouldAllowStartingObjectWhenCurrentDepthIsMaxDepth() { StringBuilder out = new StringBuilder(); JsonValueWriter writer = new JsonValueWriter(out, 2); - writer.start(Series.OBJECT); writer.start(Series.OBJECT); writer.start(Series.OBJECT); - writer.end(Series.OBJECT); writer.end(Series.OBJECT); writer.end(Series.OBJECT); - assertThat(out).hasToString("{{{}}}"); } @@ -316,15 +313,12 @@ void shouldAllowStartingObjectWhenCurrentDepthIsMaxDepth() { void shouldAllowStartingArrayWhenCurrentDepthIsMaxDepth() { StringBuilder out = new StringBuilder(); JsonValueWriter writer = new JsonValueWriter(out, 2); - writer.start(Series.ARRAY); writer.start(Series.ARRAY); writer.start(Series.ARRAY); - writer.end(Series.ARRAY); writer.end(Series.ARRAY); writer.end(Series.ARRAY); - assertThat(out).hasToString("[[[]]]"); } From 8b70ee9dbc31e74c2bca039b1e8b5c9ed635a16c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 19 Mar 2026 20:13:23 -0700 Subject: [PATCH 168/376] Escape format characters when writing changelog --- .../boot/configurationmetadata/changelog/ChangelogWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/ChangelogWriter.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/ChangelogWriter.java index 3f56347a2533..b1b5e04b6c1a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/ChangelogWriter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/ChangelogWriter.java @@ -210,7 +210,7 @@ private void writeCell(String content) { } private String escapeForTableCell(String content) { - return content.replace("|", "\\|"); + return content.replace("|", "\\|").replace("%", "%%"); } private void write(String format, Object... args) { From 264a9aa43ad0071ca5358162a600ff710b4bec37 Mon Sep 17 00:00:00 2001 From: Louis Morgan Date: Thu, 19 Mar 2026 11:32:12 +0000 Subject: [PATCH 169/376] Consider ControllerAdvice with @GraphQlTest This commit updates the GraphQL slice tests to also consider `@ControllerAdvice`-annotated beans, since Spring GraphQL loads `@GraphQlExceptionHandler`s declared in `@ControllerAdvice` beans. This also aligns `@GraphQlTest` with `@WebMvcTest`. See gh-49660 Signed-off-by: Louis Morgan --- .../pages/testing/spring-boot-applications.adoc | 2 +- .../boot/test/autoconfigure/graphql/GraphQlTest.java | 9 +++++---- .../graphql/GraphQlTypeExcludeFilter.java | 2 ++ .../graphql/GraphQlTypeExcludeFilterTests.java | 11 +++++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/spring-boot-applications.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/spring-boot-applications.adoc index cb7bd5473f2c..eb4e6ba15ee5 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/spring-boot-applications.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/spring-boot-applications.adoc @@ -426,7 +426,7 @@ There are javadoc:org.springframework.graphql.test.tester.GraphQlTester[] varian Spring Boot helps you to test your {url-spring-graphql-docs}/controllers.html[Spring GraphQL Controllers] with the javadoc:org.springframework.boot.test.autoconfigure.graphql.GraphQlTest[format=annotation] annotation. javadoc:org.springframework.boot.test.autoconfigure.graphql.GraphQlTest[format=annotation] auto-configures the Spring GraphQL infrastructure, without any transport nor server being involved. -This limits scanned beans to javadoc:org.springframework.stereotype.Controller[format=annotation], javadoc:org.springframework.graphql.execution.RuntimeWiringConfigurer[], javadoc:org.springframework.boot.jackson.JsonComponent[], javadoc:org.springframework.core.convert.converter.Converter[], javadoc:org.springframework.core.convert.converter.GenericConverter[], javadoc:org.springframework.graphql.execution.DataFetcherExceptionResolver[], javadoc:graphql.execution.instrumentation.Instrumentation[] and javadoc:org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer[]. +This limits scanned beans to javadoc:org.springframework.stereotype.Controller[format=annotation], javadoc:org.springframework.web.bind.annotation.ControllerAdvice[format=annotation], javadoc:org.springframework.graphql.execution.RuntimeWiringConfigurer[], javadoc:org.springframework.boot.jackson.JsonComponent[], javadoc:org.springframework.core.convert.converter.Converter[], javadoc:org.springframework.core.convert.converter.GenericConverter[], javadoc:org.springframework.graphql.execution.DataFetcherExceptionResolver[], javadoc:graphql.execution.instrumentation.Instrumentation[] and javadoc:org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer[]. Regular javadoc:org.springframework.stereotype.Component[format=annotation] and javadoc:org.springframework.boot.context.properties.ConfigurationProperties[format=annotation] beans are not scanned when the javadoc:org.springframework.boot.test.autoconfigure.graphql.GraphQlTest[format=annotation] annotation is used. javadoc:org.springframework.boot.context.properties.EnableConfigurationProperties[format=annotation] can be used to include javadoc:org.springframework.boot.context.properties.ConfigurationProperties[format=annotation] beans. diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTest.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTest.java index b5f834ddfb73..0f6c1827ca8c 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTest.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTest.java @@ -47,6 +47,7 @@ * relevant to GraphQL tests, including the following: *
    *
  • {@code @Controller} + *
  • {@code @ControllerAdvice} *
  • {@code RuntimeWiringConfigurer} *
  • {@code @JsonComponent} *
  • {@code Converter} @@ -123,10 +124,10 @@ * Determines if default filtering should be used with * {@link SpringBootApplication @SpringBootApplication}. By default, only * {@code @Controller} (when no explicit {@link #controllers() controllers} are - * defined), {@code RuntimeWiringConfigurer}, {@code @JsonComponent}, - * {@code Converter}, {@code GenericConverter}, {@code DataFetcherExceptionResolver}, - * {@code Instrumentation} and {@code GraphQlSourceBuilderCustomizer} beans are - * included. + * defined), {@code ControllerAdvice}, {@code RuntimeWiringConfigurer}, + * {@code @JsonComponent}, {@code Converter}, {@code GenericConverter}, + * {@code DataFetcherExceptionResolver}, {@code Instrumentation} and + * {@code GraphQlSourceBuilderCustomizer} beans are included. * @see #includeFilters() * @see #excludeFilters() * @return if default filters should be used diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilter.java index 8f9bdbd01829..d822eba3640d 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilter.java @@ -34,6 +34,7 @@ import org.springframework.stereotype.Controller; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.ControllerAdvice; /** * {@link TypeExcludeFilter} for {@link GraphQlTest @GraphQlTest}. @@ -51,6 +52,7 @@ public class GraphQlTypeExcludeFilter extends StandardAnnotationCustomizableType static { Set> includes = new LinkedHashSet<>(); + includes.add(ControllerAdvice.class); includes.add(JsonComponent.class); includes.add(RuntimeWiringConfigurer.class); includes.add(Converter.class); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java index f5442bc2e91c..afb068e43c47 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java @@ -42,6 +42,7 @@ import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.ControllerAdvice; import static org.assertj.core.api.Assertions.assertThat; @@ -60,6 +61,7 @@ void matchWhenHasNoControllers() throws Exception { assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isFalse(); assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); + assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -75,6 +77,7 @@ void matchWhenHasController() throws Exception { assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isTrue(); assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); + assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -90,6 +93,7 @@ void matchNotUsingDefaultFilters() throws Exception { assertThat(excludes(filter, Controller1.class)).isTrue(); assertThat(excludes(filter, Controller2.class)).isTrue(); assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isTrue(); + assertThat(excludes(filter, ExampleControllerAdvice.class)).isTrue(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -105,6 +109,7 @@ void matchWithIncludeFilter() throws Exception { assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isFalse(); assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); + assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isFalse(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -120,6 +125,7 @@ void matchWithExcludeFilter() throws Exception { assertThat(excludes(filter, Controller1.class)).isTrue(); assertThat(excludes(filter, Controller2.class)).isFalse(); assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); + assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -169,6 +175,11 @@ static class Controller2 { } + @ControllerAdvice + static class ExampleControllerAdvice { + + } + @Service static class ExampleService { From af03b3b07ce655f1f2a028ba01407f672c03ce59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 20 Mar 2026 10:03:05 +0100 Subject: [PATCH 170/376] Polish "Consider ControllerAdvice with @GraphQlTest" See gh-49660 --- .../graphql/GraphQlTypeExcludeFilterTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java index afb068e43c47..d12fdba723e4 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/graphql/GraphQlTypeExcludeFilterTests.java @@ -60,8 +60,8 @@ void matchWhenHasNoControllers() throws Exception { GraphQlTypeExcludeFilter filter = new GraphQlTypeExcludeFilter(WithNoControllers.class); assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isFalse(); - assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); + assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -76,8 +76,8 @@ void matchWhenHasController() throws Exception { GraphQlTypeExcludeFilter filter = new GraphQlTypeExcludeFilter(WithController.class); assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isTrue(); - assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); + assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -92,8 +92,8 @@ void matchNotUsingDefaultFilters() throws Exception { GraphQlTypeExcludeFilter filter = new GraphQlTypeExcludeFilter(NotUsingDefaultFilters.class); assertThat(excludes(filter, Controller1.class)).isTrue(); assertThat(excludes(filter, Controller2.class)).isTrue(); - assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isTrue(); assertThat(excludes(filter, ExampleControllerAdvice.class)).isTrue(); + assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isTrue(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -108,8 +108,8 @@ void matchWithIncludeFilter() throws Exception { GraphQlTypeExcludeFilter filter = new GraphQlTypeExcludeFilter(WithIncludeFilter.class); assertThat(excludes(filter, Controller1.class)).isFalse(); assertThat(excludes(filter, Controller2.class)).isFalse(); - assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); + assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isFalse(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); @@ -124,8 +124,8 @@ void matchWithExcludeFilter() throws Exception { GraphQlTypeExcludeFilter filter = new GraphQlTypeExcludeFilter(WithExcludeFilter.class); assertThat(excludes(filter, Controller1.class)).isTrue(); assertThat(excludes(filter, Controller2.class)).isFalse(); - assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleControllerAdvice.class)).isFalse(); + assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue(); From be23431bd769314c11641c34fddd66623dcee0bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 01:05:39 +0000 Subject: [PATCH 171/376] Bump fast-xml-parser and @springio/antora-extensions in /antora Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) to 5.5.7 and updates ancestor dependency [@springio/antora-extensions](https://github.com/spring-io/antora-extensions). These dependencies need to be updated together. Updates `fast-xml-parser` from 4.5.4 to 5.5.7 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.5.4...v5.5.7) Updates `@springio/antora-extensions` from 1.14.7 to 1.14.9 - [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc) - [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.7...v1.14.9) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.5.7 dependency-type: indirect - dependency-name: "@springio/antora-extensions" dependency-version: 1.14.9 dependency-type: direct:production ... See gh-49671 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 1029 ++++++++++++++++++++++++++++++++------ antora/package.json | 2 +- 2 files changed, 888 insertions(+), 143 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index 76cf383a22fe..3ddabebb5bfe 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -9,7 +9,7 @@ "@antora/cli": "3.2.0-alpha.11", "@antora/site-generator": "3.2.0-alpha.11", "@asciidoctor/tabs": "1.0.0-beta.6", - "@springio/antora-extensions": "1.14.7", + "@springio/antora-extensions": "1.14.9", "@springio/antora-xref-extension": "1.0.0-alpha.5", "@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.10", "@springio/asciidoctor-extensions": "1.0.0-alpha.18" @@ -399,6 +399,23 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", "license": "ISC" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -434,25 +451,44 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@springio/antora-extensions": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/@springio/antora-extensions/-/antora-extensions-1.14.7.tgz", - "integrity": "sha512-lu++qtzSTAAznFvUEegoyaDfSJXQK1Ftk8NHxWhqzvPAx0t8oO3kNQE1C/5m1qs97QRtKh0UbJ26qEfEQDEcpQ==", + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/@springio/antora-extensions/-/antora-extensions-1.14.9.tgz", + "integrity": "sha512-g5eKnywcFKHjcBz4Mq6dsWXe74TB18AI2+iR0q/8o/BoYCq+bHY7NQE/3bIxVApWT5FyUxoRbKBAMvSL8koStw==", "license": "ASL-2.0", "dependencies": { - "@antora/expand-path-helper": "~2.0", - "archiver": "^5.3.1", + "@antora/expand-path-helper": "~3.0", + "archiver": "^7.0.1", "asciinema-player": "^3.6.1", "cache-directory": "~2.0", "ci": "^2.3.0", "decompress": "4.2.1", - "fast-xml-parser": "^4.5.2", + "fast-xml-parser": "^5.3.4", "handlebars": "latest" }, "engines": { "node": ">=16.0.0" } }, + "node_modules/@springio/antora-extensions/node_modules/@antora/expand-path-helper": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@antora/expand-path-helper/-/expand-path-helper-3.0.0.tgz", + "integrity": "sha512-7PdEIhk97v85/CSm3HynCsX14TR6oIVz1s233nNLsiWubE8tTnpPt4sNRJR+hpmIZ6Bx9c6QDp3XIoiyu/WYYA==", + "license": "MPL-2.0", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@springio/antora-xref-extension": { "version": "1.0.0-alpha.5", "resolved": "https://registry.npmjs.org/@springio/antora-xref-extension/-/antora-xref-extension-1.0.0-alpha.5.tgz", @@ -534,68 +570,210 @@ "node": ">=6.5" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", "dependencies": { - "archiver-utils": "^2.1.0", + "archiver-utils": "^5.0.2", "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", "dependencies": { - "glob": "^7.1.4", + "glob": "^10.0.0", "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", + "lodash": "^4.17.15", "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/archiver/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/argparse": { @@ -644,9 +822,10 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, "node_modules/async-lock": { "version": "1.4.1", @@ -710,6 +889,79 @@ } } }, + "node_modules/bare-fs": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz", + "integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz", + "integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.10.0.tgz", + "integrity": "sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz", + "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -729,16 +981,6 @@ } ] }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -894,6 +1136,24 @@ "node": ">=0.8" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -910,17 +1170,71 @@ } }, "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/concat-map": { @@ -958,15 +1272,70 @@ } }, "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", "dependencies": { "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, "node_modules/csstype": { @@ -1185,6 +1554,18 @@ "stream-shift": "^1.0.2" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1309,10 +1690,25 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "license": "MIT" }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, "node_modules/fast-xml-parser": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.4.tgz", - "integrity": "sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", + "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", "funding": [ { "type": "github", @@ -1321,7 +1717,9 @@ ], "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -1370,13 +1768,29 @@ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/fs-constants": { @@ -1670,6 +2084,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1761,6 +2184,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, "node_modules/isomorphic-git": { "version": "1.25.10", "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.25.10.tgz", @@ -1795,6 +2224,21 @@ "node": ">=6" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -1837,6 +2281,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", "dependencies": { "readable-stream": "^2.0.5" }, @@ -1848,6 +2293,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1861,46 +2307,35 @@ "node_modules/lazystream/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" }, "node_modules/lazystream/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "license": "MIT" }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/make-dir": { "version": "1.3.0", @@ -2024,6 +2459,15 @@ "minimist": "^1.2.5" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/multi-progress": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/multi-progress/-/multi-progress-4.0.0.tgz", @@ -2047,6 +2491,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2111,11 +2556,32 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "node_modules/path-expression-matcher": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2124,6 +2590,31 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -2411,6 +2902,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", "dependencies": { "minimatch": "^5.1.0" } @@ -2614,12 +3106,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/should-proxy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/should-proxy/-/should-proxy-1.0.4.tgz", "integrity": "sha512-RPQhIndEIVUCjkfkQ6rs6sOR6pkxJWCNdxtfG5pP0RVgUYbK5911kLTF0TNcCC0G3YCGd492rMollFT2aTd9iQ==", "license": "MIT" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -2706,9 +3231,9 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" }, "node_modules/streamx": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -2724,6 +3249,102 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", @@ -2745,9 +3366,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", + "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", "funding": [ { "type": "github", @@ -2757,18 +3378,15 @@ "license": "MIT" }, "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/teex": { @@ -2973,6 +3591,21 @@ "node": ">=10.13.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -2999,6 +3632,97 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3048,36 +3772,57 @@ } }, "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } } } diff --git a/antora/package.json b/antora/package.json index 38327b93e9b1..270ec3f178d2 100644 --- a/antora/package.json +++ b/antora/package.json @@ -6,7 +6,7 @@ "@antora/cli": "3.2.0-alpha.11", "@antora/site-generator": "3.2.0-alpha.11", "@antora/atlas-extension": "1.0.0-alpha.5", - "@springio/antora-extensions": "1.14.7", + "@springio/antora-extensions": "1.14.9", "@springio/antora-xref-extension": "1.0.0-alpha.5", "@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.10", "@asciidoctor/tabs": "1.0.0-beta.6", From 25b6de94d5827ee8cec5f8446b4df86573561dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 20 Mar 2026 10:29:31 +0100 Subject: [PATCH 172/376] Fix formatting --- .../boot/graphql/test/autoconfigure/GraphQlTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/module/spring-boot-graphql-test/src/main/java/org/springframework/boot/graphql/test/autoconfigure/GraphQlTest.java b/module/spring-boot-graphql-test/src/main/java/org/springframework/boot/graphql/test/autoconfigure/GraphQlTest.java index 9e406e506b5f..c0b9f6a70414 100644 --- a/module/spring-boot-graphql-test/src/main/java/org/springframework/boot/graphql/test/autoconfigure/GraphQlTest.java +++ b/module/spring-boot-graphql-test/src/main/java/org/springframework/boot/graphql/test/autoconfigure/GraphQlTest.java @@ -124,9 +124,10 @@ * {@link SpringBootApplication @SpringBootApplication}. By default, only * {@code @Controller} (when no explicit {@link #controllers() controllers} are * defined), {@code ControllerAdvice}, {@code RuntimeWiringConfigurer}, - * {@code @JacksonComponent}, {@code @JsonComponent} (deprecated), {@code Converter}, {@code GenericConverter}, - * {@code DataFetcherExceptionResolver}, {@code Instrumentation} and - * {@code GraphQlSourceBuilderCustomizer} beans are included. + * {@code @JacksonComponent}, {@code @JsonComponent} (deprecated), {@code Converter}, + * {@code GenericConverter}, {@code DataFetcherExceptionResolver}, + * {@code Instrumentation} and {@code GraphQlSourceBuilderCustomizer} beans are + * included. * @see #includeFilters() * @see #excludeFilters() * @return if default filters should be used From aec36b88eff6a8a77782662b7260e9a7a0c8e759 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 20 Mar 2026 12:50:35 +0000 Subject: [PATCH 173/376] Upgrade to Antora UI 0.4.26 Closes gh-49679 --- antora/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/antora/package.json b/antora/package.json index 270ec3f178d2..942ad9a8b6d4 100644 --- a/antora/package.json +++ b/antora/package.json @@ -13,6 +13,6 @@ "@springio/asciidoctor-extensions": "1.0.0-alpha.18" }, "config": { - "ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip" + "ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip" } } From f0a02cdbd3dfda137028ffa8525b0a943942ecb6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 20 Mar 2026 13:01:43 +0000 Subject: [PATCH 174/376] Trigger docs build once release has been promoted Closes gh-49682 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ef9cc460f09..7e32633f1a58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -148,7 +148,7 @@ jobs: name: Trigger Docs Build needs: - build-and-stage-release - - sync-to-maven-central + - promote-release permissions: actions: write runs-on: ubuntu-latest From ec167252f258720486c97c21e1f542e9bcf90f4d Mon Sep 17 00:00:00 2001 From: Lee Jiwon Date: Sat, 21 Mar 2026 22:41:11 +0900 Subject: [PATCH 175/376] Fix typos in StandardStackTracePrinter Javadoc See gh-49694 Signed-off-by: Lee Jiwon --- .../logging/StandardStackTracePrinter.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/StandardStackTracePrinter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/StandardStackTracePrinter.java index a58a63894660..d6deadd506bf 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/StandardStackTracePrinter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/StandardStackTracePrinter.java @@ -144,7 +144,7 @@ private void printFrames(Print print, StackTrace stackTrace, StackTrace enclosin /** * Return a new {@link StandardStackTracePrinter} from this one that will print all - * common frames rather the replacing them with the {@literal "... N more"} message. + * common frames rather than replacing them with the {@literal "... N more"} message. * @return a new {@link StandardStackTracePrinter} instance */ public StandardStackTracePrinter withCommonFrames() { @@ -162,7 +162,7 @@ public StandardStackTracePrinter withoutSuppressed() { /** * Return a new {@link StandardStackTracePrinter} from this one that will use ellipses - * to truncate output longer that the specified length. + * to truncate output longer than the specified length. * @param maximumLength the maximum length that can be printed * @return a new {@link StandardStackTracePrinter} instance */ @@ -173,8 +173,8 @@ public StandardStackTracePrinter withMaximumLength(int maximumLength) { } /** - * Return a new {@link StandardStackTracePrinter} from this one that filter frames - * (including caused and suppressed) deeper then the specified maximum. + * Return a new {@link StandardStackTracePrinter} from this one that filters frames + * (including caused and suppressed) deeper than the specified maximum. * @param maximumThrowableDepth the maximum throwable depth * @return a new {@link StandardStackTracePrinter} instance */ @@ -208,7 +208,7 @@ public StandardStackTracePrinter withFrameFilter(BiPredicate forma } /** - * Return a new {@link StandardStackTracePrinter} from this one uses the specified - * formatter to create a string representation of a frame. + * Return a new {@link StandardStackTracePrinter} from this one that uses the + * specified formatter to create a string representation of a frame. * @param frameFormatter the frame formatter to use * @return a new {@link StandardStackTracePrinter} instance * @see #withLineSeparator(String) From 0b45618cb506b608c216d36de0e85c04f162b6a9 Mon Sep 17 00:00:00 2001 From: Daeho Kwon Date: Sun, 22 Mar 2026 01:47:09 +0900 Subject: [PATCH 176/376] Add @ConditionalOnWebApplication to NettyReactiveWebServerAutoConfiguration See gh-49695 Signed-off-by: Daeho Kwon --- .../NettyReactiveWebServerAutoConfiguration.java | 4 ++++ .../NettyReactiveWebServerAutoConfigurationTests.java | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/module/spring-boot-reactor-netty/src/main/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfiguration.java b/module/spring-boot-reactor-netty/src/main/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfiguration.java index bf2eaf64744c..0278087e74d6 100644 --- a/module/spring-boot-reactor-netty/src/main/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfiguration.java +++ b/module/spring-boot-reactor-netty/src/main/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfiguration.java @@ -23,6 +23,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory; import org.springframework.boot.reactor.netty.NettyRouteProvider; @@ -42,10 +44,12 @@ * server. * * @author Andy Wilkinson + * @author Daeho Kwon * @since 4.0.0 */ @AutoConfiguration @ConditionalOnClass({ ReactiveHttpInputMessage.class, HttpServer.class }) +@ConditionalOnWebApplication(type = Type.REACTIVE) @EnableConfigurationProperties(NettyServerProperties.class) @Import({ ReactiveWebServerConfiguration.class, ReactorResourceFactoryConfiguration.class }) public final class NettyReactiveWebServerAutoConfiguration { diff --git a/module/spring-boot-reactor-netty/src/test/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfigurationTests.java b/module/spring-boot-reactor-netty/src/test/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfigurationTests.java index 453fdfc83b14..882c79b2fbee 100644 --- a/module/spring-boot-reactor-netty/src/test/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfigurationTests.java +++ b/module/spring-boot-reactor-netty/src/test/java/org/springframework/boot/reactor/netty/autoconfigure/NettyReactiveWebServerAutoConfigurationTests.java @@ -19,10 +19,13 @@ import org.junit.jupiter.api.Test; import reactor.netty.http.server.HttpServer; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory; import org.springframework.boot.reactor.netty.NettyServerCustomizer; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.server.autoconfigure.reactive.AbstractReactiveWebServerAutoConfigurationTests; +import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,6 +42,7 @@ * @author Raheela Aslam * @author Madhura Bhave * @author Scott Frederick + * @author Daeho Kwon */ // @DirtiesUrlFactories class NettyReactiveWebServerAutoConfigurationTests extends AbstractReactiveWebServerAutoConfigurationTests { @@ -67,6 +71,13 @@ void nettyServerCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { }); } + @Test + void autoConfigurationDoesNotApplyToNonWebApplication() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(NettyReactiveWebServerAutoConfiguration.class)) + .run((context) -> assertThat(context).doesNotHaveBean(ReactiveWebServerFactory.class)); + } + @Configuration(proxyBeanMethods = false) static class NettyServerCustomizerConfiguration { From e65732e1780ceb6542caefa0f0d07483fae65a75 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 23 Mar 2026 09:50:02 +0100 Subject: [PATCH 177/376] Fix Flyway sub-folders support in Native image Prior to this commit, the native image support for scanning Flyway migration scripts would not scan sub-folders at runtime when running a Native image. This commit ensures that the resource scanning at runtime also considers sub-directories for native images. Fixes gh-49661 --- .../flyway/NativeImageResourceProvider.java | 2 +- .../flyway/FlywayAutoConfigurationTests.java | 2 ++ ...NativeImageResourceProviderCustomizerTests.java | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProvider.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProvider.java index 7970d9aad671..47dc32b64d03 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProvider.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProvider.java @@ -145,7 +145,7 @@ private void initialize() { private Resource[] getResources(PathMatchingResourcePatternResolver resolver, Location location, Resource root) { try { - return resolver.getResources(root.getURI() + "/*"); + return resolver.getResources(root.getURI() + "/**/*"); } catch (IOException ex) { throw new UncheckedIOException("Failed to list resources for " + location.getDescriptor(), ex); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index 169528051323..56ce9402515e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -968,6 +968,8 @@ void shouldRegisterResourceHints() { new FlywayAutoConfigurationRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader()); assertThat(RuntimeHintsPredicates.resource().forResource("db/migration/")).accepts(runtimeHints); assertThat(RuntimeHintsPredicates.resource().forResource("db/migration/V1__init.sql")).accepts(runtimeHints); + assertThat(RuntimeHintsPredicates.resource().forResource("db/migration/nested/V2__users.sql")) + .accepts(runtimeHints); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java index 083e5b606ad2..9563a85082de 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.testsupport.classpath.resources.WithResource; +import org.springframework.boot.testsupport.classpath.resources.WithResources; import static org.assertj.core.api.Assertions.assertThat; @@ -56,6 +57,19 @@ void nativeImageResourceProviderShouldFindMigrations() { assertThat(migrations).containsExactly(migration); } + @Test + @WithResources({ @WithResource(name = "db/migration/V1__init.sql"), + @WithResource(name = "db/migration/nested/V2__users.sql") }) + void nativeImageResourceProviderShouldFindNestedMigrations() { + FluentConfiguration configuration = new FluentConfiguration(); + this.customizer.customize(configuration); + ResourceProvider resourceProvider = configuration.getResourceProvider(); + Collection migrations = resourceProvider.getResources("V", new String[] { ".sql" }); + LoadableResource v1 = resourceProvider.getResource("V1__init.sql"); + LoadableResource v2 = resourceProvider.getResource("nested/V2__users.sql"); + assertThat(migrations).containsExactly(v1, v2); + } + @Test void shouldBackOffOnCustomResourceProvider() { FluentConfiguration configuration = new FluentConfiguration(); From 1e5fd258881b3791d4062469473981a8b4b725ec Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 23 Mar 2026 09:43:56 +0000 Subject: [PATCH 178/376] Overhaul Spring Session documentation following modularization Closes gh-49704 --- .../reference/pages/web/spring-session.adoc | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-session.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-session.adoc index 088035670620..12326cf0df79 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-session.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-session.adoc @@ -2,23 +2,16 @@ = Spring Session Spring Boot provides {url-spring-session-site}[Spring Session] auto-configuration for a range of data stores. -When building a servlet web application, the following stores can be auto-configured: +For each data store, a specific Spring Boot starter is provided. -* Redis -* JDBC +When building a servlet web application, the following stores can be auto-configured: -Additionally, {url-spring-boot-for-apache-geode-site}[Spring Boot for Apache Geode] provides {url-spring-boot-for-apache-geode-docs}#geode-session[auto-configuration for using Apache Geode as a session store]. +* Redis (`spring-boot-starter-session-data-redis`) +* JDBC (`spring-boot-starter-session-jdbc`) The servlet auto-configuration replaces the need to use `@Enable*HttpSession`. -If a single Spring Session module is present on the classpath, Spring Boot uses that store implementation automatically. -If you have more than one implementation, Spring Boot uses the following order for choosing a specific implementation: - -. Redis -. JDBC -. If neither Redis nor JDBC are available, we do not configure a javadoc:org.springframework.session.SessionRepository[]. - -When building a reactive web application, the Redis store can be auto-configured. +When building a reactive web application, the Redis store can be auto-configured by depending on `spring-boot-starter-session-data-redis`. This replaces the need to use `@EnableRedisWebSession`. Each store has specific additional settings. @@ -37,4 +30,5 @@ If that property is not set with a servlet web application, the auto-configurati You can take control over Spring Session's configuration using `@Enable*HttpSession` (servlet) or `@EnableRedisWebSession` (reactive). This will cause the auto-configuration to back off. -Spring Session can then be configured using the annotation's attributes rather than the previously described configuration properties. +Alternatively, depend on the relevant Spring Session module directly rather than using one of Spring Boot's starters for Spring Session. +With either approach, Spring Session can then be configured using the annotation's attributes rather than the previously described configuration properties. From 5cf51ba98d4190fbd42a44ab109d9dc7a7a3abc7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 23 Mar 2026 14:45:10 +0000 Subject: [PATCH 179/376] Relax ordering assertion when testing resource provider Flyway's org.flywaydb.core.internal.scanner.Scanner does not make any ordering guarantees so neither does our NativeImageResourceProvider. It also isn't required by the contract where a Collection is returned so we can relax the assertion in the test so that it doesn't care about the ordering. See gh-49661 --- .../flyway/NativeImageResourceProviderCustomizerTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java index 9563a85082de..e2e467274eaa 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizerTests.java @@ -67,7 +67,7 @@ void nativeImageResourceProviderShouldFindNestedMigrations() { Collection migrations = resourceProvider.getResources("V", new String[] { ".sql" }); LoadableResource v1 = resourceProvider.getResource("V1__init.sql"); LoadableResource v2 = resourceProvider.getResource("nested/V2__users.sql"); - assertThat(migrations).containsExactly(v1, v2); + assertThat(migrations).containsExactlyInAnyOrder(v1, v2); } @Test From 94536b0428da0f14396e5c82566020a3ccc8c37f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Mar 2026 11:30:15 +0000 Subject: [PATCH 180/376] Upgrade to Jackson 2.21.2 The Jackson team have ended support for Jackson 2.19.x (and 2.20.x). In response to this, this commit upgrades to Jackson 2.21.2. 2.21.x is designated as an LTS release so future minor upgrades should not be necessary. Closes gh-49365 --- gradle.properties | 2 +- .../properties/ConfigurationPropertiesReportEndpoint.java | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0ebffc19bf29..c77990cd337d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ checkstyleToolVersion=10.12.4 commonsCodecVersion=1.18.0 graalVersion=22.3 hamcrestVersion=3.0 -jacksonVersion=2.19.4 +jacksonVersion=2.21.2 javaFormatVersion=0.0.47 junitJupiterVersion=5.12.2 kotlinVersion=1.9.25 diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java index 7bf7036efadf..f6b90d849855 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java @@ -180,13 +180,19 @@ protected void configureJsonMapper(JsonMapper.Builder builder) { builder.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); builder.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false); builder.configure(MapperFeature.USE_STD_BEAN_NAMING, true); - builder.serializationInclusion(Include.NON_NULL); + configureInclusion(builder); applyConfigurationPropertiesFilter(builder); applySerializationModifier(builder); builder.addModule(new JavaTimeModule()); builder.addModule(new ConfigurationPropertiesModule()); } + @SuppressWarnings("deprecation") + private void configureInclusion(JsonMapper.Builder builder) { + // Avoid using defaultPropertyInclusion to retain compatibility with Jackson 2.19. + builder.serializationInclusion(Include.NON_NULL); + } + private void applyConfigurationPropertiesFilter(JsonMapper.Builder builder) { builder.annotationIntrospector(new ConfigurationPropertiesAnnotationIntrospector()); builder From fda3a0d8ba4ad3e3ee9a8ea5f93f8c5dceff27df Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 23 Mar 2026 15:18:54 +0000 Subject: [PATCH 181/376] Fix capitalization of comment prefix for upgrade attention issues Closes gh-49711 --- .../create-github-release/changelog-generator-commercial.yml | 2 +- .../actions/create-github-release/changelog-generator-oss.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/create-github-release/changelog-generator-commercial.yml b/.github/actions/create-github-release/changelog-generator-commercial.yml index 1e621c5a42b0..e7ff74533e7f 100644 --- a/.github/actions/create-github-release/changelog-generator-commercial.yml +++ b/.github/actions/create-github-release/changelog-generator-commercial.yml @@ -6,7 +6,7 @@ changelog: summary: mode: "member-comment" config: - prefix: "Attention Required:" + prefix: "Attention required:" - title: ":star: New Features" labels: - "type: enhancement" diff --git a/.github/actions/create-github-release/changelog-generator-oss.yml b/.github/actions/create-github-release/changelog-generator-oss.yml index 480b7c221cba..23d48098c279 100644 --- a/.github/actions/create-github-release/changelog-generator-oss.yml +++ b/.github/actions/create-github-release/changelog-generator-oss.yml @@ -6,7 +6,7 @@ changelog: summary: mode: "member-comment" config: - prefix: "Attention Required:" + prefix: "Attention required:" - title: ":star: New Features" labels: - "type: enhancement" From d7078e08b89a3644d158f04998ebb19399986297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Jerna=C5=9B?= Date: Mon, 23 Mar 2026 13:11:52 +0100 Subject: [PATCH 182/376] Add some more Kotlin examples and trivial style fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See gh-49710 Signed-off-by: Łukasz Jernaś --- .../reference/pages/features/kotlin.adoc | 3 +- .../errorpages/MyErrorViewResolver.java | 2 +- .../MyConditionEvaluationReportingTests.kt | 5 +-- .../devtools/restart/disable/MyApplication.kt | 13 +++---- .../springapplication/MyApplication.kt | 3 +- .../applicationcontext/MyDemoBean.kt | 35 +++++++++++++++++++ .../docs/web/servlet/jersey/MyEndpoint.kt | 32 +++++++++++++++++ .../docs/web/servlet/jersey/MyJerseyConfig.kt | 29 +++++++++++++++ 8 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/embeddedcontainer/applicationcontext/MyDemoBean.kt create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyEndpoint.kt create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyJerseyConfig.kt diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/kotlin.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/kotlin.adoc index f46ba392e6d7..ec3ba2cb9223 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/kotlin.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/kotlin.adoc @@ -121,7 +121,8 @@ javadoc:org.springframework.boot.context.properties.ConfigurationProperties[form data class KotlinExampleProperties( val name: String, val description: String, - val myService: MyService) { + val myService: MyService +) { data class MyService( val apiToken: String, diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/errorhandling/errorpages/MyErrorViewResolver.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/errorhandling/errorpages/MyErrorViewResolver.java index 5effa076d37a..2b338243359c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/errorhandling/errorpages/MyErrorViewResolver.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/errorhandling/errorpages/MyErrorViewResolver.java @@ -31,7 +31,7 @@ public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus stat // Use the request or status to optionally return a ModelAndView if (status == HttpStatus.INSUFFICIENT_STORAGE) { // We could add custom model values here - new ModelAndView("myview"); + return new ModelAndView("myview"); } return null; } diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt index 00a30745cde7..441eee62e66d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/developingautoconfiguration/testing/MyConditionEvaluationReportingTests.kt @@ -29,8 +29,9 @@ class MyConditionEvaluationReportingTests { fun autoConfigTest() { ApplicationContextRunner() .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) - .run { context: AssertableApplicationContext? -> } + .run { context: AssertableApplicationContext? -> + // Test something... + } } } - diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt index d3e24276e688..467a9191277c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt @@ -18,15 +18,12 @@ package org.springframework.boot.docs.using.devtools.restart.disable import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication @SpringBootApplication -object MyApplication { - - @JvmStatic - fun main(args: Array) { - System.setProperty("spring.devtools.restart.enabled", "false") - SpringApplication.run(MyApplication::class.java, *args) - } +class MyApplication +fun main(args: Array) { + System.setProperty("spring.devtools.restart.enabled", "false") + runApplication(*args) } - diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/usingthespringbootapplicationannotation/springapplication/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/usingthespringbootapplicationannotation/springapplication/MyApplication.kt index 2f7bbdd303ee..5211675a940a 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/usingthespringbootapplicationannotation/springapplication/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/usingthespringbootapplicationannotation/springapplication/MyApplication.kt @@ -19,11 +19,10 @@ package org.springframework.boot.docs.using.usingthespringbootapplicationannotat import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication -// same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan +// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan @SpringBootApplication class MyApplication fun main(args: Array) { runApplication(*args) } - diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/embeddedcontainer/applicationcontext/MyDemoBean.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/embeddedcontainer/applicationcontext/MyDemoBean.kt new file mode 100644 index 000000000000..7cce3c56f74c --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/embeddedcontainer/applicationcontext/MyDemoBean.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docs.web.servlet.embeddedcontainer.applicationcontext + +import jakarta.servlet.ServletContext +import org.springframework.boot.context.event.ApplicationStartedEvent +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationListener +import org.springframework.web.context.WebApplicationContext + +class MyDemoBean : ApplicationListener { + + @Suppress("unused") + private var servletContext: ServletContext? = null + + override fun onApplicationEvent(event: ApplicationStartedEvent) { + val applicationContext: ApplicationContext = event.applicationContext + this.servletContext = (applicationContext as WebApplicationContext).servletContext + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyEndpoint.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyEndpoint.kt new file mode 100644 index 000000000000..74a66c171d30 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyEndpoint.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docs.web.servlet.jersey + +import jakarta.ws.rs.GET +import jakarta.ws.rs.Path +import org.springframework.stereotype.Component + +@Component +@Path("/hello") +class MyEndpoint { + + @GET + fun message(): String { + return "Hello" + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyJerseyConfig.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyJerseyConfig.kt new file mode 100644 index 000000000000..9397e3ba6985 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/jersey/MyJerseyConfig.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.docs.web.servlet.jersey + +import org.glassfish.jersey.server.ResourceConfig +import org.springframework.stereotype.Component + +@Component +class MyJerseyConfig : ResourceConfig() { + + init { + register(MyEndpoint::class.java) + } + +} From e15755bc808931adcd21792205588146b6cbfda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 23 Mar 2026 16:19:48 +0100 Subject: [PATCH 183/376] Polish "Add some more Kotlin examples and trivial style fixes" See gh-49710 --- .../boot/docs/using/devtools/restart/disable/MyApplication.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt index 467a9191277c..2bc9f362c75d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/using/devtools/restart/disable/MyApplication.kt @@ -16,7 +16,6 @@ package org.springframework.boot.docs.using.devtools.restart.disable -import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication From a8640c8272f04928a466274ae402bf8264da66ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:52:55 +0000 Subject: [PATCH 184/376] Bump jfrog/setup-jfrog-cli from 4.9.1 to 4.10.0 Bumps [jfrog/setup-jfrog-cli](https://github.com/jfrog/setup-jfrog-cli) from 4.9.1 to 4.10.0. - [Release notes](https://github.com/jfrog/setup-jfrog-cli/releases) - [Commits](https://github.com/jfrog/setup-jfrog-cli/compare/279b1f629f43dd5bc658d8361ac4802a7ef8d2d5...86dacb6974c66cc99e7651e1205f6581aaddba9a) --- updated-dependencies: - dependency-name: jfrog/setup-jfrog-cli dependency-version: 4.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... See gh-49718 Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e32633f1a58..e71468b63834 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 + uses: jfrog/setup-jfrog-cli@86dacb6974c66cc99e7651e1205f6581aaddba9a # v4.10.0 env: JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }} - name: Promote open source build From 06b006aaa5c5f90b65775567618aaac1d0cca1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 24 Mar 2026 10:10:54 +0100 Subject: [PATCH 185/376] Polish "Bump jfrog/setup-jfrog-cli from 4.9.1 to 4.10.0" See gh-49718 --- .github/actions/publish-gradle-plugin/action.yml | 2 +- .github/actions/sync-to-maven-central/action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/publish-gradle-plugin/action.yml b/.github/actions/publish-gradle-plugin/action.yml index 5fd67bdcdd0e..51ad33c708e3 100644 --- a/.github/actions/publish-gradle-plugin/action.yml +++ b/.github/actions/publish-gradle-plugin/action.yml @@ -21,7 +21,7 @@ runs: using: composite steps: - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 + uses: jfrog/setup-jfrog-cli@86dacb6974c66cc99e7651e1205f6581aaddba9a # v4.10.0 env: JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - name: Download Artifacts diff --git a/.github/actions/sync-to-maven-central/action.yml b/.github/actions/sync-to-maven-central/action.yml index 46b2c7d7e323..2dd1aec3e8e3 100644 --- a/.github/actions/sync-to-maven-central/action.yml +++ b/.github/actions/sync-to-maven-central/action.yml @@ -17,7 +17,7 @@ runs: using: composite steps: - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 + uses: jfrog/setup-jfrog-cli@86dacb6974c66cc99e7651e1205f6581aaddba9a # v4.10.0 env: JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - name: Download Release Artifacts From 5a836da33991f1198b2d8c4c53423a3e13755af9 Mon Sep 17 00:00:00 2001 From: Lee Jiwon Date: Tue, 24 Mar 2026 13:36:20 +0900 Subject: [PATCH 186/376] Fix incorrect indefinite articles in Javadoc See gh-49723 Signed-off-by: Lee Jiwon --- .../java/org/springframework/boot/loader/launch/Archive.java | 2 +- .../JsonMixinModuleEntriesBeanRegistrationAotProcessor.java | 2 +- .../src/main/java/org/springframework/boot/json/JsonWriter.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Archive.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Archive.java index 66f4a048b0d7..04376a6a553c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Archive.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Archive.java @@ -98,7 +98,7 @@ default void close() throws Exception { * Factory method to create an appropriate {@link Archive} from the given * {@link Class} target. * @param target a target class that will be used to find the archive code source - * @return an new {@link Archive} instance + * @return a new {@link Archive} instance * @throws Exception if the archive cannot be created */ static Archive create(Class target) throws Exception { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java index 4bd0a63fc798..82d956f87866 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonMixinModuleEntriesBeanRegistrationAotProcessor.java @@ -37,7 +37,7 @@ /** * {@link BeanRegistrationAotProcessor} that replaces any {@link JsonMixinModuleEntries} - * by an hard-coded equivalent. This has the effect of disabling scanning at runtime. + * by a hard-coded equivalent. This has the effect of disabling scanning at runtime. * * @author Stephane Nicoll */ diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonWriter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonWriter.java index 011da6781e2a..219d2f2c198b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonWriter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonWriter.java @@ -391,7 +391,7 @@ public Member whenNotNull() { /** * Only include this member when an extracted value is not {@code null}. - * @param extractor an function used to extract the value to test + * @param extractor a function used to extract the value to test * @return a {@link Member} which may be configured further */ public Member whenNotNull(Function extractor) { From 17e256f16b3a1ed53e4609954bae545e7e840879 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 24 Mar 2026 13:18:44 -0700 Subject: [PATCH 187/376] Restore correct grouping of initial ConfigDataContributors Update `ConfigDataEnvironment` to restore previous property override behavior by correctly grouping the initial `ConfigDataContributor` instances. This regression was cause by the polish commit 646db448 which attempted to simplify the fix for gh-49324 by including all initial contributors in a single contributor node. This change inadvertently changed the iteration order for the `spring.config.location` and `spring.config.additional-location` properties which still need to be expanded and added in reverse order. Fixes gh-49724 --- .../context/config/ConfigDataEnvironment.java | 26 ++++++++++--------- .../config/ConfigDataEnvironmentTests.java | 18 ++++++++++++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index 47d400302b1d..4c96ac0bc6f2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -198,21 +198,23 @@ ConfigDataEnvironmentContributors getContributors() { private List getInitialImportContributors(Binder binder) { List initialContributors = new ArrayList<>(); - addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); - addInitialImportContributors(initialContributors, - bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS)); - addInitialImportContributors(initialContributors, - bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS)); + addInitialImportContributors(initialContributors, binder, IMPORT_PROPERTY, EMPTY_LOCATIONS, false); + addInitialImportContributors(initialContributors, binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS, true); + addInitialImportContributors(initialContributors, binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS, true); return initialContributors; } - private ConfigDataLocation[] bindLocations(Binder binder, String propertyName, ConfigDataLocation[] other) { - return binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(other); - } - - private void addInitialImportContributors(List initialContributors, - ConfigDataLocation[] locations) { - addInitialImportContributors(initialContributors, List.of(locations)); + private void addInitialImportContributors(List initialContributors, Binder binder, + String propertyName, ConfigDataLocation[] defaultValue, boolean registerIndividually) { + ConfigDataLocation[] locations = binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(defaultValue); + if (registerIndividually) { + for (int i = locations.length - 1; i >= 0; i--) { + addInitialImportContributors(initialContributors, List.of(locations[i])); + } + } + else { + addInitialImportContributors(initialContributors, List.of(locations)); + } } private void addInitialImportContributors(List initialContributors, diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java index bb0b3501f685..2c4a194dfea6 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; @@ -29,6 +31,7 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -144,7 +147,7 @@ void createCreatesInitialImportContributorsInCorrectOrder() { .map(ConfigDataEnvironmentContributor::getImports) .map(Object::toString) .toArray(); - assertThat(imports).containsExactly("[i1, i2]", "[a1, a2]", "[l1, l2]"); + assertThat(imports).containsExactly("[i1, i2]", "[a2]", "[a1]", "[l2]", "[l1]"); } @Test @@ -377,6 +380,19 @@ public Enumeration getResources(String name) throws IOException { .containsOnly(SeparateClassLoaderConfigDataLoader.class); } + @Test // gh-49724 + @WithResource(name = "application-local.properties", content = "test.property=classpath-local") + void processAndApplyWhenExternalFileConfigOverridesProfileSpecificClasspathConfig(@TempDir Path tempDir) + throws IOException { + Files.writeString(tempDir.resolve("application.properties"), "test.property=file-default\n"); + this.environment.setProperty("spring.config.location", + "optional:classpath:/,optional:file:" + tempDir.toAbsolutePath() + "/"); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, + this.environment, this.resourceLoader, List.of("local"), null); + configDataEnvironment.processAndApply(); + assertThat(this.environment.getProperty("test.property")).isEqualTo("file-default"); + } + private String getConfigLocation(TestInfo info) { return "optional:classpath:" + info.getTestClass().get().getName().replace('.', '/') + "-" + info.getTestMethod().get().getName() + ".properties"; From add1f9763ebe033886b015684d24387cd5dc5cbb Mon Sep 17 00:00:00 2001 From: Lee Jiwon Date: Wed, 25 Mar 2026 09:27:34 +0900 Subject: [PATCH 188/376] Fix minor Javadoc wording errors Signed-off-by: Lee Jiwon See gh-49735 --- .../org/springframework/boot/actuate/endpoint/Operation.java | 2 +- .../boot/autoconfigure/condition/OnPropertyCondition.java | 2 +- .../boot/autoconfigure/flyway/FlywayDataSource.java | 2 +- .../boot/autoconfigure/liquibase/LiquibaseDataSource.java | 2 +- .../org/springframework/boot/test/context/SpringBootTest.java | 2 +- .../boot/configurationmetadata/changelog/Difference.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java index 0bef5ea58ab8..8e616c85996a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Operation.java @@ -35,7 +35,7 @@ public interface Operation { * Invoke the underlying operation using the given {@code context}. Results intended * to be returned in the body of the response should additionally implement * {@link OperationResponseBody}. - * @param context the context in to use when invoking the operation + * @param context the context to use when invoking the operation * @return the result of the operation, may be {@code null} */ Object invoke(InvocationContext context); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java index 1043f4c6fca8..beefac457b89 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnPropertyCondition.java @@ -38,7 +38,7 @@ import org.springframework.util.StringUtils; /** - * {@link Condition} that checks if properties are defined in environment. + * {@link Condition} that checks if properties are defined in the environment. * * @author Maciej Walkowiak * @author Phillip Webb diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayDataSource.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayDataSource.java index 191a5b6dbf09..fda9e901cf2c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayDataSource.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayDataSource.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Qualifier; /** - * Qualifier annotation for a DataSource to be injected in to Flyway. If used for a second + * Qualifier annotation for a DataSource to be injected into Flyway. If used for a second * data source, the other (main) one would normally be marked as {@code @Primary}. * * @author Dave Syer diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseDataSource.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseDataSource.java index 75089670c3ba..ebbb5f5ec61f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseDataSource.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseDataSource.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Qualifier; /** - * Qualifier annotation for a DataSource to be injected in to Liquibase. If used for a + * Qualifier annotation for a DataSource to be injected into Liquibase. If used for a * second data source, the other (main) one would normally be marked as {@code @Primary}. * * @author Eddú Meléndez diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java index d3be00ee9253..4ec1041ec39c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java @@ -134,7 +134,7 @@ UseMainMethod useMainMethod() default UseMainMethod.NEVER; /** - * An enumeration web environment modes. + * An enumeration of web environment modes. */ enum WebEnvironment { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/Difference.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/Difference.java index a921810f04a4..e13b771bd189 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/Difference.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-metadata-changelog-generator/src/main/java/org/springframework/boot/configurationmetadata/changelog/Difference.java @@ -20,7 +20,7 @@ import org.springframework.boot.configurationmetadata.Deprecation.Level; /** - * A difference the metadata. + * A difference in the metadata. * * @param type the type of the difference * @param oldProperty the old property From d44683180da31a231cd39638dcfb29d2d69635be Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 24 Mar 2026 18:05:02 -0700 Subject: [PATCH 189/376] Support constructor binding method-level `@NestedConfigurationProperty` Update `spring-boot-configuration-processor` to support method-level `@NestedConfigurationProperty` annotations when constructor binding is being used. Fixes gh-49734 --- ...onstructorParameterPropertyDescriptor.java | 3 +- ...ationMetadataAnnotationProcessorTests.java | 8 ++++ .../NestedPropertiesMethodImmutable.java | 37 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/method/NestedPropertiesMethodImmutable.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConstructorParameterPropertyDescriptor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConstructorParameterPropertyDescriptor.java index c8b4e6b714cd..5aa13fd77b5f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConstructorParameterPropertyDescriptor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConstructorParameterPropertyDescriptor.java @@ -51,7 +51,8 @@ protected List getDeprecatableElements() { @Override protected boolean isMarkedAsNested(MetadataGenerationEnvironment environment) { - return environment.getNestedConfigurationPropertyAnnotation(this.field) != null; + return environment.getNestedConfigurationPropertyAnnotation(this.field) != null + || environment.getNestedConfigurationPropertyAnnotation(getGetter()) != null; } @Override diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 9add8959618e..2cb7cdad3a15 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -27,6 +27,7 @@ import org.springframework.boot.configurationprocessor.metadata.Metadata; import org.springframework.boot.configurationsample.deprecation.Dbcp2Configuration; import org.springframework.boot.configurationsample.method.NestedPropertiesMethod; +import org.springframework.boot.configurationsample.method.NestedPropertiesMethodImmutable; import org.springframework.boot.configurationsample.record.ExampleRecord; import org.springframework.boot.configurationsample.record.NestedPropertiesRecord; import org.springframework.boot.configurationsample.record.RecordWithGetter; @@ -394,6 +395,13 @@ void nestedClassMethod() { assertThat(metadata).has(Metadata.withProperty("method-nested.inner.nested.my-nested-property")); } + @Test + void nestedClassMethodImmutable() { + ConfigurationMetadata metadata = compile(NestedPropertiesMethodImmutable.class); + assertThat(metadata).has(Metadata.withGroup("immutable-nested.nested")); + assertThat(metadata).has(Metadata.withProperty("immutable-nested.nested.my-nested-property")); + } + @Test void nestedClassChildProperties() { ConfigurationMetadata metadata = compile(ClassWithNestedProperties.class); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/method/NestedPropertiesMethodImmutable.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/method/NestedPropertiesMethodImmutable.java new file mode 100644 index 000000000000..b0b0447658bd --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/method/NestedPropertiesMethodImmutable.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationsample.method; + +import org.springframework.boot.configurationsample.TestConfigurationProperties; +import org.springframework.boot.configurationsample.TestNestedConfigurationProperty; + +@TestConfigurationProperties("immutable-nested") +public class NestedPropertiesMethodImmutable { + + private final NestedProperty nested; + + NestedPropertiesMethodImmutable(NestedProperty nested) { + this.nested = nested; + + } + + @TestNestedConfigurationProperty + public NestedProperty getNested() { + return this.nested; + } + +} From 664467563b4316acfa84f67262c574c0875ab0a5 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Wed, 25 Mar 2026 09:29:23 +0800 Subject: [PATCH 190/376] Remove empty package "org.springframework.boot.actuate.data" See gh-49741 Signed-off-by: Yanming Zhou --- .../boot/actuate/data/package-info.java | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/package-info.java diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/package-info.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/package-info.java deleted file mode 100644 index 67d96c0f62c9..000000000000 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/data/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Actuator support dependent on Spring Data. - */ -@NullMarked -package org.springframework.boot.actuate.data; - -import org.jspecify.annotations.NullMarked; From 328267249f71336f642effe8aaf80e39d9d9be65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 25 Mar 2026 07:31:46 +0100 Subject: [PATCH 191/376] Make DevTools tests more tolerant to wrapped DataSource Closes gh-49745 --- ...stractDevToolsDataSourceAutoConfigurationTests.java | 10 ++++++---- ...DevToolsPooledDataSourceAutoConfigurationTests.java | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java index 8831afd661ed..34e06010ba71 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/AbstractDevToolsDataSourceAutoConfigurationTests.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.test.util.TestPropertyValues; @@ -34,6 +35,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -160,10 +162,10 @@ static DataSourceSpyBeanPostProcessor dataSourceSpyBeanPostProcessor() { } - static class DataSourceSpyBeanPostProcessor implements BeanPostProcessor { + static class DataSourceSpyBeanPostProcessor implements BeanPostProcessor, Ordered { @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof DataSource) { bean = spy(bean); } @@ -171,8 +173,8 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { } @Override - public Object postProcessAfterInitialization(Object bean, String beanName) { - return bean; + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; } } diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java index 6eb2babdafba..a0bd54d40c21 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsPooledDataSourceAutoConfigurationTests.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.DataSourceUnwrapper; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; @@ -135,7 +136,8 @@ void inMemoryDerbyIsShutdown() throws Exception { try (ConfigurableApplicationContext context = getContext( () -> createContext("org.apache.derby.jdbc.EmbeddedDriver", "jdbc:derby:memory:test;create=true", DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class))) { - HikariDataSource dataSource = context.getBean(HikariDataSource.class); + HikariDataSource dataSource = DataSourceUnwrapper.unwrap(context.getBean(DataSource.class), + HikariDataSource.class); JdbcTemplate jdbc = new JdbcTemplate(dataSource); jdbc.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1"); HikariPoolMXBean pool = dataSource.getHikariPoolMXBean(); From 025b527e87f9820c1142468fdcf0b21db3cd192d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 10:51:01 +0000 Subject: [PATCH 192/376] Fix WebSocketMessagingAutoConfiguration in the absence of Jackson Previously, the absence of Jackson would result in the loss of some configuration that does not depend on Jackson. That configuration is: - the inbound and outbound channels' executors - the bean that forces the stompWebSocketHandlerMapping bean to be eager when lazy init is enabled This commit updates the auto-configuration so that only the Jackson message converter configuration backs off in Jackson's absence. Fixes gh-49750 --- .../WebSocketMessagingAutoConfiguration.java | 56 ++++++++----- ...SocketMessagingAutoConfigurationTests.java | 83 ++++++++++++------- 2 files changed, 89 insertions(+), 50 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java index 73e5ebc30d72..567c7785a710 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.messaging.converter.ByteArrayMessageConverter; @@ -39,7 +40,6 @@ import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.StringMessageConverter; -import org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.util.MimeTypeUtils; import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration; @@ -55,30 +55,31 @@ */ @AutoConfiguration(after = JacksonAutoConfiguration.class) @ConditionalOnWebApplication(type = Type.SERVLET) -@ConditionalOnClass(WebSocketMessageBrokerConfigurer.class) +@ConditionalOnClass({ WebSocketMessageBrokerConfigurer.class, DelegatingWebSocketMessageBrokerConfiguration.class }) +@ConditionalOnBean(DelegatingWebSocketMessageBrokerConfiguration.class) public class WebSocketMessagingAutoConfiguration { + @Bean + static LazyInitializationExcludeFilter eagerStompWebSocketHandlerMapping() { + return (name, definition, type) -> name.equals("stompWebSocketHandlerMapping"); + } + + @Bean + WebSocketMessageBrokerExecutorConfigurer webSocketMessageBrokerExecutorConfigurer( + Map taskExecutors) { + return new WebSocketMessageBrokerExecutorConfigurer(taskExecutors); + } + @Configuration(proxyBeanMethods = false) - @ConditionalOnBean({ DelegatingWebSocketMessageBrokerConfiguration.class, ObjectMapper.class }) - @ConditionalOnClass({ ObjectMapper.class, AbstractMessageBrokerConfiguration.class }) + @ConditionalOnBean(ObjectMapper.class) + @ConditionalOnClass(ObjectMapper.class) @Order(0) static class WebSocketMessageConverterConfiguration implements WebSocketMessageBrokerConfigurer { private final ObjectMapper objectMapper; - private final AsyncTaskExecutor executor; - - WebSocketMessageConverterConfiguration(ObjectMapper objectMapper, - Map taskExecutors) { + WebSocketMessageConverterConfiguration(ObjectMapper objectMapper) { this.objectMapper = objectMapper; - this.executor = determineAsyncTaskExecutor(taskExecutors); - } - - private static AsyncTaskExecutor determineAsyncTaskExecutor(Map taskExecutors) { - if (taskExecutors.size() == 1) { - return taskExecutors.values().iterator().next(); - } - return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); } @Override @@ -93,6 +94,23 @@ public boolean configureMessageConverters(List messageConverte return false; } + } + + static class WebSocketMessageBrokerExecutorConfigurer implements WebSocketMessageBrokerConfigurer, Ordered { + + private final AsyncTaskExecutor executor; + + WebSocketMessageBrokerExecutorConfigurer(Map taskExecutors) { + this.executor = determineAsyncTaskExecutor(taskExecutors); + } + + private static AsyncTaskExecutor determineAsyncTaskExecutor(Map taskExecutors) { + if (taskExecutors.size() == 1) { + return taskExecutors.values().iterator().next(); + } + return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); + } + @Override public void configureClientInboundChannel(ChannelRegistration registration) { if (this.executor != null) { @@ -107,9 +125,9 @@ public void configureClientOutboundChannel(ChannelRegistration registration) { } } - @Bean - static LazyInitializationExcludeFilter eagerStompWebSocketHandlerMapping() { - return (name, definition, type) -> name.equals("stompWebSocketHandlerMapping"); + @Override + public int getOrder() { + return 0; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java index 75d6283806ff..d25cb6ea391e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketMessagingAutoConfigurationTests.java @@ -19,11 +19,10 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -38,9 +37,9 @@ import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; -import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; +import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration.WebSocketMessageBrokerExecutorConfigurer; import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.util.TestPropertyValues; @@ -56,7 +55,6 @@ import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.SimpleMessageConverter; import org.springframework.messaging.simp.annotation.SubscribeMapping; -import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompFrameHandler; @@ -142,51 +140,74 @@ void customizedConverterTypesMatchDefaultConverterTypes() { } @Test - void predefinedThreadExecutorIsSelectedForInboundChannel() { + void asyncTaskExecutorBeanIsSelectedForInboundChannel() { AsyncTaskExecutor expectedExecutor = new SimpleAsyncTaskExecutor(); - ChannelRegistration registration = new ChannelRegistration(); - WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration( - new ObjectMapper(), - Map.of(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME, expectedExecutor)); - configuration.configureClientInboundChannel(registration); - assertThat(registration).extracting("executor").isEqualTo(expectedExecutor); + this.context.register(WebSocketMessagingConfiguration.class); + this.context.registerBean(AsyncTaskExecutor.class, () -> expectedExecutor); + this.context.refresh(); + assertThat(this.context.getBean("clientInboundChannelExecutor", Executor.class)).isSameAs(expectedExecutor); } @Test - void predefinedThreadExecutorIsSelectedForOutboundChannel() { + void asyncTaskExecutorBeanIsSelectedForOutboundChannel() { AsyncTaskExecutor expectedExecutor = new SimpleAsyncTaskExecutor(); - ChannelRegistration registration = new ChannelRegistration(); - WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration( - new ObjectMapper(), - Map.of(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME, expectedExecutor)); - configuration.configureClientOutboundChannel(registration); - assertThat(registration).extracting("executor").isEqualTo(expectedExecutor); + this.context.register(WebSocketMessagingConfiguration.class); + this.context.registerBean(AsyncTaskExecutor.class, () -> expectedExecutor); + this.context.refresh(); + assertThat(this.context.getBean("clientOutboundChannelExecutor", Executor.class)).isSameAs(expectedExecutor); + } + + @Test + void withMultipleAsyncTaskExecutorBeansApplicationTaskExecutorIsSelectedForInboundChannel() { + AsyncTaskExecutor applicationTaskExecutor = new SimpleAsyncTaskExecutor(); + AsyncTaskExecutor additionalTaskExecutor = new SimpleAsyncTaskExecutor(); + this.context.registerBean("applicationTaskExecutor", AsyncTaskExecutor.class, () -> applicationTaskExecutor); + this.context.registerBean(AsyncTaskExecutor.class, () -> additionalTaskExecutor); + this.context.register(WebSocketMessagingConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean("clientInboundChannelExecutor", Executor.class)) + .isSameAs(applicationTaskExecutor); + } + + @Test + void withMultipleAsyncTaskExecutorBeansApplicationTaskExecutorIsSelectedForOutboundChannel() { + AsyncTaskExecutor applicationTaskExecutor = new SimpleAsyncTaskExecutor(); + AsyncTaskExecutor additionalTaskExecutor = new SimpleAsyncTaskExecutor(); + this.context.registerBean("applicationTaskExecutor", AsyncTaskExecutor.class, () -> applicationTaskExecutor); + this.context.registerBean(AsyncTaskExecutor.class, () -> additionalTaskExecutor); + this.context.register(WebSocketMessagingConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean("clientOutboundChannelExecutor", Executor.class)) + .isSameAs(applicationTaskExecutor); } @Test + @SuppressWarnings("unchecked") void webSocketMessageBrokerConfigurerOrdering() throws Throwable { - TestPropertyValues.of("server.port:0", "spring.jackson.serialization.indent-output:true").applyTo(this.context); + TestPropertyValues.of("server.port:0").applyTo(this.context); this.context.register(WebSocketMessagingConfiguration.class, CustomLowWebSocketMessageBrokerConfigurer.class, - CustomHighWebSocketMessageBrokerConfigurer.class); + CustomHighWebSocketMessageBrokerConfigurer.class, JacksonAutoConfiguration.class); this.context.refresh(); DelegatingWebSocketMessageBrokerConfiguration delegatingConfiguration = this.context .getBean(DelegatingWebSocketMessageBrokerConfiguration.class); - CustomHighWebSocketMessageBrokerConfigurer high = this.context - .getBean(CustomHighWebSocketMessageBrokerConfigurer.class); - WebSocketMessageConverterConfiguration autoConfiguration = this.context - .getBean(WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration.class); - WebSocketMessagingConfiguration configuration = this.context.getBean(WebSocketMessagingConfiguration.class); - CustomLowWebSocketMessageBrokerConfigurer low = this.context - .getBean(CustomLowWebSocketMessageBrokerConfigurer.class); assertThat(delegatingConfiguration).extracting("configurers") .asInstanceOf(InstanceOfAssertFactories.LIST) - .containsExactly(high, autoConfiguration, configuration, low); + .satisfies((configurers) -> { + assertThat(configurers).hasSize(5); + assertThat(configurers).first().isInstanceOf(CustomHighWebSocketMessageBrokerConfigurer.class); + assertThat((List) configurers.subList(1, 3)).contains( + this.context.getBean(WebSocketMessageConverterConfiguration.class), + this.context.getBean(WebSocketMessageBrokerExecutorConfigurer.class)); + assertThat((List) configurers.subList(3, 5)).contains( + this.context.getBean(WebSocketMessagingConfiguration.class), + this.context.getBean(CustomLowWebSocketMessageBrokerConfigurer.class)); + }); } private List getCustomizedConverters() { List customizedConverters = new ArrayList<>(); WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration configuration = new WebSocketMessagingAutoConfiguration.WebSocketMessageConverterConfiguration( - new ObjectMapper(), Collections.emptyMap()); + new ObjectMapper()); configuration.configureMessageConverters(customizedConverters); return customizedConverters; } @@ -198,7 +219,7 @@ private List getDefaultConverters() { } private Object performStompSubscription(String topic) throws Throwable { - TestPropertyValues.of("server.port:0", "spring.jackson.serialization.indent-output:true").applyTo(this.context); + TestPropertyValues.of("server.port:0").applyTo(this.context); this.context.register(WebSocketMessagingConfiguration.class); this.context.refresh(); WebSocketStompClient stompClient = new WebSocketStompClient(this.sockJsClient); @@ -261,7 +282,7 @@ public void handleTransportError(StompSession session, Throwable exception) { @EnableWebSocket @EnableConfigurationProperties @EnableWebSocketMessageBroker - @ImportAutoConfiguration({ JacksonAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class, + @ImportAutoConfiguration({ ServletWebServerFactoryAutoConfiguration.class, WebSocketMessagingAutoConfiguration.class, DispatcherServletAutoConfiguration.class }) static class WebSocketMessagingConfiguration implements WebSocketMessageBrokerConfigurer { From 96eea1812dd65a5f0475ddc6f2e88ce9de86d6d7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 11:49:51 +0000 Subject: [PATCH 193/376] Fix WebSocketMessagingAutoConfiguration without a JsonMapper bean Previously, if Jackson was on the classpath but no JsonMapper bean was present, the app would fail to start. This commit updates the auto-configuration so that the parts that require a JsonMapper bean back of when no such bean is present. The sample problem also exists with Jackson 2 and an ObjectMapper bean. This commit also corrects that. Fixes gh-49749 --- .../WebSocketMessagingAutoConfiguration.java | 43 ++++--------- ...SocketMessagingAutoConfigurationTests.java | 62 +++++++++++-------- 2 files changed, 50 insertions(+), 55 deletions(-) diff --git a/module/spring-boot-websocket/src/main/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfiguration.java b/module/spring-boot-websocket/src/main/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfiguration.java index abb22362dcc7..fc59240e7a5a 100644 --- a/module/spring-boot-websocket/src/main/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfiguration.java +++ b/module/spring-boot-websocket/src/main/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfiguration.java @@ -71,41 +71,16 @@ static LazyInitializationExcludeFilter eagerStompWebSocketHandlerMapping() { } @Bean - WebSocketMessageBrokerExecutorConfigurer webSocketMessageBrokerExecutorConfigurer( + SpringBootWebSocketMessageBrokerConfigurer springBootWebSocketMessageBrokerConfigurer( Map taskExecutors) { - return new WebSocketMessageBrokerExecutorConfigurer(taskExecutors); - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnBean(JsonMapper.class) - @ConditionalOnClass(JsonMapper.class) - @Order(0) - static class SpringBootWebSocketMessageBrokerConfiguration implements WebSocketMessageBrokerConfigurer { - - private final JsonMapper jsonMapper; - - SpringBootWebSocketMessageBrokerConfiguration(JsonMapper jsonMapper) { - this.jsonMapper = jsonMapper; - } - - @Override - public boolean configureMessageConverters(List messageConverters) { - messageConverters.add(new StringMessageConverter()); - messageConverters.add(new ByteArrayMessageConverter()); - JacksonJsonMessageConverter converter = new JacksonJsonMessageConverter(this.jsonMapper); - DefaultContentTypeResolver resolver = new DefaultContentTypeResolver(); - resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON); - converter.setContentTypeResolver(resolver); - messageConverters.add(converter); - return false; - } - + return new SpringBootWebSocketMessageBrokerConfigurer(taskExecutors); } @Order(1) @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "spring.websocket.messaging.preferred-json-mapper", havingValue = "jackson", matchIfMissing = true) + @ConditionalOnBean(JsonMapper.class) @ConditionalOnClass(JsonMapper.class) static class JacksonWebSocketMessageConverterConfiguration implements WebSocketMessageBrokerConfigurer { @@ -132,6 +107,7 @@ public boolean configureMessageConverters(List messageConverte @Deprecated(since = "4.0.0", forRemoval = true) @SuppressWarnings("removal") @Conditional(NoJacksonOrJackson2Preferred.class) + @ConditionalOnBean(ObjectMapper.class) @ConditionalOnClass(ObjectMapper.class) static class Jackson2WebSocketMessageConverterConfiguration implements WebSocketMessageBrokerConfigurer { @@ -172,11 +148,11 @@ static class Jackson2Preferred { } - static class WebSocketMessageBrokerExecutorConfigurer implements WebSocketMessageBrokerConfigurer, Ordered { + static class SpringBootWebSocketMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer, Ordered { private final @Nullable AsyncTaskExecutor executor; - WebSocketMessageBrokerExecutorConfigurer(Map taskExecutors) { + SpringBootWebSocketMessageBrokerConfigurer(Map taskExecutors) { this.executor = determineAsyncTaskExecutor(taskExecutors); } @@ -188,6 +164,13 @@ static class WebSocketMessageBrokerExecutorConfigurer implements WebSocketMessag return taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); } + @Override + public boolean configureMessageConverters(List messageConverters) { + messageConverters.add(new StringMessageConverter()); + messageConverters.add(new ByteArrayMessageConverter()); + return false; + } + @Override public void configureClientInboundChannel(ChannelRegistration registration) { if (this.executor != null) { diff --git a/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java b/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java index 96422e0dbd3d..958c6eabdd22 100644 --- a/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java +++ b/module/spring-boot-websocket/src/test/java/org/springframework/boot/websocket/autoconfigure/servlet/WebSocketMessagingAutoConfigurationTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.websocket.autoconfigure.servlet; import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -34,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.skyscreamer.jsonassert.JSONAssert; -import tools.jackson.databind.json.JsonMapper; import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -51,8 +49,7 @@ import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; import org.springframework.boot.websocket.autoconfigure.servlet.WebSocketMessagingAutoConfiguration.JacksonWebSocketMessageConverterConfiguration; -import org.springframework.boot.websocket.autoconfigure.servlet.WebSocketMessagingAutoConfiguration.SpringBootWebSocketMessageBrokerConfiguration; -import org.springframework.boot.websocket.autoconfigure.servlet.WebSocketMessagingAutoConfiguration.WebSocketMessageBrokerExecutorConfigurer; +import org.springframework.boot.websocket.autoconfigure.servlet.WebSocketMessagingAutoConfiguration.SpringBootWebSocketMessageBrokerConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @@ -119,6 +116,7 @@ void tearDown() { @Test void basicMessagingWithJsonResponse() throws Throwable { + this.context.register(JacksonAutoConfiguration.class); Object result = performStompSubscription("/app/json"); JSONAssert.assertEquals("{\"foo\" : 5,\"bar\" : \"baz\"}", new String((byte[]) result), true); } @@ -138,7 +136,10 @@ void whenLazyInitializationIsEnabledThenBasicMessagingWorks() throws Throwable { @Test void customizedConverterTypesMatchDefaultConverterTypes() { - List customizedConverters = getCustomizedConverters(); + this.context.register(WebSocketMessagingConfiguration.class, JacksonAutoConfiguration.class); + this.context.refresh(); + List customizedConverters = getConverters( + this.context.getBean(DelegatingWebSocketMessageBrokerConfiguration.class)); List defaultConverters = getDefaultConverters(); assertThat(customizedConverters).hasSameSizeAs(defaultConverters); Iterator customizedIterator = customizedConverters.iterator(); @@ -202,13 +203,11 @@ void webSocketMessageBrokerConfigurerOrdering() throws Throwable { assertThat(delegatingConfiguration).extracting("configurers") .asInstanceOf(InstanceOfAssertFactories.LIST) .satisfies((configurers) -> { - assertThat(configurers).hasSize(6); + assertThat(configurers).hasSize(5); assertThat(configurers).first().isInstanceOf(CustomHighWebSocketMessageBrokerConfigurer.class); - assertThat((List) configurers.subList(1, 3)).contains( - this.context.getBean(SpringBootWebSocketMessageBrokerConfiguration.class), - this.context.getBean(WebSocketMessageBrokerExecutorConfigurer.class)); - assertThat(configurers.get(3)).isInstanceOf(JacksonWebSocketMessageConverterConfiguration.class); - assertThat((List) configurers.subList(4, 6)).contains( + assertThat(configurers.get(1)).isInstanceOf(SpringBootWebSocketMessageBrokerConfigurer.class); + assertThat(configurers.get(2)).isInstanceOf(JacksonWebSocketMessageConverterConfiguration.class); + assertThat((List) configurers.subList(3, 5)).contains( this.context.getBean(WebSocketMessagingConfiguration.class), this.context.getBean(CustomLowWebSocketMessageBrokerConfigurer.class)); }); @@ -234,26 +233,39 @@ void shouldUseJackson2WhenPreferred() { @SuppressWarnings("removal") @ClassPathExclusions("jackson-*-3*") void shouldUseJackson2WhenJacksonIsMissing() { - TestPropertyValues.of("server.port:0").applyTo(this.context); - this.context.register(WebSocketMessagingConfiguration.class, CustomLowWebSocketMessageBrokerConfigurer.class, - CustomHighWebSocketMessageBrokerConfigurer.class); + this.context.register(WebSocketMessagingConfiguration.class); this.context.registerBean(ObjectMapper.class); this.context.refresh(); - assertThat(AssertableApplicationContext.get(() -> this.context)).hasSingleBean( - org.springframework.boot.websocket.autoconfigure.servlet.WebSocketMessagingAutoConfiguration.Jackson2WebSocketMessageConverterConfiguration.class) + assertThat(AssertableApplicationContext.get(() -> this.context)) + .hasSingleBean(WebSocketMessagingAutoConfiguration.Jackson2WebSocketMessageConverterConfiguration.class) .doesNotHaveBean(JacksonWebSocketMessageConverterConfiguration.class); } - private List getCustomizedConverters() { - List customizedConverters = new ArrayList<>(); - WebSocketMessagingAutoConfiguration.SpringBootWebSocketMessageBrokerConfiguration configuration = new WebSocketMessagingAutoConfiguration.SpringBootWebSocketMessageBrokerConfiguration( - new JsonMapper()); - configuration.configureMessageConverters(customizedConverters); - return customizedConverters; + @Test + @Deprecated(since = "4.0.4", forRemoval = true) + @SuppressWarnings("removal") + @ClassPathExclusions("jackson-*-3*") + void jackson2ConfigurationShouldBackOffWhenThereIsNoObjectMapperBean() { + this.context.register(WebSocketMessagingConfiguration.class); + this.context.refresh(); + assertThat(AssertableApplicationContext.get(() -> this.context)) + .doesNotHaveBean(WebSocketMessagingAutoConfiguration.Jackson2WebSocketMessageConverterConfiguration.class) + .doesNotHaveBean(JacksonWebSocketMessageConverterConfiguration.class); + } + + @Test + void jacksonConfigurationShouldBackOffWhenThereIsNoJsonMapperBean() { + this.context.register(WebSocketMessagingConfiguration.class); + this.context.refresh(); + assertThat(AssertableApplicationContext.get(() -> this.context)) + .doesNotHaveBean(JacksonWebSocketMessageConverterConfiguration.class); } private List getDefaultConverters() { - DelegatingWebSocketMessageBrokerConfiguration configuration = new DelegatingWebSocketMessageBrokerConfiguration(); + return getConverters(new DelegatingWebSocketMessageBrokerConfiguration()); + } + + private List getConverters(DelegatingWebSocketMessageBrokerConfiguration configuration) { CompositeMessageConverter compositeDefaultConverter = configuration.brokerMessageConverter(); return compositeDefaultConverter.getConverters(); } @@ -325,8 +337,8 @@ public void handleTransportError(StompSession session, Throwable exception) { @EnableWebSocket @EnableConfigurationProperties @EnableWebSocketMessageBroker - @ImportAutoConfiguration({ JacksonAutoConfiguration.class, TomcatServletWebServerAutoConfiguration.class, - WebSocketMessagingAutoConfiguration.class, DispatcherServletAutoConfiguration.class }) + @ImportAutoConfiguration({ TomcatServletWebServerAutoConfiguration.class, WebSocketMessagingAutoConfiguration.class, + DispatcherServletAutoConfiguration.class }) static class WebSocketMessagingConfiguration implements WebSocketMessageBrokerConfigurer { @Override From 2841d874cfffed8f8c75bf631289aa4283281d3a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:11:24 +0000 Subject: [PATCH 194/376] Upgrade to Zipkin Reporter 3.5.3 Closes gh-49756 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 9e9e89920a7c..c77f5cdb685b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -127,7 +127,7 @@ bom { .formatted(version.major(), version.minor())) } } - library("Zipkin Reporter", "3.5.1") { + library("Zipkin Reporter", "3.5.3") { group("io.zipkin.reporter2") { bom("zipkin-reporter-bom") } From ef876fe5d2648ec11eab45546725bd0f21c34870 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:11:32 +0000 Subject: [PATCH 195/376] Upgrade to Hibernate 6.6.45.Final Closes gh-49757 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index c77f5cdb685b..6ed76f58f705 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -561,7 +561,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "6.6.44.Final") { + library("Hibernate", "6.6.45.Final") { prohibit { versionRange "[7.0.0.Alpha1,)" because "it exceeds our Jakarta EE 10 baseline" From 851ddda4225a97ba8f5030454f276a559bcb3b71 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:11:40 +0000 Subject: [PATCH 196/376] Upgrade to jOOQ 3.19.31 Closes gh-49758 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 6ed76f58f705..c77542152b14 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1100,7 +1100,7 @@ bom { ] } } - library("jOOQ", "3.19.30") { + library("jOOQ", "3.19.31") { prohibit { versionRange "[3.20.0,)" because "it requires Java 21" From 093429614119c03ab660e30fd21dacbd404a10fe Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:11:48 +0000 Subject: [PATCH 197/376] Upgrade to Netty 4.1.132.Final Closes gh-49759 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index c77542152b14..69b88e785977 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1741,7 +1741,7 @@ bom { releaseNotes("https://github.com/neo4j/neo4j-java-driver/releases/tag/{version}") } } - library("Netty", "4.1.131.Final") { + library("Netty", "4.1.132.Final") { prohibit { contains ".Alpha" contains ".Beta" From 32a51d5d709b758786f371e063754d99c0348755 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:11:56 +0000 Subject: [PATCH 198/376] Upgrade to Tomcat 10.1.53 Closes gh-49760 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c77990cd337d..ca11c05104b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,6 +21,6 @@ nativeBuildToolsVersion=0.10.6 snakeYamlVersion=2.4 springFrameworkVersion=6.2.17 springFramework60xVersion=6.0.23 -tomcatVersion=10.1.52 +tomcatVersion=10.1.53 kotlin.stdlib.default.dependency=false From 4b37ecbf6db93913f811f4d3f64a7f800ea39573 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:12:04 +0000 Subject: [PATCH 199/376] Upgrade to Undertow 2.3.24.Final Closes gh-49761 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 69b88e785977..1d48e95ccf94 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2638,7 +2638,7 @@ bom { releaseNotes("https://github.com/pingidentity/ldapsdk/releases/tag/{version}") } } - library("Undertow", "2.3.23.Final") { + library("Undertow", "2.3.24.Final") { group("io.undertow") { modules = [ "undertow-core", From d9ac9f9de1ddc30eeaa98d2f8e7652299647e57d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:29:52 +0000 Subject: [PATCH 200/376] Upgrade to Zipkin Reporter 3.5.3 Closes gh-49762 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index df06f9d58bf9..31a0729fd413 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -117,7 +117,7 @@ bom { .formatted(version.major(), version.minor())) } } - library("Zipkin Reporter", "3.5.1") { + library("Zipkin Reporter", "3.5.3") { group("io.zipkin.reporter2") { bom("zipkin-reporter-bom") } From 9dfb854dae66bcc3d4ef9350a60ce9f8353c5494 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:29:56 +0000 Subject: [PATCH 201/376] Upgrade to Brave 6.3.1 Closes gh-49763 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 31a0729fd413..89d1f98a2c4e 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -126,7 +126,7 @@ bom { releaseNotes("https://github.com/openzipkin/zipkin-reporter-java/releases/tag/{version}") } } - library("Brave", "6.3.0") { + library("Brave", "6.3.1") { group("io.zipkin.brave") { bom("brave-bom") } From b6dccd4fd609ca2b9509bec4cd3f4dfa810429cb Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:30:00 +0000 Subject: [PATCH 202/376] Upgrade to Jackson 2 Bom 2.21.2 Closes gh-49764 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8aa818750fbe..8eab73e7770e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ checkstyleToolVersion=10.12.4 commonsCodecVersion=1.19.0 graalVersion=25 hamcrestVersion=3.0 -jackson2Version=2.21.1 +jackson2Version=2.21.2 jacksonVersion=3.1.0 javaFormatVersion=0.0.47 junitJupiterVersion=6.0.3 From 7e6833bc9c5b73bba6920cead989e28d64f982ff Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:30:05 +0000 Subject: [PATCH 203/376] Upgrade to jOOQ 3.19.31 Closes gh-49765 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 89d1f98a2c4e..c97a86ee7d92 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1070,7 +1070,7 @@ bom { ] } } - library("jOOQ", "3.19.30") { + library("jOOQ", "3.19.31") { prohibit { versionRange "[3.20.0,)" because "it requires Java 21" From fd94ca0a0baab48a055b3dfe8fd4d09daec766b9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:30:09 +0000 Subject: [PATCH 204/376] Upgrade to Netty 4.2.11.Final Closes gh-49766 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index c97a86ee7d92..2573a869e9d8 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1698,7 +1698,7 @@ bom { releaseNotes("https://github.com/neo4j/neo4j-java-driver/releases/tag/{version}") } } - library("Netty", "4.2.10.Final") { + library("Netty", "4.2.11.Final") { prohibit { contains ".Alpha" contains ".Beta" From ba70d41a998c8e77d185dd1d7e4ace80ed8cd7e2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 25 Mar 2026 12:30:13 +0000 Subject: [PATCH 205/376] Upgrade to Tomcat 11.0.20 Closes gh-49767 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8eab73e7770e..aa96d51321fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,6 +23,6 @@ nullabilityPluginVersion=0.0.11 snakeYamlVersion=2.5 springFrameworkVersion=7.0.6 springFramework60xVersion=6.0.23 -tomcatVersion=11.0.18 +tomcatVersion=11.0.20 kotlin.stdlib.default.dependency=false From 696a60e8fd2ce2bff1cf96c2706a97cf64b49a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 25 Mar 2026 14:07:18 +0100 Subject: [PATCH 206/376] Full auto-configure transaction management in slice tests Closes gh-49716 --- .../data/mongo/DataMongoTestIntegrationTests.java | 9 +++++++++ .../data/neo4j/DataNeo4jTestIntegrationTests.java | 9 +++++++++ ...autoconfigure.data.jdbc.AutoConfigureDataJdbc.imports | 1 + ...toconfigure.data.mongo.AutoConfigureDataMongo.imports | 1 + ...toconfigure.data.neo4j.AutoConfigureDataNeo4j.imports | 1 + ...toconfigure.data.r2dbc.AutoConfigureDataR2dbc.imports | 1 + ...oot.test.autoconfigure.jdbc.AutoConfigureJdbc.imports | 1 + ...oot.test.autoconfigure.jooq.AutoConfigureJooq.imports | 1 + ...st.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports | 1 + .../data/jdbc/DataJdbcTestIntegrationTests.java | 9 +++++++++ .../data/r2dbc/DataR2dbcTestIntegrationTests.java | 9 +++++++++ .../autoconfigure/jooq/JooqTestIntegrationTests.java | 9 +++++++++ .../orm/jpa/DataJpaTestIntegrationTests.java | 9 +++++++++ 13 files changed, 61 insertions(+) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java index c04d629bd073..b01ce8dbc218 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java @@ -23,6 +23,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.boot.testsupport.container.TestImage; @@ -78,4 +80,11 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java index c387dbaf40e0..a7b30e1edc1a 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java @@ -23,6 +23,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.boot.testsupport.container.TestImage; @@ -80,4 +82,11 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc.imports index eb4b3faada1b..36eb93bb6bfd 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc.imports @@ -8,4 +8,5 @@ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo.imports index cd75eda62b4a..f71a752fd7ba 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo.imports @@ -6,5 +6,6 @@ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfigura org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j.imports index 96aef94577bd..9f987f2cc826 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j.imports @@ -5,4 +5,5 @@ org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfigura org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.r2dbc.AutoConfigureDataR2dbc.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.r2dbc.AutoConfigureDataR2dbc.imports index 678494ab914a..9dee2ea1a4a3 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.r2dbc.AutoConfigureDataR2dbc.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.data.r2dbc.AutoConfigureDataR2dbc.imports @@ -7,4 +7,5 @@ org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc.imports index 480dcff0e7c1..7399568ef498 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc.imports @@ -7,4 +7,5 @@ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jooq.AutoConfigureJooq.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jooq.AutoConfigureJooq.imports index 1e042e2c0858..06d97b0eab99 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jooq.AutoConfigureJooq.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.jooq.AutoConfigureJooq.imports @@ -6,4 +6,5 @@ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports index 83465fdeba7e..3b13180d8e37 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports @@ -9,4 +9,5 @@ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration +org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration optional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestIntegrationTests.java index b7f449b898d0..1f17e9a1427a 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/jdbc/DataJdbcTestIntegrationTests.java @@ -24,6 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; @@ -89,4 +91,11 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/r2dbc/DataR2dbcTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/r2dbc/DataR2dbcTestIntegrationTests.java index 37f85dd77cff..cbbeb62f6db9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/r2dbc/DataR2dbcTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/r2dbc/DataR2dbcTestIntegrationTests.java @@ -25,6 +25,8 @@ import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.r2dbc.core.DatabaseClient; @@ -72,4 +74,11 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestIntegrationTests.java index d23a5bce81eb..1392e68a7e83 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/jooq/JooqTestIntegrationTests.java @@ -27,6 +27,8 @@ import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.test.autoconfigure.orm.jpa.ExampleComponent; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.context.ApplicationContext; @@ -91,4 +93,11 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestIntegrationTests.java index 0b94228e0b03..8d93174cd048 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTestIntegrationTests.java @@ -24,6 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.data.repository.config.BootstrapMode; @@ -120,6 +122,13 @@ void serviceConnectionAutoConfigurationWasImported() { assertThat(this.applicationContext).has(importedAutoConfiguration(ServiceConnectionAutoConfiguration.class)); } + @Test + void transactionAutoConfigurationWasImported() { + assertThat(this.applicationContext).has(importedAutoConfiguration(TransactionAutoConfiguration.class)); + assertThat(this.applicationContext) + .has(importedAutoConfiguration(TransactionManagerCustomizationAutoConfiguration.class)); + } + @Test void bootstrapModeIsDefaultByDefault() { assertThat(this.applicationContext.getEnvironment().getProperty("spring.data.jpa.repositories.bootstrap-mode")) From c1694b50c29e37a162a3d9ad43f4e4b434698247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 25 Mar 2026 16:02:45 +0100 Subject: [PATCH 207/376] Add missing Spring Integration test module to the relevant starter Closes gh-49784 --- starter/spring-boot-starter-integration-test/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/starter/spring-boot-starter-integration-test/build.gradle b/starter/spring-boot-starter-integration-test/build.gradle index 1637a2532d1b..fc55fad6d5d7 100644 --- a/starter/spring-boot-starter-integration-test/build.gradle +++ b/starter/spring-boot-starter-integration-test/build.gradle @@ -23,4 +23,5 @@ description = "Starter for testing Spring Integration" dependencies { api(project(":starter:spring-boot-starter-integration")) api(project(":starter:spring-boot-starter-test")) + api("org.springframework.integration:spring-integration-test") } From a413e9545fd1efe9a9548ec70c86f87559c907f1 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 26 Mar 2026 07:15:58 +0000 Subject: [PATCH 208/376] Upgrade to Netty 4.2.12.Final Closes gh-49794 --- platform/spring-boot-dependencies/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/spring-boot-dependencies/build.gradle b/platform/spring-boot-dependencies/build.gradle index 2573a869e9d8..245e48cfc896 100644 --- a/platform/spring-boot-dependencies/build.gradle +++ b/platform/spring-boot-dependencies/build.gradle @@ -1698,7 +1698,7 @@ bom { releaseNotes("https://github.com/neo4j/neo4j-java-driver/releases/tag/{version}") } } - library("Netty", "4.2.11.Final") { + library("Netty", "4.2.12.Final") { prohibit { contains ".Alpha" contains ".Beta" From 6c9e52a1745d255e096d1334593636d005f68143 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 26 Mar 2026 07:55:02 +0000 Subject: [PATCH 209/376] Next development version (v3.5.14-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ca11c05104b5..59312c8cf3a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.5.13-SNAPSHOT +version=3.5.14-SNAPSHOT latestVersion=false spring.build-type=oss From 1d8997f604c6215dddb8ba9653c442d31f4b75b9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 26 Mar 2026 07:56:29 +0000 Subject: [PATCH 210/376] Next development version (v4.0.6-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index aa96d51321fc..67d200083c77 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.0.5-SNAPSHOT +version=4.0.6-SNAPSHOT latestVersion=false spring.build-type=oss From 29674df3ee7b2cb5e68a8d005a3f3433fc2d8f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 26 Mar 2026 11:11:17 +0100 Subject: [PATCH 211/376] Remove duplicated default in manual Jackson metadata Closes gh-49797 --- .../jackson/JacksonAutoConfiguration.java | 22 ++++++++----------- .../jackson/JacksonProperties.java | 2 +- ...itional-spring-configuration-metadata.json | 4 ---- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java index 75e69f962e6e..20597bbdba4b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java @@ -326,19 +326,15 @@ private void configureDefaultLeniency(Jackson2ObjectMapperBuilder builder) { private void configureConstructorDetector(Jackson2ObjectMapperBuilder builder) { ConstructorDetectorStrategy strategy = this.jacksonProperties.getConstructorDetector(); - if (strategy != null) { - builder.postConfigurer((objectMapper) -> { - switch (strategy) { - case USE_PROPERTIES_BASED -> - objectMapper.setConstructorDetector(ConstructorDetector.USE_PROPERTIES_BASED); - case USE_DELEGATING -> - objectMapper.setConstructorDetector(ConstructorDetector.USE_DELEGATING); - case EXPLICIT_ONLY -> - objectMapper.setConstructorDetector(ConstructorDetector.EXPLICIT_ONLY); - default -> objectMapper.setConstructorDetector(ConstructorDetector.DEFAULT); - } - }); - } + builder.postConfigurer((objectMapper) -> { + switch (strategy) { + case USE_PROPERTIES_BASED -> + objectMapper.setConstructorDetector(ConstructorDetector.USE_PROPERTIES_BASED); + case USE_DELEGATING -> objectMapper.setConstructorDetector(ConstructorDetector.USE_DELEGATING); + case EXPLICIT_ONLY -> objectMapper.setConstructorDetector(ConstructorDetector.EXPLICIT_ONLY); + default -> objectMapper.setConstructorDetector(ConstructorDetector.DEFAULT); + } + }); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java index 780f818928ad..d57e901dc76c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java @@ -104,7 +104,7 @@ public class JacksonProperties { * Strategy to use to auto-detect constructor, and in particular behavior with * single-argument constructors. */ - private ConstructorDetectorStrategy constructorDetector; + private ConstructorDetectorStrategy constructorDetector = ConstructorDetectorStrategy.DEFAULT; /** * Time zone used when formatting dates. For instance, "America/Los_Angeles" or diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index ba1912b889ff..545a5c2f8c43 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1854,10 +1854,6 @@ "name": "spring.info.git.location", "defaultValue": "classpath:git.properties" }, - { - "name": "spring.jackson.constructor-detector", - "defaultValue": "default" - }, { "name": "spring.jackson.datatype.enum", "description": "Jackson on/off features for enums." From 14aa3aa4911f82401874615be9754bdad89d654b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:36:24 +0000 Subject: [PATCH 212/376] Bump picomatch in /antora Bumps and [picomatch](https://github.com/micromatch/picomatch). These dependencies needed to be updated together. Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) Updates `picomatch` from 4.0.3 to 4.0.4 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect ... See gh-49790 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index 3ddabebb5bfe..4d69484fd0ca 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -2388,9 +2388,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -2621,9 +2621,9 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" From 1b6e68ecf19becee9380373929754f9855f2197a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 26 Mar 2026 14:56:34 +0000 Subject: [PATCH 213/376] Upgrade to GitHub Changelog Generator 0.0.14 Closes gh-49803 --- .github/actions/create-github-release/action.yml | 2 +- .../actions/create-github-release/changelog-generator-oss.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml index e7a0b31329e5..d4b7d778148a 100644 --- a/.github/actions/create-github-release/action.yml +++ b/.github/actions/create-github-release/action.yml @@ -18,7 +18,7 @@ runs: using: composite steps: - name: Generate Changelog - uses: spring-io/github-changelog-generator@c247eb874a8bbc2c7b91ed7227d8eb66eb639d38 #v0.0.13 + uses: spring-io/github-changelog-generator@f7d7a87a3e7c627ecb8c26cf086c38ac5a939721 #v0.0.14 with: config-file: ${{ inputs.commercial && '.github/actions/create-github-release/changelog-generator-commercial.yml' || '.github/actions/create-github-release/changelog-generator-oss.yml' }} milestone: ${{ inputs.milestone }} diff --git a/.github/actions/create-github-release/changelog-generator-oss.yml b/.github/actions/create-github-release/changelog-generator-oss.yml index 23d48098c279..56f6608cd7aa 100644 --- a/.github/actions/create-github-release/changelog-generator-oss.yml +++ b/.github/actions/create-github-release/changelog-generator-oss.yml @@ -21,6 +21,10 @@ changelog: sort: "title" labels: - "type: dependency-upgrade" + summary: + mode: "body-regex" + config: + expression: '(Upgrade to \[.*\]\(.*\)).*' issues: generate_links: true ports: From 4527245ebc228a3433b263b5392b28fb398283e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 26 Mar 2026 15:46:56 +0100 Subject: [PATCH 214/376] Polish javadoc --- .../boot/autoconfigure/SpringBootApplication.java | 4 ++-- .../boot/devtools/restart/DefaultRestartInitializer.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SpringBootApplication.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SpringBootApplication.java index 845aef8a8254..b9aa76d3c97c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SpringBootApplication.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SpringBootApplication.java @@ -85,7 +85,7 @@ *

    * Note: this setting is an alias for * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} - * scanning or Spring Data {@link Repository} scanning. For those you should add + * scanning or Spring Data {@code Repository} scanning. For those you should add * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and * {@code @Enable...Repositories} annotations. * @return base packages to scan @@ -103,7 +103,7 @@ *

    * Note: this setting is an alias for * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} - * scanning or Spring Data {@link Repository} scanning. For those you should add + * scanning or Spring Data {@code Repository} scanning. For those you should add * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and * {@code @Enable...Repositories} annotations. * @return base packages to scan diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/DefaultRestartInitializer.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/DefaultRestartInitializer.java index 5b2c94ee7c99..cef1d07d1d52 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/DefaultRestartInitializer.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/DefaultRestartInitializer.java @@ -43,9 +43,9 @@ public URL[] getInitialUrls(Thread thread) { } /** - * Returns if the thread is for a main invocation. By default {@link #isMain(Thread) - * checks the name of the thread} and {@link #isDevelopmentClassLoader(ClassLoader) - * the context classloader}. + * Returns if the thread is for a main invocation. By default + * {@link #isMainThread(Thread)} checks the name of the thread} and + * {@link #isDevelopmentClassLoader(ClassLoader) the context classloader}. * @param thread the thread to check * @return {@code true} if the thread is a main invocation * @see #isMainThread From 2d744b20c4e5f718bc0fcef47c75ca409191964f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:19:58 +0000 Subject: [PATCH 215/376] Bump convict from 6.2.4 to 6.2.5 in /antora Bumps [convict](https://github.com/mozilla/node-convict) from 6.2.4 to 6.2.5. - [Changelog](https://github.com/mozilla/node-convict/blob/master/CHANGELOG.md) - [Commits](https://github.com/mozilla/node-convict/commits) --- updated-dependencies: - dependency-name: convict dependency-version: 6.2.5 dependency-type: indirect ... See gh-49809 Signed-off-by: dependabot[bot] --- antora/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/antora/package-lock.json b/antora/package-lock.json index 4d69484fd0ca..e47b00e01ab2 100644 --- a/antora/package-lock.json +++ b/antora/package-lock.json @@ -1243,9 +1243,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/convict": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.4.tgz", - "integrity": "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/convict/-/convict-6.2.5.tgz", + "integrity": "sha512-JtXpxqDqJ8P0UwEHwhxLzCIXQy97vlYBZR222Sbzb1q1Erex9ASrztJ29SyhWFQjod1AeFBaPzEEC8YvtZMIYg==", "license": "Apache-2.0", "dependencies": { "lodash.clonedeep": "^4.5.0", From 9a74fd55d327c8916c0a11dcc0aa887ce6913847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 27 Mar 2026 11:31:51 +0100 Subject: [PATCH 216/376] Adapt to code style changes in IntelliJ IDEA 2026.1 --- .idea/codeStyles/Project.xml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index cdd2a9295b1d..594f11918494 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -8,19 +8,6 @@