From 4258953190f6bd77d19b583a6535c9ee692091a9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 23 Aug 2024 14:56:20 +0100 Subject: [PATCH 001/271] Ensure that building the SNI test apps produces Java 17 bytecode See gh-41980 --- .../spring-boot-sni-client-app/build.gradle | 5 +++++ .../spring-boot-sni-reactive-app/build.gradle | 7 ++++++- .../spring-boot-sni-servlet-app/build.gradle | 7 ++++++- .../springframework/boot/sni/SniIntegrationTests.java | 9 ++++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-client-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-client-app/build.gradle index 3d8ee7040c67..416a2061fbba 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-client-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-client-app/build.gradle @@ -5,6 +5,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../int-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-reactive-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-reactive-app/build.gradle index e38367a66b3d..367da508300b 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-reactive-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-reactive-app/build.gradle @@ -2,11 +2,16 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { id "java" - id "org.springframework.boot" version "3.3.0-SNAPSHOT" + id "org.springframework.boot" } apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../int-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-servlet-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-servlet-app/build.gradle index 8a7548629ea7..9a4275e643fb 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-servlet-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/spring-boot-sni-servlet-app/build.gradle @@ -2,11 +2,16 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { id "java" - id "org.springframework.boot" version "3.3.0-SNAPSHOT" + id "org.springframework.boot" } apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../int-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/src/intTest/java/org/springframework/boot/sni/SniIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/src/intTest/java/org/springframework/boot/sni/SniIntegrationTests.java index 4fe4c177d5ba..8140199551c9 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/src/intTest/java/org/springframework/boot/sni/SniIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-sni-tests/src/intTest/java/org/springframework/boot/sni/SniIntegrationTests.java @@ -21,6 +21,7 @@ import java.util.Map; import org.awaitility.Awaitility; +import org.awaitility.core.ConditionTimeoutException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.testcontainers.containers.GenericContainer; @@ -56,7 +57,13 @@ class SniIntegrationTests { void home(String webStack, String server) { try (ApplicationContainer serverContainer = new ServerApplicationContainer(webStack, server)) { serverContainer.start(); - Awaitility.await().atMost(Duration.ofSeconds(60)).until(serverContainer::isRunning); + try { + Awaitility.await().atMost(Duration.ofSeconds(60)).until(serverContainer::isRunning); + } + catch (ConditionTimeoutException ex) { + System.out.println(serverContainer.getLogs()); + throw ex; + } String serverLogs = serverContainer.getLogs(); assertThat(serverLogs).contains(SERVER_START_MESSAGES.get(server)); try (ApplicationContainer clientContainer = new ClientApplicationContainer()) { From 4b0b5c263dfaec169b928f83abe89fd15053ec46 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 23 Aug 2024 15:11:55 +0100 Subject: [PATCH 002/271] Ensure that building test apps produces Java 17 bytecode See gh-41980 --- .../spring-boot-launch-script-tests-app/build.gradle | 5 +++++ .../spring-boot-loader-classic-tests-app/build.gradle | 5 +++++ .../spring-boot-loader-tests-app/build.gradle | 5 +++++ .../spring-boot-loader-tests-signed-jar/build.gradle | 5 +++++ .../spring-boot-server-tests-app/build.gradle | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/spring-boot-launch-script-tests-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/spring-boot-launch-script-tests-app/build.gradle index 9e9dc0d23820..9a21688370fd 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/spring-boot-launch-script-tests-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/spring-boot-launch-script-tests-app/build.gradle @@ -5,6 +5,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../docker-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-classic-tests/spring-boot-loader-classic-tests-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-classic-tests/spring-boot-loader-classic-tests-app/build.gradle index e7b3725dbbfd..f8a908f75f9e 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-classic-tests/spring-boot-loader-classic-tests-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-classic-tests/spring-boot-loader-classic-tests-app/build.gradle @@ -5,6 +5,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../docker-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-app/build.gradle index 209479c32ca2..90b170798fc4 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-app/build.gradle @@ -5,6 +5,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../docker-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-signed-jar/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-signed-jar/build.gradle index e3554106e640..d3471f6c09df 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-signed-jar/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/spring-boot-loader-tests-signed-jar/build.gradle @@ -7,6 +7,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../docker-test-maven-repository"} mavenCentral() diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle index bd73d368e598..ed6d0bdd68f3 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle @@ -9,6 +9,11 @@ plugins { apply plugin: "io.spring.dependency-management" +java { + sourceCompatibility = '17' + targetCompatibility = '17' +} + repositories { maven { url "file:${rootDir}/../test-repository"} mavenCentral() From 162c929a80605f6f1d776757527dbbb6db3fb944 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 23 Aug 2024 16:29:20 +0100 Subject: [PATCH 003/271] Remove workaround that should now be redundant See gh-41980 --- .../gradle/junit/GradleProjectBuilder.java | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleProjectBuilder.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleProjectBuilder.java index fc7277a3081f..1f63d8677122 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleProjectBuilder.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleProjectBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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,23 +18,17 @@ import java.io.File; -import org.gradle.api.JavaVersion; import org.gradle.api.Project; -import org.gradle.internal.nativeintegration.services.NativeServices; -import org.gradle.internal.nativeintegration.services.NativeServices.NativeServicesMode; import org.gradle.testfixtures.ProjectBuilder; -import org.gradle.testfixtures.internal.ProjectBuilderImpl; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * Helper class to build Gradle {@link Project Projects} for test fixtures. Wraps - * functionality of Gradle's own {@link ProjectBuilder} in order to work around an issue - * on JDK 17 and 18. + * functionality of Gradle's own {@link ProjectBuilder}. * * @author Christoph Dreis - * @see Gradle Support JDK 17 */ public final class GradleProjectBuilder { @@ -68,14 +62,6 @@ public Project build() { if (StringUtils.hasText(this.name)) { builder.withName(this.name); } - if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) { - NativeServices.initializeOnClient(userHome, NativeServicesMode.ENABLED); - try { - ProjectBuilderImpl.getGlobalServices(); - } - catch (Throwable ignore) { - } - } return builder.build(); } From 45b83d5d80ba9530ccad14eabb2210ca7911bf11 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 28 Aug 2024 09:34:31 +0200 Subject: [PATCH 004/271] Register reflection hints for ApplicationProperties Closes gh-42038 --- .../boot/ApplicationProperties.java | 9 +++++++++ .../boot/SpringApplication.java | 9 --------- .../resources/META-INF/spring/aot.factories | 2 +- .../boot/ApplicationPropertiesTests.java | 18 ++++++++++++++++++ .../boot/SpringApplicationTests.java | 15 --------------- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationProperties.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationProperties.java index 994e9381e0cf..34848c776966 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationProperties.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ApplicationProperties.java @@ -20,6 +20,7 @@ import java.util.Set; import org.springframework.boot.Banner.Mode; +import org.springframework.boot.context.properties.bind.BindableRuntimeHintsRegistrar; import org.springframework.boot.logging.LoggingSystemProperty; import org.springframework.core.env.Environment; @@ -156,4 +157,12 @@ void setWebApplicationType(WebApplicationType webApplicationType) { this.webApplicationType = webApplicationType; } + static class ApplicationPropertiesRuntimeHints extends BindableRuntimeHintsRegistrar { + + ApplicationPropertiesRuntimeHints() { + super(ApplicationProperties.class); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index fed80707e5aa..005f8e81d3c0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -58,7 +58,6 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.boot.Banner.Mode; 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.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.convert.ApplicationConversionService; @@ -1594,14 +1593,6 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) } - static class SpringApplicationRuntimeHints extends BindableRuntimeHintsRegistrar { - - SpringApplicationRuntimeHints() { - super(SpringApplication.class); - } - - } - /** * Exception that can be thrown to silently exit a running {@link SpringApplication} * without handling run failures. diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories index 905cd7174065..d938a89eda1d 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories @@ -1,5 +1,5 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ -org.springframework.boot.SpringApplication.SpringApplicationRuntimeHints,\ +org.springframework.boot.ApplicationProperties.ApplicationPropertiesRuntimeHints,\ org.springframework.boot.SpringApplicationBannerPrinter.SpringApplicationBannerPrinterRuntimeHints,\ org.springframework.boot.WebApplicationType.WebApplicationTypeRuntimeHints,\ org.springframework.boot.context.config.ConfigDataLocationRuntimeHints,\ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ApplicationPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ApplicationPropertiesTests.java index ead83d7c38b7..2620bc87450f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ApplicationPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ApplicationPropertiesTests.java @@ -18,6 +18,9 @@ import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; +import org.springframework.boot.ApplicationProperties.ApplicationPropertiesRuntimeHints; import org.springframework.boot.Banner.Mode; import org.springframework.mock.env.MockEnvironment; @@ -44,4 +47,19 @@ void bannerModeShouldBeOffIfStructuredLoggingIsEnabled() { assertThat(properties.getBannerMode(environment)).isEqualTo(Mode.OFF); } + @Test + void shouldRegisterHints() { + RuntimeHints hints = new RuntimeHints(); + new ApplicationPropertiesRuntimeHints().registerHints(hints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.reflection().onType(ApplicationProperties.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onMethod(ApplicationProperties.class, "setBannerMode")) + .accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onMethod(ApplicationProperties.class, "getSources")) + .accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onMethod(ApplicationProperties.class, "setSources")) + .accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onMethod(ApplicationProperties.class, "getBannerMode")) + .rejects(hints); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 353553d14b8f..2f07827407ab 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -44,8 +44,6 @@ import reactor.core.publisher.Mono; import org.springframework.aot.AotDetector; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCurrentlyInCreationException; import org.springframework.beans.factory.ObjectProvider; @@ -58,7 +56,6 @@ import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.boot.Banner.Mode; import org.springframework.boot.BootstrapRegistry.InstanceSupplier; -import org.springframework.boot.SpringApplication.SpringApplicationRuntimeHints; import org.springframework.boot.availability.AvailabilityChangeEvent; import org.springframework.boot.availability.AvailabilityState; import org.springframework.boot.availability.LivenessState; @@ -1412,18 +1409,6 @@ public void contextLoaded(ConfigurableApplicationContext context) { then(listener).should(never()).onApplicationEvent(any(ApplicationFailedEvent.class)); } - @Test - void shouldRegisterHints() { - RuntimeHints hints = new RuntimeHints(); - new SpringApplicationRuntimeHints().registerHints(hints, getClass().getClassLoader()); - assertThat(RuntimeHintsPredicates.reflection().onType(SpringApplication.class)).accepts(hints); - assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "setBannerMode")) - .accepts(hints); - assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "getSources")).accepts(hints); - assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "setSources")).accepts(hints); - assertThat(RuntimeHintsPredicates.reflection().onMethod(SpringApplication.class, "load")).rejects(hints); - } - @Test // gh-32555 void shouldUseAotInitializer() { SpringApplication application = new SpringApplication(ExampleAotProcessedMainClass.class); From ea0142f849f8cf4da24211c8c0717915d5d1349d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 22 Aug 2024 12:21:30 +0100 Subject: [PATCH 005/271] Use FileSystemOperations instead of Project copy and sync Closes gh-41998 --- .../springframework/boot/build/SyncAppSource.java | 13 ++++++++++--- .../boot/build/cli/HomebrewFormula.java | 11 +++++++++-- .../build/mavenplugin/PrepareMavenBinaries.java | 12 +++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/SyncAppSource.java b/buildSrc/src/main/java/org/springframework/boot/build/SyncAppSource.java index ae318adf0f84..0281f2896e91 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/SyncAppSource.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/SyncAppSource.java @@ -16,8 +16,11 @@ package org.springframework.boot.build; +import javax.inject.Inject; + import org.gradle.api.DefaultTask; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileSystemOperations; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; @@ -32,8 +35,12 @@ */ public abstract class SyncAppSource extends DefaultTask { - public SyncAppSource() { + private final FileSystemOperations fileSystemOperations; + + @Inject + public SyncAppSource(FileSystemOperations fileSystemOperations) { getPluginVersion().convention(getProject().provider(() -> getProject().getVersion().toString())); + this.fileSystemOperations = fileSystemOperations; } @InputDirectory @@ -47,11 +54,11 @@ public SyncAppSource() { @TaskAction void syncAppSources() { - getProject().sync((copySpec) -> { + this.fileSystemOperations.sync((copySpec) -> { copySpec.from(getSourceDirectory()); copySpec.into(getDestinationDirectory()); copySpec.filter((line) -> line.replace("id \"org.springframework.boot\"", - "id \"org.springframework.boot\" version \"" + getProject().getVersion() + "\"")); + "id \"org.springframework.boot\" version \"" + getPluginVersion().get() + "\"")); }); } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java b/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java index 23d90fae7c9e..e96487213da0 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java @@ -19,11 +19,14 @@ import java.io.File; import java.security.MessageDigest; +import javax.inject.Inject; + import org.apache.commons.codec.digest.DigestUtils; import org.gradle.api.DefaultTask; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileSystemOperations; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.tasks.Input; @@ -43,12 +46,16 @@ */ public abstract class HomebrewFormula extends DefaultTask { - public HomebrewFormula() { + private final FileSystemOperations fileSystemOperations; + + @Inject + public HomebrewFormula(FileSystemOperations fileSystemOperations) { Project project = getProject(); MapProperty properties = getProperties(); properties.put("hash", getArchive().map((archive) -> sha256(archive.getAsFile()))); getProperties().put("repo", ArtifactRelease.forProject(project).getDownloadRepo()); getProperties().put("version", project.getVersion().toString()); + this.fileSystemOperations = fileSystemOperations; } private String sha256(File file) { @@ -77,7 +84,7 @@ private String sha256(File file) { @TaskAction void createFormula() { - getProject().copy((copy) -> { + this.fileSystemOperations.copy((copy) -> { copy.from(getTemplate()); copy.into(getOutputDir()); copy.expand(getProperties().get()); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java index ff21e553c28d..0d17113ed2c8 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java @@ -16,10 +16,13 @@ package org.springframework.boot.build.mavenplugin; +import javax.inject.Inject; + import org.gradle.api.DefaultTask; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileSystemOperations; import org.gradle.api.provider.SetProperty; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.OutputDirectory; @@ -32,6 +35,13 @@ */ public abstract class PrepareMavenBinaries extends DefaultTask { + private final FileSystemOperations fileSystemOperations; + + @Inject + public PrepareMavenBinaries(FileSystemOperations fileSystemOperations) { + this.fileSystemOperations = fileSystemOperations; + } + @OutputDirectory public abstract DirectoryProperty getOutputDir(); @@ -40,7 +50,7 @@ public abstract class PrepareMavenBinaries extends DefaultTask { @TaskAction public void prepareBinaries() { - getProject().sync((sync) -> { + this.fileSystemOperations.sync((sync) -> { sync.into(getOutputDir()); for (String version : getVersions().get()) { Configuration configuration = getProject().getConfigurations() From 6ebc9b887e52cb4635477adf59fc0995bc645506 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 22 Aug 2024 13:03:02 +0100 Subject: [PATCH 006/271] Use ArchiveOperations instead of Project's zipTree Closes gh-41999 --- .../build/mavenplugin/PrepareMavenBinaries.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java index 0d17113ed2c8..8eeb291bc703 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java @@ -21,6 +21,7 @@ import org.gradle.api.DefaultTask; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.provider.SetProperty; @@ -37,9 +38,12 @@ public abstract class PrepareMavenBinaries extends DefaultTask { private final FileSystemOperations fileSystemOperations; + private final ArchiveOperations archiveOperations; + @Inject - public PrepareMavenBinaries(FileSystemOperations fileSystemOperations) { + public PrepareMavenBinaries(FileSystemOperations fileSystemOperations, ArchiveOperations archiveOperations) { this.fileSystemOperations = fileSystemOperations; + this.archiveOperations = archiveOperations; } @OutputDirectory @@ -53,10 +57,9 @@ public void prepareBinaries() { this.fileSystemOperations.sync((sync) -> { sync.into(getOutputDir()); for (String version : getVersions().get()) { - Configuration configuration = getProject().getConfigurations() - .detachedConfiguration(getProject().getDependencies() - .create("org.apache.maven:apache-maven:" + version + ":bin@zip")); - sync.from(getProject().zipTree(configuration.getSingleFile())); + Configuration configuration = getProject().getConfigurations().detachedConfiguration( + getProject().getDependencies().create("org.apache.maven:apache-maven:" + version + ":bin@zip")); + sync.from(this.archiveOperations.zipTree(configuration.getSingleFile())); } }); From c98363d016ace048501eaa727c1d6dda7de2b52d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 28 Aug 2024 14:40:20 +0100 Subject: [PATCH 007/271] Polish formatting --- .../boot/build/mavenplugin/PrepareMavenBinaries.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java index 8eeb291bc703..5ab7b4b4829f 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java @@ -57,8 +57,9 @@ public void prepareBinaries() { this.fileSystemOperations.sync((sync) -> { sync.into(getOutputDir()); for (String version : getVersions().get()) { - Configuration configuration = getProject().getConfigurations().detachedConfiguration( - getProject().getDependencies().create("org.apache.maven:apache-maven:" + version + ":bin@zip")); + Configuration configuration = getProject().getConfigurations() + .detachedConfiguration(getProject().getDependencies() + .create("org.apache.maven:apache-maven:" + version + ":bin@zip")); sync.from(this.archiveOperations.zipTree(configuration.getSingleFile())); } }); From 71f509c9fd40b71684a7478987ee2f2d33927a96 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 28 Aug 2024 13:00:25 -0700 Subject: [PATCH 008/271] Fix broken tab markup Closes gh-42046 --- .../src/docs/antora/modules/gradle-plugin/pages/aot.adoc | 9 --------- 1 file changed, 9 deletions(-) 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 95eac8a0e0bb..ee631e308f74 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 @@ -24,15 +24,6 @@ include::example$aot/apply-native-image-plugin.gradle.kts[] ====== -.Groovy ----- ----- - -.Kotlin ----- ----- - - [[aot.processing-applications]] == Processing Applications From ad730a6c84277fd545d5039519a88fad55f01767 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 27 Aug 2024 16:31:29 -0700 Subject: [PATCH 009/271] Support Log4J2 MultiFormatStringBuilderFormattable structured messages Update Log4J2 `ElasticCommonSchemaStructuredLogFormatter` and `LogstashStructuredLogFormatter` to support Log4J2 JSON structured messages (typically `MapMessage`) Closes gh-42034 --- ...ticCommonSchemaStructuredLogFormatter.java | 3 +- .../LogstashStructuredLogFormatter.java | 3 +- .../logging/log4j2/StructuredMessage.java | 66 +++++++++++++++++++ ...mmonSchemaStructuredLogFormatterTests.java | 15 +++++ .../LogstashStructuredLogFormatterTests.java | 16 +++++ 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredMessage.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java index b883105f1a55..82e6c2c253e9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.core.time.Instant; -import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.springframework.boot.json.JsonWriter; @@ -54,7 +53,7 @@ private static void jsonMembers(Environment environment, JsonWriter.Members contextData.forEach(pairs::accept)); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java index a085823b0d18..65531a699554 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatter.java @@ -27,7 +27,6 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.core.time.Instant; -import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.springframework.boot.json.JsonWriter; @@ -51,7 +50,7 @@ class LogstashStructuredLogFormatter extends JsonWriterStructuredLogFormatter members) { members.add("@timestamp", LogEvent::getInstant).as(LogstashStructuredLogFormatter::asTimestamp); members.add("@version", "1"); - members.add("message", LogEvent::getMessage).as(Message::getFormattedMessage); + members.add("message", LogEvent::getMessage).as(StructuredMessage::get); members.add("logger_name", LogEvent::getLoggerName); members.add("thread_name", LogEvent::getThreadName); members.add("level", LogEvent::getLevel).as(Level::name); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredMessage.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredMessage.java new file mode 100644 index 000000000000..873ee2814c6f --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredMessage.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2024 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.logging.log4j2; + +import java.io.IOException; + +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.util.MultiFormatStringBuilderFormattable; + +import org.springframework.boot.json.JsonWriter.WritableJson; + +/** + * Helper used to adapt {@link Message} for structured writing. + * + * @author Phillip Webb + */ +final class StructuredMessage { + + private static final String JSON2 = "JSON"; + + private static final String[] JSON = { JSON2 }; + + private StructuredMessage() { + } + + static Object get(Message message) { + if (message instanceof MultiFormatStringBuilderFormattable multiFormatMessage + && hasJsonFormat(multiFormatMessage)) { + return WritableJson.of((out) -> formatTo(multiFormatMessage, out)); + } + return message.getFormattedMessage(); + } + + private static boolean hasJsonFormat(MultiFormatStringBuilderFormattable message) { + for (String format : message.getFormats()) { + if (JSON2.equalsIgnoreCase(format)) { + return true; + } + } + return false; + } + + private static void formatTo(MultiFormatStringBuilderFormattable message, Appendable out) throws IOException { + if (out instanceof StringBuilder stringBuilder) { + message.formatTo(JSON, stringBuilder); + } + else { + out.append(message.getFormattedMessage(JSON)); + } + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java index 5171a11bb882..f7e6a24c32f5 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap; import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.apache.logging.log4j.message.MapMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -78,4 +79,18 @@ void shouldFormatException() { java.lang.RuntimeException: Boom\\n\\tat org.springframework.boot.logging.log4j2.ElasticCommonSchemaStructuredLogFormatterTests.shouldFormatException"""); } + @Test + void shouldFormatStructuredMessage() { + MutableLogEvent event = createEvent(); + event.setMessage(new MapMessage<>().with("foo", true).with("bar", 1.0)); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + Map expectedMessage = Map.of("foo", true, "bar", 1.0); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("@timestamp", "2024-07-02T08:49:53Z", + "log.level", "INFO", "process.pid", 1, "process.thread.name", "main", "service.name", "name", + "service.version", "1.0.0", "service.environment", "test", "service.node.name", "node-1", "log.logger", + "org.example.Test", "message", expectedMessage, "ecs.version", "8.11")); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatterTests.java index 6e43ee38c44a..595933ef923e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/LogstashStructuredLogFormatterTests.java @@ -25,6 +25,7 @@ import org.apache.logging.log4j.MarkerManager.Log4jMarker; import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap; import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.apache.logging.log4j.message.MapMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -77,4 +78,19 @@ void shouldFormatException() { java.lang.RuntimeException: Boom\\n\\tat org.springframework.boot.logging.log4j2.LogstashStructuredLogFormatterTests.shouldFormatException"""); } + @Test + void shouldFormatStructuredMessage() { + MutableLogEvent event = createEvent(); + event.setMessage(new MapMessage<>().with("foo", true).with("bar", 1.0)); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + Map expectedMessage = Map.of("foo", true, "bar", 1.0); + String timestamp = DateTimeFormatter.ISO_OFFSET_DATE_TIME + .format(OffsetDateTime.ofInstant(EVENT_TIME, ZoneId.systemDefault())); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf( + map("@timestamp", timestamp, "@version", "1", "message", expectedMessage, "logger_name", + "org.example.Test", "thread_name", "main", "level", "INFO", "level_value", 400)); + } + } From 10855056cc76d6fd2e2e7bb1c1603318d03b64ff Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 29 Aug 2024 15:04:14 -0700 Subject: [PATCH 010/271] Support blank MongoDB 'replica-set-name' properties Update `null` checks to `StringUtils.hasText` to allow the `replica-set-name' property to be overridden with an empty string. Fixes gh-42055 --- ...pertiesClientSettingsBuilderCustomizer.java | 5 +++-- .../PropertiesMongoConnectionDetails.java | 6 ++++-- ...esClientSettingsBuilderCustomizerTests.java | 18 ++++++++++++++++++ .../PropertiesMongoConnectionDetailsTests.java | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java index 691064cd5e69..f45770365aba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -26,6 +26,7 @@ import org.springframework.core.Ordered; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; /** * A {@link MongoClientSettingsBuilderCustomizer} that applies properties from a @@ -90,7 +91,7 @@ private void applyCredentials(MongoClientSettings.Builder builder) { } private void applyReplicaSet(MongoClientSettings.Builder builder) { - if (this.properties.getReplicaSetName() != null) { + if (StringUtils.hasText(this.properties.getReplicaSetName())) { builder.applyToClusterSettings( (cluster) -> cluster.requiredReplicaSetName(this.properties.getReplicaSetName())); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java index 56b4bc470d15..04436c717b54 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetails.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -23,6 +23,8 @@ import com.mongodb.ConnectionString; +import org.springframework.util.StringUtils; + /** * Adapts {@link MongoProperties} to {@link MongoConnectionDetails}. * @@ -90,7 +92,7 @@ public GridFs getGridFs() { private List getOptions() { List options = new ArrayList<>(); - if (this.properties.getReplicaSetName() != null) { + if (StringUtils.hasText(this.properties.getReplicaSetName())) { options.add("replicaSet=" + this.properties.getReplicaSetName()); } if (this.properties.getUsername() != null && this.properties.getAuthenticationDatabase() != null) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTests.java index 62c506490987..b50a3c1e72f4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesClientSettingsBuilderCustomizerTests.java @@ -22,6 +22,7 @@ import com.mongodb.MongoClientSettings; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; +import com.mongodb.connection.ClusterType; import org.bson.UuidRepresentation; import org.junit.jupiter.api.Test; @@ -81,6 +82,23 @@ void replicaSetCanBeCustomized() { this.properties.setReplicaSetName("test"); MongoClientSettings settings = customizeSettings(); assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isEqualTo("test"); + assertThat(settings.getClusterSettings().getRequiredClusterType()).isEqualTo(ClusterType.REPLICA_SET); + } + + @Test + void replicaSetCanBeNull() { + this.properties.setReplicaSetName(null); + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isNull(); + assertThat(settings.getClusterSettings().getRequiredClusterType()).isEqualTo(ClusterType.UNKNOWN); + } + + @Test + void replicaSetCanBeEmptyString() { + this.properties.setReplicaSetName(""); + MongoClientSettings settings = customizeSettings(); + assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isNull(); + assertThat(settings.getClusterSettings().getRequiredClusterType()).isEqualTo(ClusterType.UNKNOWN); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java index 6d73d9d7ba5a..0b529d6e334c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/PropertiesMongoConnectionDetailsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -104,6 +104,20 @@ void replicaSetCanBeConfiguredWithDatabase() { assertThat(connectionString.getRequiredReplicaSetName()).isEqualTo("test"); } + @Test + void replicaSetCanBeNull() { + this.properties.setReplicaSetName(null); + ConnectionString connectionString = getConnectionString(); + assertThat(connectionString.getRequiredReplicaSetName()).isNull(); + } + + @Test + void replicaSetCanBeBlank() { + this.properties.setReplicaSetName(""); + ConnectionString connectionString = getConnectionString(); + assertThat(connectionString.getRequiredReplicaSetName()).isNull(); + } + @Test void whenAdditionalHostsAreConfiguredThenTheyAreIncludedInHostsOfConnectionString() { this.properties.setHost("mongo1.example.com"); From e7faca3bbbdd6ea93491125ad8037d461d3e7c77 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Wed, 21 Aug 2024 15:02:38 -0500 Subject: [PATCH 011/271] Add support for Testcontainer Redis Add support for the official `com.redis:testcontainers-redis` container. See gh-41450 --- .../spring-boot-actuator/build.gradle | 1 + .../metrics/cache/RedisCacheMetricsTests.java | 2 +- .../spring-boot-autoconfigure/build.gradle | 3 +- ...disRepositoriesAutoConfigurationTests.java | 2 +- ...iveSessionAutoConfigurationRedisTests.java | 2 +- .../SessionAutoConfigurationRedisTests.java | 2 +- .../spring-boot-dependencies/build.gradle | 10 ++++++ .../spring-boot-docker-compose/build.gradle | 3 +- .../pages/testing/testcontainers.adoc | 4 +-- .../build.gradle | 1 + .../redis/DataRedisTestIntegrationTests.java | 2 +- ...taRedisTestPropertiesIntegrationTests.java | 2 +- ...DataRedisTestReactiveIntegrationTests.java | 2 +- ...TestWithIncludeFilterIntegrationTests.java | 2 +- .../spring-boot-testcontainers/build.gradle | 1 + ...tainersLifecycleOrderIntegrationTests.java | 4 +-- ...fecycleOrderWithScopeIntegrationTests.java | 4 +-- ...sPropertySourceAutoConfigurationTests.java | 2 +- ...rviceConnectionAutoConfigurationTests.java | 4 +-- ...ontainerConnectionDetailsFactoryTests.java | 2 +- ...ontainerConnectionDetailsFactoryTests.java | 2 +- .../build.gradle | 1 + .../testsupport/container/RedisContainer.java | 35 ------------------- .../container/RedisStackContainer.java | 35 ------------------- .../boot/testsupport/container/TestImage.java | 2 ++ .../spring-boot-smoke-test-cache/build.gradle | 1 + .../SampleCacheApplicationRedisTests.java | 2 +- .../build.gradle | 1 + .../SampleRedisApplicationJedisSslTests.java | 2 +- ...ampleRedisApplicationReactiveSslTests.java | 2 +- .../redis/SampleRedisApplicationSslTests.java | 2 +- .../data/redis/SecureRedisContainer.java | 3 +- .../build.gradle | 1 + .../SampleSessionRedisApplicationTests.java | 2 +- ...esImportSampleSessionRedisApplication.java | 3 +- ...opertiesSampleSessionRedisApplication.java | 3 +- ...onImportSampleSessionRedisApplication.java | 3 +- ...nnectionSampleSessionRedisApplication.java | 5 +-- .../build.gradle | 1 + ...leSessionWebFluxRedisApplicationTests.java | 2 +- 40 files changed, 59 insertions(+), 104 deletions(-) delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisContainer.java delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisStackContainer.java diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle index a5460910c480..550d09439cc1 100644 --- a/spring-boot-project/spring-boot-actuator/build.gradle +++ b/spring-boot-project/spring-boot-actuator/build.gradle @@ -15,6 +15,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-autoconfigure")) dockerTestImplementation(project(":spring-boot-project:spring-boot-test")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.assertj:assertj-core") dockerTestImplementation("org.junit.jupiter:junit-jupiter") dockerTestImplementation("org.springframework:spring-test") diff --git a/spring-boot-project/spring-boot-actuator/src/dockerTest/java/org/springframework/boot/actuate/metrics/cache/RedisCacheMetricsTests.java b/spring-boot-project/spring-boot-actuator/src/dockerTest/java/org/springframework/boot/actuate/metrics/cache/RedisCacheMetricsTests.java index 0202312ba45a..0d200299ff2c 100644 --- a/spring-boot-project/spring-boot-actuator/src/dockerTest/java/org/springframework/boot/actuate/metrics/cache/RedisCacheMetricsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/dockerTest/java/org/springframework/boot/actuate/metrics/cache/RedisCacheMetricsTests.java @@ -19,6 +19,7 @@ import java.util.UUID; import java.util.function.BiConsumer; +import com.redis.testcontainers.RedisContainer; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; @@ -32,7 +33,6 @@ import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ContextConsumer; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Configuration; diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index 13ad7ba4cbd9..509d39ee012a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -25,7 +25,9 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-test")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.assertj:assertj-core") + dockerTestImplementation("org.awaitility:awaitility") dockerTestImplementation("org.junit.jupiter:junit-jupiter") dockerTestImplementation("org.mockito:mockito-core") dockerTestImplementation("org.springframework:spring-test") @@ -37,7 +39,6 @@ dependencies { dockerTestImplementation("org.testcontainers:neo4j") dockerTestImplementation("org.testcontainers:pulsar") dockerTestImplementation("org.testcontainers:testcontainers") - dockerTestImplementation("org.awaitility:awaitility") optional("co.elastic.clients:elasticsearch-java") { exclude group: "commons-logging", module: "commons-logging" diff --git a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java index 7e22b198f9fa..e631f8be2b82 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.data.redis; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,7 +30,6 @@ import org.springframework.boot.autoconfigure.data.redis.city.City; import org.springframework.boot.autoconfigure.data.redis.city.CityRepository; import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java index 55d55bb4e9d1..c5828492339c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.List; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -31,7 +32,6 @@ import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.http.ResponseCookie; import org.springframework.session.MapSession; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java index eecebb7ad9ca..5233fd3ad920 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/dockerTest/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.Map; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -31,7 +32,6 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 564d87a4b872..3a9b685a542f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2167,6 +2167,16 @@ bom { releaseNotes("https://github.com/testcontainers/testcontainers-java/releases/tag/{version}") } } + library("Testcontainers Redis Module", "2.2.2") { + group("com.redis") { + modules = [ + "testcontainers-redis" + ] + } + links { + site("https://testcontainers.com/modules/redis/") + } + } library("Thymeleaf", "3.1.2.RELEASE") { group("org.thymeleaf") { modules = [ diff --git a/spring-boot-project/spring-boot-docker-compose/build.gradle b/spring-boot-project/spring-boot-docker-compose/build.gradle index 48aea480e6f9..7f8532ec23a7 100644 --- a/spring-boot-project/spring-boot-docker-compose/build.gradle +++ b/spring-boot-project/spring-boot-docker-compose/build.gradle @@ -13,11 +13,12 @@ dependencies { api(project(":spring-boot-project:spring-boot")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.assertj:assertj-core") dockerTestImplementation("org.awaitility:awaitility") dockerTestImplementation("org.junit.jupiter:junit-jupiter") dockerTestImplementation("org.testcontainers:testcontainers") - + dockerTestRuntimeOnly("com.microsoft.sqlserver:mssql-jdbc") dockerTestRuntimeOnly("com.oracle.database.r2dbc:oracle-r2dbc") dockerTestRuntimeOnly("io.r2dbc:r2dbc-mssql") diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index 2e1f7b157f76..abbe71ae7a89 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -107,8 +107,8 @@ This works as long as Spring Boot is able to get the instance of the `Container` If you're using a `@Bean` method, Spring Boot won't call the bean method to get the Docker image name, because this would cause eager initialization issues. Instead, the return type of the bean method is used to find out which connection detail should be used. -This works as long as you're using typed containers, e.g. `Neo4jContainer` or `RabbitMQContainer`. -This stops working if you're using `GenericContainer`, e.g. with Redis, as shown in the following example: +This works as long as you're using typed containers such as `Neo4jContainer` or `RabbitMQContainer`. +This stops working if you're using `GenericContainer`, for example with Redis as shown in the following example: include-code::MyRedisConfiguration[] diff --git a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle index fe40c29b7bc0..2c37870ce14d 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle @@ -16,6 +16,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) dockerTestImplementation("io.projectreactor:reactor-test") + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.assertj:assertj-core") dockerTestImplementation("org.junit.jupiter:junit-jupiter") dockerTestImplementation("org.testcontainers:cassandra") diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java index 50ab4defe997..ed6a3be4cb2d 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -27,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.RedisOperations; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java index 49c6f1a67a02..e4f43ca95b94 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.test.autoconfigure.data.redis; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; @@ -23,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.core.env.Environment; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java index be773935ba6a..a32c477d971e 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.UUID; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -27,7 +28,6 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.ReactiveRedisOperations; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java index 233511a441dc..5b5f7c810ecb 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java @@ -16,13 +16,13 @@ package org.springframework.boot.test.autoconfigure.data.redis; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; diff --git a/spring-boot-project/spring-boot-testcontainers/build.gradle b/spring-boot-project/spring-boot-testcontainers/build.gradle index 6bf43ef0df00..ce5c93a6be85 100644 --- a/spring-boot-project/spring-boot-testcontainers/build.gradle +++ b/spring-boot-project/spring-boot-testcontainers/build.gradle @@ -81,6 +81,7 @@ dependencies { optional("org.testcontainers:rabbitmq") optional("org.testcontainers:redpanda") optional("org.testcontainers:r2dbc") + optional("com.redis:testcontainers-redis") testImplementation(project(":spring-boot-project:spring-boot-test")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderIntegrationTests.java index f4f912f84c24..3a5a65180963 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderIntegrationTests.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; @@ -30,7 +31,6 @@ import org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleOrderIntegrationTests.TestConfig; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,7 +63,7 @@ void eventsAreOrderedCorrectlyAfterStartup() { static class ContainerConfig { @Bean - @ServiceConnection("redis") + @ServiceConnection RedisContainer redisContainer() { return TestImage.container(EventRecordingRedisContainer.class); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderWithScopeIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderWithScopeIntegrationTests.java index 48614af9f472..a223476b9e09 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderWithScopeIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleOrderWithScopeIntegrationTests.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; @@ -34,7 +35,6 @@ import org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleOrderWithScopeIntegrationTests.TestConfig; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -74,7 +74,7 @@ static class ContainerConfig { @Bean @Scope("custom") - @ServiceConnection("redis") + @ServiceConnection RedisContainer redisContainer() { return TestImage.container(EventRecordingRedisContainer.class); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfigurationTests.java index 310c0758f0fa..8cf5003f64ae 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfigurationTests.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -28,7 +29,6 @@ import org.springframework.boot.testcontainers.lifecycle.BeforeTestcontainerUsedEvent; import org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleApplicationContextInitializer; import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.ApplicationEvent; import org.springframework.context.annotation.Bean; diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionAutoConfigurationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionAutoConfigurationTests.java index 85a958359fb9..d4f6ff944ebb 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionAutoConfigurationTests.java @@ -18,6 +18,7 @@ import java.util.Set; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -32,7 +33,6 @@ import org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleApplicationContextInitializer; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -134,7 +134,7 @@ static class WithRedisAutoConfiguration { static class ContainerConfiguration { @Bean - @ServiceConnection("redis") + @ServiceConnection RedisContainer redisContainer() { return TestImage.container(RedisContainer.class); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTests.java index 29dfbbfb84fe..e7c68c4140b3 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactoryTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.testcontainers.service.connection.redis; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -25,7 +26,6 @@ import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnection; diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisStackContainerConnectionDetailsFactoryTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisStackContainerConnectionDetailsFactoryTests.java index 03bcabb7e5ed..1616e8f1eb84 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisStackContainerConnectionDetailsFactoryTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/RedisStackContainerConnectionDetailsFactoryTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.testcontainers.service.connection.redis; +import com.redis.testcontainers.RedisStackContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -25,7 +26,6 @@ import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisStackContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnection; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle index 967cc8dc958d..ffe76829fe27 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/build.gradle @@ -31,4 +31,5 @@ dependencies { optional("org.testcontainers:pulsar") optional("org.testcontainers:rabbitmq") optional("org.testcontainers:redpanda") + optional("com.redis:testcontainers-redis") } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisContainer.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisContainer.java deleted file mode 100644 index 98d9bb189636..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisContainer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012-2024 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.testsupport.container; - -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -/** - * A {@link GenericContainer} for Redis. - * - * @author Andy Wilkinson - * @author Madhura Bhave - */ -public class RedisContainer extends GenericContainer { - - public RedisContainer(DockerImageName dockerImageName) { - super(dockerImageName); - addExposedPorts(6379); - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisStackContainer.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisStackContainer.java deleted file mode 100644 index 47ba11cd58b3..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/RedisStackContainer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012-2024 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.testsupport.container; - -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -/** - * A {@link GenericContainer} for Redis Stack. - * - * @author Andy Wilkinson - * @author Madhura Bhave - */ -public class RedisStackContainer extends GenericContainer { - - public RedisStackContainer(DockerImageName dockerImageName) { - super(dockerImageName); - addExposedPorts(6379); - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java index 3eba7e032139..d8fb5feb71b3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java @@ -23,6 +23,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import com.redis.testcontainers.RedisContainer; +import com.redis.testcontainers.RedisStackContainer; import org.testcontainers.activemq.ActiveMQContainer; import org.testcontainers.activemq.ArtemisContainer; import org.testcontainers.containers.CassandraContainer; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/build.gradle index 6e5dcd3193f4..290e7c4479a2 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/build.gradle @@ -32,6 +32,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.testcontainers:junit-jupiter") ehcache(enforcedPlatform(project(":spring-boot-project:spring-boot-dependencies"))) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/dockerTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/dockerTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java index f7e56d5a9008..732fe2ae9b92 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/dockerTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/dockerTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java @@ -16,6 +16,7 @@ package smoketest.cache; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -23,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/build.gradle index 83040c90c97d..b2c41ccdba3e 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/build.gradle @@ -11,6 +11,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("io.projectreactor:reactor-core") dockerTestImplementation("io.projectreactor:reactor-test") dockerTestImplementation("org.junit.jupiter:junit-jupiter") diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationJedisSslTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationJedisSslTests.java index 391f6c390ee9..228fb8d74276 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationJedisSslTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationJedisSslTests.java @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -27,7 +28,6 @@ import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.data.redis.core.RedisOperations; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationReactiveSslTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationReactiveSslTests.java index 70f9da5bb4b2..3a2d4e56fded 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationReactiveSslTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationReactiveSslTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.UUID; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -27,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.data.redis.core.ReactiveRedisOperations; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationSslTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationSslTests.java index 2eaff7063a79..63733d9d349b 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationSslTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SampleRedisApplicationSslTests.java @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -26,7 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.data.redis.core.RedisOperations; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SecureRedisContainer.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SecureRedisContainer.java index e05cd829ac08..4f059740630e 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SecureRedisContainer.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-redis/src/dockerTest/java/smoketest/data/redis/SecureRedisContainer.java @@ -16,11 +16,10 @@ package smoketest.data.redis; +import com.redis.testcontainers.RedisContainer; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import org.springframework.boot.testsupport.container.RedisContainer; - /** * A {@link RedisContainer} for Redis with SSL configuration. * diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/build.gradle index 5a4231ea1d1a..9698a20f1648 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/build.gradle @@ -10,6 +10,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) dockerTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.testcontainers:junit-jupiter") implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator")) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java index 5a19cbf43d94..b9de22b75e46 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -29,7 +30,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesImportSampleSessionRedisApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesImportSampleSessionRedisApplication.java index 7fa5cc292fa3..94bf66a521da 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesImportSampleSessionRedisApplication.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesImportSampleSessionRedisApplication.java @@ -16,9 +16,10 @@ package smoketest.session.redis; +import com.redis.testcontainers.RedisContainer; + import org.springframework.boot.SpringApplication; import org.springframework.boot.testcontainers.context.ImportTestcontainers; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesSampleSessionRedisApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesSampleSessionRedisApplication.java index f9f8b88d3b06..fd1d048abc15 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesSampleSessionRedisApplication.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestPropertiesSampleSessionRedisApplication.java @@ -16,9 +16,10 @@ package smoketest.session.redis; +import com.redis.testcontainers.RedisContainer; + import org.springframework.boot.SpringApplication; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Bean; import org.springframework.test.context.DynamicPropertyRegistry; diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionImportSampleSessionRedisApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionImportSampleSessionRedisApplication.java index 3d439566c122..c7b4b5275cda 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionImportSampleSessionRedisApplication.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionImportSampleSessionRedisApplication.java @@ -16,10 +16,11 @@ package smoketest.session.redis; +import com.redis.testcontainers.RedisContainer; + import org.springframework.boot.SpringApplication; import org.springframework.boot.testcontainers.context.ImportTestcontainers; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; public class TestServiceConnectionImportSampleSessionRedisApplication { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionSampleSessionRedisApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionSampleSessionRedisApplication.java index 028380bfb688..7a101080294b 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionSampleSessionRedisApplication.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/dockerTest/java/smoketest/session/redis/TestServiceConnectionSampleSessionRedisApplication.java @@ -16,10 +16,11 @@ package smoketest.session.redis; +import com.redis.testcontainers.RedisContainer; + import org.springframework.boot.SpringApplication; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Bean; @@ -33,7 +34,7 @@ public static void main(String[] args) { static class ContainerConfiguration { @Bean - @ServiceConnection("redis") + @ServiceConnection RedisContainer redisContainer() { return TestImage.container(RedisContainer.class); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/build.gradle index 0846636a9404..29fe11c2e629 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/build.gradle @@ -10,6 +10,7 @@ dependencies { dockerTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.redis:testcontainers-redis") dockerTestImplementation("org.testcontainers:junit-jupiter") implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security")) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/dockerTest/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/dockerTest/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java index 1fae3300252f..eed9fca91c80 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/dockerTest/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/dockerTest/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.Base64; +import com.redis.testcontainers.RedisContainer; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -28,7 +29,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.testsupport.container.RedisContainer; import org.springframework.boot.testsupport.container.TestImage; import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.WebClient; From fa686bb593f2a3b85497546d3995c4050501ef61 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 29 Aug 2024 19:23:21 -0700 Subject: [PATCH 012/271] Support Testcontainer Redis with custom image names Update `RedisContainerConnectionDetailsFactory` so that it can also support `RedisContainer` with a custom name. Closes gh-41450 --- ...ontainerConnectionDetailsFactoryTests.java | 53 +++++++++++++++++++ .../ContainerConnectionDetailsFactory.java | 29 +++++++--- .../connection/ContainerConnectionSource.java | 12 ++++- ...edisContainerConnectionDetailsFactory.java | 10 +++- .../TestContainerConnectionSource.java | 43 +++++++++++++++ 5 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/TestContainerConnectionSource.java diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java new file mode 100644 index 000000000000..af2d09016d4e --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection.redis; + +import java.util.Map; + +import com.redis.testcontainers.RedisContainer; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testcontainers.service.connection.TestContainerConnectionSource; +import org.springframework.core.annotation.MergedAnnotation; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link RedisContainerConnectionDetailsFactory} when using a custom contain + * without "redis" as the name. + * + * @author Phillip Webb + */ +class CustomRedisContainerConnectionDetailsFactoryTests { + + @Test + void getConnectionDetailsWhenRedisContainerWithCustomName() { + ConnectionDetailsFactories factories = new ConnectionDetailsFactories(); + MergedAnnotation annotation = MergedAnnotation.of(ServiceConnection.class, + Map.of("value", "")); + ContainerConnectionSource source = TestContainerConnectionSource.create("test", null, + RedisContainer.class, "mycustomimage", annotation, null); + Map, ConnectionDetails> connectionDetails = factories.getConnectionDetails(source, true); + assertThat(connectionDetails.get(RedisConnectionDetails.class)).isNotNull(); + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java index 1e8e0d24ac77..4b601d423ab3 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java @@ -104,12 +104,10 @@ public final D getConnectionDetails(ContainerConnectionSource source) { } try { Class[] generics = resolveGenerics(); - Class containerType = generics[0]; - Class connectionDetailsType = generics[1]; - for (String connectionName : this.connectionNames) { - if (source.accepts(connectionName, containerType, connectionDetailsType)) { - return getContainerConnectionDetails(source); - } + Class requiredContainerType = generics[0]; + Class requiredConnectionDetailsType = generics[1]; + if (sourceAccepts(source, requiredContainerType, requiredConnectionDetailsType)) { + return getContainerConnectionDetails(source); } } catch (NoClassDefFoundError ex) { @@ -118,6 +116,25 @@ public final D getConnectionDetails(ContainerConnectionSource source) { return null; } + /** + * Return if the give source accepts the connection. By default this method checks + * each connection name. + * @param source the container connection source + * @param requiredContainerType the required container type + * @param requiredConnectionDetailsType the required connection details type + * @return if the source accepts the connection + * @since 3.4.0 + */ + protected boolean sourceAccepts(ContainerConnectionSource source, Class requiredContainerType, + Class requiredConnectionDetailsType) { + for (String requiredConnectionName : this.connectionNames) { + if (source.accepts(requiredConnectionName, requiredContainerType, requiredConnectionDetailsType)) { + return true; + } + } + return false; + } + private boolean hasRequiredClasses() { return ObjectUtils.isEmpty(this.requiredClassNames) || Arrays.stream(this.requiredClassNames) .allMatch((requiredClassName) -> ClassUtils.isPresent(requiredClassName, null)); diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java index 30aece533d18..da480628d6e3 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -90,7 +90,15 @@ private static String getOrDeduceConnectionName(String connectionName, String co return null; } - boolean accepts(String requiredConnectionName, Class requiredContainerType, + /** + * Return is this source accepts the given connection. + * @param requiredConnectionName the required connection name or {@code null} + * @param requiredContainerType the required container type + * @param requiredConnectionDetailsType the required connection details type + * @return if the connection is accepted by this source + * @since 3.4.0 + */ + public boolean accepts(String requiredConnectionName, Class requiredContainerType, Class requiredConnectionDetailsType) { if (StringUtils.hasText(requiredConnectionName) && !requiredConnectionName.equalsIgnoreCase(this.connectionName)) { diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java index 5d10886d6961..9048b65f19cd 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java @@ -18,6 +18,7 @@ import java.util.List; +import com.redis.testcontainers.RedisContainer; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; @@ -49,7 +50,14 @@ class RedisContainerConnectionDetailsFactory } @Override - public RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource> source) { + protected boolean sourceAccepts(ContainerConnectionSource> source, Class requiredContainerType, + Class requiredConnectionDetailsType) { + return super.sourceAccepts(source, requiredContainerType, requiredConnectionDetailsType) + || source.accepts(ANY_CONNECTION_NAME, RedisContainer.class, requiredConnectionDetailsType); + } + + @Override + protected RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource> source) { return new RedisContainerConnectionDetails(source); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/TestContainerConnectionSource.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/TestContainerConnectionSource.java new file mode 100644 index 000000000000..dda8088eceb6 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/TestContainerConnectionSource.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection; + +import java.util.function.Supplier; + +import org.testcontainers.containers.Container; + +import org.springframework.boot.origin.Origin; +import org.springframework.core.annotation.MergedAnnotation; + +/** + * Factory for tests to create a {@link ContainerConnectionSource}. + * + * @author Phillip Webb + */ +public final class TestContainerConnectionSource { + + private TestContainerConnectionSource() { + } + + public static > ContainerConnectionSource create(String beanNameSuffix, Origin origin, + Class containerType, String containerImageName, MergedAnnotation annotation, + Supplier containerSupplier) { + return new ContainerConnectionSource<>(beanNameSuffix, origin, containerType, containerImageName, annotation, + containerSupplier); + } + +} From cc2dc558f10fa337741808aecd7ff2102c05c1eb Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 31 Aug 2024 11:18:47 -0700 Subject: [PATCH 013/271] Don't report already migrated properties when has group Refine the fix adding commit 962936370a so that items with a group are correctly checked. Fixes gh-42068 --- .../properties/migrator/PropertiesMigrationReporter.java | 2 +- .../src/test/resources/metadata/sample-metadata.json | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java b/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java index 4be09608c1f8..435214b1d631 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java @@ -146,7 +146,7 @@ private void addMigration(ConfigurationPropertySource propertySource, private boolean hasSameName(ConfigurationProperty property, ConfigurationMetadataProperty replacement) { return (property.getOrigin() instanceof PropertySourceOrigin propertySourceOrigin) - && Objects.equals(propertySourceOrigin.getPropertyName(), replacement.getName()); + && Objects.equals(propertySourceOrigin.getPropertyName(), replacement.getId()); } private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/resources/metadata/sample-metadata.json b/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/resources/metadata/sample-metadata.json index fefdb392d6a7..58e086930ae9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/resources/metadata/sample-metadata.json +++ b/spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/resources/metadata/sample-metadata.json @@ -1,4 +1,10 @@ { + "groups": [ + { + "name": "relaxed", + "type": "com.example.SourceType" + } + ], "properties": [ { "name": "test.two", @@ -64,7 +70,8 @@ }, { "name": "relaxed.this-that-the-other", - "type": "java.lang.String" + "type": "java.lang.String", + "sourceType": "com.example.SourceType" } ] } From 8c1d9872d2766037c498685b6a674de63e7cf248 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 1 Sep 2024 15:09:29 -0700 Subject: [PATCH 014/271] Fix support for large zip files Update `spring-boot-loader` to support large zip files by correctly dealing with unsigned ints. Fixes gh-42012 --- .../boot/loader/zip/FileDataBlock.java | 5 +++-- .../springframework/boot/loader/zip/ZipContent.java | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/FileDataBlock.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/FileDataBlock.java index cd0a1da4321c..34acb8729f25 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/FileDataBlock.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/FileDataBlock.java @@ -71,14 +71,15 @@ public int read(ByteBuffer dst, long pos) throws IOException { throw new IllegalArgumentException("Position must not be negative"); } ensureOpen(ClosedChannelException::new); - int remaining = (int) (this.size - pos); + long remaining = this.size - pos; if (remaining <= 0) { return -1; } int originalDestinationLimit = -1; if (dst.remaining() > remaining) { originalDestinationLimit = dst.limit(); - dst.limit(dst.position() + remaining); + long updatedLimit = dst.position() + remaining; + dst.limit((updatedLimit > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) updatedLimit); } int result = this.fileAccess.read(dst, this.offset + pos); if (originalDestinationLimit != -1) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java index d9c5f1c689c7..7a61b789a7c1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java @@ -636,7 +636,8 @@ private static ZipContent loadContent(Source source, Kind kind, FileDataBlock da private static long getStartOfZipContent(FileDataBlock data, ZipEndOfCentralDirectoryRecord eocd, Zip64EndOfCentralDirectoryRecord zip64Eocd) throws IOException { long specifiedOffsetToStartOfCentralDirectory = (zip64Eocd != null) - ? zip64Eocd.offsetToStartOfCentralDirectory() : eocd.offsetToStartOfCentralDirectory(); + ? zip64Eocd.offsetToStartOfCentralDirectory() + : Integer.toUnsignedLong(eocd.offsetToStartOfCentralDirectory()); long sizeOfCentralDirectoryAndEndRecords = getSizeOfCentralDirectoryAndEndRecords(eocd, zip64Eocd); long actualOffsetToStartOfCentralDirectory = data.size() - sizeOfCentralDirectoryAndEndRecords; return actualOffsetToStartOfCentralDirectory - specifiedOffsetToStartOfCentralDirectory; @@ -650,7 +651,8 @@ private static long getSizeOfCentralDirectoryAndEndRecords(ZipEndOfCentralDirect result += Zip64EndOfCentralDirectoryLocator.SIZE; result += zip64Eocd.size(); } - result += (zip64Eocd != null) ? zip64Eocd.sizeOfCentralDirectory() : eocd.sizeOfCentralDirectory(); + result += (zip64Eocd != null) ? zip64Eocd.sizeOfCentralDirectory() + : Integer.toUnsignedLong(eocd.sizeOfCentralDirectory()); return result; } @@ -796,10 +798,10 @@ public CloseableDataBlock openContent() throws IOException { private FileDataBlock getContent() throws IOException { FileDataBlock content = this.content; if (content == null) { - int pos = this.centralRecord.offsetToLocalHeader(); + long pos = Integer.toUnsignedLong(this.centralRecord.offsetToLocalHeader()); checkNotZip64Extended(pos); ZipLocalFileHeaderRecord localHeader = ZipLocalFileHeaderRecord.load(ZipContent.this.data, pos); - int size = this.centralRecord.compressedSize(); + long size = Integer.toUnsignedLong(this.centralRecord.compressedSize()); checkNotZip64Extended(size); content = ZipContent.this.data.slice(pos + localHeader.size(), size); this.content = content; @@ -807,7 +809,7 @@ private FileDataBlock getContent() throws IOException { return content; } - private void checkNotZip64Extended(int value) throws IOException { + private void checkNotZip64Extended(long value) throws IOException { if (value == 0xFFFFFFFF) { throw new IOException("Zip64 extended information extra fields are not supported"); } From a8452b54b57a0cdde8bd6570470141d0ed6d91ec Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Sat, 31 Aug 2024 11:30:18 +0900 Subject: [PATCH 015/271] Polish See gh-42069 --- .../boot/buildpack/platform/build/BuildLog.java | 1 + .../gradle/tasks/bundling/BootBuildImageIntegrationTests.java | 4 ++-- .../java/org/springframework/boot/maven/BuildImageTests.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java index c753db8bb847..2f21bfa653e8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildLog.java @@ -118,6 +118,7 @@ public interface BuildLog { * Log that a cache cleanup step was not completed successfully. * @param cache the cache * @param exception any exception that caused the failure + * @since 3.2.6 */ void failedCleaningWorkDir(Cache cache, Exception exception); 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 48536e53675e..219b150cc0d1 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 @@ -320,9 +320,9 @@ void buildsImageWithBindCaches() throws IOException { cleanupCache(launchCachePath); } - private static void cleanupCache(Path buildCachePath) { + private static void cleanupCache(Path cachePath) { try { - FileSystemUtils.deleteRecursively(buildCachePath); + FileSystemUtils.deleteRecursively(cachePath); } catch (Exception ex) { // ignore diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/dockerTest/java/org/springframework/boot/maven/BuildImageTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/dockerTest/java/org/springframework/boot/maven/BuildImageTests.java index 140be1a6e0ab..d3ae54c08185 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/dockerTest/java/org/springframework/boot/maven/BuildImageTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/dockerTest/java/org/springframework/boot/maven/BuildImageTests.java @@ -443,9 +443,9 @@ void whenBuildImageIsInvokedWithBindCaches(MavenBuild mavenBuild) { }); } - private static void cleanupCache(Path buildCachePath) { + private static void cleanupCache(Path cachePath) { try { - FileSystemUtils.deleteRecursively(buildCachePath); + FileSystemUtils.deleteRecursively(cachePath); } catch (Exception ex) { // ignore From fd9d907ef3c7de4aab604c999f273872076ffee9 Mon Sep 17 00:00:00 2001 From: martinfrancois Date: Sun, 1 Sep 2024 21:59:21 +0200 Subject: [PATCH 016/271] Improve formatting for Docker configuration example with Colima See gh-42078 --- .../modules/gradle-plugin/pages/packaging-oci-image.adoc | 7 ++++++- .../antora/modules/maven-plugin/pages/build-image.adoc | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc index c809d8ef7a32..e0bf6658d0de 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc @@ -602,7 +602,12 @@ TIP: With the `podman` CLI installed, the command `podman info --format='{{.Host ==== Docker Configuration for Colima The plugin can communicate with the Docker daemon provided by https://github.com/abiosoft/colima[Colima]. -The `DOCKER_HOST` environment variable can be set by using the command `export DOCKER_HOST=$(docker context inspect colima -f '{{.Endpoints.docker.Host}}').` +The `DOCKER_HOST` environment variable can be set by using the following command: + +[source,shell,subs="verbatim,attributes"] +---- +$ export DOCKER_HOST=$(docker context inspect colima -f '{{.Endpoints.docker.Host}}') +---- The plugin can also be configured to use Colima daemon by providing connection details similar to those shown in the following example: diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/antora/modules/maven-plugin/pages/build-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/antora/modules/maven-plugin/pages/build-image.adoc index 815aebdf3691..701a5c7f7c2e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/antora/modules/maven-plugin/pages/build-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/antora/modules/maven-plugin/pages/build-image.adoc @@ -511,7 +511,12 @@ TIP: With the `colima` CLI installed, the command `podman info --format='{{.Host ==== Docker Configuration for Colima The plugin can communicate with the Docker daemon provided by https://github.com/abiosoft/colima[Colima]. -The `DOCKER_HOST` environment variable can be set by using the command `export DOCKER_HOST=$(docker context inspect colima -f '{{.Endpoints.docker.Host}}').` +The `DOCKER_HOST` environment variable can be set by using the following command: + +[source,shell,subs="verbatim,attributes"] +---- +$ export DOCKER_HOST=$(docker context inspect colima -f '{{.Endpoints.docker.Host}}') +---- The plugin can also be configured to use Colima daemon by providing connection details similar to those shown in the following example: From 7cb1671871a8d2b4049ad699c5a0443dbcc99efc Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Sun, 1 Sep 2024 21:10:29 +0700 Subject: [PATCH 017/271] Use pattern matching with cast See gh-42076 --- .../java/org/springframework/boot/build/bom/BomExtension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 59bc52845431..d746ca6143eb 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 @@ -365,8 +365,8 @@ public void setPlugins(List plugins) { } public Object methodMissing(String name, Object args) { - if (args instanceof Object[] && ((Object[]) args).length == 1) { - Object arg = ((Object[]) args)[0]; + if (args instanceof Object[] objects && objects.length == 1) { + Object arg = objects[0]; if (arg instanceof Closure closure) { ModuleHandler moduleHandler = new ModuleHandler(); closure.setResolveStrategy(Closure.DELEGATE_FIRST); From 2c012ed0925a63d09682190a11f34f02bb45e7ae Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sun, 1 Sep 2024 19:01:19 -0700 Subject: [PATCH 018/271] Polish 'Use pattern matching with cast' See gh-42076 --- .../org/springframework/boot/build/bom/BomExtension.java | 5 ++--- 1 file changed, 2 insertions(+), 3 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 d746ca6143eb..64564fd4c09b 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 @@ -365,9 +365,8 @@ public void setPlugins(List plugins) { } public Object methodMissing(String name, Object args) { - if (args instanceof Object[] objects && objects.length == 1) { - Object arg = objects[0]; - if (arg instanceof Closure closure) { + if (args instanceof Object[] argsArray && argsArray.length == 1) { + if (argsArray[0] instanceof Closure closure) { ModuleHandler moduleHandler = new ModuleHandler(); closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure.setDelegate(moduleHandler); From 102fce39def8d060f5648b050ec20765636e20f4 Mon Sep 17 00:00:00 2001 From: Piyal Ahmed Date: Sun, 1 Sep 2024 12:48:18 +0600 Subject: [PATCH 019/271] Polish See gh-42075 --- .../boot/build/antora/GenerateAntoraPlaybook.java | 2 +- .../org/springframework/boot/build/bom/ManagedDependencies.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/antora/GenerateAntoraPlaybook.java b/buildSrc/src/main/java/org/springframework/boot/build/antora/GenerateAntoraPlaybook.java index 42514c24fd6a..7391ae6bda98 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/antora/GenerateAntoraPlaybook.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/antora/GenerateAntoraPlaybook.java @@ -175,7 +175,7 @@ private void addAntoraContentStartPaths(Set startPaths) { private void addDir(Map data) { Path playbookDir = toRealPath(getOutputFile().get().getAsFile().toPath()).getParent(); Path outputDir = toRealPath(getProject().getBuildDir().toPath().resolve("site")); - data.put("output", Map.of("dir", "." + File.separator + playbookDir.relativize(outputDir).toString())); + data.put("output", Map.of("dir", "." + File.separator + playbookDir.relativize(outputDir))); } @SuppressWarnings("unchecked") diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/ManagedDependencies.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/ManagedDependencies.java index c9066aa053bd..cb6ca9581c3e 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/ManagedDependencies.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/ManagedDependencies.java @@ -99,7 +99,7 @@ static ManagedDependencies ofBom(File bom) { static String asId(String groupId, String artifactId, String version, String classifier) { String id = groupId + ":" + artifactId + ":" + version; - if (classifier != null && classifier.length() > 0) { + if (classifier != null && !classifier.isEmpty()) { id = id + ":" + classifier; } return id; From d0fe5c24d2da8868516b20d6442efa89a7ed748b Mon Sep 17 00:00:00 2001 From: LeeJaeHoon Date: Thu, 29 Aug 2024 02:19:33 +0900 Subject: [PATCH 020/271] Apply instanceof pattern matching See gh-42049 --- .../builder/ParentContextApplicationContextInitializer.java | 5 +++-- .../boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java index 1c843b5d649c..ecaee4c79a5c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java @@ -72,8 +72,9 @@ public int getOrder() { @Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext context = event.getApplicationContext(); - if (context instanceof ConfigurableApplicationContext && context == event.getSource()) { - context.publishEvent(new ParentContextAvailableEvent((ConfigurableApplicationContext) context)); + if (context instanceof ConfigurableApplicationContext configurableApplicationContext + && context == event.getSource()) { + context.publishEvent(new ParentContextAvailableEvent(configurableApplicationContext)); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java index 9c15b6290bdb..9aa390f1f6c9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java @@ -201,9 +201,8 @@ private void flatten(Properties properties, Map input, String pa // Need a compound key flatten(properties, (Map) value, name); } - else if (value instanceof Collection) { + else if (value instanceof Collection collection) { // Need a compound key - Collection collection = (Collection) value; properties.put(name, StringUtils.collectionToCommaDelimitedString(collection)); int count = 0; for (Object item : collection) { From 08106b5a69302c4a8af26ffd8aa8dcfc18d9b187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=EC=A0=95=ED=99=98?= <70272622+bazzi2548@users.noreply.github.com> Date: Sun, 25 Aug 2024 02:11:29 +0900 Subject: [PATCH 021/271] Use List.copyOf() instead of Collections.unmodifiableList() See gh-42019 --- .../metadata/CompositeDataSourcePoolMetadataProvider.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java index 47b360cf426d..9c67626252b5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/metadata/CompositeDataSourcePoolMetadataProvider.java @@ -16,7 +16,6 @@ package org.springframework.boot.jdbc.metadata; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -40,8 +39,7 @@ public class CompositeDataSourcePoolMetadataProvider implements DataSourcePoolMe * @param providers the data source pool metadata providers */ public CompositeDataSourcePoolMetadataProvider(Collection providers) { - this.providers = (providers != null) ? Collections.unmodifiableList(new ArrayList<>(providers)) - : Collections.emptyList(); + this.providers = (providers != null) ? List.copyOf(providers) : Collections.emptyList(); } @Override From aa40c0fec0c3fba516e19be89992c4ed2b86d982 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Wed, 28 Aug 2024 23:01:23 +0200 Subject: [PATCH 022/271] Add support for configuring Pulsar client IO and listener threads Add configuration properties that allow users to configure number of IO threads and listener threads used by the Pulsar client. See gh-42052 --- .../pulsar/PulsarProperties.java | 40 +++++++++++++++++++ .../pulsar/PulsarPropertiesMapper.java | 3 ++ .../pulsar/PulsarPropertiesMapperTests.java | 5 +++ .../pulsar/PulsarPropertiesTests.java | 11 +++++ 4 files changed, 59 insertions(+) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java index 961173243002..e7cbd0340e09 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java @@ -44,6 +44,7 @@ * @author Chris Bono * @author Phillip Webb * @author Swamy Mavuri + * @author Vedran Pavic * @since 3.2.0 */ @ConfigurationProperties("spring.pulsar") @@ -136,6 +137,11 @@ public static class Client { */ private final Authentication authentication = new Authentication(); + /** + * Thread related configuration. + */ + private final Threads threads = new Threads(); + /** * Failover settings. */ @@ -177,6 +183,10 @@ public Authentication getAuthentication() { return this.authentication; } + public Threads getThreads() { + return this.threads; + } + public Failover getFailover() { return this.failover; } @@ -959,6 +969,36 @@ public void setParam(Map param) { } + public static class Threads { + + /** + * Number of threads to be used for handling connections to brokers. + */ + private Integer io; + + /** + * Number of threads to be used for message listeners. + */ + private Integer listener; + + public Integer getIo() { + return this.io; + } + + public void setIo(Integer io) { + this.io = io; + } + + public Integer getListener() { + return this.listener; + } + + public void setListener(Integer listener) { + this.listener = listener; + } + + } + public static class Failover { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index aa9f505b4cb2..9665c4cdb942 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -50,6 +50,7 @@ * @author Chris Bono * @author Phillip Webb * @author Swamy Mavuri + * @author Vedran Pavic */ final class PulsarPropertiesMapper { @@ -68,6 +69,8 @@ void customizeClientBuilder(ClientBuilder clientBuilder, PulsarConnectionDetails map.from(properties::getConnectionTimeout).to(timeoutProperty(clientBuilder::connectionTimeout)); map.from(properties::getOperationTimeout).to(timeoutProperty(clientBuilder::operationTimeout)); map.from(properties::getLookupTimeout).to(timeoutProperty(clientBuilder::lookupTimeout)); + map.from(properties.getThreads()::getIo).to(clientBuilder::ioThreads); + map.from(properties.getThreads()::getListener).to(clientBuilder::listenerThreads); map.from(this.properties.getTransaction()::isEnabled).whenTrue().to(clientBuilder::enableTransaction); customizeAuthentication(properties.getAuthentication(), clientBuilder::authentication); customizeServiceUrlProviderBuilder(clientBuilder::serviceUrl, clientBuilder::serviceUrlProvider, properties, diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java index f23584aab0ab..dbacef33f9c3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java @@ -59,6 +59,7 @@ * @author Chris Bono * @author Phillip Webb * @author Swamy Mavuri + * @author Vedran Pavic */ class PulsarPropertiesMapperTests { @@ -69,6 +70,8 @@ void customizeClientBuilderWhenHasNoAuthentication() { properties.getClient().setConnectionTimeout(Duration.ofSeconds(1)); properties.getClient().setOperationTimeout(Duration.ofSeconds(2)); properties.getClient().setLookupTimeout(Duration.ofSeconds(3)); + properties.getClient().getThreads().setIo(3); + properties.getClient().getThreads().setListener(10); ClientBuilder builder = mock(ClientBuilder.class); new PulsarPropertiesMapper(properties).customizeClientBuilder(builder, new PropertiesPulsarConnectionDetails(properties)); @@ -76,6 +79,8 @@ void customizeClientBuilderWhenHasNoAuthentication() { then(builder).should().connectionTimeout(1000, TimeUnit.MILLISECONDS); then(builder).should().operationTimeout(2000, TimeUnit.MILLISECONDS); then(builder).should().lookupTimeout(3000, TimeUnit.MILLISECONDS); + then(builder).should().ioThreads(3); + then(builder).should().listenerThreads(10); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java index 0c173b3567cb..6ef42ef83452 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java @@ -54,6 +54,7 @@ * @author Soby Chacko * @author Phillip Webb * @author Swamy Mavuri + * @author Vedran Pavic */ class PulsarPropertiesTests { @@ -88,6 +89,16 @@ void bindAuthentication() { assertThat(properties.getAuthentication().getParam()).containsEntry("token", "1234"); } + @Test + void bindThread() { + Map map = new HashMap<>(); + map.put("spring.pulsar.client.threads.io", "3"); + map.put("spring.pulsar.client.threads.listener", "10"); + PulsarProperties.Client properties = bindProperties(map).getClient(); + assertThat(properties.getThreads().getIo()).isEqualTo(3); + assertThat(properties.getThreads().getListener()).isEqualTo(10); + } + @Test void bindFailover() { Map map = new HashMap<>(); From d2f0b2b0903f4286f7cd66def5cce82f005de9fc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Sep 2024 09:48:33 +0100 Subject: [PATCH 023/271] Correct package statements of Testing section's Kotlin snippets Closes gh-42094 --- .../propagation/SomeRepository.kt | 19 -------- .../dynamicproperties/MyIntegrationTests.kt | 48 ------------------- .../serviceconnections/MyIntegrationTests.kt | 43 ----------------- .../MyRedisConfiguration.kt | 33 ------------- .../vanilla/MyIntegrationTests.kt | 41 ---------------- .../MyJdbcTests.kt | 4 +- .../MyTransactionalTests.kt | 4 +- .../autoconfiguredjooq/MyJooqTests.kt | 4 +- .../MyRestClientServiceTests.kt | 4 +- .../MyRestTemplateServiceTests.kt | 4 +- .../RemoteVehicleDetailsService.kt | 4 +- .../MyDataCassandraTests.kt | 4 +- .../SomeRepository.kt | 4 +- .../MyDataCouchbaseTests.kt | 4 +- .../SomeRepository.kt | 4 +- .../MyDataElasticsearchTests.kt | 4 +- .../SomeRepository.kt | 4 +- .../MyNonTransactionalTests.kt | 4 +- .../withdb/MyRepositoryTests.kt | 4 +- .../withoutdb/MyRepositoryTests.kt | 4 +- .../withoutdb/User.kt | 4 +- .../withoutdb/UserRepository.kt | 4 +- .../inmemory/MyDataLdapTests.kt | 4 +- .../server/MyDataLdapTests.kt | 4 +- .../MyDataMongoDbTests.kt | 4 +- .../nopropagation/MyDataNeo4jTests.kt | 4 +- .../propagation/MyDataNeo4jTests.kt | 4 +- .../propagation}/SomeRepository.kt | 4 +- .../MyDataRedisTests.kt | 4 +- .../SomeRepository.kt | 19 ++++++++ .../withmockmvc/MyRestDocsConfiguration.kt | 4 +- .../MyResultHandlerConfiguration.kt | 4 +- .../withmockmvc/MyUserDocumentationTests.kt | 4 +- .../withmockmvc/UserController.kt | 4 +- .../MyRestDocsConfiguration.kt | 4 +- .../MyUserDocumentationTests.kt | 4 +- .../MyRestDocsConfiguration.kt | 4 +- .../MyUsersDocumentationTests.kt | 4 +- ...estClientBuilderCustomizerConfiguration.kt | 4 +- .../client/MyWebServiceClientTests.kt | 4 +- .../client/Request.kt | 4 +- .../client/Response.kt | 4 +- .../client/SomeWebService.kt | 4 +- .../server/ExampleEndpoint.kt | 4 +- .../server/MyWebServiceServerTests.kt | 4 +- .../detectingwebapptype/MyWebFluxTests.kt | 4 +- .../excludingconfiguration/MyTests.kt | 4 +- .../MyTestsConfiguration.kt | 4 +- .../springbootapplications/jmx/MyJmxTests.kt | 4 +- .../springbootapplications/jmx/SampleApp.kt | 4 +- .../jsontests/MyJsonAssertJTests.kt | 4 +- .../jsontests/MyJsonTests.kt | 4 +- .../jsontests/SomeObject.kt | 4 +- .../jsontests/VehicleDetails.kt | 4 +- .../mockingbeans/bean/MyTests.kt | 4 +- .../mockingbeans/bean/RemoteService.kt | 4 +- .../mockingbeans/bean/Reverser.kt | 4 +- .../mockingbeans/listener/MyConfig.kt | 4 +- .../mockingbeans/listener/MyTests.kt | 4 +- .../GraphQlIntegrationTests.kt | 4 +- .../GreetingControllerTests.kt | 4 +- .../springmvctests/MyControllerTests.kt | 4 +- .../springmvctests/MyHtmlUnitTests.kt | 4 +- .../springmvctests/UserVehicleController.kt | 4 +- .../springmvctests/UserVehicleService.kt | 4 +- .../springmvctests/VehicleDetails.kt | 4 +- .../springwebfluxtests/MyControllerTests.kt | 4 +- .../UserVehicleController.kt | 4 +- .../springwebfluxtests/UserVehicleService.kt | 4 +- .../springwebfluxtests/VehicleDetails.kt | 4 +- .../MyApplication.kt | 4 +- .../MyMongoConfiguration.kt | 4 +- .../MyWebConfiguration.kt | 4 +- .../MyWebMvcConfigurer.kt | 4 +- .../scan/MyApplication.kt | 4 +- .../MyApplicationArgumentTests.kt | 4 +- .../usingmain/always/MyApplicationTests.kt | 4 +- .../usingmain/custom/MyApplication.kt | 4 +- .../usingmain/typical/MyApplication.kt | 4 +- .../withmockenvironment/MyMockMvcTests.kt | 4 +- .../MyMockWebTestClientTests.kt | 4 +- .../MyRandomPortTestRestTemplateTests.kt | 4 +- .../MyRandomPortWebTestClientTests.kt | 4 +- .../dynamicproperties/MyIntegrationTests.kt | 3 +- .../serviceconnections/MyIntegrationTests.kt | 2 +- .../MyRedisConfiguration.kt | 2 +- .../vanilla/MyIntegrationTests.kt | 2 +- .../Config.kt | 4 +- .../MyConfigFileTests.kt | 4 +- .../outputcapture/MyOutputCaptureTests.kt | 4 +- .../testpropertyvalues/MyEnvironmentTests.kt | 4 +- .../testresttemplate/MySpringBootTests.kt | 4 +- .../MySpringBootTestsConfiguration.kt | 4 +- .../utilities/testresttemplate/MyTests.kt | 4 +- 94 files changed, 191 insertions(+), 357 deletions(-) delete mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt delete mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt delete mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyIntegrationTests.kt delete mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt delete mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/vanilla/MyIntegrationTests.kt rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features/testing/springbootapplications/autoconfiguredspringdataredis => testing/springbootapplications/autoconfiguredspringdatacassandra}/SomeRepository.kt (78%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features/testing/springbootapplications/autoconfiguredspringdatacassandra => testing/springbootapplications/autoconfiguredspringdatacouchbase}/SomeRepository.kt (78%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features/testing/springbootapplications/autoconfiguredspringdatacouchbase => testing/springbootapplications/autoconfiguredspringdataelasticsearch}/SomeRepository.kt (78%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt (78%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt (79%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features/testing/springbootapplications/autoconfiguredspringdataelasticsearch => testing/springbootapplications/autoconfiguredspringdataneo4j/propagation}/SomeRepository.kt (77%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt (83%) create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt (86%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt (85%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt (89%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt (77%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt (86%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt (89%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt (85%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt (86%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt (86%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/excludingconfiguration/MyTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt (79%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jmx/MyJmxTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jmx/SampleApp.kt (86%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jsontests/MyJsonTests.kt (91%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jsontests/SomeObject.kt (81%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/jsontests/VehicleDetails.kt (81%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/mockingbeans/bean/MyTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/mockingbeans/bean/RemoteService.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/mockingbeans/bean/Reverser.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/mockingbeans/listener/MyConfig.kt (79%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/mockingbeans/listener/MyTests.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt (91%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springmvctests/MyControllerTests.kt (91%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springmvctests/UserVehicleController.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springmvctests/UserVehicleService.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springmvctests/VehicleDetails.kt (81%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt (90%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt (79%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt (80%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt (85%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt (82%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt (83%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/usingmain/always/MyApplicationTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/usingmain/custom/MyApplication.kt (85%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/usingmain/typical/MyApplication.kt (85%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt (91%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/configdataapplicationcontextinitializer/Config.kt (78%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt (84%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/outputcapture/MyOutputCaptureTests.kt (88%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt (87%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/testresttemplate/MySpringBootTests.kt (92%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt (92%) rename spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/{features => }/testing/utilities/testresttemplate/MyTests.kt (87%) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt deleted file mode 100644 index 77b41f7fd07f..000000000000 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2012-2022 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.features.testing.springbootapplications.autoconfiguredspringdataneo4j.propagation - -interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt deleted file mode 100644 index 642ae1cb7659..000000000000 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2022 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.features.testing.testcontainers.dynamicproperties - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.DynamicPropertyRegistry -import org.springframework.test.context.DynamicPropertySource -import org.testcontainers.containers.Neo4jContainer -import org.testcontainers.junit.jupiter.Container -import org.testcontainers.junit.jupiter.Testcontainers - -@Testcontainers -@SpringBootTest -class MyIntegrationTests { - - @Test - fun myTest() { - // ... - } - - companion object { - - @Container - val neo4j = Neo4jContainer("neo4j:5") - - @DynamicPropertySource - fun neo4jProperties(registry: DynamicPropertyRegistry) { - registry.add("spring.neo4j.uri") { neo4j.boltUrl } - } - - } - -} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyIntegrationTests.kt deleted file mode 100644 index 1e09326cbdb5..000000000000 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyIntegrationTests.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2023 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.features.testing.testcontainers.serviceconnections - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.testcontainers.service.connection.ServiceConnection -import org.testcontainers.containers.Neo4jContainer -import org.testcontainers.junit.jupiter.Container -import org.testcontainers.junit.jupiter.Testcontainers - -@Testcontainers -@SpringBootTest -class MyIntegrationTests { - - @Test - fun myTest() { - // ... - } - - companion object { - - @Container - @ServiceConnection - val neo4j = Neo4jContainer("neo4j:5") - - } - -} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt deleted file mode 100644 index d221837eab55..000000000000 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2023 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.features.testing.testcontainers.serviceconnections - -import org.springframework.boot.test.context.TestConfiguration -import org.springframework.boot.testcontainers.service.connection.ServiceConnection -import org.springframework.context.annotation.Bean -import org.testcontainers.containers.GenericContainer - -@TestConfiguration(proxyBeanMethods = false) -class MyRedisConfiguration { - - @Bean - @ServiceConnection(name = "redis") - fun redisContainer(): GenericContainer<*> { - return GenericContainer("redis:7") - } - -} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/vanilla/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/vanilla/MyIntegrationTests.kt deleted file mode 100644 index e62e5804d7d6..000000000000 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/testcontainers/vanilla/MyIntegrationTests.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2022 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.features.testing.testcontainers.vanilla - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest -import org.testcontainers.containers.Neo4jContainer -import org.testcontainers.junit.jupiter.Container -import org.testcontainers.junit.jupiter.Testcontainers - -@Testcontainers -@SpringBootTest -class MyIntegrationTests { - - @Test - fun myTest() { - // ... - } - - companion object { - - @Container - val neo4j = Neo4jContainer("neo4j:5") - - } - -} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt index 18b31d60388c..76befa9d829c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/additionalautoconfigurationandslicing/MyJdbcTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.additionalautoconfigurationandslicing +package org.springframework.boot.docs.testing.springbootapplications.additionalautoconfigurationandslicing import org.springframework.boot.autoconfigure.ImportAutoConfiguration import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt index 9ccab1796f9b..47e511c093d1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjdbc/MyTransactionalTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredjdbc +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredjdbc import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest import org.springframework.transaction.annotation.Propagation diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt index 62a39624c12d..254ac05400c1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredjooq/MyJooqTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredjooq +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredjooq import org.jooq.DSLContext import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt index 9d6d561da02a..02cb3c106dcd 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestClientServiceTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredrestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredrestclient import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt index 5b51eae5c68d..202d645024b1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/MyRestTemplateServiceTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredrestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredrestclient import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt index f03184e7060d..e64d6cc41ed0 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredrestclient/RemoteVehicleDetailsService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredrestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredrestclient class RemoteVehicleDetailsService { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt index 0d12dd405d0d..05674bc1892e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/MyDataCassandraTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatacassandra +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatacassandra import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt similarity index 78% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt index a27027bf6034..d32bf41a1c53 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataredis +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatacassandra interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt index d2268b776c67..553f920066c7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/MyDataCouchbaseTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatacouchbase +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatacouchbase import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt similarity index 78% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt index 7d422fa1b9b9..ef5d7f5bd7e1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacassandra/SomeRepository.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatacassandra +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatacouchbase interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt index efe4e7dd47cf..c123d7d1170b 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/MyDataElasticsearchTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataelasticsearch +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataelasticsearch import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt similarity index 78% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt index a9f8f797e7b2..6f5dfd2120a9 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatacouchbase/SomeRepository.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatacouchbase +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataelasticsearch interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt index 40de9a76e7ea..4f898a4e837b 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/MyNonTransactionalTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatajpa +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatajpa import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.transaction.annotation.Propagation diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt index 51378c51e682..678805e5dd54 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withdb/MyRepositoryTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatajpa.withdb +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatajpa.withdb import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt index ade356e18df4..0b46d184f0da 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/MyRepositoryTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt similarity index 78% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt index a747adfa93cb..d317da025f11 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/User.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb class User(val username: String, val employeeNumber: String) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt similarity index 79% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt index ae06280c40a8..f517637624f2 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatajpa/withoutdb/UserRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatajpa.withoutdb interface UserRepository { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt index 285c37ade28a..c52fc28a701f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/inmemory/MyDataLdapTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataldap.inmemory +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataldap.inmemory import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt index 6265c7eb66cb..5e905e60436f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataldap/server/MyDataLdapTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataldap.server +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataldap.server import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt index 80d27503fa3a..72f9b345ca6d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdatamongodb/MyDataMongoDbTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdatamongodb +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdatamongodb import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt index 7460c673e284..228e0173efb5 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/nopropagation/MyDataNeo4jTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataneo4j.nopropagation +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataneo4j.nopropagation import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest import org.springframework.transaction.annotation.Propagation diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt index c284cb6d668b..a69c02384546 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/MyDataNeo4jTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataneo4j.propagation +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataneo4j.propagation import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt similarity index 77% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt index 5821c6ddd897..e13ff03032b6 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataelasticsearch/SomeRepository.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataneo4j/propagation/SomeRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataelasticsearch +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataneo4j.propagation interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt index 6b5e3dbb1500..5a619337f323 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/MyDataRedisTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringdataredis +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringdataredis import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt new file mode 100644 index 000000000000..e328b041da5e --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringdataredis/SomeRepository.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2012-2024 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.testing.springbootapplications.autoconfiguredspringdataredis + +interface SomeRepository diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt similarity index 86% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt index afdcf491fc48..4c1336a959bb 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyRestDocsConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer import org.springframework.boot.test.context.TestConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt similarity index 85% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt index 44a53da8d919..fe9e6b8f432a 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyResultHandlerConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt similarity index 89% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt index 3bdd188515ec..101402064daa 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/MyUserDocumentationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt similarity index 77% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt index 527b12d88ddc..d932051c0cd0 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withmockmvc/UserController.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withmockmvc class UserController diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt similarity index 86% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt index 7f332a838b5e..b5338b1b4ddb 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer import org.springframework.boot.test.context.TestConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt similarity index 89% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt index b9b8b0e301d9..9aaa3ea60377 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured import io.restassured.RestAssured import io.restassured.specification.RequestSpecification diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt similarity index 85% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt index be95090d4c05..845ecf8eb489 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyRestDocsConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer import org.springframework.boot.test.context.TestConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt index fa75f024d279..8161976e285c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyUsersDocumentationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt index 157dc98d3091..2ba6f1b7d0a5 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredspringrestdocs/withwebtestclient/MyWebTestClientBuilderCustomizerConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredspringrestdocs.withwebtestclient import org.springframework.boot.test.context.TestConfiguration import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt index a72ffe0ac3d6..b960a69c7657 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/MyWebServiceClientTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.client +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.client import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt index eb6ff9e58381..059a9473e1e7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Request.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.client +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.client import jakarta.xml.bind.annotation.XmlRootElement diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt index df939da9a7ab..e99ec4f8f88c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/Response.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.client +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.client import jakarta.xml.bind.annotation.XmlAccessType import jakarta.xml.bind.annotation.XmlAccessorType diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt similarity index 86% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt index b6bedacb577b..aafdd7fea4ca 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/client/SomeWebService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.client +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.client import org.springframework.boot.webservices.client.WebServiceTemplateBuilder import org.springframework.stereotype.Service diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt similarity index 86% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt index 9f8ee2c07abf..7f7ddd8b4cd2 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/ExampleEndpoint.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.server +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.server import javax.xml.transform.Source diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt index ce4ef870dd3e..f7b6bf746ce2 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/autoconfiguredwebservices/server/MyWebServiceServerTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.server +package org.springframework.boot.docs.testing.springbootapplications.autoconfiguredwebservices.server import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt index 4fe954cb6a2c..db06422fafd7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/detectingwebapptype/MyWebFluxTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.detectingwebapptype +package org.springframework.boot.docs.testing.springbootapplications.detectingwebapptype import org.springframework.boot.test.context.SpringBootTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTests.kt index 9582e0852684..4c6e8d9fbf29 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.excludingconfiguration +package org.springframework.boot.docs.testing.springbootapplications.excludingconfiguration import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt similarity index 79% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt index f2598ca0c9fa..211fd9b7841b 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/excludingconfiguration/MyTestsConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.excludingconfiguration +package org.springframework.boot.docs.testing.springbootapplications.excludingconfiguration class MyTestsConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/MyJmxTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/MyJmxTests.kt index 7616cdf2bf18..97988a741f0e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/MyJmxTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jmx +package org.springframework.boot.docs.testing.springbootapplications.jmx import javax.management.MBeanServer diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/SampleApp.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/SampleApp.kt similarity index 86% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/SampleApp.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/SampleApp.kt index c9428ec14d15..b5651cc454f1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jmx/SampleApp.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jmx/SampleApp.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jmx +package org.springframework.boot.docs.testing.springbootapplications.jmx import org.springframework.boot.SpringBootConfiguration import org.springframework.boot.autoconfigure.ImportAutoConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt index beea17d5de52..4ac615c2ec9a 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonAssertJTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jsontests +package org.springframework.boot.docs.testing.springbootapplications.jsontests import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.within diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonTests.kt similarity index 91% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonTests.kt index 6f13c3094407..46712e5ea3fc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/MyJsonTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/MyJsonTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jsontests +package org.springframework.boot.docs.testing.springbootapplications.jsontests import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/SomeObject.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/SomeObject.kt similarity index 81% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/SomeObject.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/SomeObject.kt index 15a544a1d81f..b279ce708848 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/SomeObject.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/SomeObject.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jsontests +package org.springframework.boot.docs.testing.springbootapplications.jsontests @Suppress("UNUSED_PARAMETER") class SomeObject(value: Float) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/VehicleDetails.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/VehicleDetails.kt similarity index 81% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/VehicleDetails.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/VehicleDetails.kt index 4ae99479f6c9..0d019b931e10 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/jsontests/VehicleDetails.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/jsontests/VehicleDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.jsontests +package org.springframework.boot.docs.testing.springbootapplications.jsontests data class VehicleDetails(val make: String, val model: String) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/MyTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/MyTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/MyTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/MyTests.kt index 14833f413683..ad25cc5b1d95 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/MyTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/MyTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.mockingbeans.bean +package org.springframework.boot.docs.testing.springbootapplications.mockingbeans.bean import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/RemoteService.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/RemoteService.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/RemoteService.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/RemoteService.kt index f79f32d26d76..658244320e83 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/RemoteService.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/RemoteService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.mockingbeans.bean +package org.springframework.boot.docs.testing.springbootapplications.mockingbeans.bean class RemoteService { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/Reverser.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/Reverser.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/Reverser.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/Reverser.kt index 982aa813cac3..9d5d7fef348e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/bean/Reverser.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/bean/Reverser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.mockingbeans.bean +package org.springframework.boot.docs.testing.springbootapplications.mockingbeans.bean class Reverser { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyConfig.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyConfig.kt similarity index 79% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyConfig.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyConfig.kt index fb681d574ff0..203bf9c5bfbf 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyConfig.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.mockingbeans.listener +package org.springframework.boot.docs.testing.springbootapplications.mockingbeans.listener class MyConfig diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyTests.kt index 66c4795b7116..c591fc84dabd 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/mockingbeans/listener/MyTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/mockingbeans/listener/MyTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.mockingbeans.listener +package org.springframework.boot.docs.testing.springbootapplications.mockingbeans.listener import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt similarity index 91% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt index f8b0f3ea0940..c1d4592c4966 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GraphQlIntegrationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springgraphqltests +package org.springframework.boot.docs.testing.springbootapplications.springgraphqltests import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt index 902d9cec26a5..53980c411620 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springgraphqltests/GreetingControllerTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springgraphqltests +package org.springframework.boot.docs.testing.springbootapplications.springgraphqltests import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyControllerTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyControllerTests.kt similarity index 91% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyControllerTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyControllerTests.kt index 9879efc62a02..bcef92d2410d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyControllerTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyControllerTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springmvctests +package org.springframework.boot.docs.testing.springbootapplications.springmvctests import org.junit.jupiter.api.Test import org.mockito.BDDMockito.given diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt index 27119f4052f8..cdfedba741cb 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/MyHtmlUnitTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springmvctests +package org.springframework.boot.docs.testing.springbootapplications.springmvctests import com.gargoylesoftware.htmlunit.WebClient import com.gargoylesoftware.htmlunit.html.HtmlPage diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleController.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleController.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleController.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleController.kt index 45d6c3d2a88d..32b4b58874d1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleController.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleController.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springmvctests +package org.springframework.boot.docs.testing.springbootapplications.springmvctests class UserVehicleController diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleService.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleService.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleService.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleService.kt index 3a9c14532180..a0aa92b8fffa 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/UserVehicleService.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/UserVehicleService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springmvctests +package org.springframework.boot.docs.testing.springbootapplications.springmvctests @Suppress("UNUSED_PARAMETER") class UserVehicleService { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/VehicleDetails.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/VehicleDetails.kt similarity index 81% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/VehicleDetails.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/VehicleDetails.kt index a8032d5f43c5..448560939f93 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springmvctests/VehicleDetails.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springmvctests/VehicleDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springmvctests +package org.springframework.boot.docs.testing.springbootapplications.springmvctests data class VehicleDetails(val make: String, val model: String) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt similarity index 90% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt index ab2d64e11e7a..e46c798bb39e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/MyControllerTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springwebfluxtests +package org.springframework.boot.docs.testing.springbootapplications.springwebfluxtests import org.junit.jupiter.api.Test import org.mockito.BDDMockito.given diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt similarity index 79% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt index ccd70bd0b702..7d3ad1f583df 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleController.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springwebfluxtests +package org.springframework.boot.docs.testing.springbootapplications.springwebfluxtests class UserVehicleController \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt index b1a7448224d8..8696f9f64e20 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/UserVehicleService.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springwebfluxtests +package org.springframework.boot.docs.testing.springbootapplications.springwebfluxtests @Suppress("UNUSED_PARAMETER") class UserVehicleService { diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt similarity index 80% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt index 5ca26c27d3a5..ea3be41f1946 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/springwebfluxtests/VehicleDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.springwebfluxtests +package org.springframework.boot.docs.testing.springbootapplications.springwebfluxtests data class VehicleDetails(val make: String, val model: String) diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt index b7139b67c488..479133df0013 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyApplication.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.userconfigurationandslicing +package org.springframework.boot.docs.testing.springbootapplications.userconfigurationandslicing import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.data.mongodb.config.EnableMongoAuditing diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt index f0c056d8eff9..b138ad38d041 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyMongoConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.userconfigurationandslicing +package org.springframework.boot.docs.testing.springbootapplications.userconfigurationandslicing import org.springframework.context.annotation.Configuration import org.springframework.data.mongodb.config.EnableMongoAuditing diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt similarity index 85% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt index 38372dbc8088..cf5129aafd25 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.userconfigurationandslicing +package org.springframework.boot.docs.testing.springbootapplications.userconfigurationandslicing import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt similarity index 82% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt index 7168fa99035a..eaa57fba45dd 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/MyWebMvcConfigurer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.userconfigurationandslicing +package org.springframework.boot.docs.testing.springbootapplications.userconfigurationandslicing import org.springframework.stereotype.Component import org.springframework.web.servlet.config.annotation.WebMvcConfigurer diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt similarity index 83% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt index 75b0ade0b5ea..f01db92229f0 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/userconfigurationandslicing/scan/MyApplication.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.userconfigurationandslicing.scan +package org.springframework.boot.docs.testing.springbootapplications.userconfigurationandslicing.scan import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.context.annotation.ComponentScan diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt index dcc79a971e29..2ae91cee8bab 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingapplicationarguments/MyApplicationArgumentTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.usingapplicationarguments +package org.springframework.boot.docs.testing.springbootapplications.usingapplicationarguments import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/always/MyApplicationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/always/MyApplicationTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/always/MyApplicationTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/always/MyApplicationTests.kt index b4f07a60524b..b065a6c664f9 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/always/MyApplicationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/always/MyApplicationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.usingmain.custom.always +package org.springframework.boot.docs.testing.springbootapplications.usingmain.always import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/custom/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/custom/MyApplication.kt similarity index 85% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/custom/MyApplication.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/custom/MyApplication.kt index d33e20ac7bbe..08408803b4f6 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/custom/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/custom/MyApplication.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.usingmain.custom +package org.springframework.boot.docs.testing.springbootapplications.usingmain.custom import org.springframework.boot.Banner import org.springframework.boot.runApplication diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/typical/MyApplication.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/typical/MyApplication.kt similarity index 85% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/typical/MyApplication.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/typical/MyApplication.kt index 38758f47bd82..a498939c4c27 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/usingmain/typical/MyApplication.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/usingmain/typical/MyApplication.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.usingmain.typical +package org.springframework.boot.docs.testing.springbootapplications.usingmain.typical import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.docs.using.structuringyourcode.locatingthemainclass.MyApplication diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt similarity index 91% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt index 10e10bae2f5b..a5d792ad03f1 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockMvcTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.withmockenvironment +package org.springframework.boot.docs.testing.springbootapplications.withmockenvironment import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt index 08adafec5870..6fa96f8b06bc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withmockenvironment/MyMockWebTestClientTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.withmockenvironment +package org.springframework.boot.docs.testing.springbootapplications.withmockenvironment import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt index 1a769cfd6370..5a330401134d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortTestRestTemplateTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.withrunningserver +package org.springframework.boot.docs.testing.springbootapplications.withrunningserver import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt index 9d3aa6930375..b196d71e7d5c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/springbootapplications/withrunningserver/MyRandomPortWebTestClientTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.springbootapplications.withrunningserver +package org.springframework.boot.docs.testing.springbootapplications.withrunningserver import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt index c2aa497a5702..ca5697de93c8 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.springframework.boot.docs.testing.testcontainers.dynamicproperties; +package org.springframework.boot.docs.testing.testcontainers.dynamicproperties import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.kt index a5e1071d544e..f50a262ad32f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.testing.testcontainers.serviceconnections; +package org.springframework.boot.docs.testing.testcontainers.serviceconnections import org.junit.jupiter.api.Test; import org.testcontainers.containers.Neo4jContainer; diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt index fb5ac0340673..35f7027cfdfc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyRedisConfiguration.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.testing.testcontainers.serviceconnections; +package org.springframework.boot.docs.testing.testcontainers.serviceconnections import org.springframework.boot.test.context.TestConfiguration import org.springframework.boot.testcontainers.service.connection.ServiceConnection diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/vanilla/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/vanilla/MyIntegrationTests.kt index 587ed84cce62..f2215052995f 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/vanilla/MyIntegrationTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/vanilla/MyIntegrationTests.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.testing.testcontainers.vanilla; +package org.springframework.boot.docs.testing.testcontainers.vanilla import org.junit.jupiter.api.Test; import org.testcontainers.containers.Neo4jContainer; diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/Config.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/Config.kt similarity index 78% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/Config.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/Config.kt index ed2572df2afd..ae69f62bbd25 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/Config.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/Config.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.configdataapplicationcontextinitializer +package org.springframework.boot.docs.testing.utilities.configdataapplicationcontextinitializer class Config diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt similarity index 84% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt index b61258c6b76a..ef24a4a79ff4 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/configdataapplicationcontextinitializer/MyConfigFileTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.configdataapplicationcontextinitializer +package org.springframework.boot.docs.testing.utilities.configdataapplicationcontextinitializer import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer import org.springframework.test.context.ContextConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/outputcapture/MyOutputCaptureTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/outputcapture/MyOutputCaptureTests.kt similarity index 88% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/outputcapture/MyOutputCaptureTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/outputcapture/MyOutputCaptureTests.kt index dfbc01759529..623f693867de 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/outputcapture/MyOutputCaptureTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/outputcapture/MyOutputCaptureTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.outputcapture +package org.springframework.boot.docs.testing.utilities.outputcapture import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt index eddf9ab0c4e1..4930df4f3e20 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testpropertyvalues/MyEnvironmentTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.testpropertyvalues +package org.springframework.boot.docs.testing.utilities.testpropertyvalues import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTests.kt similarity index 92% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTests.kt index 35ca97396573..6f47e39d8013 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.testresttemplate +package org.springframework.boot.docs.testing.utilities.testresttemplate import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt similarity index 92% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt index 5bb52cd03a6a..6d3c10a267ba 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MySpringBootTestsConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.testresttemplate +package org.springframework.boot.docs.testing.utilities.testresttemplate import org.springframework.boot.SpringBootConfiguration import org.springframework.boot.autoconfigure.ImportAutoConfiguration diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MyTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MyTests.kt similarity index 87% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MyTests.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MyTests.kt index d6bd28b10138..dac2139855df 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/utilities/testresttemplate/MyTests.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/utilities/testresttemplate/MyTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docs.features.testing.utilities.testresttemplate +package org.springframework.boot.docs.testing.utilities.testresttemplate import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test From e7d6bd6ccd9e225912ef3fc577f84a2aef47c064 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 3 Sep 2024 14:29:49 +0200 Subject: [PATCH 024/271] Test spring-boot-maven-plugin against Maven 3.9.9 Closes gh-42097 --- .../spring-boot-tools/spring-boot-maven-plugin/build.gradle | 2 +- 1 file changed, 1 insertion(+), 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 c4b7184a6b75..b8542d9e5300 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 @@ -171,7 +171,7 @@ task xsdResources(type: Sync) { } prepareMavenBinaries { - versions = [ "3.9.6", "3.6.3" ] + versions = [ "3.9.9", "3.6.3" ] } artifacts { From aa83bbee3d6b0f78eedf7385f60c838b86ed26be Mon Sep 17 00:00:00 2001 From: arefbehboudi Date: Tue, 3 Sep 2024 12:27:39 +0330 Subject: [PATCH 025/271] Polish See gh-42095 --- ...vletComponentRegisteringPostProcessor.java | 30 ++++++++----------- .../web/servlet/support/ErrorPageFilter.java | 4 +-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java index 6494d931a2a0..9bb266a887a7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java @@ -121,26 +121,20 @@ public void setApplicationContext(ApplicationContext applicationContext) throws @Override public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { - return new BeanFactoryInitializationAotContribution() { - - @Override - public void applyTo(GenerationContext generationContext, - BeanFactoryInitializationCode beanFactoryInitializationCode) { - for (String beanName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition definition = beanFactory.getBeanDefinition(beanName); - if (Objects.equals(definition.getBeanClassName(), - WebListenerHandler.ServletComponentWebListenerRegistrar.class.getName())) { - String listenerClassName = (String) definition.getConstructorArgumentValues() - .getArgumentValue(0, String.class) - .getValue(); - generationContext.getRuntimeHints() - .reflection() - .registerType(TypeReference.of(listenerClassName), - MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); - } + return (generationContext, beanFactoryInitializationCode) -> { + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition definition = beanFactory.getBeanDefinition(beanName); + if (Objects.equals(definition.getBeanClassName(), + WebListenerHandler.ServletComponentWebListenerRegistrar.class.getName())) { + String listenerClassName = (String) definition.getConstructorArgumentValues() + .getArgumentValue(0, String.class) + .getValue(); + generationContext.getRuntimeHints() + .reflection() + .registerType(TypeReference.of(listenerClassName), + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } } - }; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java index d1a615718cc4..761cfbb17cde 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java @@ -321,12 +321,12 @@ private static class ErrorWrapperResponse extends HttpServletResponseWrapper { } @Override - public void sendError(int status) throws IOException { + public void sendError(int status) { sendError(status, null); } @Override - public void sendError(int status, String message) throws IOException { + public void sendError(int status, String message) { this.status = status; this.message = message; this.hasErrorToSend = true; From 55f2af1279cbcf2cf105af11a2b98a249611e772 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Sep 2024 14:56:14 +0100 Subject: [PATCH 026/271] Polish "Polish" See gh-42095 --- .../servlet/ServletComponentRegisteringPostProcessor.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java index 9bb266a887a7..646b02aebb40 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java @@ -22,14 +22,12 @@ import java.util.Objects; import java.util.Set; -import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.TypeReference; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; -import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -131,8 +129,7 @@ public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableL .getValue(); generationContext.getRuntimeHints() .reflection() - .registerType(TypeReference.of(listenerClassName), - MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + .registerType(TypeReference.of(listenerClassName), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } } }; From 0cd99a7435bd70f31fabf9785f4ed25781e41a75 Mon Sep 17 00:00:00 2001 From: Leszek Jasek <6629813+Alchemik@users.noreply.github.com> Date: Tue, 3 Sep 2024 01:42:56 +0200 Subject: [PATCH 027/271] Improve "Command-line Completion" section Replaced deprecated Spring CLI options with the current ones, improved section related to completion setup for zsh. See gh-42091 --- .../src/docs/asciidoc/getting-started/installing.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/installing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/installing.adoc index 4c5d3cac36e6..a489d5f5b8db 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/installing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/installing.adoc @@ -167,15 +167,15 @@ If you are on a Mac and use https://www.macports.org/[MacPorts], you can install [[getting-started.installing.cli.completion]] ==== Command-line Completion The Spring Boot CLI includes scripts that provide command completion for the https://en.wikipedia.org/wiki/Bash_%28Unix_shell%29[BASH] and https://en.wikipedia.org/wiki/Z_shell[zsh] shells. -You can `source` the script (also named `spring`) in any shell or put it in your personal or system-wide bash completion initialization. -On a Debian system, the system-wide scripts are in `/shell-completion/bash` and all scripts in that directory are executed when a new shell starts. +You can `source` the script named `spring` (`_spring` for zsh) or put it in your personal or system-wide bash completion initialization. +On a Debian system, the system-wide scripts are in `/shell-completion/` and all scripts in that directory are executed when a new shell starts. For example, to run the script manually if you have installed by using SDKMAN!, use the following commands: [source,shell,indent=0,subs="verbatim"] ---- $ . ~/.sdkman/candidates/springboot/current/shell-completion/bash/spring $ spring - grab help jar run test version + encodepassword help init shell version ---- NOTE: If you install the Spring Boot CLI by using Homebrew or MacPorts, the command-line completion scripts are automatically registered with your shell. From aeafa207276af368da9e5f590444816fbb6beca0 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 3 Sep 2024 12:07:33 -0700 Subject: [PATCH 028/271] Prevent 'Recursive update' exceptions with Restarter Update `Restarter` to prevent 'Recursive update' `IllegalStateException` from being thrown. Calls to `objectFactory.getObject()` now happen outside of `computeIfAbsent`. Fixes gh-41571 --- .../boot/devtools/restart/Restarter.java | 7 ++++++- .../boot/devtools/restart/RestarterTests.java | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java index 284056061943..4d71ceddd127 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/Restarter.java @@ -440,7 +440,12 @@ private LeakSafeThread getLeakSafeThread() { } public Object getOrAddAttribute(String name, final ObjectFactory objectFactory) { - return this.attributes.computeIfAbsent(name, (ignore) -> objectFactory.getObject()); + Object value = this.attributes.get(name); + if (value == null) { + value = objectFactory.getObject(); + this.attributes.put(name, value); + } + return value; } public Object removeAttribute(String name) { diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java index 75689433fff6..ed1dd1bb3a17 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestarterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -145,15 +145,28 @@ void addClassLoaderFiles() { } @Test - @SuppressWarnings("rawtypes") void getOrAddAttributeWithExistingAttribute() { Restarter.getInstance().getOrAddAttribute("x", () -> "abc"); - ObjectFactory objectFactory = mock(ObjectFactory.class); + ObjectFactory objectFactory = mock(ObjectFactory.class); Object attribute = Restarter.getInstance().getOrAddAttribute("x", objectFactory); assertThat(attribute).isEqualTo("abc"); then(objectFactory).shouldHaveNoInteractions(); } + @Test + void getOrAddAttributeWithRecursion() { + Restarter restarter = Restarter.getInstance(); + Object added = restarter.getOrAddAttribute("postgresContainer", () -> { + restarter.getOrAddAttribute("rabbitContainer", () -> "def"); + return "abc"; + }); + ObjectFactory objectFactory = mock(ObjectFactory.class); + assertThat(added).isEqualTo("abc"); + assertThat(restarter.getOrAddAttribute("postgresContainer", objectFactory)).isEqualTo("abc"); + assertThat(restarter.getOrAddAttribute("rabbitContainer", objectFactory)).isEqualTo("def"); + then(objectFactory).shouldHaveNoInteractions(); + } + @Test void getThreadFactory() throws Exception { final ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); From d0c9e0e9e75dc6ccb6773013786e69e6fe7bae55 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 3 Sep 2024 21:08:48 +0200 Subject: [PATCH 029/271] List types of OpenTelemetry support in Spring Boot Closes gh-41227 --- .../src/docs/asciidoc/actuator/observability.adoc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc index 8175b7dd62fd..772667c34025 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc @@ -76,7 +76,15 @@ The preceding example will prevent all observations whose name contains "denied" [[actuator.observability.opentelemetry]] === OpenTelemetry Support -Spring Boot's actuator module includes basic support for https://opentelemetry.io/[OpenTelemetry]. + +NOTE: There are several ways to support https://opentelemetry.io/[OpenTelemetry] in your application. +You can use the https://opentelemetry.io/docs/zero-code/java/agent/[OpenTelemetry Java Agent] or the https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/[OpenTelemetry Spring Boot Starter], +which are supported by the OTel community; the metrics and traces use the semantic conventions defined by OTel libraries. +This documentation describes OpenTelemetry as officially supported by the Spring team, using Micrometer and the OTLP exporter; +the metrics and traces use the semantic conventions described in the Spring projects documentation, such as {spring-framework-docs}/integration/observability.html[Spring Framework]. + + +Spring Boot's actuator module includes basic support for OpenTelemetry. It provides a bean of type `OpenTelemetry`, and if there are beans of type `SdkTracerProvider`, `ContextPropagators`, `SdkLoggerProvider` or `SdkMeterProvider` in the application context, they automatically get registered. Additionally, it provides a `Resource` bean. From 8358630bd55d614067d5aad035c87a7014492baa Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 3 Sep 2024 20:15:56 -0700 Subject: [PATCH 030/271] Remove "Converting a Spring Boot JAR Application to a WAR" Remove "Converting a Spring Boot JAR Application to a WAR" since the guide is no longer available. Closes gh-42110 --- README.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/README.adoc b/README.adoc index 51b4ee3db304..f570bfec5fcc 100755 --- a/README.adoc +++ b/README.adoc @@ -170,7 +170,6 @@ The https://spring.io/[spring.io] site contains several guides that show how to * https://spring.io/guides/gs/spring-boot/[Building an Application with Spring Boot] is an introductory guide that shows you how to create an application, run it, and add some management services. * https://spring.io/guides/gs/actuator-service/[Building a RESTful Web Service with Spring Boot Actuator] is a guide to creating a REST web service and also shows how the server can be configured. -* https://spring.io/guides/gs/convert-jar-to-war/[Converting a Spring Boot JAR Application to a WAR] shows you how to run applications in a web server as a WAR file. From 26fb0eecb5980b0863839fec501b9cac66f4c2f8 Mon Sep 17 00:00:00 2001 From: Einar Pehrson Date: Tue, 3 Sep 2024 22:25:14 +0200 Subject: [PATCH 031/271] Fix StatsD link typo on Metrics documentation page The documentation link for StatsD metrics has incorrect anchor text. See gh-42109 --- .../docs/antora/modules/reference/pages/actuator/metrics.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc index b6427791f711..ccdf3c9a9faa 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc @@ -20,7 +20,7 @@ Spring Boot Actuator provides dependency management and auto-configuration for { - xref:actuator/metrics.adoc#actuator.metrics.export.signalfx[] - xref:actuator/metrics.adoc#actuator.metrics.export.simple[] (in-memory) - xref:actuator/metrics.adoc#actuator.metrics.export.stackdriver[] -- xref:actuator/metrics.adoc#actuator.metrics.export.statsd[D] +- xref:actuator/metrics.adoc#actuator.metrics.export.statsd[] - xref:actuator/metrics.adoc#actuator.metrics.export.wavefront[] TIP: To learn more about Micrometer's capabilities, see its {url-micrometer-docs}[reference documentation], in particular the {url-micrometer-docs-concepts}[concepts section]. From 43afb5bf6910e45347958742d4a6caa0df693241 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 3 Sep 2024 20:31:15 -0700 Subject: [PATCH 032/271] Fix Spring Framework documentation link See gh-41227 --- .../antora/modules/reference/pages/actuator/observability.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc index 82f76ca267f4..9c236aee528d 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc @@ -85,7 +85,7 @@ NOTE: There are several ways to support https://opentelemetry.io/[OpenTelemetry] You can use the https://opentelemetry.io/docs/zero-code/java/agent/[OpenTelemetry Java Agent] or the https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/[OpenTelemetry Spring Boot Starter], which are supported by the OTel community; the metrics and traces use the semantic conventions defined by OTel libraries. This documentation describes OpenTelemetry as officially supported by the Spring team, using Micrometer and the OTLP exporter; -the metrics and traces use the semantic conventions described in the Spring projects documentation, such as {spring-framework-docs}/integration/observability.html[Spring Framework]. +the metrics and traces use the semantic conventions described in the Spring projects documentation, such as {url-spring-framework-docs}/integration/observability.html[Spring Framework]. Spring Boot's actuator module includes basic support for OpenTelemetry. From ddf7af7ce3bda2f2a4f89daf4765ea169c96bcd5 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Wed, 28 Aug 2024 23:31:58 +0200 Subject: [PATCH 033/271] Add support for configuring Pulsar listener container concurrency Add a configuration property that allows users to configure Pulsar message listener container concurrency. See gh-42062 --- .../pulsar/PulsarAutoConfiguration.java | 6 +++++- .../boot/autoconfigure/pulsar/PulsarProperties.java | 13 +++++++++++++ .../pulsar/PulsarPropertiesMapper.java | 8 ++++++++ .../pulsar/PulsarReactivePropertiesMapper.java | 4 +++- .../pulsar/PulsarPropertiesMapperTests.java | 12 ++++++++++++ .../autoconfigure/pulsar/PulsarPropertiesTests.java | 2 ++ .../pulsar/PulsarReactivePropertiesMapperTests.java | 5 ++++- 7 files changed, 47 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java index 5b5f9dc41235..9d2f4d88d319 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java @@ -69,6 +69,7 @@ * @author Alexander Preuß * @author Phillip Webb * @author Jonas Geiregat + * @author Vedran Pavic * @since 3.2.0 */ @AutoConfiguration @@ -187,7 +188,10 @@ ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( } pulsarTransactionManager.ifUnique(containerProperties.transactions()::setTransactionManager); this.propertiesMapper.customizeContainerProperties(containerProperties); - return new ConcurrentPulsarListenerContainerFactory<>(pulsarConsumerFactory, containerProperties); + ConcurrentPulsarListenerContainerFactory listenerContainerFactory = new ConcurrentPulsarListenerContainerFactory<>( + pulsarConsumerFactory, containerProperties); + this.propertiesMapper.customizeConcurrentPulsarListenerContainerFactory(listenerContainerFactory); + return listenerContainerFactory; } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java index e7cbd0340e09..3972eec5029f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarProperties.java @@ -811,6 +811,11 @@ public static class Listener { */ private SchemaType schemaType; + /** + * Number of threads used by listener container. + */ + private Integer concurrency; + /** * Whether to record observations for when the Observations API is available and * the client supports it. @@ -825,6 +830,14 @@ public void setSchemaType(SchemaType schemaType) { this.schemaType = schemaType; } + public Integer getConcurrency() { + return this.concurrency; + } + + public void setConcurrency(Integer concurrency) { + this.concurrency = concurrency; + } + public boolean isObservationEnabled() { return this.observationEnabled; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index 9665c4cdb942..d02d04f457d9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -39,6 +39,7 @@ import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.json.JsonWriter; +import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.core.PulsarTemplate; import org.springframework.pulsar.listener.PulsarContainerProperties; import org.springframework.pulsar.reader.PulsarReaderContainerProperties; @@ -198,6 +199,13 @@ private void customizePulsarContainerListenerProperties(PulsarContainerPropertie map.from(properties::isObservationEnabled).to(containerProperties::setObservationEnabled); } + void customizeConcurrentPulsarListenerContainerFactory( + ConcurrentPulsarListenerContainerFactory listenerContainerFactory) { + PulsarProperties.Listener properties = this.properties.getListener(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(properties::getConcurrency).to(listenerContainerFactory::setConcurrency); + } + void customizeReaderBuilder(ReaderBuilder readerBuilder) { PulsarProperties.Reader properties = this.properties.getReader(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java index 2f79bbae615f..9abf379a5198 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -31,6 +31,7 @@ * * @author Chris Bono * @author Phillip Webb + * @author Vedran Pavic */ final class PulsarReactivePropertiesMapper { @@ -93,6 +94,7 @@ private void customizePulsarContainerListenerProperties(ReactivePulsarContainerP PulsarProperties.Listener properties = this.properties.getListener(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(properties::getSchemaType).to(containerProperties::setSchemaType); + map.from(properties::getConcurrency).to(containerProperties::setConcurrency); } void customizeMessageReaderBuilder(ReactiveMessageReaderBuilder builder) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java index dbacef33f9c3..79e9818685a0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java @@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.pulsar.PulsarProperties.Consumer; import org.springframework.boot.autoconfigure.pulsar.PulsarProperties.Failover.BackupCluster; +import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.core.PulsarProducerFactory; import org.springframework.pulsar.core.PulsarTemplate; import org.springframework.pulsar.listener.PulsarContainerProperties; @@ -272,6 +273,17 @@ void customizeContainerProperties() { assertThat(containerProperties.transactions().isEnabled()).isTrue(); } + @Test + void customizeConcurrentPulsarListenerContainerFactory() { + PulsarProperties properties = new PulsarProperties(); + properties.getListener().setConcurrency(10); + ConcurrentPulsarListenerContainerFactory listenerContainerFactory = mock( + ConcurrentPulsarListenerContainerFactory.class); + new PulsarPropertiesMapper(properties) + .customizeConcurrentPulsarListenerContainerFactory(listenerContainerFactory); + then(listenerContainerFactory).should().setConcurrency(10); + } + @Test @SuppressWarnings("unchecked") void customizeReaderBuilder() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java index 6ef42ef83452..72fbdd0e73f4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesTests.java @@ -393,9 +393,11 @@ class ListenerProperties { void bind() { Map map = new HashMap<>(); map.put("spring.pulsar.listener.schema-type", "avro"); + map.put("spring.pulsar.listener.concurrency", "10"); map.put("spring.pulsar.listener.observation-enabled", "true"); PulsarProperties.Listener properties = bindProperties(map).getListener(); assertThat(properties.getSchemaType()).isEqualTo(SchemaType.AVRO); + assertThat(properties.getConcurrency()).isEqualTo(10); assertThat(properties.isObservationEnabled()).isTrue(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java index df078b21a354..b31de0290322 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -48,6 +48,7 @@ * * @author Chris Bono * @author Phillip Webb + * @author Vedran Pavic */ class PulsarReactivePropertiesMapperTests { @@ -120,10 +121,12 @@ void customizeContainerProperties() { PulsarProperties properties = new PulsarProperties(); properties.getConsumer().getSubscription().setType(SubscriptionType.Shared); properties.getListener().setSchemaType(SchemaType.AVRO); + properties.getListener().setConcurrency(10); ReactivePulsarContainerProperties containerProperties = new ReactivePulsarContainerProperties<>(); new PulsarReactivePropertiesMapper(properties).customizeContainerProperties(containerProperties); assertThat(containerProperties.getSubscriptionType()).isEqualTo(SubscriptionType.Shared); assertThat(containerProperties.getSchemaType()).isEqualTo(SchemaType.AVRO); + assertThat(containerProperties.getConcurrency()).isEqualTo(10); } @Test From f3645bba139614411f7b57af819f1af774c005e5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 3 Sep 2024 21:04:26 -0700 Subject: [PATCH 034/271] Update copyright year of changed files --- .../boot/autoconfigure/task/TaskExecutionProperties.java | 2 +- .../boot/autoconfigure/task/TaskSchedulingProperties.java | 2 +- .../boot/loader/launch/ExecutableArchiveLauncher.java | 2 +- .../org/springframework/boot/loader/launch/JarLauncher.java | 2 +- .../java/org/springframework/boot/loader/launch/Launcher.java | 2 +- .../springframework/boot/loader/launch/PropertiesLauncher.java | 2 +- .../org/springframework/boot/loader/launch/WarLauncher.java | 2 +- .../boot/loader/launch/AbstractLauncherTests.java | 2 +- .../springframework/boot/loader/launch/JarLauncherTests.java | 2 +- .../boot/loader/launch/PropertiesLauncherTests.java | 2 +- .../springframework/boot/loader/launch/WarLauncherTests.java | 2 +- .../java/org/springframework/boot/json/BasicJsonParser.java | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java index 008021344612..6deb352e4801 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java index a3da30df0530..2e7fda80db39 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/ExecutableArchiveLauncher.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/ExecutableArchiveLauncher.java index 6992b6718d8b..efedff53985c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/ExecutableArchiveLauncher.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/ExecutableArchiveLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/JarLauncher.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/JarLauncher.java index 7569fa4f2668..ca899df8f606 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/JarLauncher.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/JarLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Launcher.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Launcher.java index 54ae5ef8eae9..b04d8cea6800 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Launcher.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/Launcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/PropertiesLauncher.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/PropertiesLauncher.java index c3ad4eb31e05..f230f1b734d2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/PropertiesLauncher.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/PropertiesLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/WarLauncher.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/WarLauncher.java index d32aa85a7a4e..18b9a4016977 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/WarLauncher.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/launch/WarLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/AbstractLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/AbstractLauncherTests.java index 2fd0f9b80ce6..4a987a9aa0ec 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/AbstractLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/AbstractLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/JarLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/JarLauncherTests.java index dd2d300f2c3e..353b376b7a14 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/JarLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/JarLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/PropertiesLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/PropertiesLauncherTests.java index 1a6dd9395166..b6589e942b00 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/PropertiesLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/PropertiesLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/WarLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/WarLauncherTests.java index 609fa6ea417f..e8f184866857 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/WarLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/launch/WarLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java index 6a16d0c84c19..38a66f9fa8c6 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. From 8d44fd5f3e50db4039bf99a4d2243d600971244a Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 4 Sep 2024 08:47:32 +0200 Subject: [PATCH 035/271] Improve docker without buildpacks documentation Explain how CDS can be enabled with plain Dockerfiles. Closes gh-42106 --- .../how-to/pages/class-data-sharing.adoc | 6 +++ .../container-images/dockerfiles.adoc | 53 ++++++++++--------- .../modules/reference/partials/dockerfile | 21 ++++++++ 3 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc index 7b94fec7a464..591b359b0b08 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc @@ -15,6 +15,12 @@ This will cause the buildpack to do a training run of the application, save the The Paketo Buildpack for Spring Boot https://github.com/paketo-buildpacks/spring-boot?tab=readme-ov-file#configuration[documentation] has information on other configuration options that can be enabled with builder environment variables, like `CDS_TRAINING_JAVA_TOOL_OPTIONS` that allows to override the default `JAVA_TOOL_OPTIONS`, only for the CDS training run. +[[howto.class-data-sharing.dockerfiles]] +== Packaging an Application Using CDS and Dockerfiles + +If you don't want to use Cloud Native Buildpacks, it is also possible to use CDS with a `Dockerfile`. +For more information about that, please see the xref:reference:packaging/container-images/dockerfiles.adoc#packaging.container-images.dockerfiles.cds[Dockerfiles reference documentation]. + [[howto.class-data-sharing.training-run-configuration]] == Preventing Remote Services Interaction During the Training Run diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc index a3a7417b0e2c..ec1e817612a8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/packaging/container-images/dockerfiles.adoc @@ -1,7 +1,7 @@ [[packaging.container-images.dockerfiles]] = Dockerfiles -While it is possible to convert a Spring Boot uber jar into a Docker image with just a few lines in the Dockerfile, using the xref:packaging/container-images/efficient-images.adoc#packaging.container-images.efficient-images.layering[layering feature] will result in an optimized image. +While it is possible to convert a Spring Boot uber jar into a Docker image with just a few lines in the `Dockerfile`, using the xref:packaging/container-images/efficient-images.adoc#packaging.container-images.efficient-images.layering[layering feature] will result in an optimized image. When you create a jar containing the layers index file, the `spring-boot-jarmode-tools` jar will be added as a dependency to your jar. With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers. @@ -28,32 +28,12 @@ Available commands: help Help about any command ---- -The `extract` command can be used to easily split the application into layers to be added to the Dockerfile. -Here is an example of a Dockerfile using `jarmode`. +The `extract` command can be used to easily split the application into layers to be added to the `Dockerfile`. +Here is an example of a `Dockerfile` using `jarmode`. [source,dockerfile] ---- -# Perform the extraction in a separate builder container -FROM bellsoft/liberica-openjre-debian:17-cds AS builder -WORKDIR /builder -# This points to the built jar file in the target folder -# Adjust this to 'build/libs/*.jar' if you're using Gradle -ARG JAR_FILE=target/*.jar -# Copy the jar file to the working directory and rename it to application.jar -COPY ${JAR_FILE} application.jar -# Extract the jar file using an efficient layout -RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted - -# This is the runtime container -FROM bellsoft/liberica-openjre-debian:17-cds -WORKDIR /application -# Copy the extracted jar contents from the builder container into the working directory in the runtime container -# Every copy step creates a new docker layer -# This allows docker to only pull the changes it really needs -COPY --from=builder /builder/extracted/dependencies/ ./ -COPY --from=builder /builder/extracted/spring-boot-loader/ ./ -COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ -COPY --from=builder /builder/extracted/application/ ./ +include::reference:partial$dockerfile[] # Start the application jar - this is not the uber jar used by the builder # This jar only contains application code and references to the extracted jar files # This layout is efficient to start up and CDS friendly @@ -67,10 +47,31 @@ Assuming the above `Dockerfile` is in the current directory, your Docker image c $ docker build --build-arg JAR_FILE=path/to/myapp.jar . ---- -This is a multi-stage Dockerfile. +This is a multi-stage `Dockerfile`. The builder stage extracts the directories that are needed later. Each of the `COPY` commands relates to the layers extracted by the jarmode. -Of course, a Dockerfile can be written without using the `jarmode`. +Of course, a `Dockerfile` can be written without using the `jarmode`. You can use some combination of `unzip` and `mv` to move things to the right layer but `jarmode` simplifies that. Additionally, the layout created by the `jarmode` is CDS friendly out of the box. + + + +[[packaging.container-images.dockerfiles.cds]] +== CDS + +If you want to additionally enable xref:reference:packaging/class-data-sharing.adoc[CDS], you can use this `Dockerfile`: +[source,dockerfile] +---- +include::reference:partial$dockerfile[] +# Execute the CDS training run +RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar application.jar +# Start the application jar with CDS enabled - this is not the uber jar used by the builder +# This jar only contains application code and references to the extracted jar files +# This layout is efficient to start up and CDS friendly +ENTRYPOINT ["java", "-XX:SharedArchiveFile=application.jsa", "-jar", "application.jar"] +---- + +This is mostly the same as the above `Dockerfile`. +As the last steps, it creates the CDS archive by doing a training run and passes the CDS parameter to `java -jar`. + diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile new file mode 100644 index 000000000000..8985fde04bee --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/partials/dockerfile @@ -0,0 +1,21 @@ +# Perform the extraction in a separate builder container +FROM bellsoft/liberica-openjre-debian:17-cds AS builder +WORKDIR /builder +# This points to the built jar file in the target folder +# Adjust this to 'build/libs/*.jar' if you're using Gradle +ARG JAR_FILE=target/*.jar +# Copy the jar file to the working directory and rename it to application.jar +COPY ${JAR_FILE} application.jar +# Extract the jar file using an efficient layout +RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted + +# This is the runtime container +FROM bellsoft/liberica-openjre-debian:17-cds +WORKDIR /application +# Copy the extracted jar contents from the builder container into the working directory in the runtime container +# Every copy step creates a new docker layer +# This allows docker to only pull the changes it really needs +COPY --from=builder /builder/extracted/dependencies/ ./ +COPY --from=builder /builder/extracted/spring-boot-loader/ ./ +COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ +COPY --from=builder /builder/extracted/application/ ./ From cc432f9611487872b8086c4a1a4849d083468c41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:56:26 +0000 Subject: [PATCH 036/271] Bump gradle/actions from 4.0.0 to 4.0.1 Bumps [gradle/actions](https://github.com/gradle/actions) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/af1da67850ed9a4cedd57bfd976089dd991e2582...16bf8bc8fe830fa669c3c9f914d3eb147c629707) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] See gh-42090 --- .github/workflows/build-pull-request.yml | 4 ++-- .github/workflows/verify.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index bfe505fe9c5c..ae132039b12b 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -23,9 +23,9 @@ jobs: - name: Check Out uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/wrapper-validation@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Set Up Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 - name: Build env: CI: 'true' diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 0737d7b1480f..f46a9fa33364 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -42,7 +42,7 @@ jobs: - name: Set Up Homebrew uses: Homebrew/actions/setup-homebrew@7657c9512f50e1c35b640971116425935bab3eea - name: Set Up Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: false - name: Configure Gradle Properties From b3781eecbcfbb2fa70b205cd636c39e129611be7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 08:41:57 +0100 Subject: [PATCH 037/271] Polish "Bump gradle/actions from 4.0.0 to 4.0.1" See gh-42090 --- .github/actions/prepare-gradle-build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 29bdc27f00f6..8a8883f00074 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -32,7 +32,7 @@ runs: ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} ${{ inputs.java-toolchain == 'true' && '17' || '' }} - name: Set Up Gradle - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/wrapper-validation@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: false develocity-access-key: ${{ inputs.develocity-access-key }} From 6da8251e0faf2dedc8305e910ed03e3ef3c24fba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:56:19 +0000 Subject: [PATCH 038/271] Bump jfrog/setup-jfrog-cli from 4.2.2 to 4.3.2 Bumps [jfrog/setup-jfrog-cli](https://github.com/jfrog/setup-jfrog-cli) from 4.2.2 to 4.3.2. - [Release notes](https://github.com/jfrog/setup-jfrog-cli/releases) - [Commits](https://github.com/jfrog/setup-jfrog-cli/compare/26532cdb5b1ea07940f10d57666fd988048fc903...cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3) --- updated-dependencies: - dependency-name: jfrog/setup-jfrog-cli dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] See gh-42089 --- .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 f263538b156d..d77184fa4c3a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@26532cdb5b1ea07940f10d57666fd988048fc903 # v4.2.2 + uses: jfrog/setup-jfrog-cli@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 env: JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - name: Promote build From 542c810f672e2ef93cfb1120e1d3a67c43e054e9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 08:49:07 +0100 Subject: [PATCH 039/271] Polish "Bump jfrog/setup-jfrog-cli from 4.2.2 to 4.3.2" See gh-42089 --- .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 2d29223aa42e..e0c61b138e14 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@26532cdb5b1ea07940f10d57666fd988048fc903 # v4.2.2 + uses: jfrog/setup-jfrog-cli@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 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 43486ab71001..b71c6a6f8776 100644 --- a/.github/actions/sync-to-maven-central/action.yml +++ b/.github/actions/sync-to-maven-central/action.yml @@ -20,7 +20,7 @@ runs: using: composite steps: - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@26532cdb5b1ea07940f10d57666fd988048fc903 # v4.2.2 + uses: jfrog/setup-jfrog-cli@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 env: JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - name: Download Release Artifacts From 593fecbe351afa6c0cfbe8b21fbb30b152ae3433 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:13:54 +0100 Subject: [PATCH 040/271] Start building against Micrometer 1.12.10 snapshots See gh-42121 --- 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 c40944bee67c..07cd24ad9735 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1004,7 +1004,7 @@ bom { ] } } - library("Micrometer", "1.12.9") { + library("Micrometer", "1.12.10-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [ From 49d4f7ac0e626b474f69eb6def21946f72f84751 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:13:59 +0100 Subject: [PATCH 041/271] Start building against Micrometer Tracing 1.2.10 snapshots See gh-42122 --- 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 07cd24ad9735..42f71581cc21 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1017,7 +1017,7 @@ bom { ] } } - library("Micrometer Tracing", "1.2.9") { + library("Micrometer Tracing", "1.2.10-SNAPSHOT") { considerSnapshots() group("io.micrometer") { imports = [ From a465e37908da02d93afd204334f52fcc8fb5ad16 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:03 +0100 Subject: [PATCH 042/271] Start building against Reactor Bom 2023.0.10 snapshots See gh-42123 --- 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 42f71581cc21..07cfe21c0c99 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1391,7 +1391,7 @@ bom { ] } } - library("Reactor Bom", "2023.0.9") { + library("Reactor Bom", "2023.0.10-SNAPSHOT") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From ced01c1dd36bc41f513d6d51cdb859d14349905e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:08 +0100 Subject: [PATCH 043/271] Start building against Spring Data Bom 2023.1.10 snapshots See gh-42124 --- 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 07cfe21c0c99..8fc16ebad3e2 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1583,7 +1583,7 @@ bom { ] } } - library("Spring Data Bom", "2023.1.9") { + library("Spring Data Bom", "2023.1.10-SNAPSHOT") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 69379d4fdabdc064d88907b4f3cddf286eb3dd74 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:12 +0100 Subject: [PATCH 044/271] Start building against Spring Framework 6.1.13 snapshots See gh-42125 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a22eadc918fa..2407ea66eca2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ junitJupiterVersion=5.10.3 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.9.28 -springFrameworkVersion=6.1.12 +springFrameworkVersion=6.1.13-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.28 From 68c331f496ff3e92979ebca5e1c33697969c6bb4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:17 +0100 Subject: [PATCH 045/271] Start building against Spring Integration 6.2.9 snapshots See gh-42126 --- 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 8fc16ebad3e2..862ada0ad3f4 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1618,7 +1618,7 @@ bom { ] } } - library("Spring Integration", "6.2.8") { + library("Spring Integration", "6.2.9-SNAPSHOT") { considerSnapshots() group("org.springframework.integration") { imports = [ From e972ddfe501240311d382c7e2c807caf0964297c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:21 +0100 Subject: [PATCH 046/271] Start building against Spring Kafka 3.1.9 snapshots See gh-42127 --- 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 862ada0ad3f4..ed94e3e45a78 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1626,7 +1626,7 @@ bom { ] } } - library("Spring Kafka", "3.1.8") { + library("Spring Kafka", "3.1.9-SNAPSHOT") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 4b1fcdb74a9b29ab5cf647d655e6bdb760d71a5a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 12:14:26 +0100 Subject: [PATCH 047/271] Start building against Spring Pulsar 1.0.10 snapshots See gh-42128 --- 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 ed94e3e45a78..272295df9e5c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1646,7 +1646,7 @@ bom { ] } } - library("Spring Pulsar", "1.0.9") { + library("Spring Pulsar", "1.0.10-SNAPSHOT") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From bca7486bdeb1f3076798fead1ef7a1771e5d66c7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:16:54 +0100 Subject: [PATCH 048/271] Fix Prepare Gradle Build action See gh-42090 --- .github/actions/prepare-gradle-build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 8a8883f00074..7ba072319eaf 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -32,7 +32,7 @@ runs: ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} ${{ inputs.java-toolchain == 'true' && '17' || '' }} - name: Set Up Gradle - uses: gradle/actions/wrapper-validation@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 with: cache-read-only: false develocity-access-key: ${{ inputs.develocity-access-key }} From 23f8e16d4a0e348ce5de9c1743896f8179bead9e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:28 +0100 Subject: [PATCH 049/271] Start building against Micrometer 1.13.4 snapshots See gh-42129 --- 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 db618b2ea89f..c7a61e8ce73f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1280,7 +1280,7 @@ bom { ] } } - library("Micrometer", "1.13.3") { + library("Micrometer", "1.13.4-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [ From 44882e1ec02adad5bedc1f31782a0df078655411 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:32 +0100 Subject: [PATCH 050/271] Start building against Micrometer Tracing 1.3.4 snapshots See gh-42130 --- 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 c7a61e8ce73f..67f2f21869c5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1300,7 +1300,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.3.3") { + library("Micrometer Tracing", "1.3.4-SNAPSHOT") { considerSnapshots() group("io.micrometer") { imports = [ From acc60dcab05ac8d4d5b330d5a31e6209f91d82ac Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:37 +0100 Subject: [PATCH 051/271] Start building against Reactor Bom 2023.0.10 snapshots See gh-42131 --- 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 67f2f21869c5..775b75c7a2ae 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1704,7 +1704,7 @@ bom { ] } } - library("Reactor Bom", "2023.0.9") { + library("Reactor Bom", "2023.0.10-SNAPSHOT") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From 009ccddcab77659ffd9b4a3c9ee25070a8fc146c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:42 +0100 Subject: [PATCH 052/271] Start building against Spring Data Bom 2024.0.4 snapshots See gh-42132 --- 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 775b75c7a2ae..a0386377b89b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1948,7 +1948,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2024.0.3") { + library("Spring Data Bom", "2024.0.4-SNAPSHOT") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 9e741f9d8019fbf30b325e94a31384bc1ec60ab6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:46 +0100 Subject: [PATCH 053/271] Start building against Spring Framework 6.1.13 snapshots See gh-42133 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 93953523daf6..1df263340053 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ junitJupiterVersion=5.10.3 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.2 -springFrameworkVersion=6.1.12 +springFrameworkVersion=6.1.13-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.28 snakeYamlVersion=2.2 From 725b1789a4732c2fea09d02e7dd6c969971a87d2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:51 +0100 Subject: [PATCH 054/271] Start building against Spring Integration 6.3.4 snapshots See gh-42134 --- 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 a0386377b89b..ce8a46df38a2 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2009,7 +2009,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "6.3.3") { + library("Spring Integration", "6.3.4-SNAPSHOT") { considerSnapshots() group("org.springframework.integration") { imports = [ From f804e82d41c4b75ede023d641e195fb002dfbb40 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:07:55 +0100 Subject: [PATCH 055/271] Start building against Spring Kafka 3.2.4 snapshots See gh-42135 --- 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 ce8a46df38a2..8e2ad0b1fedc 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2025,7 +2025,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.2.3") { + library("Spring Kafka", "3.2.4-SNAPSHOT") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 92ed9efd2cd2c698cef2575e71e95c8413c3e7f7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 13:08:00 +0100 Subject: [PATCH 056/271] Start building against Spring Pulsar 1.1.4 snapshots See gh-42136 --- 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 8e2ad0b1fedc..33ff87a4e546 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2060,7 +2060,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.1.3") { + library("Spring Pulsar", "1.1.4-SNAPSHOT") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From 9f79769030c2b7df8985b405a6e21ac5d6ada0eb Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:01 +0100 Subject: [PATCH 057/271] Start building against Micrometer 1.14.0 snapshots See gh-42137 --- .../cache/CacheMetricsAutoConfigurationTests.java | 6 ++++-- .../metrics/cache/CacheMetricsRegistrarTests.java | 14 +++++++++----- .../spring-boot-dependencies/build.gradle | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java index 1d3894f07ff6..9cd4365df026 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -51,7 +51,9 @@ void autoConfiguredCache2kIsInstrumented() { @Test void autoConfiguredCacheManagerIsInstrumented() { - this.contextRunner.withPropertyValues("spring.cache.type=caffeine", "spring.cache.cache-names=cache1,cache2") + this.contextRunner + .withPropertyValues("spring.cache.type=caffeine", "spring.cache.cache-names=cache1,cache2", + "spring.cache.caffeine.spec=recordStats") .run((context) -> { MeterRegistry registry = context.getBean(MeterRegistry.class); registry.get("cache.gets").tags("name", "cache1").tags("cache.manager", "cacheManager").meter(); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/CacheMetricsRegistrarTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/CacheMetricsRegistrarTests.java index 8b6dbc783a09..e0894f539d0c 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/CacheMetricsRegistrarTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/cache/CacheMetricsRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -41,7 +41,9 @@ class CacheMetricsRegistrarTests { void bindToSupportedCache() { CacheMetricsRegistrar registrar = new CacheMetricsRegistrar(this.meterRegistry, Collections.singleton(new CaffeineCacheMeterBinderProvider())); - assertThat(registrar.bindCacheToRegistry(new CaffeineCache("test", Caffeine.newBuilder().build()))).isTrue(); + assertThat( + registrar.bindCacheToRegistry(new CaffeineCache("test", Caffeine.newBuilder().recordStats().build()))) + .isTrue(); assertThat(this.meterRegistry.get("cache.gets").tags("name", "test").meter()).isNotNull(); } @@ -49,8 +51,8 @@ void bindToSupportedCache() { void bindToSupportedCacheWrappedInTransactionProxy() { CacheMetricsRegistrar registrar = new CacheMetricsRegistrar(this.meterRegistry, Collections.singleton(new CaffeineCacheMeterBinderProvider())); - assertThat(registrar.bindCacheToRegistry( - new TransactionAwareCacheDecorator(new CaffeineCache("test", Caffeine.newBuilder().build())))) + assertThat(registrar.bindCacheToRegistry(new TransactionAwareCacheDecorator( + new CaffeineCache("test", Caffeine.newBuilder().recordStats().build())))) .isTrue(); assertThat(this.meterRegistry.get("cache.gets").tags("name", "test").meter()).isNotNull(); } @@ -58,7 +60,9 @@ void bindToSupportedCacheWrappedInTransactionProxy() { @Test void bindToUnsupportedCache() { CacheMetricsRegistrar registrar = new CacheMetricsRegistrar(this.meterRegistry, Collections.emptyList()); - assertThat(registrar.bindCacheToRegistry(new CaffeineCache("test", Caffeine.newBuilder().build()))).isFalse(); + assertThat( + registrar.bindCacheToRegistry(new CaffeineCache("test", Caffeine.newBuilder().recordStats().build()))) + .isFalse(); assertThat(this.meterRegistry.find("cache.gets").tags("name", "test").meter()).isNull(); } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3a9b685a542f..a7dfc0fdd93c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1301,7 +1301,7 @@ bom { ] } } - library("Micrometer", "1.14.0-M2") { + library("Micrometer", "1.14.0-SNAPSHOT") { considerSnapshots() group("io.micrometer") { modules = [ From 0982c6521361a828ece5e1961e3702fa248d3a01 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:05 +0100 Subject: [PATCH 058/271] Start building against Micrometer Tracing 1.4.0 snapshots See gh-42138 --- 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 a7dfc0fdd93c..0b35ed542022 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1321,7 +1321,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.4.0-M2") { + library("Micrometer Tracing", "1.4.0-SNAPSHOT") { considerSnapshots() group("io.micrometer") { imports = [ From 814999a3159e86228bb1d365984a23f675419a90 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:10 +0100 Subject: [PATCH 059/271] Start building against Reactor Bom 2024.0.0 snapshots See gh-42139 --- 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 0b35ed542022..5f580c60d3c0 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1695,7 +1695,7 @@ bom { ] } } - library("Reactor Bom", "2024.0.0-M5") { + library("Reactor Bom", "2024.0.0-SNAPSHOT") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From 504b738a1067978e1a13612885d207cad5d6d4d8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:14 +0100 Subject: [PATCH 060/271] Start building against Spring AMQP 3.2.0 snapshots See gh-42140 --- 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 5f580c60d3c0..8f41f97653ac 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1892,7 +1892,7 @@ bom { ] } } - library("Spring AMQP", "3.2.0-M2") { + library("Spring AMQP", "3.2.0-SNAPSHOT") { considerSnapshots() group("org.springframework.amqp") { imports = [ From b691f7c4bcce053476140d9baffc90fbe629c9db Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:19 +0100 Subject: [PATCH 061/271] Start building against Spring Authorization Server 1.4.0 snapshots See gh-42141 --- 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 8f41f97653ac..9f2278227b44 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1907,7 +1907,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-amqp/releases/tag/v{version}") } } - library("Spring Authorization Server", "1.4.0-M1") { + library("Spring Authorization Server", "1.4.0-SNAPSHOT") { considerSnapshots() group("org.springframework.security") { modules = [ From a12e36237c092c4e4046afa6a24de6486c827236 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:24 +0100 Subject: [PATCH 062/271] Start building against Spring Batch 5.2.0 snapshots See gh-42142 --- 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 9f2278227b44..74faf74eb164 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1923,7 +1923,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-authorization-server/releases/tag/{version}") } } - library("Spring Batch", "5.1.2") { + library("Spring Batch", "5.2.0-SNAPSHOT") { considerSnapshots() group("org.springframework.batch") { imports = [ From 620947f99a3cc107fa8ac80bf9e2b4c1a7abd10d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:28 +0100 Subject: [PATCH 063/271] Start building against Spring Data Bom 2024.1.0 snapshots See gh-42143 --- 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 74faf74eb164..b9ca130c8fc7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1939,7 +1939,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2024.0.3") { + library("Spring Data Bom", "2024.1.0-SNAPSHOT") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 2635d1ec8adaac158d288032e64229c240283c9d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:33 +0100 Subject: [PATCH 064/271] Start building against Spring Framework 6.2.0 snapshots See gh-42144 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ab959290c2db..8b98ca334191 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ junitJupiterVersion=5.11.0 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.2 -springFrameworkVersion=6.2.0-M7 +springFrameworkVersion=6.2.0-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.28 snakeYamlVersion=2.2 From 5880d3857e06980194a8798a00be9f1b52de00a5 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:37 +0100 Subject: [PATCH 065/271] Start building against Spring Integration 6.4.0 snapshots See gh-42145 --- 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 b9ca130c8fc7..40b1300e2743 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2000,7 +2000,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "6.4.0-M2") { + library("Spring Integration", "6.4.0-SNAPSHOT") { considerSnapshots() group("org.springframework.integration") { imports = [ From 1aca11c622ef20b870f2703713c9c1c81826924a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:42 +0100 Subject: [PATCH 066/271] Start building against Spring Kafka 3.3.0 snapshots See gh-42146 --- 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 40b1300e2743..804280e9cd1c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2016,7 +2016,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.3.0-M2") { + library("Spring Kafka", "3.3.0-SNAPSHOT") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 23ad50ee6033e6595a008694b162edf31247b153 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:46 +0100 Subject: [PATCH 067/271] Start building against Spring Pulsar 1.2.0 snapshots See gh-42147 --- .../boot/autoconfigure/pulsar/PulsarPropertiesMapper.java | 1 + .../boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java | 1 + spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index d02d04f457d9..ad96d6031b84 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -199,6 +199,7 @@ private void customizePulsarContainerListenerProperties(PulsarContainerPropertie map.from(properties::isObservationEnabled).to(containerProperties::setObservationEnabled); } + @SuppressWarnings("removal") void customizeConcurrentPulsarListenerContainerFactory( ConcurrentPulsarListenerContainerFactory listenerContainerFactory) { PulsarProperties.Listener properties = this.properties.getListener(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java index 79e9818685a0..baab6c4078f4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java @@ -274,6 +274,7 @@ void customizeContainerProperties() { } @Test + @SuppressWarnings("removal") void customizeConcurrentPulsarListenerContainerFactory() { PulsarProperties properties = new PulsarProperties(); properties.getListener().setConcurrency(10); diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 804280e9cd1c..7e2dca2eb870 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2051,7 +2051,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.2.0-M1") { + library("Spring Pulsar", "1.2.0-SNAPSHOT") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From 0232f27b5edb33380586965c500f895c0ada20f9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:51 +0100 Subject: [PATCH 068/271] Start building against Spring Security 6.4.0 snapshots See gh-42148 --- 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 7e2dca2eb870..e9def2a2c537 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2093,7 +2093,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-retry/releases/tag/v{version}") } } - library("Spring Security", "6.4.0-M3") { + library("Spring Security", "6.4.0-SNAPSHOT") { considerSnapshots() group("org.springframework.security") { imports = [ From 0ce417061257fee46f6c149263d5aa14b362bcea Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 14:48:55 +0100 Subject: [PATCH 069/271] Start building against Spring Session 3.4.0 snapshots See gh-42149 --- 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 e9def2a2c537..80f59195c6e1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2109,7 +2109,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-security/releases/tag/{version}") } } - library("Spring Session", "3.4.0-M2") { + library("Spring Session", "3.4.0-SNAPSHOT") { considerSnapshots() prohibit { startsWith(["Apple-", "Bean-", "Corn-", "Dragonfruit-"]) From f024c193e4ae5c4263077d8fada4959639231248 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 5 Sep 2024 09:03:46 +0100 Subject: [PATCH 070/271] Upgrade to Elasticsearch Client 8.15.0 Closes gh-42155 --- 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 80f59195c6e1..e883780dbc86 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -334,7 +334,7 @@ bom { releaseNotes("https://github.com/ehcache/ehcache3/releases/tag/v{version}") } } - library("Elasticsearch Client", "8.13.4") { + library("Elasticsearch Client", "8.15.0") { group("org.elasticsearch.client") { modules = [ "elasticsearch-rest-client" { From 62ef81b5c8a1f3641271e61a2851c80e689f39cd Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Fri, 30 Aug 2024 14:27:57 -0500 Subject: [PATCH 071/271] Add subscription name to Pulsar mapped config props The subscription name config prop was not being set on the Pulsar listener container properties. This commit adds the subscription name to the Pulsar property mappers. See gh-42067 --- .../boot/autoconfigure/pulsar/PulsarPropertiesMapper.java | 1 + .../autoconfigure/pulsar/PulsarReactivePropertiesMapper.java | 1 + .../boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java | 2 ++ .../pulsar/PulsarReactivePropertiesMapperTests.java | 2 ++ 4 files changed, 6 insertions(+) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index ad96d6031b84..f645616892b5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -190,6 +190,7 @@ private void customizePulsarContainerConsumerSubscriptionProperties(PulsarContai PulsarProperties.Consumer.Subscription properties = this.properties.getConsumer().getSubscription(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(properties::getType).to(containerProperties::setSubscriptionType); + map.from(properties::getName).to(containerProperties::setSubscriptionName); } private void customizePulsarContainerListenerProperties(PulsarContainerProperties containerProperties) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java index 9abf379a5198..f936a6c8afcd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapper.java @@ -88,6 +88,7 @@ private void customizePulsarContainerConsumerSubscriptionProperties( PulsarProperties.Consumer.Subscription properties = this.properties.getConsumer().getSubscription(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(properties::getType).to(containerProperties::setSubscriptionType); + map.from(properties::getName).to(containerProperties::setSubscriptionName); } private void customizePulsarContainerListenerProperties(ReactivePulsarContainerProperties containerProperties) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java index baab6c4078f4..353d78ea128d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java @@ -262,12 +262,14 @@ void customizeConsumerBuilder() { void customizeContainerProperties() { PulsarProperties properties = new PulsarProperties(); properties.getConsumer().getSubscription().setType(SubscriptionType.Shared); + properties.getConsumer().getSubscription().setName("my-subscription"); properties.getListener().setSchemaType(SchemaType.AVRO); properties.getListener().setObservationEnabled(true); properties.getTransaction().setEnabled(true); PulsarContainerProperties containerProperties = new PulsarContainerProperties("my-topic-pattern"); new PulsarPropertiesMapper(properties).customizeContainerProperties(containerProperties); assertThat(containerProperties.getSubscriptionType()).isEqualTo(SubscriptionType.Shared); + assertThat(containerProperties.getSubscriptionName()).isEqualTo("my-subscription"); assertThat(containerProperties.getSchemaType()).isEqualTo(SchemaType.AVRO); assertThat(containerProperties.isObservationEnabled()).isTrue(); assertThat(containerProperties.transactions().isEnabled()).isTrue(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java index b31de0290322..1c45f1aa9c09 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactivePropertiesMapperTests.java @@ -120,11 +120,13 @@ void customizeMessageConsumerBuilder() { void customizeContainerProperties() { PulsarProperties properties = new PulsarProperties(); properties.getConsumer().getSubscription().setType(SubscriptionType.Shared); + properties.getConsumer().getSubscription().setName("my-subscription"); properties.getListener().setSchemaType(SchemaType.AVRO); properties.getListener().setConcurrency(10); ReactivePulsarContainerProperties containerProperties = new ReactivePulsarContainerProperties<>(); new PulsarReactivePropertiesMapper(properties).customizeContainerProperties(containerProperties); assertThat(containerProperties.getSubscriptionType()).isEqualTo(SubscriptionType.Shared); + assertThat(containerProperties.getSubscriptionName()).isEqualTo("my-subscription"); assertThat(containerProperties.getSchemaType()).isEqualTo(SchemaType.AVRO); assertThat(containerProperties.getConcurrency()).isEqualTo(10); } From 4eba42f6ddb58445a41870b23fac4f2b15afb77f Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Wed, 4 Sep 2024 12:22:37 +0200 Subject: [PATCH 072/271] Improve Pulsar listener container concurrency configuration This is a follow-up to gh-42062 that utilizes newly introduced `concurrency` property in `PulsarContainerProperties` to simplify auto-configuration support for Pulsar listener container concurrency. See: https://github.com/spring-projects/spring-pulsar/issues/820 See gh-42120 --- .../pulsar/PulsarAutoConfiguration.java | 6 +----- .../pulsar/PulsarPropertiesMapper.java | 10 +--------- .../pulsar/PulsarPropertiesMapperTests.java | 15 ++------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java index 9d2f4d88d319..5b5f9dc41235 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java @@ -69,7 +69,6 @@ * @author Alexander Preuß * @author Phillip Webb * @author Jonas Geiregat - * @author Vedran Pavic * @since 3.2.0 */ @AutoConfiguration @@ -188,10 +187,7 @@ ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( } pulsarTransactionManager.ifUnique(containerProperties.transactions()::setTransactionManager); this.propertiesMapper.customizeContainerProperties(containerProperties); - ConcurrentPulsarListenerContainerFactory listenerContainerFactory = new ConcurrentPulsarListenerContainerFactory<>( - pulsarConsumerFactory, containerProperties); - this.propertiesMapper.customizeConcurrentPulsarListenerContainerFactory(listenerContainerFactory); - return listenerContainerFactory; + return new ConcurrentPulsarListenerContainerFactory<>(pulsarConsumerFactory, containerProperties); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index f645616892b5..cfe34eb6f8ba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -39,7 +39,6 @@ import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.json.JsonWriter; -import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.core.PulsarTemplate; import org.springframework.pulsar.listener.PulsarContainerProperties; import org.springframework.pulsar.reader.PulsarReaderContainerProperties; @@ -197,17 +196,10 @@ private void customizePulsarContainerListenerProperties(PulsarContainerPropertie PulsarProperties.Listener properties = this.properties.getListener(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(properties::getSchemaType).to(containerProperties::setSchemaType); + map.from(properties::getConcurrency).to(containerProperties::setConcurrency); map.from(properties::isObservationEnabled).to(containerProperties::setObservationEnabled); } - @SuppressWarnings("removal") - void customizeConcurrentPulsarListenerContainerFactory( - ConcurrentPulsarListenerContainerFactory listenerContainerFactory) { - PulsarProperties.Listener properties = this.properties.getListener(); - PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(properties::getConcurrency).to(listenerContainerFactory::setConcurrency); - } - void customizeReaderBuilder(ReaderBuilder readerBuilder) { PulsarProperties.Reader properties = this.properties.getReader(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java index 353d78ea128d..5e8ca006fcd2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapperTests.java @@ -41,7 +41,6 @@ import org.springframework.boot.autoconfigure.pulsar.PulsarProperties.Consumer; import org.springframework.boot.autoconfigure.pulsar.PulsarProperties.Failover.BackupCluster; -import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.core.PulsarProducerFactory; import org.springframework.pulsar.core.PulsarTemplate; import org.springframework.pulsar.listener.PulsarContainerProperties; @@ -264,6 +263,7 @@ void customizeContainerProperties() { properties.getConsumer().getSubscription().setType(SubscriptionType.Shared); properties.getConsumer().getSubscription().setName("my-subscription"); properties.getListener().setSchemaType(SchemaType.AVRO); + properties.getListener().setConcurrency(10); properties.getListener().setObservationEnabled(true); properties.getTransaction().setEnabled(true); PulsarContainerProperties containerProperties = new PulsarContainerProperties("my-topic-pattern"); @@ -271,22 +271,11 @@ void customizeContainerProperties() { assertThat(containerProperties.getSubscriptionType()).isEqualTo(SubscriptionType.Shared); assertThat(containerProperties.getSubscriptionName()).isEqualTo("my-subscription"); assertThat(containerProperties.getSchemaType()).isEqualTo(SchemaType.AVRO); + assertThat(containerProperties.getConcurrency()).isEqualTo(10); assertThat(containerProperties.isObservationEnabled()).isTrue(); assertThat(containerProperties.transactions().isEnabled()).isTrue(); } - @Test - @SuppressWarnings("removal") - void customizeConcurrentPulsarListenerContainerFactory() { - PulsarProperties properties = new PulsarProperties(); - properties.getListener().setConcurrency(10); - ConcurrentPulsarListenerContainerFactory listenerContainerFactory = mock( - ConcurrentPulsarListenerContainerFactory.class); - new PulsarPropertiesMapper(properties) - .customizeConcurrentPulsarListenerContainerFactory(listenerContainerFactory); - then(listenerContainerFactory).should().setConcurrency(10); - } - @Test @SuppressWarnings("unchecked") void customizeReaderBuilder() { From f7ba5f1bbcb8774eac634a8a3980c2a7be1efaa0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 5 Sep 2024 14:25:51 +0100 Subject: [PATCH 073/271] Polish support for Testcontainers Redis - Add support for RedisStackContainer - Update the docs Closes gh-41450 --- .../reference/pages/testing/testcontainers.adoc | 2 +- ...edisContainerConnectionDetailsFactoryTests.java | 14 +++++++++++++- .../RedisContainerConnectionDetailsFactory.java | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index abbe71ae7a89..5781b86b96a0 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -87,7 +87,7 @@ The following service connection factories are provided in the `spring-boot-test | Containers of type `RabbitMQContainer` | `RedisConnectionDetails` -| Containers named "redis", "redis/redis-stack" or "redis/redis-stack-server" +| Containers of type `com.redis.testcontainers.RedisContainer` or `com.redis.testcontainers.RedisStackContainer`, or containers named "redis", "redis/redis-stack" or "redis/redis-stack-server" | `ZipkinConnectionDetails` | Containers named "openzipkin/zipkin" diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java index af2d09016d4e..5d8dc04158fe 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/redis/CustomRedisContainerConnectionDetailsFactoryTests.java @@ -19,6 +19,7 @@ import java.util.Map; import com.redis.testcontainers.RedisContainer; +import com.redis.testcontainers.RedisStackContainer; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; @@ -32,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link RedisContainerConnectionDetailsFactory} when using a custom contain + * Test for {@link RedisContainerConnectionDetailsFactory} when using a custom container * without "redis" as the name. * * @author Phillip Webb @@ -50,4 +51,15 @@ void getConnectionDetailsWhenRedisContainerWithCustomName() { assertThat(connectionDetails.get(RedisConnectionDetails.class)).isNotNull(); } + @Test + void getConnectionDetailsWhenRedisStackContainerWithCustomName() { + ConnectionDetailsFactories factories = new ConnectionDetailsFactories(); + MergedAnnotation annotation = MergedAnnotation.of(ServiceConnection.class, + Map.of("value", "")); + ContainerConnectionSource source = TestContainerConnectionSource.create("test", null, + RedisStackContainer.class, "mycustomimage", annotation, null); + Map, ConnectionDetails> connectionDetails = factories.getConnectionDetails(source, true); + assertThat(connectionDetails.get(RedisConnectionDetails.class)).isNotNull(); + } + } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java index 9048b65f19cd..134824058dc2 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java @@ -19,6 +19,7 @@ import java.util.List; import com.redis.testcontainers.RedisContainer; +import com.redis.testcontainers.RedisStackContainer; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; @@ -53,7 +54,8 @@ class RedisContainerConnectionDetailsFactory protected boolean sourceAccepts(ContainerConnectionSource> source, Class requiredContainerType, Class requiredConnectionDetailsType) { return super.sourceAccepts(source, requiredContainerType, requiredConnectionDetailsType) - || source.accepts(ANY_CONNECTION_NAME, RedisContainer.class, requiredConnectionDetailsType); + || source.accepts(ANY_CONNECTION_NAME, RedisContainer.class, requiredConnectionDetailsType) + || source.accepts(ANY_CONNECTION_NAME, RedisStackContainer.class, requiredConnectionDetailsType); } @Override From 4261ed9c2b7fcf06472b2f535ca688b0943c6ca6 Mon Sep 17 00:00:00 2001 From: famaridon Date: Mon, 12 Aug 2024 09:59:26 +0200 Subject: [PATCH 074/271] Added documentation for configuring OpenTelemetry SDK logs See gh-41825 --- .../reference/pages/actuator/loggers.adoc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc index 3c6e0534acd9..8567b9c6730c 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc @@ -31,3 +31,23 @@ To configure a given logger, `POST` a partial entity to the resource's URI, as t ---- TIP: To "`reset`" the specific level of the logger (and use the default configuration instead), you can pass a value of `null` as the `configuredLevel`. + + + +[[actuator.loggers.opentelemetry]] +== OpenTelemetry +By default, the OpenTelemetry SDK logs are not configured. You can provide the location of the OpenTelemetry logs endpoint to configure it: + +[configprops,yaml] +---- +management: + otlp: + logging: + endpoint: "https://otlp.example.com:4318/v1/logs" +---- + +NOTE: The OpenTelemetry Logback appender and Log4j appender are not part of Spring Boot, for more details, see the https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/logback/logback-appender-1.0/library[OpenTelemetry Logback appender] or https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j/log4j-appender-2.17/library[OpenTelemetry Log4j2 appender] in the https://github.com/open-telemetry/opentelemetry-java-instrumentation[OpenTelemetry Java instrumentation GitHub repository] + +TIP: Ensure that you add the appender to your `logback.xml` or `logback-spring.xml` (or the equivalent configuration file for Log4j). + +TIP: The `OpenTelemetryAppender` requires access to an OpenTelemetry instance to function properly. This instance must be set programmatically during application startup by using an `ApplicationListener` for the `ApplicationReadyEvent`. From bc3bcd68d695288afdd08f00cdccedb6ff1d46a5 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 5 Sep 2024 15:16:00 +0200 Subject: [PATCH 075/271] Polish "Added documentation for configuring OpenTelemetry SDK logs" See gh-41825 --- .../spring-boot-docs/build.gradle | 1 + .../reference/pages/actuator/loggers.adoc | 13 +++++-- .../OpenTelemetryAppenderInitializer.java | 39 +++++++++++++++++++ .../spring-boot-parent/build.gradle | 7 ++++ 4 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/loggers/opentelemetry/OpenTelemetryAppenderInitializer.java diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 1d74605652d6..bcecb75f5535 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -83,6 +83,7 @@ dependencies { implementation("io.micrometer:micrometer-tracing") implementation("io.micrometer:micrometer-registry-graphite") implementation("io.micrometer:micrometer-registry-jmx") + implementation("io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0") implementation("io.projectreactor.netty:reactor-netty-http") implementation("io.undertow:undertow-core") implementation("jakarta.annotation:jakarta.annotation-api") diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc index 8567b9c6730c..7205f387ab03 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/loggers.adoc @@ -36,7 +36,8 @@ TIP: To "`reset`" the specific level of the logger (and use the default configur [[actuator.loggers.opentelemetry]] == OpenTelemetry -By default, the OpenTelemetry SDK logs are not configured. You can provide the location of the OpenTelemetry logs endpoint to configure it: +By default, logging via OpenTelemetry is not configured. +You have to provide the location of the OpenTelemetry logs endpoint to configure it: [configprops,yaml] ---- @@ -46,8 +47,12 @@ management: endpoint: "https://otlp.example.com:4318/v1/logs" ---- -NOTE: The OpenTelemetry Logback appender and Log4j appender are not part of Spring Boot, for more details, see the https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/logback/logback-appender-1.0/library[OpenTelemetry Logback appender] or https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j/log4j-appender-2.17/library[OpenTelemetry Log4j2 appender] in the https://github.com/open-telemetry/opentelemetry-java-instrumentation[OpenTelemetry Java instrumentation GitHub repository] +NOTE: The OpenTelemetry Logback appender and Log4j appender are not part of Spring Boot. +For more details, see the https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/logback/logback-appender-1.0/library[OpenTelemetry Logback appender] or the https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j/log4j-appender-2.17/library[OpenTelemetry Log4j2 appender] in the https://github.com/open-telemetry/opentelemetry-java-instrumentation[OpenTelemetry Java instrumentation GitHub repository]. -TIP: Ensure that you add the appender to your `logback.xml` or `logback-spring.xml` (or the equivalent configuration file for Log4j). +TIP: You have to configure the appender in your `logback-spring.xml` or `log4j2-spring.xml` configuration to get OpenTelemetry logging working. -TIP: The `OpenTelemetryAppender` requires access to an OpenTelemetry instance to function properly. This instance must be set programmatically during application startup by using an `ApplicationListener` for the `ApplicationReadyEvent`. +The `OpenTelemetryAppender` for both Logback and Log4j requires access to an `OpenTelemetry` instance to function properly. +This instance must be set programmatically during application startup, which can be done like this: + +include-code::OpenTelemetryAppenderInitializer[] diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/loggers/opentelemetry/OpenTelemetryAppenderInitializer.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/loggers/opentelemetry/OpenTelemetryAppenderInitializer.java new file mode 100644 index 000000000000..5a49979f38af --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/actuator/loggers/opentelemetry/OpenTelemetryAppenderInitializer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2024 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.actuator.loggers.opentelemetry; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; + +@Component +class OpenTelemetryAppenderInitializer implements InitializingBean { + + private final OpenTelemetry openTelemetry; + + OpenTelemetryAppenderInitializer(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + @Override + public void afterPropertiesSet() { + OpenTelemetryAppender.install(this.openTelemetry); + } + +} diff --git a/spring-boot-project/spring-boot-parent/build.gradle b/spring-boot-project/spring-boot-parent/build.gradle index 8803e7c7a78a..e44f757d2465 100644 --- a/spring-boot-project/spring-boot-parent/build.gradle +++ b/spring-boot-project/spring-boot-parent/build.gradle @@ -159,6 +159,13 @@ bom { ] } } + library("OpenTelemetry Logback Appender", "2.7.0-alpha") { + group("io.opentelemetry.instrumentation") { + modules = [ + "opentelemetry-logback-appender-1.0" + ] + } + } library("Plexus Build API", "0.0.7") { group("org.sonatype.plexus") { modules = [ From 2144a159f24d92b6afaac3e53ee4324a1a9d525a Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 08:21:38 +0200 Subject: [PATCH 076/271] Remove spring-web dependency from ZipkinHttpClientSender Closes gh-42160 --- .../tracing/zipkin/HttpSender.java | 17 +++++++++-------- .../tracing/zipkin/ZipkinHttpClientSender.java | 4 ++-- .../zipkin/ZipkinRestTemplateSender.java | 4 ++-- .../tracing/zipkin/ZipkinWebClientSender.java | 4 ++-- .../zipkin/ZipkinHttpClientSenderTests.java | 3 +++ 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java index ab0f677dbff1..2d0b6db80c6e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java @@ -27,7 +27,8 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.unit.DataSize; /** @@ -60,20 +61,20 @@ protected byte[] newBody(List list) { @Override protected void postSpans(URI endpoint, byte[] body) throws IOException { - HttpHeaders headers = getDefaultHeaders(); + MultiValueMap headers = getDefaultHeaders(); if (needsCompression(body)) { body = compress(body); - headers.set("Content-Encoding", "gzip"); + headers.add("Content-Encoding", "gzip"); } postSpans(endpoint, headers, body); } - abstract void postSpans(URI endpoint, HttpHeaders headers, byte[] body) throws IOException; + abstract void postSpans(URI endpoint, MultiValueMap headers, byte[] body) throws IOException; - HttpHeaders getDefaultHeaders() { - HttpHeaders headers = new HttpHeaders(); - headers.set("b3", "0"); - headers.set("Content-Type", this.encoding.mediaType()); + MultiValueMap getDefaultHeaders() { + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add("b3", "0"); + headers.add("Content-Type", this.encoding.mediaType()); return headers; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java index 2f982a54da20..8fa737f78c81 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java @@ -29,7 +29,7 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; /** * A {@link HttpSender} which uses the JDK {@link HttpClient} for HTTP communication. @@ -50,7 +50,7 @@ class ZipkinHttpClientSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) throws IOException { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) throws IOException { Builder request = HttpRequest.newBuilder() .POST(BodyPublishers.ofByteArray(body)) .uri(endpoint) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java index de66c6a8fc39..88bf8d5aec7d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java @@ -22,8 +22,8 @@ import zipkin2.reporter.HttpEndpointSupplier.Factory; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; /** @@ -45,7 +45,7 @@ class ZipkinRestTemplateSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) { HttpEntity request = new HttpEntity<>(body, headers); this.restTemplate.exchange(endpoint, HttpMethod.POST, request, Void.class); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java index 3f555defa1cb..8ded275a6dfb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java @@ -22,7 +22,7 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.WebClient; /** @@ -47,7 +47,7 @@ class ZipkinWebClientSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) { this.webClient.post() .uri(endpoint) .headers((h) -> h.addAll(headers)) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java index 56e11284d76f..4dbc69079291 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java @@ -35,6 +35,8 @@ import zipkin2.reporter.HttpEndpointSupplier; import zipkin2.reporter.HttpEndpointSuppliers; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatException; import static org.assertj.core.api.Assertions.assertThatIOException; @@ -44,6 +46,7 @@ * * @author Moritz Halbritter */ +@ClassPathExclusions("spring-web-*.jar") class ZipkinHttpClientSenderTests extends ZipkinHttpSenderTests { private MockWebServer mockBackEnd; From dc428e3fc993c4f3d061c62530d69118bd2d4f72 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 08:30:44 +0200 Subject: [PATCH 077/271] Fix deprecations for OpenTelemetry in tests --- .../opentelemetry/OpenTelemetryAutoConfigurationTests.java | 5 ++--- .../tracing/OpenTelemetryAutoConfigurationTests.java | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java index 6848c8003d16..14ab7b3c6380 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfigurationTests.java @@ -24,7 +24,6 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.semconv.ResourceAttributes; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -86,7 +85,7 @@ void shouldApplySpringApplicationNameToResource() { this.runner.withPropertyValues("spring.application.name=my-application").run((context) -> { Resource resource = context.getBean(Resource.class); assertThat(resource.getAttributes().asMap()) - .contains(entry(ResourceAttributes.SERVICE_NAME, "my-application")); + .contains(entry(AttributeKey.stringKey("service.name"), "my-application")); }); } @@ -112,7 +111,7 @@ void shouldFallbackToDefaultApplicationNameIfSpringApplicationNameIsNotSet() { this.runner.run((context) -> { Resource resource = context.getBean(Resource.class); assertThat(resource.getAttributes().asMap()) - .contains(entry(ResourceAttributes.SERVICE_NAME, "unknown_service")); + .contains(entry(AttributeKey.stringKey("service.name"), "unknown_service")); }); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java index f794d9b87206..7b1a04d4528d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java @@ -53,7 +53,6 @@ import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.semconv.ResourceAttributes; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -195,7 +194,7 @@ void shouldSetupDefaultResourceAttributes() { exporter.await(Duration.ofSeconds(10)); SpanData spanData = exporter.getExportedSpans().get(0); Map, Object> expectedAttributes = Resource.getDefault() - .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "unknown_service"))) + .merge(Resource.create(Attributes.of(AttributeKey.stringKey("service.name"), "unknown_service"))) .getAttributes() .asMap(); assertThat(spanData.getResource().getAttributes().asMap()).isEqualTo(expectedAttributes); From 1679a72b0ec07273725f00c2cdbc0b92701c2cac Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 08:21:38 +0200 Subject: [PATCH 078/271] Remove spring-web dependency from ZipkinHttpClientSender Closes gh-42161 --- .../tracing/zipkin/HttpSender.java | 17 +++++++++-------- .../tracing/zipkin/ZipkinHttpClientSender.java | 4 ++-- .../zipkin/ZipkinRestTemplateSender.java | 4 ++-- .../tracing/zipkin/ZipkinWebClientSender.java | 4 ++-- .../zipkin/ZipkinHttpClientSenderTests.java | 3 +++ 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java index ab0f677dbff1..2d0b6db80c6e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/HttpSender.java @@ -27,7 +27,8 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.unit.DataSize; /** @@ -60,20 +61,20 @@ protected byte[] newBody(List list) { @Override protected void postSpans(URI endpoint, byte[] body) throws IOException { - HttpHeaders headers = getDefaultHeaders(); + MultiValueMap headers = getDefaultHeaders(); if (needsCompression(body)) { body = compress(body); - headers.set("Content-Encoding", "gzip"); + headers.add("Content-Encoding", "gzip"); } postSpans(endpoint, headers, body); } - abstract void postSpans(URI endpoint, HttpHeaders headers, byte[] body) throws IOException; + abstract void postSpans(URI endpoint, MultiValueMap headers, byte[] body) throws IOException; - HttpHeaders getDefaultHeaders() { - HttpHeaders headers = new HttpHeaders(); - headers.set("b3", "0"); - headers.set("Content-Type", this.encoding.mediaType()); + MultiValueMap getDefaultHeaders() { + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add("b3", "0"); + headers.add("Content-Type", this.encoding.mediaType()); return headers; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java index 2f982a54da20..8fa737f78c81 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSender.java @@ -29,7 +29,7 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; /** * A {@link HttpSender} which uses the JDK {@link HttpClient} for HTTP communication. @@ -50,7 +50,7 @@ class ZipkinHttpClientSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) throws IOException { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) throws IOException { Builder request = HttpRequest.newBuilder() .POST(BodyPublishers.ofByteArray(body)) .uri(endpoint) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java index de66c6a8fc39..88bf8d5aec7d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateSender.java @@ -22,8 +22,8 @@ import zipkin2.reporter.HttpEndpointSupplier.Factory; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; /** @@ -45,7 +45,7 @@ class ZipkinRestTemplateSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) { HttpEntity request = new HttpEntity<>(body, headers); this.restTemplate.exchange(endpoint, HttpMethod.POST, request, Void.class); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java index 3f555defa1cb..8ded275a6dfb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientSender.java @@ -22,7 +22,7 @@ import zipkin2.reporter.Encoding; import zipkin2.reporter.HttpEndpointSupplier.Factory; -import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.WebClient; /** @@ -47,7 +47,7 @@ class ZipkinWebClientSender extends HttpSender { } @Override - void postSpans(URI endpoint, HttpHeaders headers, byte[] body) { + void postSpans(URI endpoint, MultiValueMap headers, byte[] body) { this.webClient.post() .uri(endpoint) .headers((h) -> h.addAll(headers)) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java index 56e11284d76f..4dbc69079291 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinHttpClientSenderTests.java @@ -35,6 +35,8 @@ import zipkin2.reporter.HttpEndpointSupplier; import zipkin2.reporter.HttpEndpointSuppliers; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatException; import static org.assertj.core.api.Assertions.assertThatIOException; @@ -44,6 +46,7 @@ * * @author Moritz Halbritter */ +@ClassPathExclusions("spring-web-*.jar") class ZipkinHttpClientSenderTests extends ZipkinHttpSenderTests { private MockWebServer mockBackEnd; From e39d9434eac391578f2cf1a4d2c5412da92c1681 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 09:18:35 +0200 Subject: [PATCH 079/271] Reapply "Merge pull request #41213 from timpeeters" This reverts commit 653443adc1e42a6ab32a88b886cf63f56c4db3fc. See gh-41213 --- .../build.gradle | 1 + .../tracing/otlp/OtlpAutoConfiguration.java | 6 +- .../tracing/otlp/OtlpProperties.java | 29 ++++- .../otlp/OtlpTracingConfigurations.java | 25 ++++- ...itional-spring-configuration-metadata.json | 4 + ...OtlpAutoConfigurationIntegrationTests.java | 105 +++++++++++++++++- .../otlp/OtlpAutoConfigurationTests.java | 15 +++ 7 files changed, 175 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index 649eb13a5ce3..b7a6e154bf6e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -161,6 +161,7 @@ dependencies { testImplementation("org.awaitility:awaitility") testImplementation("org.cache2k:cache2k-api") testImplementation("org.eclipse.jetty.ee10:jetty-ee10-webapp") + testImplementation("org.eclipse.jetty.http2:jetty-http2-server") testImplementation("org.glassfish.jersey.ext:jersey-spring6") testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson") testImplementation("org.hamcrest:hamcrest") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfiguration.java index 25a7f16ae7f8..3c495a1125b4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfiguration.java @@ -36,8 +36,10 @@ * the future, see: opentelemetry-java#3651. * Because this class configures components from the OTel SDK, it can't support HTTP/JSON. - * To keep things simple, we only auto-configure HTTP/protobuf. If you want to use gRPC, - * define an {@link OtlpGrpcSpanExporter} and this auto-configuration will back off. + * By default, we auto-configure HTTP/protobuf. If you want to use gRPC, you need to set + * {@code management.otlp.tracing.transport=grpc}. If you define a + * {@link OtlpHttpSpanExporter} or {@link OtlpGrpcSpanExporter}, this auto-configuration + * will back off. * * @author Jonatan Ivanov * @author Moritz Halbritter diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java index 7631740c08f0..16c16e67c086 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java @@ -44,6 +44,11 @@ public class OtlpProperties { */ private Duration timeout = Duration.ofSeconds(10); + /** + * Transport used to send the spans. + */ + private Transport transport = Transport.HTTP; + /** * Method used to compress the payload. */ @@ -70,6 +75,14 @@ public void setTimeout(Duration timeout) { this.timeout = timeout; } + public Transport getTransport() { + return this.transport; + } + + public void setTransport(Transport transport) { + this.transport = transport; + } + public Compression getCompression() { return this.compression; } @@ -86,7 +99,21 @@ public void setHeaders(Map headers) { this.headers = headers; } - enum Compression { + public enum Transport { + + /** + * HTTP transport. + */ + HTTP, + + /** + * gRPC transport. + */ + GRPC + + } + + public enum Compression { /** * Gzip compression. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java index 142696717172..f222fb249d5a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java @@ -20,6 +20,8 @@ import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -66,13 +68,14 @@ public String getUrl() { } @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingBean({ OtlpGrpcSpanExporter.class, OtlpHttpSpanExporter.class }) + @ConditionalOnBean(OtlpTracingConnectionDetails.class) + @ConditionalOnEnabledTracing("otlp") static class Exporters { @Bean - @ConditionalOnMissingBean(value = OtlpHttpSpanExporter.class, - type = "io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter") - @ConditionalOnBean(OtlpTracingConnectionDetails.class) - @ConditionalOnEnabledTracing("otlp") + @ConditionalOnProperty(prefix = "management.otlp.tracing", name = "transport", havingValue = "http", + matchIfMissing = true) OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, OtlpTracingConnectionDetails connectionDetails) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() @@ -85,6 +88,20 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, return builder.build(); } + @Bean + @ConditionalOnProperty(prefix = "management.otlp.tracing", name = "transport", havingValue = "grpc") + OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpProperties properties, + OtlpTracingConnectionDetails connectionDetails) { + OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() + .setEndpoint(connectionDetails.getUrl()) + .setTimeout(properties.getTimeout()) + .setCompression(properties.getCompression().name().toLowerCase()); + for (Entry header : properties.getHeaders().entrySet()) { + builder.addHeader(header.getKey(), header.getValue()); + } + return builder.build(); + } + } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 5304c1a536d8..f62c3cb21be0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -2058,6 +2058,10 @@ "type": "java.lang.Boolean", "description": "Whether auto-configuration of tracing is enabled to export OTLP traces." }, + { + "name": "management.otlp.tracing.transport", + "defaultValue": "http" + }, { "name": "management.server.add-application-context-header", "type": "java.lang.Boolean", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java index fd6b5397ebad..5bc38b90beff 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java @@ -16,12 +16,15 @@ package org.springframework.boot.actuate.autoconfigure.tracing.otlp; -import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import io.micrometer.tracing.Tracer; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.export.SpanExporter; import okhttp3.mockwebserver.MockResponse; @@ -29,6 +32,16 @@ import okhttp3.mockwebserver.RecordedRequest; import okio.Buffer; import okio.GzipSource; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.Callback; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,6 +49,7 @@ import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfigurationIntegrationTests.MockGrpcServer.RecordedGrpcRequest; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -57,14 +71,18 @@ class OtlpAutoConfigurationIntegrationTests { private final MockWebServer mockWebServer = new MockWebServer(); + private final MockGrpcServer mockGrpcServer = new MockGrpcServer(); + @BeforeEach - void setUp() throws IOException { + void startServers() throws Exception { this.mockWebServer.start(); + this.mockGrpcServer.start(); } @AfterEach - void tearDown() throws IOException { + void stopServers() throws Exception { this.mockWebServer.close(); + this.mockGrpcServer.close(); } @Test @@ -113,4 +131,85 @@ void httpSpanExporterCanBeConfiguredToUseGzipCompression() { }); } + @Test + void grpcSpanExporterShouldExportSpans() { + this.contextRunner + .withPropertyValues( + "management.otlp.tracing.endpoint=http://localhost:%d".formatted(this.mockGrpcServer.getPort()), + "management.otlp.tracing.headers.custom=42", "management.otlp.tracing.transport=grpc") + .run((context) -> { + context.getBean(Tracer.class).nextSpan().name("test").end(); + assertThat(context.getBean(OtlpGrpcSpanExporter.class).flush()) + .isSameAs(CompletableResultCode.ofSuccess()); + RecordedGrpcRequest request = this.mockGrpcServer.takeRequest(10, TimeUnit.SECONDS); + assertThat(request).isNotNull(); + assertThat(request.headers().get("Content-Type")).isEqualTo("application/grpc"); + assertThat(request.headers().get("custom")).isEqualTo("42"); + assertThat(request.bodyAsString()).contains("org.springframework.boot"); + }); + } + + static class MockGrpcServer { + + private final Server server = createServer(); + + private final BlockingQueue recordedRequests = new LinkedBlockingQueue<>(); + + void start() throws Exception { + this.server.start(); + } + + void close() throws Exception { + this.server.stop(); + } + + int getPort() { + return this.server.getURI().getPort(); + } + + RecordedGrpcRequest takeRequest(int timeout, TimeUnit unit) throws InterruptedException { + return this.recordedRequests.poll(timeout, unit); + } + + void recordRequest(RecordedGrpcRequest request) { + this.recordedRequests.add(request); + } + + private Server createServer() { + Server server = new Server(); + server.addConnector(createConnector(server)); + server.setHandler(new GrpcHandler()); + return server; + } + + private ServerConnector createConnector(Server server) { + ServerConnector connector = new ServerConnector(server, + new HTTP2CServerConnectionFactory(new HttpConfiguration())); + connector.setPort(0); + return connector; + } + + class GrpcHandler extends Handler.Abstract { + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception { + try (InputStream in = Content.Source.asInputStream(request)) { + recordRequest(new RecordedGrpcRequest(request.getHeaders(), in.readAllBytes())); + } + response.getHeaders().add("Content-Type", "application/grpc"); + response.getHeaders().add("Grpc-Status", "0"); + callback.succeeded(); + return true; + } + + } + + record RecordedGrpcRequest(HttpFields headers, byte[] body) { + String bodyAsString() { + return new String(this.body, StandardCharsets.UTF_8); + } + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java index eba8fe11251a..2f9259c6db71 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java @@ -51,6 +51,12 @@ void shouldNotSupplyBeansIfPropertyIsNotSet() { this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class)); } + @Test + void shouldNotSupplyBeansIfGrpcTransportIsEnabledButPropertyIsNotSet() { + this.contextRunner.withPropertyValues("management.otlp.tracing.transport=grpc") + .run((context) -> assertThat(context).doesNotHaveBean(OtlpGrpcSpanExporter.class)); + } + @Test void shouldSupplyBeans() { this.contextRunner.withPropertyValues("management.otlp.tracing.endpoint=http://localhost:4318/v1/traces") @@ -58,6 +64,15 @@ void shouldSupplyBeans() { .hasSingleBean(SpanExporter.class)); } + @Test + void shouldSupplyBeansIfGrpcTransportIsEnabled() { + this.contextRunner + .withPropertyValues("management.otlp.tracing.endpoint=http://localhost:4317/v1/traces", + "management.otlp.tracing.transport=grpc") + .run((context) -> assertThat(context).hasSingleBean(OtlpGrpcSpanExporter.class) + .hasSingleBean(SpanExporter.class)); + } + @Test void shouldNotSupplyBeansIfGlobalTracingIsDisabled() { this.contextRunner.withPropertyValues("management.tracing.enabled=false") From 9a81796e62322451103e3536c9d4decea156c6be Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 09:45:58 +0200 Subject: [PATCH 080/271] Polish "Reapply "Merge pull request #41213 from timpeeters"" See gh-41213 --- .../otlp/OtlpLoggingProperties.java | 15 +------- .../opentelemetry/otlp/Compression.java | 37 +++++++++++++++++++ .../opentelemetry/otlp/Transport.java | 37 +++++++++++++++++++ .../opentelemetry/otlp/package-info.java | 20 ++++++++++ .../tracing/otlp/OtlpProperties.java | 30 +-------------- ...itional-spring-configuration-metadata.json | 4 -- 6 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Compression.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Transport.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java index b7d484a9466a..c41d6d9a8355 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Compression; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -82,18 +83,4 @@ public Map getHeaders() { return this.headers; } - public enum Compression { - - /** - * Gzip compression. - */ - GZIP, - - /** - * No compression. - */ - NONE - - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Compression.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Compression.java new file mode 100644 index 000000000000..15183900d4b0 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Compression.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 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.actuate.autoconfigure.opentelemetry.otlp; + +/** + * Algorithm used to compress OTLP data. + * + * @author Moritz Halbritter + * @since 3.4.0 + */ +public enum Compression { + + /** + * Gzip compression. + */ + GZIP, + + /** + * No compression. + */ + NONE + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Transport.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Transport.java new file mode 100644 index 000000000000..443a263a393a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/Transport.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 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.actuate.autoconfigure.opentelemetry.otlp; + +/** + * Transport used to send OTLP data. + * + * @author Moritz Halbritter + * @since 3.4.0 + */ +public enum Transport { + + /** + * HTTP transport. + */ + HTTP, + + /** + * gRPC transport. + */ + GRPC + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java new file mode 100644 index 000000000000..519a94919dd6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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. + */ + +/** + * Classes for OpenTelemetry's OTLP. + */ +package org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java index 16c16e67c086..fe1ae7836af5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpProperties.java @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.Map; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Compression; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -99,32 +101,4 @@ public void setHeaders(Map headers) { this.headers = headers; } - public enum Transport { - - /** - * HTTP transport. - */ - HTTP, - - /** - * gRPC transport. - */ - GRPC - - } - - public enum Compression { - - /** - * Gzip compression. - */ - GZIP, - - /** - * No compression. - */ - NONE - - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index f62c3cb21be0..5304c1a536d8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -2058,10 +2058,6 @@ "type": "java.lang.Boolean", "description": "Whether auto-configuration of tracing is enabled to export OTLP traces." }, - { - "name": "management.otlp.tracing.transport", - "defaultValue": "http" - }, { "name": "management.server.add-application-context-header", "type": "java.lang.Boolean", From 7adf843bfdbf00e26bd50652743b23b7931d380d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 4 Jul 2024 19:27:08 -0600 Subject: [PATCH 081/271] Add service connection from Opentelemetry Collector for Logging Adds ConnectionDetails from Docker Compose and Testcontainers. See gh-41324 --- .../otlp/OtlpLoggingProperties.java | 2 +- ...nectionDetailsFactoryIntegrationTests.java | 38 +++++++++++ ...DockerComposeConnectionDetailsFactory.java | 65 +++++++++++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../pages/features/dev-services.adoc | 3 + .../pages/testing/testcontainers.adoc | 3 + ...nectionDetailsFactoryIntegrationTests.java | 63 ++++++++++++++++++ ...gingContainerConnectionDetailsFactory.java | 63 ++++++++++++++++++ .../main/resources/META-INF/spring.factories | 1 + 9 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java index c41d6d9a8355..ac25ebef7b80 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java @@ -35,7 +35,7 @@ public class OtlpLoggingProperties { /** * URL to the OTel collector's HTTP API. */ - private String endpoint; + private String endpoint = "http://localhost:4318/v1/logs"; /** * Call timeout for the OTel Collector to process an exported batch of data. This diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 000000000000..239fdb0f5687 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2024 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.docker.compose.service.connection.otlp; + +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.testsupport.container.TestImage; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for + * {@link OpenTelemetryLoggingDockerComposeConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +class OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests { + + @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY) + void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) { + assertThat(connectionDetails.getEndpoint()).startsWith("http://").endsWith("/v1/logs"); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java new file mode 100644 index 000000000000..5a92fb199e1a --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2024 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.docker.compose.service.connection.otlp; + +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +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; + +/** + * {@link DockerComposeConnectionDetailsFactory} to create + * {@link OtlpLoggingConnectionDetails} for an OTLP service. + * + * @author Eddú Meléndez + */ +class OpenTelemetryLoggingDockerComposeConnectionDetailsFactory + extends DockerComposeConnectionDetailsFactory { + + private static final int OTLP_PORT = 4318; + + OpenTelemetryLoggingDockerComposeConnectionDetailsFactory() { + super("otel/opentelemetry-collector-contrib", + "org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration"); + } + + @Override + protected OtlpLoggingConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { + return new OpenTelemetryLoggingDockerComposeConnectionDetails(source.getRunningService()); + } + + private static final class OpenTelemetryLoggingDockerComposeConnectionDetails extends DockerComposeConnectionDetails + implements OtlpLoggingConnectionDetails { + + private final String host; + + private final int port; + + private OpenTelemetryLoggingDockerComposeConnectionDetails(RunningService source) { + super(source); + this.host = source.host(); + this.port = source.ports().get(OTLP_PORT); + } + + @Override + public String getEndpoint() { + return "http://%s:%d/v1/logs".formatted(this.host, this.port); + } + + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories index d5b70a5f7cc3..a532e257d139 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/spring.factories @@ -23,6 +23,7 @@ org.springframework.boot.docker.compose.service.connection.oracle.OracleFreeJdbc org.springframework.boot.docker.compose.service.connection.oracle.OracleXeJdbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.oracle.OracleFreeR2dbcDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.oracle.OracleXeR2dbcDockerComposeConnectionDetailsFactory,\ +org.springframework.boot.docker.compose.service.connection.otlp.OpenTelemetryLoggingDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.otlp.OpenTelemetryMetricsDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.otlp.OpenTelemetryTracingDockerComposeConnectionDetailsFactory,\ org.springframework.boot.docker.compose.service.connection.postgres.PostgresJdbcDockerComposeConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc index fe59b9d944fb..0eea9b29bdc7 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc @@ -107,6 +107,9 @@ The following service connections are currently supported: | `Neo4jConnectionDetails` | Containers named "neo4j" or "bitnami/neo4j" +| `OtlpLoggingConnectionDetails` +| Containers named "otel/opentelemetry-collector-contrib" + | `OtlpMetricsConnectionDetails` | Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index 5781b86b96a0..fd795b4f8775 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -71,6 +71,9 @@ The following service connection factories are provided in the `spring-boot-test | `Neo4jConnectionDetails` | Containers of type `Neo4jContainer` +| `OtlpLoggingConnectionDetails` +| Containers named "otel/opentelemetry-collector-contrib" + | `OtlpMetricsConnectionDetails` | Containers named "otel/opentelemetry-collector-contrib" or of type `LgtmStackContainer` diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 000000000000..6ded3b289472 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection.otlp; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link OpenTelemetryLoggingContainerConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +class OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final GenericContainer container = TestImage.OPENTELEMETRY.genericContainer().withExposedPorts(4318); + + @Autowired + private OtlpLoggingConnectionDetails connectionDetails; + + @Test + void connectionCanBeMadeToOpenTelemetryContainer() { + assertThat(this.connectionDetails.getEndpoint()) + .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/logs"); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(OtlpAutoConfiguration.class) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java new file mode 100644 index 000000000000..502a9655e64f --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection.otlp; + +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create + * {@link OtlpLoggingConnectionDetails} from a + * {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using + * the {@code "otel/opentelemetry-collector-contrib"} image. + * + * @author Eddú Meléndez + */ +class OpenTelemetryLoggingContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory, OtlpLoggingConnectionDetails> { + + OpenTelemetryLoggingContainerConnectionDetailsFactory() { + super("otel/opentelemetry-collector-contrib", + "org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration"); + } + + @Override + protected OtlpLoggingConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new OpenTelemetryLoggingContainerConnectionDetails(source); + } + + private static final class OpenTelemetryLoggingContainerConnectionDetails + extends ContainerConnectionDetails> implements OtlpLoggingConnectionDetails { + + private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource> source) { + super(source); + } + + @Override + public String getEndpoint() { + return "http://%s:%d/v1/logs".formatted(getContainer().getHost(), getContainer().getMappedPort(4318)); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index 6e222c0012b0..23f293f975e2 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -25,6 +25,7 @@ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerC org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryLoggingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryMetricsContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryTracingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.pulsar.PulsarContainerConnectionDetailsFactory,\ From 8fc1bca56c1de9202b7df75fa6e7f13c1b3f97e0 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 10:20:15 +0200 Subject: [PATCH 082/271] Polish "Add service connection from Opentelemetry Collector for Logging" See gh-41324 --- .../logging/opentelemetry/otlp/OtlpLoggingProperties.java | 2 +- ...ngDockerComposeConnectionDetailsFactoryIntegrationTests.java | 2 +- ...enTelemetryLoggingDockerComposeConnectionDetailsFactory.java | 2 +- ...oggingContainerConnectionDetailsFactoryIntegrationTests.java | 2 +- .../OpenTelemetryLoggingContainerConnectionDetailsFactory.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java index ac25ebef7b80..c41d6d9a8355 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingProperties.java @@ -35,7 +35,7 @@ public class OtlpLoggingProperties { /** * URL to the OTel collector's HTTP API. */ - private String endpoint = "http://localhost:4318/v1/logs"; + private String endpoint; /** * Call timeout for the OTel Collector to process an exported batch of data. This diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java index 239fdb0f5687..623c5497535b 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -32,7 +32,7 @@ class OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY) void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) { - assertThat(connectionDetails.getEndpoint()).startsWith("http://").endsWith("/v1/logs"); + assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/logs"); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java index 5a92fb199e1a..6b09c90c23ba 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java @@ -56,7 +56,7 @@ private OpenTelemetryLoggingDockerComposeConnectionDetails(RunningService source } @Override - public String getEndpoint() { + public String getUrl() { return "http://%s:%d/v1/logs".formatted(this.host, this.port); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java index 6ded3b289472..d1cd7f1ed807 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java @@ -50,7 +50,7 @@ class OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests { @Test void connectionCanBeMadeToOpenTelemetryContainer() { - assertThat(this.connectionDetails.getEndpoint()) + assertThat(this.connectionDetails.getUrl()) .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/logs"); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java index 502a9655e64f..cd84f6d1ea7a 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java @@ -54,7 +54,7 @@ private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource } @Override - public String getEndpoint() { + public String getUrl() { return "http://%s:%d/v1/logs".formatted(getContainer().getHost(), getContainer().getMappedPort(4318)); } From 81c903cde71adb10f5882e0549cdb154c4babf72 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Thu, 29 Feb 2024 16:06:51 +0800 Subject: [PATCH 083/271] Add config prop for Spring Data Web's serialization mode See gh-39797 --- .../web/SpringDataWebAutoConfiguration.java | 10 +++- .../data/web/SpringDataWebProperties.java | 17 ++++++- .../SpringDataWebAutoConfigurationTests.java | 49 +++++++++++++++++-- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfiguration.java index 584ec7010daf..1193e0eeaee3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -31,6 +31,7 @@ import org.springframework.data.web.config.EnableSpringDataWebSupport; import org.springframework.data.web.config.PageableHandlerMethodArgumentResolverCustomizer; import org.springframework.data.web.config.SortHandlerMethodArgumentResolverCustomizer; +import org.springframework.data.web.config.SpringDataWebSettings; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** @@ -42,6 +43,7 @@ * * @author Andy Wilkinson * @author Vedran Pavic + * @author Yanming Zhou * @since 1.2.0 */ @AutoConfiguration(after = RepositoryRestMvcAutoConfiguration.class) @@ -79,4 +81,10 @@ public SortHandlerMethodArgumentResolverCustomizer sortCustomizer() { return (resolver) -> resolver.setSortParameter(this.properties.getSort().getSortParameter()); } + @Bean + @ConditionalOnMissingBean + public SpringDataWebSettings springDataWebSettings() { + return new SpringDataWebSettings(this.properties.getPageable().getSerializationMode()); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebProperties.java index a736f404036e..529ebd2c67db 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -17,11 +17,13 @@ package org.springframework.boot.autoconfigure.data.web; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode; /** * Configuration properties for Spring Data Web. * * @author Vedran Pavic + * @author Yanming Zhou * @since 2.0.0 */ @ConfigurationProperties("spring.data.web") @@ -81,6 +83,11 @@ public static class Pageable { */ private int maxPageSize = 2000; + /** + * Configures how to render spring data Pageable instances. + */ + private PageSerializationMode serializationMode = PageSerializationMode.DIRECT; + public String getPageParameter() { return this.pageParameter; } @@ -137,6 +144,14 @@ public void setMaxPageSize(int maxPageSize) { this.maxPageSize = maxPageSize; } + public PageSerializationMode getSerializationMode() { + return this.serializationMode; + } + + public void setSerializationMode(PageSerializationMode serializationMode) { + this.serializationMode = serializationMode; + } + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java index 67cf7109ca3c..e3a828e7863f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -21,9 +21,13 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.PageRequest; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.data.web.SortHandlerMethodArgumentResolver; +import org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode; +import org.springframework.data.web.config.SpringDataWebSettings; import static org.assertj.core.api.Assertions.assertThat; @@ -33,6 +37,7 @@ * @author Andy Wilkinson * @author Vedran Pavic * @author Stephane Nicoll + * @author Yanming Zhou */ class SpringDataWebAutoConfigurationTests { @@ -53,13 +58,16 @@ void autoConfigurationBacksOffInNonWebApplicationContexts() { @Test void customizePageable() { - this.contextRunner.withPropertyValues("spring.data.web.pageable.page-parameter=p", - "spring.data.web.pageable.size-parameter=s", "spring.data.web.pageable.default-page-size=10", - "spring.data.web.pageable.prefix=abc", "spring.data.web.pageable.qualifier-delimiter=__", - "spring.data.web.pageable.max-page-size=100", "spring.data.web.pageable.one-indexed-parameters=true") + this.contextRunner + .withPropertyValues("spring.data.web.pageable.page-parameter=p", + "spring.data.web.pageable.size-parameter=s", "spring.data.web.pageable.default-page-size=10", + "spring.data.web.pageable.prefix=abc", "spring.data.web.pageable.qualifier-delimiter=__", + "spring.data.web.pageable.max-page-size=100", "spring.data.web.pageable.serialization-mode=VIA_DTO", + "spring.data.web.pageable.one-indexed-parameters=true") .run((context) -> { PageableHandlerMethodArgumentResolver argumentResolver = context .getBean(PageableHandlerMethodArgumentResolver.class); + SpringDataWebSettings springDataWebSettings = context.getBean(SpringDataWebSettings.class); assertThat(argumentResolver).hasFieldOrPropertyWithValue("pageParameterName", "p"); assertThat(argumentResolver).hasFieldOrPropertyWithValue("sizeParameterName", "s"); assertThat(argumentResolver).hasFieldOrPropertyWithValue("oneIndexedParameters", true); @@ -67,6 +75,7 @@ void customizePageable() { assertThat(argumentResolver).hasFieldOrPropertyWithValue("qualifierDelimiter", "__"); assertThat(argumentResolver).hasFieldOrPropertyWithValue("fallbackPageable", PageRequest.of(0, 10)); assertThat(argumentResolver).hasFieldOrPropertyWithValue("maxPageSize", 100); + assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(PageSerializationMode.VIA_DTO); }); } @@ -76,6 +85,7 @@ void defaultPageable() { SpringDataWebProperties.Pageable properties = new SpringDataWebProperties().getPageable(); PageableHandlerMethodArgumentResolver argumentResolver = context .getBean(PageableHandlerMethodArgumentResolver.class); + SpringDataWebSettings springDataWebSettings = context.getBean(SpringDataWebSettings.class); assertThat(argumentResolver).hasFieldOrPropertyWithValue("pageParameterName", properties.getPageParameter()); assertThat(argumentResolver).hasFieldOrPropertyWithValue("sizeParameterName", @@ -88,6 +98,7 @@ void defaultPageable() { assertThat(argumentResolver).hasFieldOrPropertyWithValue("fallbackPageable", PageRequest.of(0, properties.getDefaultPageSize())); assertThat(argumentResolver).hasFieldOrPropertyWithValue("maxPageSize", properties.getMaxPageSize()); + assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(properties.getSerializationMode()); }); } @@ -100,4 +111,32 @@ void customizeSort() { }); } + @Test + void customizePageSerializationModeViaConfigProps() { + this.contextRunner.withPropertyValues("spring.data.web.pageable.serialization-mode=VIA_DTO").run((context) -> { + SpringDataWebSettings springDataWebSettings = context.getBean(SpringDataWebSettings.class); + assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(PageSerializationMode.VIA_DTO); + }); + } + + @Test + void customizePageSerializationModeViaCustomBean() { + this.contextRunner.withUserConfiguration(AppConfiguration.class) + .withPropertyValues("spring.data.web.pageable.serialization-mode=VIA_DTO") + .run((context) -> { + SpringDataWebSettings springDataWebSettings = context.getBean(SpringDataWebSettings.class); + assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(PageSerializationMode.DIRECT); + }); + } + + @Configuration + static class AppConfiguration { + + @Bean + SpringDataWebSettings springDataWebSettings() { + return new SpringDataWebSettings(PageSerializationMode.DIRECT); + } + + } + } From ac4c24e450ab66924e2d8d3bfd972a0bb9692262 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Sep 2024 09:40:11 +0100 Subject: [PATCH 084/271] Polish "Add config prop for Spring Data Web's serialization mode" See gh-39797 --- .../SpringDataWebAutoConfigurationTests.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java index e3a828e7863f..d3a1e92406ff 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/web/SpringDataWebAutoConfigurationTests.java @@ -21,8 +21,6 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.PageRequest; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.data.web.SortHandlerMethodArgumentResolver; @@ -121,22 +119,14 @@ void customizePageSerializationModeViaConfigProps() { @Test void customizePageSerializationModeViaCustomBean() { - this.contextRunner.withUserConfiguration(AppConfiguration.class) - .withPropertyValues("spring.data.web.pageable.serialization-mode=VIA_DTO") + this.contextRunner + .withBean("customSpringDataWebSettings", SpringDataWebSettings.class, + () -> new SpringDataWebSettings(PageSerializationMode.VIA_DTO)) .run((context) -> { + assertThat(context).doesNotHaveBean("springDataWebSettings"); SpringDataWebSettings springDataWebSettings = context.getBean(SpringDataWebSettings.class); - assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(PageSerializationMode.DIRECT); + assertThat(springDataWebSettings.pageSerializationMode()).isEqualTo(PageSerializationMode.VIA_DTO); }); } - @Configuration - static class AppConfiguration { - - @Bean - SpringDataWebSettings springDataWebSettings() { - return new SpringDataWebSettings(PageSerializationMode.DIRECT); - } - - } - } From 4f576031bc9bf0a6c3f4e476b1a1fe20ad63a511 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Sep 2024 09:52:10 +0100 Subject: [PATCH 085/271] Polish configuration property reference Closes gh-42162 --- .../spring-boot-docs/src/docs/asciidoc/features/aop.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/aop.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/aop.adoc index ad7dcc6c4f7b..05c8b85c2187 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/aop.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/aop.adoc @@ -4,6 +4,6 @@ Spring Boot provides auto-configuration for aspect-oriented programming (AOP). You can learn more about AOP with Spring in the {spring-framework-docs}/core/aop-api.html[Spring Framework reference documentation]. By default, Spring Boot's auto-configuration configures Spring AOP to use CGLib proxies. -To use JDK proxies instead, set `configprop:spring.aop.proxy-target-class` to `false`. +To use JDK proxies instead, set configprop:spring.aop.proxy-target-class[] to `false`. If AspectJ is on the classpath, Spring Boot's auto-configuration will automatically enable AspectJ auto proxy such that `@EnableAspectJAutoProxy` is not required. From f8130791ea92c68dd73a9b69f1abbd18fdb6f7a8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Sep 2024 10:17:20 +0100 Subject: [PATCH 086/271] Update docs to reflect new no handler found exception behavior Closes gh-42164 --- .../spring-boot-docs/src/docs/asciidoc/web/servlet.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc index 0b63e82d3f1a..1c4f6219b3aa 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc @@ -285,10 +285,10 @@ The strategy can be configured using the configprop:spring.mvc.pathmatch.matchin matching-strategy: "ant-path-matcher" ---- -By default, Spring MVC will send a 404 Not Found error response if a handler is not found for a request. -To have a `NoHandlerFoundException` thrown instead, set configprop:spring.mvc.throw-exception-if-no-handler-found to `true`. +Spring MVC will throw a `NoHandlerFoundException` if a handler is not found for a request. Note that, by default, the <> is mapped to `+/**+` and will, therefore, provide a handler for all requests. -For a `NoHandlerFoundException` to be thrown, you must also set configprop:spring.mvc.static-path-pattern[] to a more specific value such as `/resources/**` or set configprop:spring.web.resources.add-mappings[] to `false` to disable serving of static content entirely. +If no static content is available, `ResourceHttpRequestHandler` will throw a `NoResourceFoundException`. +For a `NoHandlerFoundException` to be thrown, set configprop:spring.mvc.static-path-pattern[] to a more specific value such as `/resources/**` or set configprop:spring.web.resources.add-mappings[] to `false` to disable serving of static content entirely. From 3651ff87cd665ff61326d1a36ef1ec401732a5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 19 Mar 2024 14:17:11 -0500 Subject: [PATCH 087/271] Reinstate auto-configuration support for embedded ActiveMQ This commit restores auto-configuration for using an Embedded broker with ActiveMQ classic. Contrary to its 2.7.x version, "spring-boot-starter-activemq" no longer adds the broker for consistency with Artemis, and to keep the existing 3.x behavior. Rather than "inMemory", a "s.a.embedded.enabled" property has been reintroduced that matches the name used by Artemis. The documentation has been updated to mention that the broker dependency must be added to use it. Closes gh-38404 --- .../build.gradle | 1 + .../spring-boot-autoconfigure/build.gradle | 1 + .../jms/activemq/ActiveMQProperties.java | 33 ++++++++++++- .../ActiveMQAutoConfigurationTests.java | 6 +-- .../jms/activemq/ActiveMQPropertiesTests.java | 19 +++++++- .../reference/pages/messaging/jms.adoc | 19 +++++++- .../build.gradle | 14 ++++++ .../smoketest/activemq/embedded/Consumer.java | 30 ++++++++++++ .../smoketest/activemq/embedded/Producer.java | 45 +++++++++++++++++ .../embedded/SampleActiveMQApplication.java | 40 ++++++++++++++++ .../src/main/resources/application.properties | 0 .../SampleActiveMQApplicationTests.java | 48 +++++++++++++++++++ 12 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/build.gradle create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Consumer.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Producer.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/SampleActiveMQApplication.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/resources/application.properties create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/test/java/smoketest/activemq/embedded/SampleActiveMQApplicationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index b7a6e154bf6e..6ae4cf452814 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -77,6 +77,7 @@ dependencies { optional("jakarta.persistence:jakarta.persistence-api") optional("jakarta.servlet:jakarta.servlet-api") optional("javax.cache:cache-api") + optional("org.apache.activemq:activemq-broker") optional("org.apache.activemq:activemq-client") optional("org.apache.commons:commons-dbcp2") { exclude group: "commons-logging", module: "commons-logging" diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index 509d39ee012a..094b81160da2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -77,6 +77,7 @@ dependencies { optional("jakarta.ws.rs:jakarta.ws.rs-api") optional("javax.cache:cache-api") optional("javax.money:money-api") + optional("org.apache.activemq:activemq-broker") optional("org.apache.activemq:activemq-client") optional("org.apache.activemq:artemis-jakarta-client") { exclude group: "commons-logging", module: "commons-logging" diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java index 2877479a08e6..9c43a6b29945 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -37,6 +37,8 @@ @ConfigurationProperties(prefix = "spring.activemq") public class ActiveMQProperties { + private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false"; + private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616"; /** @@ -54,6 +56,8 @@ public class ActiveMQProperties { */ private String password; + private final Embedded embedded = new Embedded(); + /** * Time to wait before considering a close complete. */ @@ -99,6 +103,10 @@ public void setPassword(String password) { this.password = password; } + public Embedded getEmbedded() { + return this.embedded; + } + public Duration getCloseTimeout() { return this.closeTimeout; } @@ -135,9 +143,32 @@ String determineBrokerUrl() { if (this.brokerUrl != null) { return this.brokerUrl; } + if (this.embedded.isEnabled()) { + return DEFAULT_EMBEDDED_BROKER_URL; + } return DEFAULT_NETWORK_BROKER_URL; } + /** + * Configuration for an embedded ActiveMQ broker. + */ + public static class Embedded { + + /** + * Whether to enable embedded mode if the ActiveMQ Broker is available. + */ + private boolean enabled = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + public static class Packages { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java index 2815e3e2ff50..b6f31fbf7784 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -48,14 +48,14 @@ class ActiveMQAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class, JmsAutoConfiguration.class)); @Test - void brokerIsLocalhostByDefault() { + void brokerIsEmbeddedByDefault() { this.contextRunner.withUserConfiguration(EmptyConfiguration.class).run((context) -> { assertThat(context).hasSingleBean(CachingConnectionFactory.class).hasBean("jmsConnectionFactory"); CachingConnectionFactory connectionFactory = context.getBean(CachingConnectionFactory.class); assertThat(context.getBean("jmsConnectionFactory")).isSameAs(connectionFactory); assertThat(connectionFactory.getTargetConnectionFactory()).isInstanceOf(ActiveMQConnectionFactory.class); assertThat(((ActiveMQConnectionFactory) connectionFactory.getTargetConnectionFactory()).getBrokerURL()) - .isEqualTo("tcp://localhost:61616"); + .isEqualTo("vm://localhost?broker.persistent=false"); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQPropertiesTests.java index 3c4ee27987c1..78c45042ef68 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQPropertiesTests.java @@ -31,13 +31,15 @@ */ class ActiveMQPropertiesTests { + private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false"; + private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616"; private final ActiveMQProperties properties = new ActiveMQProperties(); @Test - void getBrokerUrlIsLocalhostByDefault() { - assertThat(this.properties.determineBrokerUrl()).isEqualTo(DEFAULT_NETWORK_BROKER_URL); + void getBrokerUrlIsEmbeddedByDefault() { + assertThat(this.properties.determineBrokerUrl()).isEqualTo(DEFAULT_EMBEDDED_BROKER_URL); } @Test @@ -46,6 +48,19 @@ void getBrokerUrlUseExplicitBrokerUrl() { assertThat(this.properties.determineBrokerUrl()).isEqualTo("tcp://activemq.example.com:71717"); } + @Test + void getBrokerUrlWithEmbeddedSetToFalse() { + this.properties.getEmbedded().setEnabled(false); + assertThat(this.properties.determineBrokerUrl()).isEqualTo(DEFAULT_NETWORK_BROKER_URL); + } + + @Test + void getExplicitBrokerUrlAlwaysWins() { + this.properties.setBrokerUrl("tcp://activemq.example.com:71717"); + this.properties.getEmbedded().setEnabled(false); + assertThat(this.properties.determineBrokerUrl()).isEqualTo("tcp://activemq.example.com:71717"); + } + @Test void setTrustAllPackages() { ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/jms.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/jms.adoc index f66a18d01257..0b7910318b92 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/jms.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/jms.adoc @@ -12,11 +12,26 @@ Spring Boot also auto-configures the necessary infrastructure to send and receiv == ActiveMQ "Classic" Support When https://activemq.apache.org/components/classic[ActiveMQ "Classic"] is available on the classpath, Spring Boot can configure a `ConnectionFactory`. +If the broker is present, an embedded broker is automatically started and configured (provided no broker URL is specified through configuration and the embedded broker is not disabled in the configuration). NOTE: If you use `spring-boot-starter-activemq`, the necessary dependencies to connect to an ActiveMQ "Classic" instance are provided, as is the Spring infrastructure to integrate with JMS. +Adding `org.apache.activemq:activemq-broker` to your application lets you use the embedded broker. ActiveMQ "Classic" configuration is controlled by external configuration properties in `+spring.activemq.*+`. -By default, ActiveMQ "Classic" is auto-configured to use the https://activemq.apache.org/tcp-transport-reference[TCP transport], connecting by default to `tcp://localhost:61616`. The following example shows how to change the default broker URL: + +If `activemq-broker` is on the classpath, ActiveMQ "Classic" is auto-configured to use the https://activemq.apache.org/vm-transport-reference.html[VM transport], which starts a broker embedded in the same JVM instance. + +You can disable the embedded broker by configuring the configprop:spring.activemq.embedded.enabled[] property, as shown in the following example: + +[configprops,yaml] +---- +spring: + activemq: + embedded: + enabled: false +---- + +The embedded broker will also be disabled if you configure the broker URL, as shown in the following example: [configprops,yaml] ---- @@ -27,6 +42,8 @@ spring: password: "secret" ---- +If you want to take full control over the embedded broker, see https://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection.html[the ActiveMQ "Classic" documentation] for further information. + By default, a `CachingConnectionFactory` wraps the native `ConnectionFactory` with sensible settings that you can control by external configuration properties in `+spring.jms.*+`: [configprops,yaml] diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/build.gradle new file mode 100644 index 000000000000..c6dd37d2d52e --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/build.gradle @@ -0,0 +1,14 @@ +plugins { + id "java" + id "org.springframework.boot.conventions" +} + +description = "Spring Boot Actuator ActiveMQ Embedded smoke test" + +dependencies { + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-activemq")) + + runtimeOnly("org.apache.activemq:activemq-broker") + + testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) +} \ No newline at end of file diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Consumer.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Consumer.java new file mode 100644 index 000000000000..66cc31e048eb --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Consumer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-2024 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.activemq.embedded; + +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +@Component +public class Consumer { + + @JmsListener(destination = "sample.queue") + public void receiveQueue(String text) { + System.out.println(text); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Producer.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Producer.java new file mode 100644 index 000000000000..ddeacda378e1 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/Producer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2024 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.activemq.embedded; + +import jakarta.jms.Queue; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.jms.core.JmsMessagingTemplate; +import org.springframework.stereotype.Component; + +@Component +public class Producer implements CommandLineRunner { + + @Autowired + private JmsMessagingTemplate jmsMessagingTemplate; + + @Autowired + private Queue queue; + + @Override + public void run(String... args) throws Exception { + send("Sample message"); + System.out.println("Message was sent to the Queue"); + } + + public void send(String msg) { + this.jmsMessagingTemplate.convertAndSend(this.queue, msg); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/SampleActiveMQApplication.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/SampleActiveMQApplication.java new file mode 100644 index 000000000000..6890cce28f65 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/java/smoketest/activemq/embedded/SampleActiveMQApplication.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2024 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.activemq.embedded; + +import jakarta.jms.Queue; +import org.apache.activemq.command.ActiveMQQueue; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.jms.annotation.EnableJms; + +@SpringBootApplication +@EnableJms +public class SampleActiveMQApplication { + + @Bean + public Queue queue() { + return new ActiveMQQueue("sample.queue"); + } + + public static void main(String[] args) { + SpringApplication.run(SampleActiveMQApplication.class, args); + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/resources/application.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/main/resources/application.properties new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/test/java/smoketest/activemq/embedded/SampleActiveMQApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/test/java/smoketest/activemq/embedded/SampleActiveMQApplicationTests.java new file mode 100644 index 000000000000..76f0e6f56188 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-activemq-embedded/src/test/java/smoketest/activemq/embedded/SampleActiveMQApplicationTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2024 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.activemq.embedded; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for ActiveMQ smoke test with embedded broker. + * + * @author Stephane Nicoll + */ +@SpringBootTest +@ExtendWith(OutputCaptureExtension.class) +class SampleActiveMQApplicationTests { + + @Autowired + private Producer producer; + + @Test + void sendSimpleMessage(CapturedOutput output) throws InterruptedException { + this.producer.send("Test message"); + Thread.sleep(1000L); + assertThat(output).contains("Test message"); + } + +} From a89ae3fbee647a8d83578c2317544c12a3391869 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 Sep 2024 11:30:28 +0100 Subject: [PATCH 088/271] Improve laziness of Pem and JKS SSL store bundles Fixes gh-42119 --- .../boot/ssl/jks/JksSslStoreBundle.java | 20 +++++---- .../boot/ssl/pem/LoadedPemSslStore.java | 12 +++++- .../boot/ssl/pem/PemSslStoreBundle.java | 29 +++++++------ .../boot/ssl/jks/JksSslStoreBundleTests.java | 42 ++++++++++--------- .../boot/ssl/pem/LoadedPemSslStoreTests.java | 18 +++++++- .../boot/ssl/pem/PemSslStoreBundleTests.java | 18 +++++++- 6 files changed, 97 insertions(+), 42 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/jks/JksSslStoreBundle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/jks/JksSslStoreBundle.java index 8f475f1c8337..adcffa654e63 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/jks/JksSslStoreBundle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/jks/JksSslStoreBundle.java @@ -24,12 +24,14 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; +import java.util.function.Supplier; import org.springframework.boot.ssl.SslStoreBundle; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; +import org.springframework.util.function.SingletonSupplier; /** * {@link SslStoreBundle} backed by a Java keystore. @@ -43,9 +45,9 @@ public class JksSslStoreBundle implements SslStoreBundle { private final JksSslStoreDetails keyStoreDetails; - private final KeyStore keyStore; + private final Supplier keyStore; - private final KeyStore trustStore; + private final Supplier trustStore; /** * Create a new {@link JksSslStoreBundle} instance. @@ -54,13 +56,13 @@ public class JksSslStoreBundle implements SslStoreBundle { */ public JksSslStoreBundle(JksSslStoreDetails keyStoreDetails, JksSslStoreDetails trustStoreDetails) { this.keyStoreDetails = keyStoreDetails; - this.keyStore = createKeyStore("key", this.keyStoreDetails); - this.trustStore = createKeyStore("trust", trustStoreDetails); + this.keyStore = SingletonSupplier.of(() -> createKeyStore("key", this.keyStoreDetails)); + this.trustStore = SingletonSupplier.of(() -> createKeyStore("trust", trustStoreDetails)); } @Override public KeyStore getKeyStore() { - return this.keyStore; + return this.keyStore.get(); } @Override @@ -70,7 +72,7 @@ public String getKeyStorePassword() { @Override public KeyStore getTrustStore() { - return this.trustStore; + return this.trustStore.get(); } private KeyStore createKeyStore(String name, JksSslStoreDetails details) { @@ -127,10 +129,12 @@ private void loadKeyStore(KeyStore store, String location, char[] password) { @Override public String toString() { ToStringCreator creator = new ToStringCreator(this); - creator.append("keyStore.type", (this.keyStore != null) ? this.keyStore.getType() : "none"); + KeyStore keyStore = this.keyStore.get(); + creator.append("keyStore.type", (keyStore != null) ? keyStore.getType() : "none"); String keyStorePassword = getKeyStorePassword(); creator.append("keyStorePassword", (keyStorePassword != null) ? "******" : null); - creator.append("trustStore.type", (this.trustStore != null) ? this.trustStore.getType() : "none"); + KeyStore trustStore = this.trustStore.get(); + creator.append("trustStore.type", (trustStore != null) ? trustStore.getType() : "none"); return creator.toString(); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/LoadedPemSslStore.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/LoadedPemSslStore.java index 5edacd360e61..5f001421f57b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/LoadedPemSslStore.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/LoadedPemSslStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -97,4 +97,14 @@ public PrivateKey privateKey() { return this.privateKeySupplier.get(); } + @Override + public PemSslStore withAlias(String alias) { + return new LoadedPemSslStore(this.details.withAlias(alias)); + } + + @Override + public PemSslStore withPassword(String password) { + return new LoadedPemSslStore(this.details.withPassword(password)); + } + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemSslStoreBundle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemSslStoreBundle.java index f8f5eda84ef5..76f8731f8a8e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemSslStoreBundle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/pem/PemSslStoreBundle.java @@ -24,11 +24,13 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; +import java.util.function.Supplier; import org.springframework.boot.ssl.SslStoreBundle; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.util.function.SingletonSupplier; /** * {@link SslStoreBundle} backed by PEM-encoded certificates and private keys. @@ -42,9 +44,9 @@ public class PemSslStoreBundle implements SslStoreBundle { private static final String DEFAULT_ALIAS = "ssl"; - private final KeyStore keyStore; + private final Supplier keyStore; - private final KeyStore trustStore; + private final Supplier trustStore; /** * Create a new {@link PemSslStoreBundle} instance. @@ -66,8 +68,9 @@ public PemSslStoreBundle(PemSslStoreDetails keyStoreDetails, PemSslStoreDetails */ @Deprecated(since = "3.2.0", forRemoval = true) public PemSslStoreBundle(PemSslStoreDetails keyStoreDetails, PemSslStoreDetails trustStoreDetails, String alias) { - this.keyStore = createKeyStore("key", PemSslStore.load(keyStoreDetails), alias); - this.trustStore = createKeyStore("trust", PemSslStore.load(trustStoreDetails), alias); + this.keyStore = SingletonSupplier.of(() -> createKeyStore("key", PemSslStore.load(keyStoreDetails), alias)); + this.trustStore = SingletonSupplier + .of(() -> createKeyStore("trust", PemSslStore.load(trustStoreDetails), alias)); } /** @@ -81,13 +84,13 @@ public PemSslStoreBundle(PemSslStore pemKeyStore, PemSslStore pemTrustStore) { } private PemSslStoreBundle(PemSslStore pemKeyStore, PemSslStore pemTrustStore, String alias) { - this.keyStore = createKeyStore("key", pemKeyStore, alias); - this.trustStore = createKeyStore("trust", pemTrustStore, alias); + this.keyStore = SingletonSupplier.of(() -> createKeyStore("key", pemKeyStore, alias)); + this.trustStore = SingletonSupplier.of(() -> createKeyStore("trust", pemTrustStore, alias)); } @Override public KeyStore getKeyStore() { - return this.keyStore; + return this.keyStore.get(); } @Override @@ -97,7 +100,7 @@ public String getKeyStorePassword() { @Override public KeyStore getTrustStore() { - return this.trustStore; + return this.trustStore.get(); } private static KeyStore createKeyStore(String name, PemSslStore pemSslStore, String alias) { @@ -105,11 +108,11 @@ private static KeyStore createKeyStore(String name, PemSslStore pemSslStore, Str return null; } try { - Assert.notEmpty(pemSslStore.certificates(), "Certificates must not be empty"); + List certificates = pemSslStore.certificates(); + Assert.notEmpty(certificates, "Certificates must not be empty"); alias = (pemSslStore.alias() != null) ? pemSslStore.alias() : alias; alias = (alias != null) ? alias : DEFAULT_ALIAS; KeyStore store = createKeyStore(pemSslStore.type()); - List certificates = pemSslStore.certificates(); PrivateKey privateKey = pemSslStore.privateKey(); if (privateKey != null) { addPrivateKey(store, privateKey, alias, pemSslStore.password(), certificates); @@ -149,9 +152,11 @@ private static void addCertificates(KeyStore keyStore, List cer @Override public String toString() { ToStringCreator creator = new ToStringCreator(this); - creator.append("keyStore.type", (this.keyStore != null) ? this.keyStore.getType() : "none"); + KeyStore keyStore = this.keyStore.get(); + KeyStore trustStore = this.trustStore.get(); + creator.append("keyStore.type", (keyStore != null) ? keyStore.getType() : "none"); creator.append("keyStorePassword", null); - creator.append("trustStore.type", (this.trustStore != null) ? this.trustStore.getType() : "none"); + creator.append("trustStore.type", (trustStore != null) ? trustStore.getType() : "none"); return creator.toString(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/jks/JksSslStoreBundleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/jks/JksSslStoreBundleTests.java index 159e98c45654..bb57ec607cb7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/jks/JksSslStoreBundleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/jks/JksSslStoreBundleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -58,12 +58,10 @@ void whenStoresHaveNoValues() { } @Test - void whenTypePKCS11AndLocationThrowsException() { - assertThatIllegalStateException().isThrownBy(() -> { - JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails("PKCS11", null, "test.jks", null); - JksSslStoreDetails trustStoreDetails = null; - new JksSslStoreBundle(keyStoreDetails, trustStoreDetails); - }) + void whenTypePKCS11AndLocationGetKeyStoreThrowsException() { + JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails("PKCS11", null, "test.jks", null); + JksSslStoreBundle jksSslStoreBundle = new JksSslStoreBundle(keyStoreDetails, null); + assertThatIllegalStateException().isThrownBy(jksSslStoreBundle::getKeyStore) .withMessageContaining( "Unable to create key store: Location is 'test.jks', but must be empty or null for PKCS11 hardware key stores"); } @@ -104,22 +102,28 @@ void whenHasTrustStoreType() { @Test void whenHasKeyStoreProvider() { - assertThatIllegalStateException().isThrownBy(() -> { - JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails(null, "com.example.KeyStoreProvider", - "classpath:test.jks", "secret"); - JksSslStoreDetails trustStoreDetails = null; - new JksSslStoreBundle(keyStoreDetails, trustStoreDetails); - }).withMessageContaining("com.example.KeyStoreProvider"); + JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails(null, "com.example.KeyStoreProvider", + "classpath:test.jks", "secret"); + JksSslStoreBundle jksSslStoreBundle = new JksSslStoreBundle(keyStoreDetails, null); + assertThatIllegalStateException().isThrownBy(jksSslStoreBundle::getKeyStore) + .withMessageContaining("com.example.KeyStoreProvider"); } @Test void whenHasTrustStoreProvider() { - assertThatIllegalStateException().isThrownBy(() -> { - JksSslStoreDetails keyStoreDetails = null; - JksSslStoreDetails trustStoreDetails = new JksSslStoreDetails(null, "com.example.KeyStoreProvider", - "classpath:test.jks", "secret"); - new JksSslStoreBundle(keyStoreDetails, trustStoreDetails); - }).withMessageContaining("com.example.KeyStoreProvider"); + JksSslStoreDetails trustStoreDetails = new JksSslStoreDetails(null, "com.example.KeyStoreProvider", + "classpath:test.jks", "secret"); + JksSslStoreBundle jksSslStoreBundle = new JksSslStoreBundle(null, trustStoreDetails); + assertThatIllegalStateException().isThrownBy(jksSslStoreBundle::getTrustStore) + .withMessageContaining("com.example.KeyStoreProvider"); + } + + @Test + void storeCreationIsLazy() { + JksSslStoreDetails details = new JksSslStoreDetails(null, null, "does-not-exist", null); + JksSslStoreBundle bundle = new JksSslStoreBundle(details, details); + assertThatIllegalStateException().isThrownBy(bundle::getKeyStore); + assertThatIllegalStateException().isThrownBy(bundle::getTrustStore); } private Consumer storeContainingCertAndKey(String keyAlias, String keyPassword) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/LoadedPemSslStoreTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/LoadedPemSslStoreTests.java index b7bccce838a5..f78b777921cf 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/LoadedPemSslStoreTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/LoadedPemSslStoreTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -45,4 +45,20 @@ void privateKeyIsLoadedLazily() { assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(store::privateKey); } + @Test + void withAliasIsLazy() { + PemSslStoreDetails details = PemSslStoreDetails.forCertificate("classpath:missing-test-cert.pem") + .withPrivateKey("classpath:test-key.pem"); + PemSslStore store = new LoadedPemSslStore(details).withAlias("alias"); + assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(store::certificates); + } + + @Test + void withPasswordIsLazy() { + PemSslStoreDetails details = PemSslStoreDetails.forCertificate("classpath:missing-test-cert.pem") + .withPrivateKey("classpath:test-key.pem"); + PemSslStore store = new LoadedPemSslStore(details).withPassword("password"); + assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(store::certificates); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/PemSslStoreBundleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/PemSslStoreBundleTests.java index b34901931062..fd78a1e711e6 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/PemSslStoreBundleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/pem/PemSslStoreBundleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -27,6 +27,10 @@ import org.springframework.util.function.ThrowingConsumer; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; /** * Tests for {@link PemSslStoreBundle}. @@ -215,6 +219,18 @@ void createWithPemSslStoreCreatesInstance() { assertThat(bundle.getTrustStore()).satisfies(storeContainingCertAndKey("ssl")); } + @Test + void storeCreationIsLazy() { + PemSslStore pemSslStore = mock(PemSslStore.class); + PemSslStoreBundle bundle = new PemSslStoreBundle(pemSslStore, pemSslStore); + given(pemSslStore.certificates()).willReturn(PemContent.of(CERTIFICATE).getCertificates()); + then(pemSslStore).shouldHaveNoInteractions(); + bundle.getKeyStore(); + then(pemSslStore).should().certificates(); + bundle.getTrustStore(); + then(pemSslStore).should(times(2)).certificates(); + } + private Consumer storeContainingCert(String keyAlias) { return storeContainingCert(KeyStore.getDefaultType(), keyAlias); } From 7baa5537601ff5ae02b078c39fde704ca8bbe8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 5 Jul 2024 09:43:41 -0600 Subject: [PATCH 089/271] Support Otlp Tracing's GRPC port from service connections Otlp Tracing's exporter is configured using Transport. Current support for service connections read the mapped port for HTTP transport 4318. This commits adds support to read port for GRPC transport 4317. See gh-41333 --- .../otlp/OtlpTracingConfigurations.java | 15 +++++++++++++-- .../otlp/OtlpTracingConnectionDetails.java | 6 +++++- ...nnectionDetailsFactoryIntegrationTests.java | 1 + .../service/connection/otlp/otlp-compose.yaml | 1 + ...gDockerComposeConnectionDetailsFactory.java | 18 ++++++++++++++---- ...nnectionDetailsFactoryIntegrationTests.java | 5 ++++- ...acingContainerConnectionDetailsFactory.java | 15 +++++++++++++-- 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java index f222fb249d5a..597206f49061 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java @@ -23,6 +23,7 @@ import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -34,6 +35,7 @@ * Configurations imported by {@link OtlpAutoConfiguration}. * * @author Moritz Halbritter + * @author Eddú Meléndez */ class OtlpTracingConfigurations { @@ -63,6 +65,11 @@ public String getUrl() { return this.properties.getEndpoint(); } + @Override + public String getGrpcEndpoint() { + return this.properties.getEndpoint(); + } + } } @@ -79,7 +86,7 @@ static class Exporters { OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, OtlpTracingConnectionDetails connectionDetails) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() - .setEndpoint(connectionDetails.getUrl()) + .setEndpoint(resolveEndpoint(properties.getTransport(), connectionDetails)) .setTimeout(properties.getTimeout()) .setCompression(properties.getCompression().name().toLowerCase()); for (Entry header : properties.getHeaders().entrySet()) { @@ -93,7 +100,7 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpProperties properties, OtlpTracingConnectionDetails connectionDetails) { OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() - .setEndpoint(connectionDetails.getUrl()) + .setEndpoint(resolveEndpoint(properties.getTransport(), connectionDetails)) .setTimeout(properties.getTimeout()) .setCompression(properties.getCompression().name().toLowerCase()); for (Entry header : properties.getHeaders().entrySet()) { @@ -102,6 +109,10 @@ OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpProperties properties, return builder.build(); } + private static String resolveEndpoint(Transport transport, OtlpTracingConnectionDetails connectionDetails) { + return (transport == Transport.HTTP) ? connectionDetails.getUrl() : connectionDetails.getGrpcEndpoint(); + } + } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java index a84b11d64da3..b1eeb28b196e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -32,4 +32,8 @@ public interface OtlpTracingConnectionDetails extends ConnectionDetails { */ String getUrl(); + default String getGrpcEndpoint() { + return "http://localhost:4317/v1/traces"; + } + } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java index 50a713e7fdbc..6b9304a37d5c 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -32,6 +32,7 @@ class OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY) void runCreatesConnectionDetails(OtlpTracingConnectionDetails connectionDetails) { + assertThat(connectionDetails.getGrpcEndpoint()).startsWith("http://").endsWith("/v1/traces"); assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/traces"); } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/otlp/otlp-compose.yaml b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/otlp/otlp-compose.yaml index 258e73e333ee..86e05475417d 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/otlp/otlp-compose.yaml +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/service/connection/otlp/otlp-compose.yaml @@ -2,4 +2,5 @@ services: otlp: image: '{imageName}' ports: + - '4317' - '4318' diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java index d2be0b11eecb..3c0ad3a32b5e 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java @@ -33,7 +33,9 @@ class OpenTelemetryTracingDockerComposeConnectionDetailsFactory private static final String[] OPENTELEMETRY_IMAGE_NAMES = { "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" }; - private static final int OTLP_PORT = 4318; + private static final int OTLP_GRPC_PORT = 4317; + + private static final int OTLP_HTTP_PORT = 4318; OpenTelemetryTracingDockerComposeConnectionDetailsFactory() { super(OPENTELEMETRY_IMAGE_NAMES, @@ -50,17 +52,25 @@ private static final class OpenTelemetryTracingDockerComposeConnectionDetails ex private final String host; - private final int port; + private final int grpcPort; + + private final int httPort; private OpenTelemetryTracingDockerComposeConnectionDetails(RunningService source) { super(source); this.host = source.host(); - this.port = source.ports().get(OTLP_PORT); + this.grpcPort = source.ports().get(OTLP_GRPC_PORT); + this.httPort = source.ports().get(OTLP_HTTP_PORT); } @Override public String getUrl() { - return "http://%s:%d/v1/traces".formatted(this.host, this.port); + return "http://%s:%d/v1/traces".formatted(this.host, this.httPort); + } + + @Override + public String getGrpcEndpoint() { + return "http://%s:%d/v1/traces".formatted(this.host, this.grpcPort); } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java index 6d8760f1faaf..e4c6b4ec115c 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java @@ -43,13 +43,16 @@ class OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests { @Container @ServiceConnection - static final GenericContainer container = TestImage.OPENTELEMETRY.genericContainer().withExposedPorts(4318); + static final GenericContainer container = TestImage.OPENTELEMETRY.genericContainer() + .withExposedPorts(4317, 4318); @Autowired private OtlpTracingConnectionDetails connectionDetails; @Test void connectionCanBeMadeToOpenTelemetryContainer() { + assertThat(this.connectionDetails.getGrpcEndpoint()) + .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4317) + "/v1/traces"); assertThat(this.connectionDetails.getUrl()) .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/traces"); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java index 6c3e72ac797c..61cf174fcb3f 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -35,6 +35,10 @@ class OpenTelemetryTracingContainerConnectionDetailsFactory extends ContainerConnectionDetailsFactory, OtlpTracingConnectionDetails> { + private static final int OTLP_GRPC_PORT = 4317; + + private static final int OTLP_HTTP_PORT = 4318; + OpenTelemetryTracingContainerConnectionDetailsFactory() { super("otel/opentelemetry-collector-contrib", "org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration"); @@ -55,7 +59,14 @@ private OpenTelemetryTracingContainerConnectionDetails(ContainerConnectionSource @Override public String getUrl() { - return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), getContainer().getMappedPort(4318)); + return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), + getContainer().getMappedPort(OTLP_HTTP_PORT)); + } + + @Override + public String getGrpcEndpoint() { + return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), + getContainer().getMappedPort(OTLP_GRPC_PORT)); } } From bac330354bc7b0d5c2bfd1941884806e44eeb207 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 11:59:05 +0200 Subject: [PATCH 090/271] Polish "Support Otlp Tracing's GRPC port from service connections" See gh-41333 --- .../otlp/OtlpTracingConfigurations.java | 19 +++++++------------ .../otlp/OtlpTracingConnectionDetails.java | 18 ++++++++++++++---- .../otlp/OtlpAutoConfigurationTests.java | 2 +- ...nectionDetailsFactoryIntegrationTests.java | 4 +++- ...nectionDetailsFactoryIntegrationTests.java | 5 +++-- ...DockerComposeConnectionDetailsFactory.java | 15 ++++++++------- ...nectionDetailsFactoryIntegrationTests.java | 6 +++++- ...nectionDetailsFactoryIntegrationTests.java | 7 ++++--- ...cingContainerConnectionDetailsFactory.java | 9 +++++++-- ...cingContainerConnectionDetailsFactory.java | 16 +++++++--------- 10 files changed, 59 insertions(+), 42 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java index 597206f49061..38d4befd5b48 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java @@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.Assert; /** * Configurations imported by {@link OtlpAutoConfiguration}. @@ -61,12 +62,10 @@ static class PropertiesOtlpTracingConnectionDetails implements OtlpTracingConnec } @Override - public String getUrl() { - return this.properties.getEndpoint(); - } - - @Override - public String getGrpcEndpoint() { + public String getUrl(Transport transport) { + Assert.state(transport == this.properties.getTransport(), + "Requested transport %s doesn't match configured transport %s".formatted(transport, + this.properties.getTransport())); return this.properties.getEndpoint(); } @@ -86,7 +85,7 @@ static class Exporters { OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, OtlpTracingConnectionDetails connectionDetails) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() - .setEndpoint(resolveEndpoint(properties.getTransport(), connectionDetails)) + .setEndpoint(connectionDetails.getUrl(Transport.HTTP)) .setTimeout(properties.getTimeout()) .setCompression(properties.getCompression().name().toLowerCase()); for (Entry header : properties.getHeaders().entrySet()) { @@ -100,7 +99,7 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpProperties properties, OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpProperties properties, OtlpTracingConnectionDetails connectionDetails) { OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() - .setEndpoint(resolveEndpoint(properties.getTransport(), connectionDetails)) + .setEndpoint(connectionDetails.getUrl(Transport.GRPC)) .setTimeout(properties.getTimeout()) .setCompression(properties.getCompression().name().toLowerCase()); for (Entry header : properties.getHeaders().entrySet()) { @@ -109,10 +108,6 @@ OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpProperties properties, return builder.build(); } - private static String resolveEndpoint(Transport transport, OtlpTracingConnectionDetails connectionDetails) { - return (transport == Transport.HTTP) ? connectionDetails.getUrl() : connectionDetails.getGrpcEndpoint(); - } - } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java index b1eeb28b196e..2b556c60d70f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConnectionDetails.java @@ -16,12 +16,14 @@ package org.springframework.boot.actuate.autoconfigure.tracing.otlp; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; /** * Details required to establish a connection to an OpenTelemetry service. * * @author Eddú Meléndez + * @author Moritz Halbritter * @since 3.2.0 */ public interface OtlpTracingConnectionDetails extends ConnectionDetails { @@ -29,11 +31,19 @@ public interface OtlpTracingConnectionDetails extends ConnectionDetails { /** * Address to where tracing will be published. * @return the address to where tracing will be published + * @deprecated since 3.4.0 for removal in 3.6.0 in favor of {@link #getUrl(Transport)} */ - String getUrl(); - - default String getGrpcEndpoint() { - return "http://localhost:4317/v1/traces"; + @Deprecated(since = "3.4.0", forRemoval = true) + default String getUrl() { + return getUrl(Transport.HTTP); } + /** + * Address to where tracing will be published. + * @param transport the transport to use + * @return the address to where tracing will be published + * @since 3.4.0 + */ + String getUrl(Transport transport); + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java index 2f9259c6db71..921c20aa3f73 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationTests.java @@ -172,7 +172,7 @@ static class ConnectionDetailsConfiguration { @Bean OtlpTracingConnectionDetails otlpTracingConnectionDetails() { - return () -> "http://localhost:12345/v1/traces"; + return (transport) -> "http://localhost:12345/v1/traces"; } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java index f5158cef6d67..6852fc717eb8 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.docker.compose.service.connection.otlp; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; import org.springframework.boot.testsupport.container.TestImage; @@ -32,7 +33,8 @@ class GrafanaOpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegratio @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM) void runCreatesConnectionDetails(OtlpTracingConnectionDetails connectionDetails) { - assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/traces"); + assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/traces"); + assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/traces"); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java index 6b9304a37d5c..9b7f7932533e 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.docker.compose.service.connection.otlp; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; import org.springframework.boot.testsupport.container.TestImage; @@ -32,8 +33,8 @@ class OpenTelemetryTracingDockerComposeConnectionDetailsFactoryIntegrationTests @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY) void runCreatesConnectionDetails(OtlpTracingConnectionDetails connectionDetails) { - assertThat(connectionDetails.getGrpcEndpoint()).startsWith("http://").endsWith("/v1/traces"); - assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/traces"); + assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/traces"); + assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/traces"); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java index 3c0ad3a32b5e..7608ebb284c1 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryTracingDockerComposeConnectionDetailsFactory.java @@ -16,6 +16,7 @@ package org.springframework.boot.docker.compose.service.connection.otlp; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; @@ -26,6 +27,7 @@ * {@link OtlpTracingConnectionDetails} for an OTLP service. * * @author Eddú Meléndez + * @author Moritz Halbritter */ class OpenTelemetryTracingDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { @@ -64,13 +66,12 @@ private OpenTelemetryTracingDockerComposeConnectionDetails(RunningService source } @Override - public String getUrl() { - return "http://%s:%d/v1/traces".formatted(this.host, this.httPort); - } - - @Override - public String getGrpcEndpoint() { - return "http://%s:%d/v1/traces".formatted(this.host, this.grpcPort); + public String getUrl(Transport transport) { + int port = switch (transport) { + case HTTP -> this.httPort; + case GRPC -> this.grpcPort; + }; + return "http://%s:%d/v1/traces".formatted(this.host, port); } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java index c5139145665e..a68d72ddf4db 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java @@ -22,6 +22,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -50,7 +51,10 @@ class GrafanaOpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTes @Test void connectionCanBeMadeToOpenTelemetryContainer() { - assertThat(this.connectionDetails.getUrl()).isEqualTo("%s/v1/traces".formatted(container.getOtlpHttpUrl())); + assertThat(this.connectionDetails.getUrl(Transport.HTTP)) + .isEqualTo("%s/v1/traces".formatted(container.getOtlpHttpUrl())); + assertThat(this.connectionDetails.getUrl(Transport.GRPC)) + .isEqualTo("%s/v1/traces".formatted(container.getOtlpGrpcUrl())); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java index e4c6b4ec115c..d2ef73fd20fa 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests.java @@ -22,6 +22,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -51,10 +52,10 @@ class OpenTelemetryTracingContainerConnectionDetailsFactoryIntegrationTests { @Test void connectionCanBeMadeToOpenTelemetryContainer() { - assertThat(this.connectionDetails.getGrpcEndpoint()) - .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4317) + "/v1/traces"); - assertThat(this.connectionDetails.getUrl()) + assertThat(this.connectionDetails.getUrl(Transport.HTTP)) .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/traces"); + assertThat(this.connectionDetails.getUrl(Transport.GRPC)) + .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4317) + "/v1/traces"); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory.java index eb03927598a1..1a45c35e3563 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory.java @@ -18,6 +18,7 @@ import org.testcontainers.grafana.LgtmStackContainer; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; @@ -52,8 +53,12 @@ private OpenTelemetryTracingContainerConnectionDetails(ContainerConnectionSource } @Override - public String getUrl() { - return "%s/v1/traces".formatted(getContainer().getOtlpHttpUrl()); + public String getUrl(Transport transport) { + String url = switch (transport) { + case HTTP -> getContainer().getOtlpHttpUrl(); + case GRPC -> getContainer().getOtlpGrpcUrl(); + }; + return "%s/v1/traces".formatted(url); } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java index 61cf174fcb3f..c76f577a0db6 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryTracingContainerConnectionDetailsFactory.java @@ -19,6 +19,7 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingConnectionDetails; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; @@ -58,15 +59,12 @@ private OpenTelemetryTracingContainerConnectionDetails(ContainerConnectionSource } @Override - public String getUrl() { - return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), - getContainer().getMappedPort(OTLP_HTTP_PORT)); - } - - @Override - public String getGrpcEndpoint() { - return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), - getContainer().getMappedPort(OTLP_GRPC_PORT)); + public String getUrl(Transport transport) { + int port = switch (transport) { + case HTTP -> OTLP_HTTP_PORT; + case GRPC -> OTLP_GRPC_PORT; + }; + return "http://%s:%d/v1/traces".formatted(getContainer().getHost(), getContainer().getMappedPort(port)); } } From 861e5209efee4989d4c90e4f760202da8ad522e3 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 13:36:29 +0200 Subject: [PATCH 091/271] Add transport selection to OtlpLoggingConnectionDetails Closes gh-42171 --- .../otlp/OtlpLoggingConfigurations.java | 5 +++-- .../otlp/OtlpLoggingConnectionDetails.java | 4 +++- .../OtlpLoggingAutoConfigurationTests.java | 5 +++-- ...nectionDetailsFactoryIntegrationTests.java | 4 +++- ...DockerComposeConnectionDetailsFactory.java | 20 ++++++++++++++----- ...nectionDetailsFactoryIntegrationTests.java | 8 ++++++-- ...gingContainerConnectionDetailsFactory.java | 14 +++++++++++-- 7 files changed, 45 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConfigurations.java index b2ebb1a273c1..28e35ee3717c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConfigurations.java @@ -21,6 +21,7 @@ import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -59,7 +60,7 @@ static class PropertiesOtlpLoggingConnectionDetails implements OtlpLoggingConnec } @Override - public String getUrl() { + public String getUrl(Transport transport) { return this.properties.getEndpoint(); } @@ -77,7 +78,7 @@ static class Exporters { OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties properties, OtlpLoggingConnectionDetails connectionDetails) { OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder() - .setEndpoint(connectionDetails.getUrl()) + .setEndpoint(connectionDetails.getUrl(Transport.HTTP)) .setCompression(properties.getCompression().name().toLowerCase(Locale.US)) .setTimeout(properties.getTimeout()); properties.getHeaders().forEach(builder::addHeader); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConnectionDetails.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConnectionDetails.java index 58289d9cfd80..72146180282a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConnectionDetails.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingConnectionDetails.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; /** @@ -28,8 +29,9 @@ public interface OtlpLoggingConnectionDetails extends ConnectionDetails { /** * Address to where logs will be published. + * @param transport the transport to use * @return the address to where logs will be published */ - String getUrl(); + String getUrl(Transport transport); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingAutoConfigurationTests.java index f7974b5da4fb..25040b4e0466 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/logging/opentelemetry/otlp/OtlpLoggingAutoConfigurationTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConfigurations.ConnectionDetails.PropertiesOtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -57,7 +58,7 @@ void shouldSupplyBeans() { .run((context) -> { assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class); OtlpLoggingConnectionDetails connectionDetails = context.getBean(OtlpLoggingConnectionDetails.class); - assertThat(connectionDetails.getUrl()).isEqualTo("http://localhost:4318/v1/logs"); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/logs"); assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class) .hasSingleBean(LogRecordExporter.class); }); @@ -124,7 +125,7 @@ public static class CustomOtlpLogsConnectionDetails { @Bean public OtlpLoggingConnectionDetails customOtlpLogsConnectionDetails() { - return () -> "https://otel.example.com/v1/logs"; + return (transport) -> "https://otel.example.com/v1/logs"; } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java index 623c5497535b..786082b133d6 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.docker.compose.service.connection.otlp; import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; import org.springframework.boot.testsupport.container.TestImage; @@ -32,7 +33,8 @@ class OpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.OPENTELEMETRY) void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) { - assertThat(connectionDetails.getUrl()).startsWith("http://").endsWith("/v1/logs"); + assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs"); + assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs"); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java index 6b09c90c23ba..df4f0da1c32e 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java @@ -17,6 +17,7 @@ package org.springframework.boot.docker.compose.service.connection.otlp; import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; 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; @@ -30,7 +31,9 @@ class OpenTelemetryLoggingDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { - private static final int OTLP_PORT = 4318; + private static final int OTLP_GRPC_PORT = 4317; + + private static final int OTLP_HTTP_PORT = 4318; OpenTelemetryLoggingDockerComposeConnectionDetailsFactory() { super("otel/opentelemetry-collector-contrib", @@ -47,17 +50,24 @@ private static final class OpenTelemetryLoggingDockerComposeConnectionDetails ex private final String host; - private final int port; + private final int grpcPort; + + private final int httPort; private OpenTelemetryLoggingDockerComposeConnectionDetails(RunningService source) { super(source); this.host = source.host(); - this.port = source.ports().get(OTLP_PORT); + this.grpcPort = source.ports().get(OTLP_GRPC_PORT); + this.httPort = source.ports().get(OTLP_HTTP_PORT); } @Override - public String getUrl() { - return "http://%s:%d/v1/logs".formatted(this.host, this.port); + public String getUrl(Transport transport) { + int port = switch (transport) { + case HTTP -> this.httPort; + case GRPC -> this.grpcPort; + }; + return "http://%s:%d/v1/logs".formatted(this.host, port); } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java index d1cd7f1ed807..869560a507ec 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; @@ -43,15 +44,18 @@ class OpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests { @Container @ServiceConnection - static final GenericContainer container = TestImage.OPENTELEMETRY.genericContainer().withExposedPorts(4318); + static final GenericContainer container = TestImage.OPENTELEMETRY.genericContainer() + .withExposedPorts(4317, 4318); @Autowired private OtlpLoggingConnectionDetails connectionDetails; @Test void connectionCanBeMadeToOpenTelemetryContainer() { - assertThat(this.connectionDetails.getUrl()) + assertThat(this.connectionDetails.getUrl(Transport.HTTP)) .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4318) + "/v1/logs"); + assertThat(this.connectionDetails.getUrl(Transport.GRPC)) + .isEqualTo("http://" + container.getHost() + ":" + container.getMappedPort(4317) + "/v1/logs"); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java index cd84f6d1ea7a..8d884e6413f8 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryLoggingContainerConnectionDetailsFactory.java @@ -20,6 +20,7 @@ import org.testcontainers.containers.GenericContainer; import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; @@ -31,10 +32,15 @@ * the {@code "otel/opentelemetry-collector-contrib"} image. * * @author Eddú Meléndez + * @author Moritz Halbritter */ class OpenTelemetryLoggingContainerConnectionDetailsFactory extends ContainerConnectionDetailsFactory, OtlpLoggingConnectionDetails> { + private static final int OTLP_GRPC_PORT = 4317; + + private static final int OTLP_HTTP_PORT = 4318; + OpenTelemetryLoggingContainerConnectionDetailsFactory() { super("otel/opentelemetry-collector-contrib", "org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration"); @@ -54,8 +60,12 @@ private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource } @Override - public String getUrl() { - return "http://%s:%d/v1/logs".formatted(getContainer().getHost(), getContainer().getMappedPort(4318)); + public String getUrl(Transport transport) { + int port = switch (transport) { + case HTTP -> OTLP_HTTP_PORT; + case GRPC -> OTLP_GRPC_PORT; + }; + return "http://%s:%d/v1/logs".formatted(getContainer().getHost(), getContainer().getMappedPort(port)); } } From c907bec434f879027d5a71489382d79eecbcf189 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Fri, 6 Sep 2024 14:38:34 +0200 Subject: [PATCH 092/271] Reference context propagation from correlation ID documentation Also links from the logging documenttaion to the correlation ID documentation. Closes gh-42054 --- .../reference/pages/actuator/observability.adoc | 16 ++++++++++------ .../reference/pages/actuator/tracing.adoc | 3 +++ .../reference/pages/features/logging.adoc | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc index 9c236aee528d..e0a6fbaf9438 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/observability.adoc @@ -14,12 +14,6 @@ NOTE: Low cardinality tags will be added to metrics and traces, while high cardi Beans of type `ObservationPredicate`, `GlobalObservationConvention`, `ObservationFilter` and `ObservationHandler` will be automatically registered on the `ObservationRegistry`. You can additionally register any number of `ObservationRegistryCustomizer` beans to further configure the registry. -Observability support relies on the https://github.com/micrometer-metrics/context-propagation[Context Propagation library] for forwarding the current observation across threads and reactive pipelines. -By default, `ThreadLocal` values are not automatically reinstated in reactive operators. -This behavior is controlled with the configprop:spring.reactor.context-propagation[] property, which can be set to `auto` to enable automatic propagation. - -For more details about observations please see the {url-micrometer-docs}/observation[Micrometer Observation documentation]. - TIP: Observability for JDBC can be configured using a separate project. The https://github.com/jdbc-observations/datasource-micrometer[Datasource Micrometer project] provides a Spring Boot starter which automatically creates observations when JDBC operations are invoked. Read more about it https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/[in the reference documentation]. @@ -29,6 +23,16 @@ To enable it, add the `io.r2dbc:r2dbc-proxy` dependency to your project. +[[actuator.observability.context-propagation]] +== Context Propagation +Observability support relies on the https://github.com/micrometer-metrics/context-propagation[Context Propagation library] for forwarding the current observation across threads and reactive pipelines. +By default, `ThreadLocal` values are not automatically reinstated in reactive operators. +This behavior is controlled with the configprop:spring.reactor.context-propagation[] property, which can be set to `auto` to enable automatic propagation. + +For more details about observations please see the {url-micrometer-docs}/observation[Micrometer Observation documentation]. + + + [[actuator.observability.common-tags]] == Common Tags diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc index fcddb23e4227..7e8244e38ea1 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc @@ -93,6 +93,9 @@ logging: NOTE: In the example above, configprop:logging.include-application-name[] is set to `false` to avoid the application name being duplicated in the log messages (configprop:logging.pattern.correlation[] already contains it). It's also worth mentioning that configprop:logging.pattern.correlation[] contains a trailing space so that it is separated from the logger name that comes right after it by default. +TIP: Correlation IDs rely on context propagation. +Please read xref:reference:actuator/observability.adoc#actuator.observability.context-propagation[this documentation for more details]. + [[actuator.micrometer-tracing.propagating-traces]] 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 2f60b42b27cc..f609f5762917 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 @@ -47,6 +47,8 @@ TIP: If you have a configprop:spring.application.name[] property but don't want TIP: If you have a configprop:spring.application.group[] property but don't want it logged you can set configprop:logging.include-application-group[] to `false`. +TIP: For more details about correlation IDs, please xref:reference:actuator/tracing.adoc#actuator.micrometer-tracing.logging[see this documentation]. + [[features.logging.console-output]] From e30ae7a240a3e9f6e19b71354d87dd0ab150fa98 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 6 Sep 2024 14:22:40 +0100 Subject: [PATCH 093/271] Document problem with property binding and Kotlin value classes Closes gh-41693 --- .../spring-boot-docs/src/docs/asciidoc/features/kotlin.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/kotlin.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/kotlin.adoc index a50eb957cc29..812ee7a1e740 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/kotlin.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/kotlin.adoc @@ -108,7 +108,7 @@ TIP: `org.jetbrains.kotlinx:kotlinx-coroutines-reactor` dependency is provided b [[features.kotlin.configuration-properties]] === @ConfigurationProperties -`@ConfigurationProperties` when used in combination with <> supports classes with immutable `val` properties as shown in the following example: +`@ConfigurationProperties` when used in combination with <> supports data classes with immutable `val` properties as shown in the following example: [source,kotlin,indent=0,subs="verbatim"] ---- @@ -125,6 +125,10 @@ data class KotlinExampleProperties( } ---- +Due to the limitations of their interoperability with Java, support for value classes is limited. +In particular, relying upon a value class's default value will not work with configuration property binding. +In such cases, a data class should be used instead. + TIP: To generate <> using the annotation processor, {kotlin-docs}kapt.html[`kapt` should be configured] with the `spring-boot-configuration-processor` dependency. Note that some features (such as detecting the default value or deprecated items) are not working due to limitations in the model kapt provides. From 6cd6f7566422e5923cccf94fdeb2ddb759b62f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 13 Aug 2024 11:12:49 +0200 Subject: [PATCH 094/271] Add configuration support for ExponentialHistogram in OTLP Registry Closes gh-41837 --- .../metrics/export/otlp/OtlpProperties.java | 43 ++++++++++++++++++- .../otlp/OtlpPropertiesConfigAdapter.java | 16 +++++++ .../OtlpPropertiesConfigAdapterTests.java | 34 +++++++++++++++ .../export/otlp/OtlpPropertiesTests.java | 5 ++- 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpProperties.java index e9a038d3e664..5ad71476c6a7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import io.micrometer.registry.otlp.AggregationTemporality; +import io.micrometer.registry.otlp.HistogramFlavor; import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -57,6 +58,22 @@ public class OtlpProperties extends StepRegistryProperties { */ private Map headers; + /** + * Histogram type to be preferred when histogram publishing is enabled. + */ + private HistogramFlavor histogramFlavor = HistogramFlavor.EXPLICIT_BUCKET_HISTOGRAM; + + /** + * Max scale to use for exponential histograms, if configured. + */ + private int maxScale = 20; + + /** + * Maximum number of buckets to be used for exponential histograms, if configured. + * This has no effect on explicit bucket histograms. + */ + private int maxBucketCount = 160; + /** * Time unit for exported metrics. */ @@ -97,6 +114,30 @@ public void setHeaders(Map headers) { this.headers = headers; } + public HistogramFlavor getHistogramFlavor() { + return this.histogramFlavor; + } + + public void setHistogramFlavor(HistogramFlavor histogramFlavor) { + this.histogramFlavor = histogramFlavor; + } + + public int getMaxScale() { + return this.maxScale; + } + + public void setMaxScale(int maxScale) { + this.maxScale = maxScale; + } + + public int getMaxBucketCount() { + return this.maxBucketCount; + } + + public void setMaxBucketCount(int maxBucketCount) { + this.maxBucketCount = maxBucketCount; + } + public TimeUnit getBaseTimeUnit() { return this.baseTimeUnit; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapter.java index d30e8b7eb064..0f6d5947ebdb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapter.java @@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit; import io.micrometer.registry.otlp.AggregationTemporality; +import io.micrometer.registry.otlp.HistogramFlavor; import io.micrometer.registry.otlp.OtlpConfig; import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter; @@ -98,6 +99,21 @@ public Map headers() { return get(OtlpProperties::getHeaders, OtlpConfig.super::headers); } + @Override + public HistogramFlavor histogramFlavor() { + return get(OtlpProperties::getHistogramFlavor, OtlpConfig.super::histogramFlavor); + } + + @Override + public int maxScale() { + return get(OtlpProperties::getMaxScale, OtlpConfig.super::maxScale); + } + + @Override + public int maxBucketCount() { + return get(OtlpProperties::getMaxBucketCount, OtlpConfig.super::maxBucketCount); + } + @Override public TimeUnit baseTimeUnit() { return get(OtlpProperties::getBaseTimeUnit, OtlpConfig.super::baseTimeUnit); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapterTests.java index 5413e8c3a2f6..78c97b35c0d3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesConfigAdapterTests.java @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import io.micrometer.registry.otlp.AggregationTemporality; +import io.micrometer.registry.otlp.HistogramFlavor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -85,6 +86,39 @@ void whenPropertiesHeadersIsSetAdapterHeadersReturnsIt() { assertThat(createAdapter().headers()).containsEntry("header", "value"); } + @Test + void whenPropertiesHistogramFlavorIsNotSetAdapterHistogramFlavorReturnsExplicitBucketHistogram() { + assertThat(createAdapter().histogramFlavor()).isSameAs(HistogramFlavor.EXPLICIT_BUCKET_HISTOGRAM); + } + + @Test + void whenPropertiesHistogramFlavorIsSetAdapterHistogramFlavorReturnsIt() { + this.properties.setHistogramFlavor(HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); + assertThat(createAdapter().histogramFlavor()).isSameAs(HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM); + } + + @Test + void whenPropertiesMaxScaleIsNotSetAdapterMaxScaleReturns20() { + assertThat(createAdapter().maxScale()).isEqualTo(20); + } + + @Test + void whenPropertiesMaxScaleIsSetAdapterMaxScaleReturnsIt() { + this.properties.setMaxScale(5); + assertThat(createAdapter().maxScale()).isEqualTo(5); + } + + @Test + void whenPropertiesMaxBucketCountIsNotSetAdapterMaxBucketCountReturns160() { + assertThat(createAdapter().maxBucketCount()).isEqualTo(160); + } + + @Test + void whenPropertiesMaxBucketCountIsSetAdapterMaxBucketCountReturnsIt() { + this.properties.setMaxBucketCount(6); + assertThat(createAdapter().maxBucketCount()).isEqualTo(6); + } + @Test void whenPropertiesBaseTimeUnitIsNotSetAdapterBaseTimeUnitReturnsMillis() { assertThat(createAdapter().baseTimeUnit()).isSameAs(TimeUnit.MILLISECONDS); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesTests.java index 3046e2279dca..1ecf8fb7c4c2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/otlp/OtlpPropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -37,6 +37,9 @@ void defaultValuesAreConsistent() { assertStepRegistryDefaultValues(properties, config); assertThat(properties.getUrl()).isEqualTo(config.url()); assertThat(properties.getAggregationTemporality()).isSameAs(config.aggregationTemporality()); + assertThat(properties.getHistogramFlavor()).isSameAs(config.histogramFlavor()); + assertThat(properties.getMaxScale()).isEqualTo(config.maxScale()); + assertThat(properties.getMaxBucketCount()).isEqualTo(config.maxBucketCount()); assertThat(properties.getBaseTimeUnit()).isSameAs(config.baseTimeUnit()); } From 793e9a8795e8c8ed3ef5f001f54c647197eaabcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Aug 2024 12:02:02 -0600 Subject: [PATCH 095/271] Add OpenTelemetry Logging Service Connection from LgtmStackContainer and Docker Compose See gh-42174 --- ...nectionDetailsFactoryIntegrationTests.java | 40 +++++++++++ ...DockerComposeConnectionDetailsFactory.java | 5 +- .../pages/features/dev-services.adoc | 2 +- .../pages/testing/testcontainers.adoc | 2 +- ...nectionDetailsFactoryIntegrationTests.java | 66 ++++++++++++++++++ ...gingContainerConnectionDetailsFactory.java | 67 +++++++++++++++++++ .../main/resources/META-INF/spring.factories | 1 + 7 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory.java diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 000000000000..ab4dda44ca97 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/service/connection/otlp/GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2024 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.docker.compose.service.connection.otlp; + +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; +import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest; +import org.springframework.boot.testsupport.container.TestImage; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link OpenTelemetryLoggingDockerComposeConnectionDetailsFactory} + * using {@link TestImage#GRAFANA_OTEL_LGTM}. + * + * @author Eddú Meléndez + */ +class GrafanaOpenTelemetryLoggingDockerComposeConnectionDetailsFactoryIntegrationTests { + + @DockerComposeTest(composeFile = "otlp-compose.yaml", image = TestImage.GRAFANA_OTEL_LGTM) + void runCreatesConnectionDetails(OtlpLoggingConnectionDetails connectionDetails) { + assertThat(connectionDetails.getUrl(Transport.HTTP)).startsWith("http://").endsWith("/v1/logs"); + assertThat(connectionDetails.getUrl(Transport.GRPC)).startsWith("http://").endsWith("/v1/logs"); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java index df4f0da1c32e..be18e0c4d3bd 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/otlp/OpenTelemetryLoggingDockerComposeConnectionDetailsFactory.java @@ -31,12 +31,15 @@ class OpenTelemetryLoggingDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory { + private static final String[] OPENTELEMETRY_IMAGE_NAMES = { "otel/opentelemetry-collector-contrib", + "grafana/otel-lgtm" }; + private static final int OTLP_GRPC_PORT = 4317; private static final int OTLP_HTTP_PORT = 4318; OpenTelemetryLoggingDockerComposeConnectionDetailsFactory() { - super("otel/opentelemetry-collector-contrib", + super(OPENTELEMETRY_IMAGE_NAMES, "org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration"); } diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc index 0eea9b29bdc7..f7e026d6aaf8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc @@ -108,7 +108,7 @@ The following service connections are currently supported: | Containers named "neo4j" or "bitnami/neo4j" | `OtlpLoggingConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" | `OtlpMetricsConnectionDetails` | Containers named "otel/opentelemetry-collector-contrib", "grafana/otel-lgtm" diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index fd795b4f8775..7a0fde2bf30b 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -72,7 +72,7 @@ The following service connection factories are provided in the `spring-boot-test | Containers of type `Neo4jContainer` | `OtlpLoggingConnectionDetails` -| Containers named "otel/opentelemetry-collector-contrib" +| Containers named "otel/opentelemetry-collector-contrib" or of type `LgtmStackContainer` | `OtlpMetricsConnectionDetails` | Containers named "otel/opentelemetry-collector-contrib" or of type `LgtmStackContainer` diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 000000000000..bf5b60d20287 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection.otlp; + +import org.junit.jupiter.api.Test; +import org.testcontainers.grafana.LgtmStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory}. + * + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +class GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final LgtmStackContainer container = TestImage.container(LgtmStackContainer.class); + + @Autowired + private OtlpLoggingConnectionDetails connectionDetails; + + @Test + void connectionCanBeMadeToOpenTelemetryContainer() { + assertThat(this.connectionDetails.getUrl(Transport.GRPC)) + .isEqualTo("%s/v1/logs".formatted(container.getOtlpGrpcUrl())); + assertThat(this.connectionDetails.getUrl(Transport.HTTP)) + .isEqualTo("%s/v1/logs".formatted(container.getOtlpHttpUrl())); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(OtlpLoggingAutoConfiguration.class) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory.java new file mode 100644 index 000000000000..6e7085ae675d --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/otlp/GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2024 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.testcontainers.service.connection.otlp; + +import org.testcontainers.grafana.LgtmStackContainer; + +import org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.actuate.autoconfigure.opentelemetry.otlp.Transport; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create + * {@link OtlpLoggingConnectionDetails} from a + * {@link ServiceConnection @ServiceConnection}-annotated {@link LgtmStackContainer} using + * the {@code "grafana/otel-lgtm"} image. + * + * @author Eddú Meléndez + */ +class GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory() { + super(ANY_CONNECTION_NAME, + "org.springframework.boot.actuate.autoconfigure.logging.opentelemetry.otlp.OtlpLoggingAutoConfiguration"); + } + + @Override + protected OtlpLoggingConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new OpenTelemetryLoggingContainerConnectionDetails(source); + } + + private static final class OpenTelemetryLoggingContainerConnectionDetails + extends ContainerConnectionDetails implements OtlpLoggingConnectionDetails { + + private OpenTelemetryLoggingContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public String getUrl(Transport transport) { + String url = switch (transport) { + case HTTP -> getContainer().getOtlpHttpUrl(); + case GRPC -> getContainer().getOtlpGrpcUrl(); + }; + return "%s/v1/logs".formatted(url); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index 23f293f975e2..bd92b9665f14 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -23,6 +23,7 @@ org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContaine org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOpenTelemetryLoggingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.GrafanaOpenTelemetryTracingContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.otlp.OpenTelemetryLoggingContainerConnectionDetailsFactory,\ From cbc732832b064acc81850a4340b3ba4d3b602232 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Sep 2024 11:33:18 +0100 Subject: [PATCH 096/271] Use EnableConfigurationProperties to define MessageSourceProperties Closes gh-42181 --- .../context/MessageSourceAutoConfiguration.java | 11 ++--------- .../context/MessageSourceProperties.java | 4 +++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index 0ec92b6568b9..68e04c7f0159 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -30,7 +30,6 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.MessageSourceRuntimeHints; import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; @@ -59,18 +58,12 @@ @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Conditional(ResourceBundleCondition.class) -@EnableConfigurationProperties +@EnableConfigurationProperties(MessageSourceProperties.class) @ImportRuntimeHints(MessageSourceRuntimeHints.class) public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = {}; - @Bean - @ConfigurationProperties(prefix = "spring.messages") - public MessageSourceProperties messageSourceProperties() { - return new MessageSourceProperties(); - } - @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index fe5130a4f446..a765308af201 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -21,6 +21,7 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; /** @@ -30,6 +31,7 @@ * @author Kedar Joshi * @since 2.0.0 */ +@ConfigurationProperties(prefix = "spring.messages") public class MessageSourceProperties { /** From 9a74437fbc915751d53f370b5db677d1834581d7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Sep 2024 13:52:23 +0100 Subject: [PATCH 097/271] Delete unused javadoc CSS file Closes gh-42187 --- .../src/main/javadoc/spring-javadoc.css | 599 ------------------ 1 file changed, 599 deletions(-) delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css deleted file mode 100644 index 06ad42277c6a..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/javadoc/spring-javadoc.css +++ /dev/null @@ -1,599 +0,0 @@ -/* Javadoc style sheet */ -/* -Overall document style -*/ - -@import url('resources/fonts/dejavu.css'); - -body { - background-color:#ffffff; - color:#353833; - font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; - font-size:14px; - margin:0; -} -a:link, a:visited { - text-decoration:none; - color:#4A6782; -} -a:hover, a:focus { - text-decoration:none; - color:#bb7a2a; -} -a:active { - text-decoration:none; - color:#4A6782; -} -a[name] { - color:#353833; -} -a[name]:hover { - text-decoration:none; - color:#353833; -} -pre { - font-family:'DejaVu Sans Mono', monospace; - font-size:14px; -} -h1 { - font-size:20px; -} -h2 { - font-size:18px; -} -h3 { - font-size:16px; - font-style:italic; -} -h4 { - font-size:13px; -} -h5 { - font-size:12px; -} -h6 { - font-size:11px; -} -ul { - list-style-type:disc; -} -code, tt { - font-family:'DejaVu Sans Mono', monospace; - font-size:14px; - padding-top:4px; - margin-top:8px; - line-height:1.4em; -} -dt code { - font-family:'DejaVu Sans Mono', monospace; - font-size:14px; - padding-top:4px; -} -table tr td dt code { - font-family:'DejaVu Sans Mono', monospace; - font-size:14px; - vertical-align:top; - padding-top:4px; -} -sup { - font-size:8px; -} -/* -Document title and Copyright styles -*/ -.clear { - clear:both; - height:0px; - overflow:hidden; -} -.aboutLanguage { - float:right; - padding:0px 21px; - font-size:11px; - z-index:200; - margin-top:-9px; -} -.legalCopy { - margin-left:.5em; -} -.bar a, .bar a:link, .bar a:visited, .bar a:active { - color:#FFFFFF; - text-decoration:none; -} -.bar a:hover, .bar a:focus { - color:#bb7a2a; -} -.tab { - background-color:#0066FF; - color:#ffffff; - padding:8px; - width:5em; - font-weight:bold; -} -/* -Navigation bar styles -*/ -.bar { - background-color:#4D7A97; - color:#FFFFFF; - padding:.8em .5em .4em .8em; - height:auto;/*height:1.8em;*/ - font-size:11px; - margin:0; -} -.topNav { - background-color:#4D7A97; - color:#FFFFFF; - float:left; - padding:0; - width:100%; - clear:right; - height:2.8em; - padding-top:10px; - overflow:hidden; - font-size:12px; -} -.bottomNav { - margin-top:10px; - background-color:#4D7A97; - color:#FFFFFF; - float:left; - padding:0; - width:100%; - clear:right; - height:2.8em; - padding-top:10px; - overflow:hidden; - font-size:12px; -} -.subNav { - background-color:#dee3e9; - float:left; - width:100%; - overflow:hidden; - font-size:12px; -} -.subNav div { - clear:left; - float:left; - padding:0 0 5px 6px; - text-transform:uppercase; -} -ul.navList, ul.subNavList { - float:left; - margin:0 25px 0 0; - padding:0; -} -ul.navList li{ - list-style:none; - float:left; - padding: 5px 6px; - text-transform:uppercase; -} -ul.subNavList li{ - list-style:none; - float:left; -} -.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { - color:#FFFFFF; - text-decoration:none; - text-transform:uppercase; -} -.topNav a:hover, .bottomNav a:hover { - text-decoration:none; - color:#bb7a2a; - text-transform:uppercase; -} -.navBarCell1Rev { - background-color:#F8981D; - color:#253441; - margin: auto 5px; -} -.skipNav { - position:absolute; - top:auto; - left:-9999px; - overflow:hidden; -} -/* -Page header and footer styles -*/ -.header, .footer { - clear:both; - margin:0 20px; - padding:5px 0 0 0; -} -.indexHeader { - margin:10px; - position:relative; -} -.indexHeader span{ - margin-right:15px; -} -.indexHeader h1 { - font-size:13px; -} -.title { - color:#2c4557; - margin:10px 0; -} -.subTitle { - margin:5px 0 0 0; -} -.header ul { - margin:0 0 15px 0; - padding:0; -} -.footer ul { - margin:20px 0 5px 0; -} -.header ul li, .footer ul li { - list-style:none; - font-size:13px; -} -/* -Heading styles -*/ -div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { - background-color:#dee3e9; - border:1px solid #d0d9e0; - margin:0 0 6px -8px; - padding:7px 5px; -} -ul.blockList ul.blockList ul.blockList li.blockList h3 { - background-color:#dee3e9; - border:1px solid #d0d9e0; - margin:0 0 6px -8px; - padding:7px 5px; -} -ul.blockList ul.blockList li.blockList h3 { - padding:0; - margin:15px 0; -} -ul.blockList li.blockList h2 { - padding:0px 0 20px 0; -} -/* -Page layout container styles -*/ -.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { - clear:both; - padding:10px 20px; - position:relative; -} -.indexContainer { - margin:10px; - position:relative; - font-size:12px; -} -.indexContainer h2 { - font-size:13px; - padding:0 0 3px 0; -} -.indexContainer ul { - margin:0; - padding:0; -} -.indexContainer ul li { - list-style:none; - padding-top:2px; -} -.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { - font-size:12px; - font-weight:bold; - margin:10px 0 0 0; - color:#4E4E4E; -} -.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { - margin:5px 0 10px 0px; - font-size:14px; - font-family:'DejaVu Sans Mono',monospace; -} -.serializedFormContainer dl.nameValue dt { - margin-left:1px; - font-size:1.1em; - display:inline; - font-weight:bold; -} -.serializedFormContainer dl.nameValue dd { - margin:0 0 0 1px; - font-size:1.1em; - display:inline; -} -/* -List styles -*/ -ul.horizontal li { - display:inline; - font-size:0.9em; -} -ul.inheritance { - margin:0; - padding:0; -} -ul.inheritance li { - display:inline; - list-style:none; -} -ul.inheritance li ul.inheritance { - margin-left:15px; - padding-left:15px; - padding-top:1px; -} -ul.blockList, ul.blockListLast { - margin:10px 0 10px 0; - padding:0; -} -ul.blockList li.blockList, ul.blockListLast li.blockList { - list-style:none; - margin-bottom:15px; - line-height:1.4; -} -ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { - padding:0px 20px 5px 10px; - border:1px solid #ededed; - background-color:#f8f8f8; -} -ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { - padding:0 0 5px 8px; - background-color:#ffffff; - border:none; -} -ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { - margin-left:0; - padding-left:0; - padding-bottom:15px; - border:none; -} -ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { - list-style:none; - border-bottom:none; - padding-bottom:0; -} -table tr td dl, table tr td dl dt, table tr td dl dd { - margin-top:0; - margin-bottom:1px; -} -/* -Table styles -*/ -.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { - width:100%; - border-left:1px solid #EEE; - border-right:1px solid #EEE; - border-bottom:1px solid #EEE; -} -.overviewSummary, .memberSummary { - padding:0px; -} -.overviewSummary caption, .memberSummary caption, .typeSummary caption, -.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { - position:relative; - text-align:left; - background-repeat:no-repeat; - color:#253441; - font-weight:bold; - clear:none; - overflow:hidden; - padding:0px; - padding-top:10px; - padding-left:1px; - margin:0px; - white-space:pre; -} -.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, -.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, -.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, -.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, -.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, -.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, -.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, -.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { - color:#FFFFFF; -} -.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, -.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { - white-space:nowrap; - padding-top:5px; - padding-left:12px; - padding-right:12px; - padding-bottom:7px; - display:inline-block; - float:left; - background-color:#F8981D; - border: none; - height:16px; -} -.memberSummary caption span.activeTableTab span { - white-space:nowrap; - padding-top:5px; - padding-left:12px; - padding-right:12px; - margin-right:3px; - display:inline-block; - float:left; - background-color:#F8981D; - height:16px; -} -.memberSummary caption span.tableTab span { - white-space:nowrap; - padding-top:5px; - padding-left:12px; - padding-right:12px; - margin-right:3px; - display:inline-block; - float:left; - background-color:#4D7A97; - height:16px; -} -.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { - padding-top:0px; - padding-left:0px; - padding-right:0px; - background-image:none; - float:none; - display:inline; -} -.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, -.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { - display:none; - width:5px; - position:relative; - float:left; - background-color:#F8981D; -} -.memberSummary .activeTableTab .tabEnd { - display:none; - width:5px; - margin-right:3px; - position:relative; - float:left; - background-color:#F8981D; -} -.memberSummary .tableTab .tabEnd { - display:none; - width:5px; - margin-right:3px; - position:relative; - background-color:#4D7A97; - float:left; - -} -.overviewSummary td, .memberSummary td, .typeSummary td, -.useSummary td, .constantsSummary td, .deprecatedSummary td { - text-align:left; - padding:0px 0px 12px 10px; - width:100%; -} -th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, -td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ - vertical-align:top; - padding-right:0px; - padding-top:8px; - padding-bottom:3px; -} -th.colFirst, th.colLast, th.colOne, .constantsSummary th { - background:#dee3e9; - text-align:left; - padding:8px 3px 3px 7px; -} -td.colFirst, th.colFirst { - white-space:nowrap; - font-size:13px; -} -td.colLast, th.colLast { - font-size:13px; -} -td.colOne, th.colOne { - font-size:13px; -} -.overviewSummary td.colFirst, .overviewSummary th.colFirst, -.overviewSummary td.colOne, .overviewSummary th.colOne, -.memberSummary td.colFirst, .memberSummary th.colFirst, -.memberSummary td.colOne, .memberSummary th.colOne, -.typeSummary td.colFirst{ - width:25%; - vertical-align:top; -} -td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { - font-weight:bold; -} -.tableSubHeadingColor { - background-color:#EEEEFF; -} -.altColor { - background-color:#FFFFFF; -} -.rowColor { - background-color:#EEEEEF; -} -/* -Content styles -*/ -.description pre { - margin-top:0; -} -.deprecatedContent { - margin:0; - padding:10px 0; -} -.docSummary { - padding:0; -} - -ul.blockList ul.blockList ul.blockList li.blockList h3 { - font-style:normal; -} - -div.block { - font-size:14px; - font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; -} - -td.colLast div { - padding-top:0px; -} - - -td.colLast a { - padding-bottom:3px; -} -/* -Formatting effect styles -*/ -.sourceLineNo { - color:green; - padding:0 30px 0 0; -} -h1.hidden { - visibility:hidden; - overflow:hidden; - font-size:10px; -} -.block { - display:block; - margin:3px 10px 2px 0px; - color:#474747; -} -.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, -.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, -.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { - font-weight:bold; -} -.deprecationComment, .emphasizedPhrase, .interfaceName { - font-style:italic; -} - -div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, -div.block div.block span.interfaceName { - font-style:normal; -} - -div.contentContainer ul.blockList li.blockList h2{ - padding-bottom:0px; -} - - - -/* -Spring -*/ - -pre.code { - background-color: #F8F8F8; - border: 1px solid #CCCCCC; - border-radius: 3px 3px 3px 3px; - overflow: auto; - padding: 10px; - margin: 4px 20px 2px 0px; -} - -pre.code code, pre.code code * { - font-size: 1em; -} - -pre.code code, pre.code code * { - padding: 0 !important; - margin: 0 !important; -} - From aef56bceb9ff3c2fe3bab7fe86427c6c6148c3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Sep 2024 14:59:21 +0200 Subject: [PATCH 098/271] Replace RFC 7807 by RFC 9457 in documentation This commit updates all references to RFC 7807 by RFC 9457 since the former is now obsolete. Closes gh-41260 --- .../boot/autoconfigure/web/reactive/WebFluxProperties.java | 2 +- .../boot/autoconfigure/web/servlet/WebMvcProperties.java | 2 +- .../spring-boot-docs/src/docs/asciidoc/web/reactive.adoc | 2 +- .../spring-boot-docs/src/docs/asciidoc/web/servlet.adoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java index 670a81f3f5f4..618b5164f919 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java @@ -145,7 +145,7 @@ public void setDateTime(String dateTime) { public static class Problemdetails { /** - * Whether RFC 7807 Problem Details support should be enabled. + * Whether RFC 9457 Problem Details support should be enabled. */ private boolean enabled = false; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java index b26a4a9fd81c..85328f5d13d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java @@ -450,7 +450,7 @@ public enum MatchingStrategy { public static class Problemdetails { /** - * Whether RFC 7807 Problem Details support should be enabled. + * Whether RFC 9457 Problem Details support should be enabled. */ private boolean enabled = false; diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc index 10e233550871..fd2caf9e652a 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc @@ -165,7 +165,7 @@ For machine clients, it produces a JSON response with details of the error, the For browser clients, there is a "`whitelabel`" error handler that renders the same data in HTML format. You can also provide your own HTML templates to display errors (see the <>). -Before customizing error handling in Spring Boot directly, you can leverage the {spring-framework-docs}/web/webflux/ann-rest-exceptions.html[RFC 7807 Problem Details] support in Spring WebFlux. +Before customizing error handling in Spring Boot directly, you can leverage the {spring-framework-docs}/web/webflux/ann-rest-exceptions.html[RFC 9457 Problem Details] support in Spring WebFlux. Spring WebFlux can produce custom error messages with the `application/problem+json` media type, like: [source,json,indent=0,subs="verbatim"] diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc index 1c4f6219b3aa..a4b043333071 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc @@ -339,7 +339,7 @@ TIP: The `BasicErrorController` can be used as a base class for a custom `ErrorC This is particularly useful if you want to add a handler for a new content type (the default is to handle `text/html` specifically and provide a fallback for everything else). To do so, extend `BasicErrorController`, add a public method with a `@RequestMapping` that has a `produces` attribute, and create a bean of your new type. -As of Spring Framework 6.0, {spring-framework-docs}/web/webmvc/mvc-ann-rest-exceptions.html[RFC 7807 Problem Details] is supported. +As of Spring Framework 6.0, {spring-framework-docs}/web/webmvc/mvc-ann-rest-exceptions.html[RFC 9457 Problem Details] is supported. Spring MVC can produce custom error messages with the `application/problem+json` media type, like: [source,json,indent=0,subs="verbatim"] From b1db3ad8aea0a005c304a9db5ec2bd340a70e75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 9 Sep 2024 15:25:23 +0200 Subject: [PATCH 099/271] Clarify reason for deprecating autotime properties Closes gh-41745 --- ...ditional-spring-configuration-metadata.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 6ad7e633dde4..9456c54af832 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1936,7 +1936,7 @@ "defaultValue": true, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Requests are timed automatically." } }, { @@ -1944,7 +1944,7 @@ "description": "Computed non-aggregable percentiles to publish.", "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles." } }, { @@ -1953,7 +1953,7 @@ "defaultValue": false, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles-histogram." } }, { @@ -1979,7 +1979,7 @@ "defaultValue": true, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Requests are timed automatically." } }, { @@ -1987,7 +1987,7 @@ "description": "Computed non-aggregable percentiles to publish.", "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles." } }, { @@ -1996,7 +1996,7 @@ "defaultValue": false, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles-histogram." } }, { @@ -2029,7 +2029,7 @@ "defaultValue": true, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Requests are timed automatically." } }, { @@ -2037,7 +2037,7 @@ "description": "Computed non-aggregable percentiles to publish.", "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles." } }, { @@ -2046,7 +2046,7 @@ "defaultValue": false, "deprecation": { "level": "error", - "reason": "Should be applied at the ObservationRegistry level." + "reason": "Should be configured globally via management.metrics.distribution.percentiles-histogram." } }, { From 083ac67d130cb9755d2b696f61cf9bcc4eb04fb2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Sep 2024 15:05:32 +0100 Subject: [PATCH 100/271] Upgrade to Gradle 8.10.1 Closes gh-42195 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b4155759..0aaefbcaf0f1 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-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From ddd0d898c2133894033561f2fe01e7f98b41725b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 9 Sep 2024 12:47:39 -0700 Subject: [PATCH 101/271] Polish --- .../AutoConfigurationExcludeFilter.java | 6 +-- .../AutoConfigurationImportSelector.java | 18 +++++---- .../autoconfigure/AutoConfigurations.java | 6 +-- .../AutoConfigurationSorterTests.java | 40 ++++++++++--------- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java index ab691e235efe..40e4ef635f08 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -63,8 +63,8 @@ private boolean isAutoConfiguration(MetadataReader metadataReader) { protected List getAutoConfigurations() { if (this.autoConfigurations == null) { - this.autoConfigurations = ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader) - .getCandidates(); + ImportCandidates importCandidates = ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader); + this.autoConfigurations = importCandidates.getCandidates(); } return this.autoConfigurations; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java index e9e9f1642920..025b1aedf8d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java @@ -76,6 +76,8 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { + static final int ORDER = Ordered.LOWEST_PRECEDENCE - 1; + private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry(); private static final String[] NO_IMPORTS = {}; @@ -92,7 +94,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private ResourceLoader resourceLoader; - private ConfigurationClassFilter configurationClassFilter; + private volatile ConfigurationClassFilter configurationClassFilter; @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { @@ -177,8 +179,8 @@ protected Class getAnnotationClass() { * @return a list of candidate configurations */ protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { - List configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()) - .getCandidates(); + ImportCandidates importCandidates = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()); + List configurations = importCandidates.getCandidates(); Assert.notEmpty(configurations, "No auto configuration classes found in " + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you " @@ -254,14 +256,16 @@ protected List getAutoConfigurationImportFilters( } private ConfigurationClassFilter getConfigurationClassFilter() { - if (this.configurationClassFilter == null) { + ConfigurationClassFilter configurationClassFilter = this.configurationClassFilter; + if (configurationClassFilter == null) { List filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) { invokeAwareMethods(filter); } - this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters); + configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters); + this.configurationClassFilter = configurationClassFilter; } - return this.configurationClassFilter; + return configurationClassFilter; } protected final List removeDuplicates(List list) { @@ -344,7 +348,7 @@ protected final ResourceLoader getResourceLoader() { @Override public int getOrder() { - return Ordered.LOWEST_PRECEDENCE - 1; + return ORDER; } private static class ConfigurationClassFilter { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java index 2063a69fb249..f5654ea4db3f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -39,7 +39,7 @@ public class AutoConfigurations extends Configurations implements Ordered { private static final AutoConfigurationSorter SORTER = new AutoConfigurationSorter(new SimpleMetadataReaderFactory(), null); - private static final Ordered ORDER = new AutoConfigurationImportSelector(); + private static final int ORDER = AutoConfigurationImportSelector.ORDER; protected AutoConfigurations(Collection> classes) { super(classes); @@ -56,7 +56,7 @@ protected Collection> sort(Collection> classes) { @Override public int getOrder() { - return ORDER.getOrder(); + return ORDER; } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java index b9bb92d4663f..93a00ba89cc0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -97,19 +97,19 @@ void setup() { @Test void byOrderAnnotation() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(LOWEST, HIGHEST, DEFAULT)); + List actual = getInPriorityOrder(LOWEST, HIGHEST, DEFAULT); assertThat(actual).containsExactly(HIGHEST, DEFAULT, LOWEST); } @Test void byAutoConfigureAfter() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C)); + List actual = getInPriorityOrder(A, B, C); assertThat(actual).containsExactly(C, B, A); } @Test void byAutoConfigureAfterAliasFor() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A3, B2, C)); + List actual = getInPriorityOrder(A3, B2, C); assertThat(actual).containsExactly(C, B2, A3); } @@ -118,19 +118,19 @@ void byAutoConfigureAfterAliasForWithProperties() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A3, B2, C); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A3, B2, C)); + List actual = getInPriorityOrder(A3, B2, C); assertThat(actual).containsExactly(C, B2, A3); } @Test void byAutoConfigureBefore() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y, Z)); + List actual = getInPriorityOrder(X, Y, Z); assertThat(actual).containsExactly(Z, Y, X); } @Test void byAutoConfigureBeforeAliasFor() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y2, Z2)); + List actual = getInPriorityOrder(X, Y2, Z2); assertThat(actual).containsExactly(Z2, Y2, X); } @@ -139,44 +139,44 @@ void byAutoConfigureBeforeAliasForWithProperties() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(X, Y2, Z2); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); - List actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y2, Z2)); + List actual = getInPriorityOrder(X, Y2, Z2); assertThat(actual).containsExactly(Z2, Y2, X); } @Test void byAutoConfigureAfterDoubles() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, E)); + List actual = getInPriorityOrder(A, B, C, E); assertThat(actual).containsExactly(C, E, B, A); } @Test void byAutoConfigureMixedBeforeAndAfter() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, W, X)); + List actual = getInPriorityOrder(A, B, C, W, X); assertThat(actual).containsExactly(C, W, B, A, X); } @Test void byAutoConfigureMixedBeforeAndAfterWithClassNames() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A2, B, C, W2, X)); + List actual = getInPriorityOrder(A2, B, C, W2, X); assertThat(actual).containsExactly(C, W2, B, A2, X); } @Test void byAutoConfigureMixedBeforeAndAfterWithDifferentInputOrder() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(W, X, A, B, C)); + List actual = getInPriorityOrder(W, X, A, B, C); assertThat(actual).containsExactly(C, W, B, A, X); } @Test void byAutoConfigureAfterWithMissing() { - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B)); + List actual = getInPriorityOrder(A, B); assertThat(actual).containsExactly(B, A); } @Test void byAutoConfigureAfterWithCycle() { this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), this.autoConfigurationMetadata); - assertThatIllegalStateException().isThrownBy(() -> this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, D))) + assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(A, B, C, D)) .withMessageContaining("AutoConfigure cycle detected"); } @@ -185,7 +185,7 @@ void usesAnnotationPropertiesWhenPossible() throws Exception { MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A2, B, C, W2, X); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A2, B, C, W2, X)); + List actual = getInPriorityOrder(A2, B, C, W2, X); assertThat(actual).containsExactly(C, W2, B, A2, X); } @@ -194,7 +194,7 @@ void useAnnotationWithNoDirectLink() throws Exception { MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, E); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); - List actual = this.sorter.getInPriorityOrder(Arrays.asList(A, E)); + List actual = getInPriorityOrder(A, E); assertThat(actual).containsExactly(E, A); } @@ -203,7 +203,7 @@ void useAnnotationWithNoDirectLinkAndCycle() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, D); this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); - assertThatIllegalStateException().isThrownBy(() -> this.sorter.getInPriorityOrder(Arrays.asList(D, B))) + assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(D, B)) .withMessageContaining("AutoConfigure cycle detected"); } @@ -214,10 +214,14 @@ void byBeforeAnnotationThenOrderAnnotation() { String oa2 = OrderAutoConfigureASeedY2.class.getName(); String oa3 = OrderAutoConfigureASeedA3.class.getName(); String oa4 = OrderAutoConfigureAutoConfigureASeedG4.class.getName(); - List actual = this.sorter.getInPriorityOrder(Arrays.asList(oa4, oa3, oa2, oa1, oa)); + List actual = getInPriorityOrder(oa4, oa3, oa2, oa1, oa); assertThat(actual).containsExactly(oa1, oa2, oa3, oa4, oa); } + private List getInPriorityOrder(String... classNames) { + return this.sorter.getInPriorityOrder(Arrays.asList(classNames)); + } + private AutoConfigurationMetadata getAutoConfigurationMetadata(String... classNames) throws Exception { Properties properties = new Properties(); for (String className : classNames) { From 72588fcda78407429bcc51472c0ad06cc4747b33 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 9 Sep 2024 12:40:52 -0700 Subject: [PATCH 102/271] Provide support for deprecated auto-configuration classes Support `AutoConfiguration.replacements` file that can be placed alongside an `AutoConfiguration.imports` to replace deprecated auto-configurations. Closes gh-14860 --- .../AutoConfigurationImportSelector.java | 48 ++++++- .../AutoConfigurationReplacements.java | 133 ++++++++++++++++++ .../AutoConfigurationSorter.java | 37 +++-- .../autoconfigure/AutoConfigurations.java | 34 +++-- .../AutoConfigurationImportSelectorTests.java | 71 +++++++++- .../AutoConfigurationReplacementsTests.java | 65 +++++++++ .../AutoConfigurationSorterTests.java | 51 ++++++- .../AutoConfigurationsTests.java | 28 +++- .../TestAutoConfigurationSorter.java | 8 +- ...electorTests$TestAutoConfiguration.imports | 3 + ...orTests$TestAutoConfiguration.replacements | 2 + ...AutoConfigurationReplacements.replacements | 2 + .../developing-auto-configuration.adoc | 20 +++ .../context/annotation/Configurations.java | 30 +++- .../context/annotation/ImportCandidates.java | 7 +- .../annotation/ConfigurationsTests.java | 85 +++++++++-- 16 files changed, 566 insertions(+), 58 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacementsTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.imports create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.replacements create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationReplacementsTests$TestAutoConfigurationReplacements.replacements diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java index 025b1aedf8d8..e3da084cf8f9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java @@ -86,6 +86,8 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; + private final Class autoConfigurationAnnotation; + private ConfigurableListableBeanFactory beanFactory; private Environment environment; @@ -96,6 +98,17 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, private volatile ConfigurationClassFilter configurationClassFilter; + private volatile AutoConfigurationReplacements autoConfigurationReplacements; + + public AutoConfigurationImportSelector() { + this(null); + } + + AutoConfigurationImportSelector(Class autoConfigurationAnnotation) { + this.autoConfigurationAnnotation = (autoConfigurationAnnotation != null) ? autoConfigurationAnnotation + : AutoConfiguration.class; + } + @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { @@ -179,11 +192,12 @@ protected Class getAnnotationClass() { * @return a list of candidate configurations */ protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { - ImportCandidates importCandidates = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()); + ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, + getBeanClassLoader()); List configurations = importCandidates.getCandidates(); Assert.notEmpty(configurations, - "No auto configuration classes found in " - + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you " + "No auto configuration classes found in " + "META-INF/spring/" + + this.autoConfigurationAnnotation.getName() + ".imports. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } @@ -227,7 +241,7 @@ protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttri excluded.addAll(asList(attributes, "exclude")); excluded.addAll(asList(attributes, "excludeName")); excluded.addAll(getExcludeAutoConfigurationsProperty()); - return excluded; + return getAutoConfigurationReplacements().replaceAll(excluded); } /** @@ -268,6 +282,16 @@ private ConfigurationClassFilter getConfigurationClassFilter() { return configurationClassFilter; } + private AutoConfigurationReplacements getAutoConfigurationReplacements() { + AutoConfigurationReplacements autoConfigurationReplacements = this.autoConfigurationReplacements; + if (autoConfigurationReplacements == null) { + autoConfigurationReplacements = AutoConfigurationReplacements.load(this.autoConfigurationAnnotation, + this.beanClassLoader); + this.autoConfigurationReplacements = autoConfigurationReplacements; + } + return autoConfigurationReplacements; + } + protected final List removeDuplicates(List list) { return new ArrayList<>(new LinkedHashSet<>(list)); } @@ -409,6 +433,8 @@ private static final class AutoConfigurationGroup private AutoConfigurationMetadata autoConfigurationMetadata; + private AutoConfigurationReplacements autoConfigurationReplacements; + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; @@ -430,7 +456,15 @@ public void process(AnnotationMetadata annotationMetadata, DeferredImportSelecto () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); - AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) + AutoConfigurationImportSelector autoConfigurationImportSelector = (AutoConfigurationImportSelector) deferredImportSelector; + AutoConfigurationReplacements autoConfigurationReplacements = autoConfigurationImportSelector + .getAutoConfigurationReplacements(); + Assert.state( + this.autoConfigurationReplacements == null + || this.autoConfigurationReplacements.equals(autoConfigurationReplacements), + "Auto-configuration replacements must be the same for each call to process"); + this.autoConfigurationReplacements = autoConfigurationReplacements; + AutoConfigurationEntry autoConfigurationEntry = autoConfigurationImportSelector .getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { @@ -452,7 +486,6 @@ public Iterable selectImports() { .flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); processedConfigurations.removeAll(allExclusions); - return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .toList(); @@ -467,7 +500,8 @@ private AutoConfigurationMetadata getAutoConfigurationMetadata() { private List sortAutoConfigurations(Set configurations, AutoConfigurationMetadata autoConfigurationMetadata) { - return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata) + return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata, + this.autoConfigurationReplacements::replace) .getInPriorityOrder(configurations); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java new file mode 100644 index 000000000000..88846230c0ea --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacements.java @@ -0,0 +1,133 @@ +/* + * Copyright 2012-2024 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.autoconfigure; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.springframework.boot.context.annotation.ImportCandidates; +import org.springframework.core.io.UrlResource; +import org.springframework.util.Assert; + +/** + * Contains auto-configuration replacements used to handle deprecated or moved + * auto-configurations which may still be referenced by + * {@link AutoConfigureBefore @AutoConfigureBefore}, + * {@link AutoConfigureAfter @AutoConfigureAfter} or exclusions. + * + * @author Phillip Webb + */ +final class AutoConfigurationReplacements { + + private static final String LOCATION = "META-INF/spring/%s.replacements"; + + private final Map replacements; + + private AutoConfigurationReplacements(Map replacements) { + this.replacements = Map.copyOf(replacements); + } + + Set replaceAll(Set classNames) { + Set replaced = new LinkedHashSet<>(classNames.size()); + for (String className : classNames) { + replaced.add(replace(className)); + } + return replaced; + } + + String replace(String className) { + return this.replacements.getOrDefault(className, className); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return this.replacements.equals(((AutoConfigurationReplacements) obj).replacements); + } + + @Override + public int hashCode() { + return this.replacements.hashCode(); + } + + /** + * Loads the relocations from the classpath. Relactions are stored in files named + * {@code META-INF/spring/full-qualified-annotation-name.replacements} on the + * classpath. The file is loaded using {@link Properties#load(java.io.InputStream)} + * with each entry containing an auto-configuration class name as the key and the + * replacement class name as the value. + * @param annotation annotation to load + * @param classLoader class loader to use for loading + * @return list of names of annotated classes + */ + static AutoConfigurationReplacements load(Class annotation, ClassLoader classLoader) { + Assert.notNull(annotation, "'annotation' must not be null"); + ClassLoader classLoaderToUse = decideClassloader(classLoader); + String location = String.format(LOCATION, annotation.getName()); + Enumeration urls = findUrlsInClasspath(classLoaderToUse, location); + Map replacements = new HashMap<>(); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + replacements.putAll(readReplacements(url)); + } + return new AutoConfigurationReplacements(replacements); + } + + private static ClassLoader decideClassloader(ClassLoader classLoader) { + if (classLoader == null) { + return ImportCandidates.class.getClassLoader(); + } + return classLoader; + } + + private static Enumeration findUrlsInClasspath(ClassLoader classLoader, String location) { + try { + return classLoader.getResources(location); + } + catch (IOException ex) { + throw new IllegalArgumentException("Failed to load configurations from location [" + location + "]", ex); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static Map readReplacements(URL url) { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) { + Properties properties = new Properties(); + properties.load(reader); + return (Map) properties; + } + catch (IOException ex) { + throw new IllegalArgumentException("Unable to load replacements from location [" + url + "]", ex); + } + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java index 271fc11087d7..043da47b27c3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.UnaryOperator; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; @@ -47,11 +49,14 @@ class AutoConfigurationSorter { private final AutoConfigurationMetadata autoConfigurationMetadata; + private final UnaryOperator replacementMapper; + AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory, - AutoConfigurationMetadata autoConfigurationMetadata) { + AutoConfigurationMetadata autoConfigurationMetadata, UnaryOperator replacementMapper) { Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null"); this.metadataReaderFactory = metadataReaderFactory; this.autoConfigurationMetadata = autoConfigurationMetadata; + this.replacementMapper = replacementMapper; } List getInPriorityOrder(Collection classNames) { @@ -108,7 +113,7 @@ private void checkForCycles(Set processing, String current, String after () -> "AutoConfigure cycle detected between " + current + " and " + after); } - private static class AutoConfigurationClasses { + private class AutoConfigurationClasses { private final Map classes = new LinkedHashMap<>(); @@ -157,7 +162,7 @@ Set getClassesRequestedAfter(String className) { } - private static class AutoConfigurationClass { + private class AutoConfigurationClass { private final String className; @@ -192,20 +197,36 @@ boolean isAvailable() { Set getBefore() { if (this.before == null) { - this.before = (wasProcessed() ? this.autoConfigurationMetadata.getSet(this.className, - "AutoConfigureBefore", Collections.emptySet()) : getAnnotationValue(AutoConfigureBefore.class)); + this.before = getClassNames("AutoConfigureBefore", AutoConfigureBefore.class); } return this.before; } Set getAfter() { if (this.after == null) { - this.after = (wasProcessed() ? this.autoConfigurationMetadata.getSet(this.className, - "AutoConfigureAfter", Collections.emptySet()) : getAnnotationValue(AutoConfigureAfter.class)); + this.after = getClassNames("AutoConfigureAfter", AutoConfigureAfter.class); } return this.after; } + private Set getClassNames(String metadataKey, Class annotation) { + Set annotationValue = wasProcessed() + ? this.autoConfigurationMetadata.getSet(this.className, metadataKey, Collections.emptySet()) + : getAnnotationValue(annotation); + return applyReplacements(annotationValue); + } + + private Set applyReplacements(Set values) { + if (AutoConfigurationSorter.this.replacementMapper == null) { + return values; + } + Set replaced = new LinkedHashSet<>(values); + for (String value : values) { + replaced.add(AutoConfigurationSorter.this.replacementMapper.apply(value)); + } + return replaced; + } + private int getOrder() { if (wasProcessed()) { return this.autoConfigurationMetadata.getInteger(this.className, "AutoConfigureOrder", diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java index f5654ea4db3f..9c0b22bf9e76 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurations.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import org.springframework.boot.context.annotation.Configurations; @@ -36,22 +37,33 @@ */ public class AutoConfigurations extends Configurations implements Ordered { - private static final AutoConfigurationSorter SORTER = new AutoConfigurationSorter(new SimpleMetadataReaderFactory(), - null); + private static final SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); private static final int ORDER = AutoConfigurationImportSelector.ORDER; + static final AutoConfigurationReplacements replacements = AutoConfigurationReplacements + .load(AutoConfiguration.class, null); + + private final UnaryOperator replacementMapper; + protected AutoConfigurations(Collection> classes) { - super(classes); + this(replacements::replace, classes); } - @Override - protected Collection> sort(Collection> classes) { - List names = classes.stream().map(Class::getName).toList(); - List sorted = SORTER.getInPriorityOrder(names); - return sorted.stream() - .map((className) -> ClassUtils.resolveClassName(className, null)) - .collect(Collectors.toCollection(ArrayList::new)); + AutoConfigurations(UnaryOperator replacementMapper, Collection> classes) { + super(sorter(replacementMapper), classes); + this.replacementMapper = replacementMapper; + } + + private static UnaryOperator>> sorter(UnaryOperator replacementMapper) { + AutoConfigurationSorter sorter = new AutoConfigurationSorter(metadataReaderFactory, null, replacementMapper); + return (classes) -> { + List names = classes.stream().map(Class::getName).map(replacementMapper::apply).toList(); + List sorted = sorter.getInPriorityOrder(names); + return sorted.stream() + .map((className) -> ClassUtils.resolveClassName(className, null)) + .collect(Collectors.toCollection(ArrayList::new)); + }; } @Override @@ -61,7 +73,7 @@ public int getOrder() { @Override protected AutoConfigurations merge(Set> mergedClasses) { - return new AutoConfigurations(mergedClasses); + return new AutoConfigurations(this.replacementMapper, mergedClasses); } public static AutoConfigurations of(Class... classes) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java index b74a7cb99339..41ced26ff2ba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelectorTests.java @@ -16,15 +16,22 @@ package org.springframework.boot.autoconfigure; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -34,6 +41,8 @@ import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DeferredImportSelector.Group; +import org.springframework.context.annotation.DeferredImportSelector.Group.Entry; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.mock.env.MockEnvironment; @@ -50,7 +59,7 @@ */ class AutoConfigurationImportSelectorTests { - private final TestAutoConfigurationImportSelector importSelector = new TestAutoConfigurationImportSelector(); + private final TestAutoConfigurationImportSelector importSelector = new TestAutoConfigurationImportSelector(null); private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory(); @@ -60,9 +69,7 @@ class AutoConfigurationImportSelectorTests { @BeforeEach void setup() { - this.importSelector.setBeanFactory(this.beanFactory); - this.importSelector.setEnvironment(this.environment); - this.importSelector.setResourceLoader(new DefaultResourceLoader()); + setupImportSelector(this.importSelector); } @Test @@ -151,6 +158,17 @@ void combinedExclusionsAreApplied() { ThymeleafAutoConfiguration.class.getName()); } + @Test + void removedExclusionsAreApplied() { + TestAutoConfigurationImportSelector importSelector = new TestAutoConfigurationImportSelector( + TestAutoConfiguration.class); + setupImportSelector(importSelector); + AnnotationMetadata metadata = AnnotationMetadata.introspect(BasicEnableAutoConfiguration.class); + assertThat(importSelector.selectImports(metadata)).contains(ReplacementAutoConfiguration.class.getName()); + this.environment.setProperty("spring.autoconfigure.exclude", DeprecatedAutoConfiguration.class.getName()); + assertThat(importSelector.selectImports(metadata)).doesNotContain(ReplacementAutoConfiguration.class.getName()); + } + @Test void nonAutoConfigurationClassExclusionsShouldThrowException() { assertThatIllegalStateException() @@ -208,6 +226,22 @@ void getExclusionFilterReuseFilters() { assertThat(this.importSelector.getExclusionFilter().test("com.example.C")).isTrue(); } + @Test + void soringConsidersReplacements() { + TestAutoConfigurationImportSelector importSelector = new TestAutoConfigurationImportSelector( + TestAutoConfiguration.class); + setupImportSelector(importSelector); + AnnotationMetadata metadata = AnnotationMetadata.introspect(BasicEnableAutoConfiguration.class); + assertThat(importSelector.selectImports(metadata)).containsExactly( + AfterDeprecatedAutoConfiguration.class.getName(), ReplacementAutoConfiguration.class.getName()); + Group group = BeanUtils.instantiateClass(importSelector.getImportGroup()); + ((BeanFactoryAware) group).setBeanFactory(this.beanFactory); + group.process(metadata, importSelector); + Stream imports = StreamSupport.stream(group.selectImports().spliterator(), false); + assertThat(imports.map(Entry::getImportClassName)).containsExactly(ReplacementAutoConfiguration.class.getName(), + AfterDeprecatedAutoConfiguration.class.getName()); + } + private String[] selectImports(Class source) { return this.importSelector.selectImports(AnnotationMetadata.introspect(source)); } @@ -216,10 +250,20 @@ private List getAutoConfigurationClassNames() { return ImportCandidates.load(AutoConfiguration.class, getClass().getClassLoader()).getCandidates(); } + private void setupImportSelector(TestAutoConfigurationImportSelector importSelector) { + importSelector.setBeanFactory(this.beanFactory); + importSelector.setEnvironment(this.environment); + importSelector.setResourceLoader(new DefaultResourceLoader()); + } + private final class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector { private AutoConfigurationImportEvent lastEvent; + TestAutoConfigurationImportSelector(Class autoConfigurationAnnotation) { + super(autoConfigurationAnnotation); + } + @Override protected List getAutoConfigurationImportFilters() { return AutoConfigurationImportSelectorTests.this.filters; @@ -320,4 +364,23 @@ private final class SpringBootApplicationWithClassNameExclusions { } + static class DeprecatedAutoConfiguration { + + } + + static class ReplacementAutoConfiguration { + + } + + @AutoConfigureAfter(DeprecatedAutoConfiguration.class) + static class AfterDeprecatedAutoConfiguration { + + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface TestAutoConfiguration { + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacementsTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacementsTests.java new file mode 100644 index 000000000000..30ae61950a18 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReplacementsTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2024 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.autoconfigure; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AutoConfigurationReplacements}. + * + * @author Phillip Webb + */ +class AutoConfigurationReplacementsTests { + + private final AutoConfigurationReplacements replacements = AutoConfigurationReplacements + .load(TestAutoConfigurationReplacements.class, null); + + @Test + void replaceWhenMatchReplacesClassName() { + assertThat(this.replacements.replace("com.example.A1")).isEqualTo("com.example.A2"); + } + + @Test + void replaceWhenNoMatchReturnsOriginalClassName() { + assertThat(this.replacements.replace("com.example.Z1")).isEqualTo("com.example.Z1"); + } + + @Test + void replaceAllReplacesAllMatching() { + Set classNames = new LinkedHashSet<>( + List.of("com.example.A1", "com.example.B1", "com.example.Y1", "com.example.Z1")); + assertThat(this.replacements.replaceAll(classNames)).containsExactly("com.example.A2", "com.example.B2", + "com.example.Y1", "com.example.Z1"); + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface TestAutoConfigurationReplacements { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java index 93a00ba89cc0..9352bbc7f3b2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.function.UnaryOperator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -62,10 +63,14 @@ class AutoConfigurationSorterTests { private static final String A3 = AutoConfigureA3.class.getName(); + private static final String A_WITH_REPLACED = AutoConfigureAWithReplaced.class.getName(); + private static final String B = AutoConfigureB.class.getName(); private static final String B2 = AutoConfigureB2.class.getName(); + private static final String B_WITH_REPLACED = AutoConfigureBWithReplaced.class.getName(); + private static final String C = AutoConfigureC.class.getName(); private static final String D = AutoConfigureD.class.getName(); @@ -86,13 +91,16 @@ class AutoConfigurationSorterTests { private static final String Z2 = AutoConfigureZ2.class.getName(); + private static final UnaryOperator REPLACEMENT_MAPPER = (name) -> name.replace("Deprecated", ""); + private AutoConfigurationSorter sorter; private AutoConfigurationMetadata autoConfigurationMetadata = mock(AutoConfigurationMetadata.class); @BeforeEach void setup() { - this.sorter = new AutoConfigurationSorter(new SkipCycleMetadataReaderFactory(), this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(new SkipCycleMetadataReaderFactory(), this.autoConfigurationMetadata, + REPLACEMENT_MAPPER); } @Test @@ -117,11 +125,17 @@ void byAutoConfigureAfterAliasFor() { void byAutoConfigureAfterAliasForWithProperties() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A3, B2, C); - this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER); List actual = getInPriorityOrder(A3, B2, C); assertThat(actual).containsExactly(C, B2, A3); } + @Test + void byAutoConfigureAfterWithDeprecated() { + List actual = getInPriorityOrder(A_WITH_REPLACED, B_WITH_REPLACED, C); + assertThat(actual).containsExactly(C, B_WITH_REPLACED, A_WITH_REPLACED); + } + @Test void byAutoConfigureBefore() { List actual = getInPriorityOrder(X, Y, Z); @@ -138,7 +152,7 @@ void byAutoConfigureBeforeAliasFor() { void byAutoConfigureBeforeAliasForWithProperties() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(X, Y2, Z2); - this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER); List actual = getInPriorityOrder(X, Y2, Z2); assertThat(actual).containsExactly(Z2, Y2, X); } @@ -175,7 +189,8 @@ void byAutoConfigureAfterWithMissing() { @Test void byAutoConfigureAfterWithCycle() { - this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(new CachingMetadataReaderFactory(), this.autoConfigurationMetadata, + REPLACEMENT_MAPPER); assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(A, B, C, D)) .withMessageContaining("AutoConfigure cycle detected"); } @@ -184,7 +199,7 @@ void byAutoConfigureAfterWithCycle() { void usesAnnotationPropertiesWhenPossible() throws Exception { MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A2, B, C, W2, X); - this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER); List actual = getInPriorityOrder(A2, B, C, W2, X); assertThat(actual).containsExactly(C, W2, B, A2, X); } @@ -193,7 +208,7 @@ void usesAnnotationPropertiesWhenPossible() throws Exception { void useAnnotationWithNoDirectLink() throws Exception { MetadataReaderFactory readerFactory = new SkipCycleMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, E); - this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER); List actual = getInPriorityOrder(A, E); assertThat(actual).containsExactly(E, A); } @@ -202,7 +217,7 @@ void useAnnotationWithNoDirectLink() throws Exception { void useAnnotationWithNoDirectLinkAndCycle() throws Exception { MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); this.autoConfigurationMetadata = getAutoConfigurationMetadata(A, B, D); - this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata); + this.sorter = new AutoConfigurationSorter(readerFactory, this.autoConfigurationMetadata, REPLACEMENT_MAPPER); assertThatIllegalStateException().isThrownBy(() -> getInPriorityOrder(D, B)) .withMessageContaining("AutoConfigure cycle detected"); } @@ -307,6 +322,11 @@ static class AutoConfigureA3 { } + @AutoConfigureAfter(AutoConfigureBWithReplaced.class) + public static class AutoConfigureAWithReplaced { + + } + @AutoConfigureAfter({ AutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class }) static class AutoConfigureB { @@ -317,10 +337,21 @@ static class AutoConfigureB2 { } + @AutoConfigureAfter({ DeprecatedAutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class }) + public static class AutoConfigureBWithReplaced { + + } + static class AutoConfigureC { } + // @DeprecatedAutoConfiguration(replacement = + // "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureC") + public static class DeprecatedAutoConfigureC { + + } + @AutoConfigureAfter(AutoConfigureA.class) static class AutoConfigureD { @@ -354,6 +385,12 @@ static class AutoConfigureY2 { } + // @DeprecatedAutoConfiguration(replacement = + // "org.springframework.boot.autoconfigure.AutoConfigurationSorterTests$AutoConfigureY") + public static class DeprecatedAutoConfigureY { + + } + @AutoConfigureBefore(AutoConfigureY.class) static class AutoConfigureZ { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationsTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationsTests.java index 3b7435594a4f..dcfb3da0d7f2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationsTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure; +import java.util.Arrays; + import org.junit.jupiter.api.Test; import org.springframework.boot.context.annotation.Configurations; @@ -36,6 +38,26 @@ void ofShouldCreateOrderedConfigurations() { AutoConfigureA.class); } + @Test + void whenHasReplacementForAutoConfigureAfterShouldCreateOrderedConfigurations() { + Configurations configurations = new AutoConfigurations(this::replaceB, + Arrays.asList(AutoConfigureA.class, AutoConfigureB2.class)); + assertThat(Configurations.getClasses(configurations)).containsExactly(AutoConfigureB2.class, + AutoConfigureA.class); + } + + @Test + void whenHasReplacementForClassShouldReplaceClass() { + Configurations configurations = new AutoConfigurations(this::replaceB, + Arrays.asList(AutoConfigureA.class, AutoConfigureB.class)); + assertThat(Configurations.getClasses(configurations)).containsExactly(AutoConfigureB2.class, + AutoConfigureA.class); + } + + private String replaceB(String className) { + return (!AutoConfigureB.class.getName().equals(className)) ? className : AutoConfigureB2.class.getName(); + } + @AutoConfigureAfter(AutoConfigureB.class) static class AutoConfigureA { @@ -45,4 +67,8 @@ static class AutoConfigureB { } + static class AutoConfigureB2 { + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java index 728fbb110d48..c413059ea8d4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/TestAutoConfigurationSorter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.List; import java.util.Properties; +import java.util.function.UnaryOperator; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -29,8 +30,9 @@ */ public class TestAutoConfigurationSorter extends AutoConfigurationSorter { - public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory) { - super(metadataReaderFactory, AutoConfigurationMetadataLoader.loadMetadata(new Properties())); + public TestAutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory, + UnaryOperator replacementMapper) { + super(metadataReaderFactory, AutoConfigurationMetadataLoader.loadMetadata(new Properties()), replacementMapper); } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.imports b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.imports new file mode 100644 index 000000000000..3d625bcf4103 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.imports @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$AfterDeprecatedAutoConfiguration +org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$ReplacementAutoConfiguration + diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.replacements b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.replacements new file mode 100644 index 000000000000..b63c6b88df3c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$TestAutoConfiguration.replacements @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$DeprecatedAutoConfiguration=\ +org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$ReplacementAutoConfiguration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationReplacementsTests$TestAutoConfigurationReplacements.replacements b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationReplacementsTests$TestAutoConfigurationReplacements.replacements new file mode 100644 index 000000000000..9924a4a0c109 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationReplacementsTests$TestAutoConfigurationReplacements.replacements @@ -0,0 +1,2 @@ +com.example.A1=com.example.A2 +com.example.B1=com.example.B2 diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/developing-auto-configuration.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/developing-auto-configuration.adoc index ee1c3551b5e8..6ab3290e11de 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/developing-auto-configuration.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/developing-auto-configuration.adoc @@ -52,6 +52,26 @@ The order in which those beans are subsequently created is unaffected and is det +[[features.developing-auto-configuration.locating-auto-configuration-candidates.deprecating]] +=== Deprecating and Replacing Auto-configuration Classes + +You may need to occasionally deprecate auto-configuration classes and offer an alternative. +For example, you may want to change the package name where your auto-configuration class resides. + +Since auto-configuration classes may be referenced in `before`/`after` ordering and `excludes`, you'll need to add an additional file that tells Spring Boot how to deal with replacements. +To define replacements, create a `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements` file indicating the link between the old class and the new one. + +For example: + +[source] +---- +com.mycorp.libx.autoconfigure.LibXAutoConfiguration=com.mycorp.libx.autoconfigure.core.LibXAutoConfiguration +---- + +NOTE: The `AutoConfiguration.imports` file should also be updated to _only_ reference the replacement class. + + + [[features.developing-auto-configuration.condition-annotations]] == Condition Annotations diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/Configurations.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/Configurations.java index 49031a6e0c0a..8d86a545aad3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/Configurations.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/Configurations.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -25,6 +25,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,11 +61,32 @@ public abstract class Configurations { private static final Comparator COMPARATOR = OrderComparator.INSTANCE .thenComparing((other) -> other.getClass().getName()); + private final UnaryOperator>> sorter; + private final Set> classes; + /** + * Create a new {@link Configurations} instance. + * @param classes the configuration classes + */ protected Configurations(Collection> classes) { Assert.notNull(classes, "Classes must not be null"); Collection> sorted = sort(classes); + this.sorter = null; + this.classes = Collections.unmodifiableSet(new LinkedHashSet<>(sorted)); + } + + /** + * Create a new {@link Configurations} instance. + * @param sorter a {@link UnaryOperator} used to sort the configurations + * @param classes the configuration classes + * @since 3.4.0 + */ + protected Configurations(UnaryOperator>> sorter, Collection> classes) { + Assert.notNull(sorter, "Sorter must not be null"); + Assert.notNull(classes, "Classes must not be null"); + Collection> sorted = sorter.apply(classes); + this.sorter = sorter; this.classes = Collections.unmodifiableSet(new LinkedHashSet<>(sorted)); } @@ -72,7 +94,10 @@ protected Configurations(Collection> classes) { * Sort configuration classes into the order that they should be applied. * @param classes the classes to sort * @return a sorted set of classes + * @deprecated since 3.4.0 for removal in 3.6.0 in favor of + * {@link #Configurations(UnaryOperator, Collection)} */ + @Deprecated(since = "3.4.0", forRemoval = true) protected Collection> sort(Collection> classes) { return classes; } @@ -90,6 +115,9 @@ protected final Set> getClasses() { protected Configurations merge(Configurations other) { Set> mergedClasses = new LinkedHashSet<>(getClasses()); mergedClasses.addAll(other.getClasses()); + if (this.sorter != null) { + mergedClasses = new LinkedHashSet<>(this.sorter.apply(mergedClasses)); + } return merge(mergedClasses); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/ImportCandidates.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/ImportCandidates.java index 59fe569e94fb..5758a898d856 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/ImportCandidates.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/annotation/ImportCandidates.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -67,9 +67,8 @@ public List getCandidates() { } /** - * Loads the names of import candidates from the classpath. - * - * The names of the import candidates are stored in files named + * Loads the names of import candidates from the classpath. The names of the import + * candidates are stored in files named * {@code META-INF/spring/full-qualified-annotation-name.imports} on the classpath. * Every line contains the full qualified name of the candidate class. Comments are * supported using the # character. diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/annotation/ConfigurationsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/annotation/ConfigurationsTests.java index 71b36adf9e09..23d50cd4c7fd 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/annotation/ConfigurationsTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/annotation/ConfigurationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Set; +import java.util.function.UnaryOperator; import org.junit.jupiter.api.Test; @@ -43,32 +44,61 @@ class ConfigurationsTests { @Test void createWhenClassesIsNullShouldThrowException() { - assertThatIllegalArgumentException().isThrownBy(() -> new TestConfigurations(null)) + assertThatIllegalArgumentException().isThrownBy(() -> new TestConfigurations((Collection>) null)) .withMessageContaining("Classes must not be null"); } @Test - void createShouldSortClasses() { - TestSortedConfigurations configurations = new TestSortedConfigurations( + @Deprecated(since = "3.4.0", forRemoval = true) + void createShouldSortClassesUsingSortMethod() { + TestDeprecatedSortedConfigurations configurations = new TestDeprecatedSortedConfigurations( Arrays.asList(OutputStream.class, InputStream.class)); assertThat(configurations.getClasses()).containsExactly(InputStream.class, OutputStream.class); } @Test - void getClassesShouldMergeByClassAndSort() { - Configurations c1 = new TestSortedConfigurations(Arrays.asList(OutputStream.class, InputStream.class)); + @Deprecated(since = "3.4.0", forRemoval = true) + void getClassesShouldMergeByClassAndSortUsingSortMethod() { + Configurations c1 = new TestDeprecatedSortedConfigurations( + Arrays.asList(OutputStream.class, InputStream.class)); Configurations c2 = new TestConfigurations(Collections.singletonList(Short.class)); - Configurations c3 = new TestSortedConfigurations(Arrays.asList(String.class, Integer.class)); + Configurations c3 = new TestDeprecatedSortedConfigurations(Arrays.asList(String.class, Integer.class)); Configurations c4 = new TestConfigurations(Arrays.asList(Long.class, Byte.class)); Class[] classes = Configurations.getClasses(c1, c2, c3, c4); assertThat(classes).containsExactly(Short.class, Long.class, Byte.class, InputStream.class, Integer.class, OutputStream.class, String.class); } + @Test + void createShouldSortClasses() { + TestConfigurations configurations = new TestConfigurations(Sorter.instance, OutputStream.class, + InputStream.class); + assertThat(configurations.getClasses()).containsExactly(InputStream.class, OutputStream.class); + } + + @Test + void getClassesShouldMergeByClassAndSort() { + Configurations c1 = new TestSortedConfigurations(OutputStream.class, InputStream.class); + Configurations c2 = new TestConfigurations(Short.class); + Configurations c3 = new TestSortedConfigurations(String.class, Integer.class); + Configurations c4 = new TestConfigurations(Long.class, Byte.class); + Class[] classes = Configurations.getClasses(c1, c2, c3, c4); + assertThat(classes).containsExactly(Short.class, Long.class, Byte.class, InputStream.class, Integer.class, + OutputStream.class, String.class); + } + @Order(Ordered.HIGHEST_PRECEDENCE) static class TestConfigurations extends Configurations { - protected TestConfigurations(Collection> classes) { + TestConfigurations(Class... classes) { + this(Arrays.asList(classes)); + } + + TestConfigurations(UnaryOperator>> sorter, Class... classes) { + super(sorter, Arrays.asList(classes)); + } + + TestConfigurations(Collection> classes) { super(classes); } @@ -82,20 +112,51 @@ protected Configurations merge(Set> mergedClasses) { @Order(Ordered.LOWEST_PRECEDENCE) static class TestSortedConfigurations extends Configurations { + protected TestSortedConfigurations(Class... classes) { + this(Arrays.asList(classes)); + } + protected TestSortedConfigurations(Collection> classes) { + super(Sorter.instance, classes); + } + + @Override + protected Configurations merge(Set> mergedClasses) { + return new TestSortedConfigurations(mergedClasses); + } + + } + + @Order(Ordered.LOWEST_PRECEDENCE) + @SuppressWarnings("removal") + static class TestDeprecatedSortedConfigurations extends Configurations { + + protected TestDeprecatedSortedConfigurations(Collection> classes) { super(classes); } @Override protected Collection> sort(Collection> classes) { - ArrayList> sorted = new ArrayList<>(classes); - sorted.sort(Comparator.comparing(ClassUtils::getShortName)); - return sorted; + return Sorter.instance.apply(classes); } @Override protected Configurations merge(Set> mergedClasses) { - return new TestSortedConfigurations(mergedClasses); + return new TestDeprecatedSortedConfigurations(mergedClasses); + } + + } + + static class Sorter implements UnaryOperator>> { + + static final Sorter instance = new Sorter(); + + @Override + public Collection> apply(Collection> classes) { + ArrayList> sorted = new ArrayList<>(classes); + sorted.sort(Comparator.comparing(ClassUtils::getShortName)); + return sorted; + } } From e5fde685d4857d8d23a0a55a746c8c408a0e7fd6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 9 Sep 2024 19:20:47 -0700 Subject: [PATCH 103/271] Rename OpenTelemetryAutoConfiguration in tracing package Deprecate and replace `OpenTelemetryAutoConfiguration` with `OpenTelemetryTracingAutoConfiguration`. Closes gh-41991 --- .../OpenTelemetryAutoConfiguration.java | 169 +-------------- ...penTelemetryPropagationConfigurations.java | 2 +- ...OpenTelemetryTracingAutoConfiguration.java | 198 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 2 +- ...toconfigure.AutoConfiguration.replacements | 1 + ...intsAutoConfigurationIntegrationTests.java | 6 +- .../BaggagePropagationIntegrationTests.java | 8 +- ...lemetryTracingAutoConfigurationTests.java} | 14 +- ...OtlpAutoConfigurationIntegrationTests.java | 2 +- 9 files changed, 222 insertions(+), 180 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements rename spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/{OpenTelemetryAutoConfigurationTests.java => OpenTelemetryTracingAutoConfigurationTests.java} (97%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java index a6d6a8f48c0b..cfe3153332b1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfiguration.java @@ -16,47 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing; -import java.util.List; - -import io.micrometer.tracing.SpanCustomizer; -import io.micrometer.tracing.exporter.SpanExportingPredicate; -import io.micrometer.tracing.exporter.SpanFilter; -import io.micrometer.tracing.exporter.SpanReporter; -import io.micrometer.tracing.otel.bridge.CompositeSpanExporter; -import io.micrometer.tracing.otel.bridge.EventListener; -import io.micrometer.tracing.otel.bridge.OtelBaggageManager; -import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; -import io.micrometer.tracing.otel.bridge.OtelPropagator; -import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer; -import io.micrometer.tracing.otel.bridge.OtelTracer; -import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; -import io.micrometer.tracing.otel.bridge.Slf4JEventListener; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.SpringBootVersion; -import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.util.CollectionUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry tracing. @@ -65,134 +25,9 @@ * @author Marcin Grzejszczak * @author Yanming Zhou * @since 3.0.0 + * @deprecated since 3.4.0 in favor of {@link OpenTelemetryTracingAutoConfiguration} */ -@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", - before = { MicrometerTracingAutoConfiguration.class, NoopTracerAutoConfiguration.class }) -@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class }) -@EnableConfigurationProperties(TracingProperties.class) -@Import({ OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage.class, - OpenTelemetryPropagationConfigurations.PropagationWithBaggage.class, - OpenTelemetryPropagationConfigurations.NoPropagation.class }) +@Deprecated(since = "3.4.0", forRemoval = true) public class OpenTelemetryAutoConfiguration { - private static final Log logger = LogFactory.getLog(OpenTelemetryAutoConfiguration.class); - - private final TracingProperties tracingProperties; - - OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) { - this.tracingProperties = tracingProperties; - if (!CollectionUtils.isEmpty(this.tracingProperties.getBaggage().getLocalFields())) { - logger.warn("Local fields are not supported when using OpenTelemetry!"); - } - } - - @Bean - @ConditionalOnMissingBean - SdkTracerProvider otelSdkTracerProvider(Resource resource, SpanProcessors spanProcessors, Sampler sampler, - ObjectProvider customizers) { - SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler).setResource(resource); - spanProcessors.forEach(builder::addSpanProcessor); - customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); - return builder.build(); - } - - @Bean - @ConditionalOnMissingBean - ContextPropagators otelContextPropagators(ObjectProvider textMapPropagators) { - return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators.orderedStream().toList())); - } - - @Bean - @ConditionalOnMissingBean - Sampler otelSampler() { - Sampler rootSampler = Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability()); - return Sampler.parentBased(rootSampler); - } - - @Bean - @ConditionalOnMissingBean - SpanProcessors spanProcessors(ObjectProvider spanProcessors) { - return SpanProcessors.of(spanProcessors.orderedStream().toList()); - } - - @Bean - BatchSpanProcessor otelSpanProcessor(SpanExporters spanExporters, - ObjectProvider spanExportingPredicates, ObjectProvider spanReporters, - ObjectProvider spanFilters, ObjectProvider meterProvider) { - BatchSpanProcessorBuilder builder = BatchSpanProcessor - .builder(new CompositeSpanExporter(spanExporters.list(), spanExportingPredicates.orderedStream().toList(), - spanReporters.orderedStream().toList(), spanFilters.orderedStream().toList())); - meterProvider.ifAvailable(builder::setMeterProvider); - return builder.build(); - } - - @Bean - @ConditionalOnMissingBean - SpanExporters spanExporters(ObjectProvider spanExporters) { - return SpanExporters.of(spanExporters.orderedStream().toList()); - } - - @Bean - @ConditionalOnMissingBean - Tracer otelTracer(OpenTelemetry openTelemetry) { - return openTelemetry.getTracer("org.springframework.boot", SpringBootVersion.getVersion()); - } - - @Bean - @ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class) - OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher, - OtelCurrentTraceContext otelCurrentTraceContext) { - List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); - List tagFields = this.tracingProperties.getBaggage().getTagFields(); - return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, - new OtelBaggageManager(otelCurrentTraceContext, remoteFields, tagFields)); - } - - @Bean - @ConditionalOnMissingBean - OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) { - return new OtelPropagator(contextPropagators, tracer); - } - - @Bean - @ConditionalOnMissingBean - EventPublisher otelTracerEventPublisher(List eventListeners) { - return new OTelEventPublisher(eventListeners); - } - - @Bean - @ConditionalOnMissingBean - OtelCurrentTraceContext otelCurrentTraceContext() { - return new OtelCurrentTraceContext(); - } - - @Bean - @ConditionalOnMissingBean - Slf4JEventListener otelSlf4JEventListener() { - return new Slf4JEventListener(); - } - - @Bean - @ConditionalOnMissingBean(SpanCustomizer.class) - OtelSpanCustomizer otelSpanCustomizer() { - return new OtelSpanCustomizer(); - } - - static class OTelEventPublisher implements EventPublisher { - - private final List listeners; - - OTelEventPublisher(List listeners) { - this.listeners = listeners; - } - - @Override - public void publishEvent(Object event) { - for (EventListener listener : this.listeners) { - listener.onEvent(event); - } - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryPropagationConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryPropagationConfigurations.java index e35e132a5592..cb2210ec516e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryPropagationConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryPropagationConfigurations.java @@ -32,7 +32,7 @@ /** * OpenTelemetry propagation configurations. They are imported by - * {@link OpenTelemetryAutoConfiguration}. + * {@link OpenTelemetryTracingAutoConfiguration}. * * @author Moritz Halbritter */ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java new file mode 100644 index 000000000000..896b7809fa69 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java @@ -0,0 +1,198 @@ +/* + * Copyright 2012-2024 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.actuate.autoconfigure.tracing; + +import java.util.List; + +import io.micrometer.tracing.SpanCustomizer; +import io.micrometer.tracing.exporter.SpanExportingPredicate; +import io.micrometer.tracing.exporter.SpanFilter; +import io.micrometer.tracing.exporter.SpanReporter; +import io.micrometer.tracing.otel.bridge.CompositeSpanExporter; +import io.micrometer.tracing.otel.bridge.EventListener; +import io.micrometer.tracing.otel.bridge.OtelBaggageManager; +import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; +import io.micrometer.tracing.otel.bridge.OtelPropagator; +import io.micrometer.tracing.otel.bridge.OtelSpanCustomizer; +import io.micrometer.tracing.otel.bridge.OtelTracer; +import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; +import io.micrometer.tracing.otel.bridge.Slf4JEventListener; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.SpringBootVersion; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.util.CollectionUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry tracing. + * + * @author Moritz Halbritter + * @author Marcin Grzejszczak + * @author Yanming Zhou + * @since 3.0.0 + */ +@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", + before = { MicrometerTracingAutoConfiguration.class, NoopTracerAutoConfiguration.class }) +@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class }) +@EnableConfigurationProperties(TracingProperties.class) +@Import({ OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage.class, + OpenTelemetryPropagationConfigurations.PropagationWithBaggage.class, + OpenTelemetryPropagationConfigurations.NoPropagation.class }) +public class OpenTelemetryTracingAutoConfiguration { + + private static final Log logger = LogFactory.getLog(OpenTelemetryTracingAutoConfiguration.class); + + private final TracingProperties tracingProperties; + + OpenTelemetryTracingAutoConfiguration(TracingProperties tracingProperties) { + this.tracingProperties = tracingProperties; + if (!CollectionUtils.isEmpty(this.tracingProperties.getBaggage().getLocalFields())) { + logger.warn("Local fields are not supported when using OpenTelemetry!"); + } + } + + @Bean + @ConditionalOnMissingBean + SdkTracerProvider otelSdkTracerProvider(Resource resource, SpanProcessors spanProcessors, Sampler sampler, + ObjectProvider customizers) { + SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler).setResource(resource); + spanProcessors.forEach(builder::addSpanProcessor); + customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder.build(); + } + + @Bean + @ConditionalOnMissingBean + ContextPropagators otelContextPropagators(ObjectProvider textMapPropagators) { + return ContextPropagators.create(TextMapPropagator.composite(textMapPropagators.orderedStream().toList())); + } + + @Bean + @ConditionalOnMissingBean + Sampler otelSampler() { + Sampler rootSampler = Sampler.traceIdRatioBased(this.tracingProperties.getSampling().getProbability()); + return Sampler.parentBased(rootSampler); + } + + @Bean + @ConditionalOnMissingBean + SpanProcessors spanProcessors(ObjectProvider spanProcessors) { + return SpanProcessors.of(spanProcessors.orderedStream().toList()); + } + + @Bean + BatchSpanProcessor otelSpanProcessor(SpanExporters spanExporters, + ObjectProvider spanExportingPredicates, ObjectProvider spanReporters, + ObjectProvider spanFilters, ObjectProvider meterProvider) { + BatchSpanProcessorBuilder builder = BatchSpanProcessor + .builder(new CompositeSpanExporter(spanExporters.list(), spanExportingPredicates.orderedStream().toList(), + spanReporters.orderedStream().toList(), spanFilters.orderedStream().toList())); + meterProvider.ifAvailable(builder::setMeterProvider); + return builder.build(); + } + + @Bean + @ConditionalOnMissingBean + SpanExporters spanExporters(ObjectProvider spanExporters) { + return SpanExporters.of(spanExporters.orderedStream().toList()); + } + + @Bean + @ConditionalOnMissingBean + Tracer otelTracer(OpenTelemetry openTelemetry) { + return openTelemetry.getTracer("org.springframework.boot", SpringBootVersion.getVersion()); + } + + @Bean + @ConditionalOnMissingBean(io.micrometer.tracing.Tracer.class) + OtelTracer micrometerOtelTracer(Tracer tracer, EventPublisher eventPublisher, + OtelCurrentTraceContext otelCurrentTraceContext) { + List remoteFields = this.tracingProperties.getBaggage().getRemoteFields(); + List tagFields = this.tracingProperties.getBaggage().getTagFields(); + return new OtelTracer(tracer, otelCurrentTraceContext, eventPublisher, + new OtelBaggageManager(otelCurrentTraceContext, remoteFields, tagFields)); + } + + @Bean + @ConditionalOnMissingBean + OtelPropagator otelPropagator(ContextPropagators contextPropagators, Tracer tracer) { + return new OtelPropagator(contextPropagators, tracer); + } + + @Bean + @ConditionalOnMissingBean + EventPublisher otelTracerEventPublisher(List eventListeners) { + return new OTelEventPublisher(eventListeners); + } + + @Bean + @ConditionalOnMissingBean + OtelCurrentTraceContext otelCurrentTraceContext() { + return new OtelCurrentTraceContext(); + } + + @Bean + @ConditionalOnMissingBean + Slf4JEventListener otelSlf4JEventListener() { + return new Slf4JEventListener(); + } + + @Bean + @ConditionalOnMissingBean(SpanCustomizer.class) + OtelSpanCustomizer otelSpanCustomizer() { + return new OtelSpanCustomizer(); + } + + static class OTelEventPublisher implements EventPublisher { + + private final List listeners; + + OTelEventPublisher(List listeners) { + this.listeners = listeners; + } + + @Override + public void publishEvent(Object event) { + for (EventListener listener : this.listeners) { + listener.onEvent(event); + } + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 5bcd3472fb05..4134ac853395 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -109,7 +109,7 @@ org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributor org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.NoopTracerAutoConfiguration -org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration +org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusExemplarsAutoConfiguration org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusSimpleclientExemplarsAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements new file mode 100644 index 000000000000..a1a43a51d0ea --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements @@ -0,0 +1 @@ +org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration=org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java index c131bd263a4e..babe9873ae23 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebEndpointsAutoConfigurationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -20,7 +20,7 @@ import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration; import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -85,7 +85,7 @@ private ReactiveWebApplicationContextRunner reactiveWebRunner() { RepositoryRestMvcAutoConfiguration.class, HazelcastAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class, RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class, BraveAutoConfiguration.class, - OpenTelemetryAutoConfiguration.class }) + OpenTelemetryTracingAutoConfiguration.class }) @SpringBootConfiguration static class WebEndpointTestApplication { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java index 6cda28003d30..6fa45e12f15b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java @@ -176,7 +176,7 @@ public ApplicationContextRunner get() { public ApplicationContextRunner get() { return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer()) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class, - org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class)) + org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class)) .withPropertyValues("management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp", "management.tracing.baggage.correlation.fields=country-code,bp"); } @@ -202,7 +202,7 @@ public ApplicationContextRunner get() { public ApplicationContextRunner get() { return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer()) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class, - org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class)) + org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class)) .withPropertyValues("management.tracing.propagation.type=W3C", "management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp", "management.tracing.baggage.correlation.fields=country-code,bp"); @@ -242,7 +242,7 @@ public ApplicationContextRunner get() { public ApplicationContextRunner get() { return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer()) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class, - org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class)) + org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class)) .withPropertyValues("management.tracing.propagation.type=B3", "management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp", "management.tracing.baggage.correlation.fields=country-code,bp"); @@ -256,7 +256,7 @@ public ApplicationContextRunner get() { public ApplicationContextRunner get() { return new ApplicationContextRunner().withInitializer(new OtelApplicationContextInitializer()) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class, - org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class)) + org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class)) .withPropertyValues("management.tracing.propagation.type=B3_MULTI", "management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp", "management.tracing.baggage.correlation.fields=country-code,bp"); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java similarity index 97% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java index 7b1a04d4528d..87bed95c17f3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java @@ -63,6 +63,7 @@ import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherApplicationListener.EventPublisherBeansContextWrapper; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.context.annotation.Configurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ForkedClassPath; @@ -79,18 +80,18 @@ import static org.mockito.Mockito.mock; /** - * Tests for {@link OpenTelemetryAutoConfiguration}. + * Tests for {@link OpenTelemetryTracingAutoConfiguration}. * * @author Moritz Halbritter * @author Andy Wilkinson * @author Yanming Zhou */ -class OpenTelemetryAutoConfigurationTests { +class OpenTelemetryTracingAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of( org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration.class, - OpenTelemetryAutoConfiguration.class)); + OpenTelemetryTracingAutoConfiguration.class)); @BeforeAll static void addWrapper() { @@ -345,6 +346,13 @@ void shouldPublishEventsWhenContextStorageIsInitializedEarly() { }); } + @Test + @SuppressWarnings("removal") + void shouldUseReplacementForDeprecatedVersion() { + Class[] classes = Configurations.getClasses(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)); + assertThat(classes).containsExactly(OpenTelemetryTracingAutoConfiguration.class); + } + private void initializeOpenTelemetry(ConfigurableApplicationContext context) { context.addApplicationListener(new OpenTelemetryEventPublisherApplicationListener()); Span.current(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java index 5bc38b90beff..e705eb68ebd8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpAutoConfigurationIntegrationTests.java @@ -66,7 +66,7 @@ class OtlpAutoConfigurationIntegrationTests { .withPropertyValues("management.tracing.sampling.probability=1.0") .withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class, OpenTelemetryAutoConfiguration.class, - org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class, + org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryTracingAutoConfiguration.class, OtlpAutoConfiguration.class)); private final MockWebServer mockWebServer = new MockWebServer(); From c88e0dd2e216c8ac5a51db0bd7e0b3529b1045a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:32:14 +0000 Subject: [PATCH 104/271] Bump jfrog/setup-jfrog-cli from 4.3.2 to 4.4.1 Bumps [jfrog/setup-jfrog-cli](https://github.com/jfrog/setup-jfrog-cli) from 4.3.2 to 4.4.1. - [Release notes](https://github.com/jfrog/setup-jfrog-cli/releases) - [Commits](https://github.com/jfrog/setup-jfrog-cli/compare/cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3...9fe0f98bd45b19e6e931d457f4e98f8f84461fb5) --- updated-dependencies: - dependency-name: jfrog/setup-jfrog-cli dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] See gh-42197 --- .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 d77184fa4c3a..ecd0147c108c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 + uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 env: JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - name: Promote build From 94c5a1e8d3dbc786c3dac1810bbb0215bcd2fb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Sep 2024 09:41:06 +0200 Subject: [PATCH 105/271] Polish "Bump jfrog/setup-jfrog-cli from 4.3.2 to 4.4.1" See gh-42197 --- .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 e0c61b138e14..eed0b5a89053 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@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 + uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 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 b71c6a6f8776..0fd8ebefbbde 100644 --- a/.github/actions/sync-to-maven-central/action.yml +++ b/.github/actions/sync-to-maven-central/action.yml @@ -20,7 +20,7 @@ runs: using: composite steps: - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@cb282f675d5f7add022d9b5e5fa139bb4e6ae9e3 # v4.3.2 + uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 env: JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - name: Download Release Artifacts From bcb2049c4061e217497b6598ac424c46d5fed3c4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 10 Sep 2024 09:11:36 +0100 Subject: [PATCH 106/271] Polish OpenTelemetryTracingAutoConfiguration --- .../tracing/OpenTelemetryTracingAutoConfiguration.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java index 896b7809fa69..3f88a7009ff3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfiguration.java @@ -64,10 +64,9 @@ * @author Moritz Halbritter * @author Marcin Grzejszczak * @author Yanming Zhou - * @since 3.0.0 + * @since 3.4.0 */ -@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", - before = { MicrometerTracingAutoConfiguration.class, NoopTracerAutoConfiguration.class }) +@AutoConfiguration(before = { MicrometerTracingAutoConfiguration.class, NoopTracerAutoConfiguration.class }) @ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class }) @EnableConfigurationProperties(TracingProperties.class) @Import({ OpenTelemetryPropagationConfigurations.PropagationWithoutBaggage.class, From 69de06ac2dd4020f2dcaa3aabba532107a7cc729 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 10 Sep 2024 09:26:42 +0100 Subject: [PATCH 107/271] Remove origin support for empty YAML maps Adding origin support caused an unexpected and unwanted change in behavior where configuration property binding would fail. The failure would occur because there was no way to convert from the entry in the environment that represents the empty map to the target type. The commit changes the YAML loader to drop empty maps, effectively reverting the map portion of 3bcbb0e59491ee7fbdc8de67e43d696bb4d2e074 and gh-21704. This aligns the behavior with the decision we made in gh-19095. Origin support for an empty list has been retained as it does not have a negative effect on configuration property binding. Prior to these changes, an empty YAML list was mapped to an origin tracked value that contains an empty list. Fully reverting 3bcbb0e59491ee7fbdc8de67e43d696bb4d2e074 would have resulted in an empty YAML list being mapped to an empty string. To avoid adding a collection type to the environment, we now map an empty YAML list to an origin tracked value that contains an empty string. Closes gh-35403 --- .../boot/env/OriginTrackedYamlLoader.java | 6 +++--- .../env/OriginTrackedYamlLoaderTests.java | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java index 0eef7864dae1..9082c5034fa8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java @@ -29,11 +29,11 @@ import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.Mark; -import org.yaml.snakeyaml.nodes.CollectionNode; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.SequenceNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; @@ -105,8 +105,8 @@ public Object getData() throws NoSuchElementException { @Override protected Object constructObject(Node node) { - if (node instanceof CollectionNode && ((CollectionNode) node).getValue().isEmpty()) { - return constructTrackedObject(node, super.constructObject(node)); + if (node instanceof SequenceNode sequenceNode && sequenceNode.getValue().isEmpty()) { + return constructTrackedObject(node, ""); } if (node instanceof ScalarNode) { if (!(node instanceof KeyScalarNode)) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java index ce5fa86434b1..58720b002351 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -17,7 +17,6 @@ package org.springframework.boot.env; import java.nio.charset.StandardCharsets; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -115,18 +114,19 @@ void processListOfMaps() { void processEmptyAndNullValues() { OriginTrackedValue empty = getValue("empty"); OriginTrackedValue nullValue = getValue("null-value"); + OriginTrackedValue emptyList = getValue("emptylist"); assertThat(empty.getValue()).isEqualTo(""); assertThat(getLocation(empty)).isEqualTo("27:8"); assertThat(nullValue.getValue()).isEqualTo(""); assertThat(getLocation(nullValue)).isEqualTo("28:13"); + assertThat(emptyList.getValue()).isEqualTo(""); + assertThat(getLocation(emptyList)).isEqualTo("29:12"); } @Test - void processEmptyListAndMap() { - OriginTrackedValue emptymap = getValue("emptymap"); - OriginTrackedValue emptylist = getValue("emptylist"); - assertThat(emptymap.getValue()).isEqualTo(Collections.emptyMap()); - assertThat(emptylist.getValue()).isEqualTo(Collections.emptyList()); + void emptyMapsAreDropped() { + Object emptyMap = getValue("emptymap"); + assertThat(emptyMap).isNull(); } @Test @@ -194,11 +194,12 @@ void canLoadFilesBiggerThan3Mb() { assertThat(loaded).isNotEmpty(); } - private OriginTrackedValue getValue(String name) { + @SuppressWarnings("unchecked") + private T getValue(String name) { if (this.result == null) { this.result = this.loader.load(); } - return (OriginTrackedValue) this.result.get(0).get(name); + return (T) this.result.get(0).get(name); } private String getLocation(OriginTrackedValue value) { From 2389a9c3433d41d1c2086b44cd595ee0504df07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Sep 2024 12:21:41 +0200 Subject: [PATCH 108/271] Fix syntax error in Pulsar section Closes gh-42200 --- .../docs/antora/modules/reference/pages/messaging/pulsar.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc index 558456f1ecad..28272fdee130 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc @@ -165,7 +165,7 @@ The following component creates a reactive listener endpoint on the `someTopic` include-code::MyBean[] Spring Boot auto-configuration provides all the components necessary for `ReactivePulsarListener`, such as the `ReactivePulsarListenerContainerFactory` and the consumer factory it uses to construct the underlying reactive Pulsar consumers. -You can configure these components by specifying any of the `spring.pulsar.listener.*` and `spring.pulsar.consumer.*` prefixed application properties. +You can configure these components by specifying any of the `spring.pulsar.listener.\*` and `spring.pulsar.consumer.*` prefixed application properties. If you need more control over the consumer factory configuration, consider registering one or more `ReactiveMessageConsumerBuilderCustomizer` beans. These customizers are applied to all consumers created by the factory, and therefore all `@ReactivePulsarListener` instances. From 5cbe0e84f93e73990b6f4a5b3f6d26ad351265ec Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Sun, 8 Sep 2024 15:22:37 -0500 Subject: [PATCH 109/271] Add Pulsar container factory customizer infrastructure This commit adds the ability for users to customize the auto-configured Spring for Apache Pulsar message container factories. Each container factory holds a set of container properties that is a common target for users to configure. Allowing the customization of these properties prevents a rapid increase of configuration properties. See gh-42182 --- .../pulsar/PulsarAutoConfiguration.java | 15 +- .../pulsar/PulsarConfiguration.java | 7 + .../PulsarContainerFactoryCustomizer.java | 39 +++++ .../PulsarContainerFactoryCustomizers.java | 58 ++++++++ .../PulsarReactiveAutoConfiguration.java | 7 +- .../pulsar/PulsarAutoConfigurationTests.java | 80 +++++++++- .../pulsar/PulsarConfigurationTests.java | 9 ++ ...ulsarContainerFactoryCustomizersTests.java | 140 ++++++++++++++++++ .../PulsarReactiveAutoConfigurationTests.java | 40 +++++ .../reference/pages/messaging/pulsar.adoc | 11 +- 10 files changed, 394 insertions(+), 12 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizer.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java index 5b5f9dc41235..d5cf598b2038 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java @@ -178,7 +178,7 @@ private void applyConsumerBuilderCustomizers(List> ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( PulsarConsumerFactory pulsarConsumerFactory, SchemaResolver schemaResolver, TopicResolver topicResolver, ObjectProvider pulsarTransactionManager, - Environment environment) { + PulsarContainerFactoryCustomizers containerFactoryCustomizers, Environment environment) { PulsarContainerProperties containerProperties = new PulsarContainerProperties(); containerProperties.setSchemaResolver(schemaResolver); containerProperties.setTopicResolver(topicResolver); @@ -187,7 +187,10 @@ ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( } pulsarTransactionManager.ifUnique(containerProperties.transactions()::setTransactionManager); this.propertiesMapper.customizeContainerProperties(containerProperties); - return new ConcurrentPulsarListenerContainerFactory<>(pulsarConsumerFactory, containerProperties); + ConcurrentPulsarListenerContainerFactory containerFactory = new ConcurrentPulsarListenerContainerFactory<>( + pulsarConsumerFactory, containerProperties); + containerFactoryCustomizers.customize(containerFactory); + return containerFactory; } @Bean @@ -215,14 +218,18 @@ private void applyReaderBuilderCustomizers(List> cust @Bean @ConditionalOnMissingBean(name = "pulsarReaderContainerFactory") DefaultPulsarReaderContainerFactory pulsarReaderContainerFactory(PulsarReaderFactory pulsarReaderFactory, - SchemaResolver schemaResolver, Environment environment) { + SchemaResolver schemaResolver, PulsarContainerFactoryCustomizers containerFactoryCustomizers, + Environment environment) { PulsarReaderContainerProperties readerContainerProperties = new PulsarReaderContainerProperties(); readerContainerProperties.setSchemaResolver(schemaResolver); if (Threading.VIRTUAL.isActive(environment)) { readerContainerProperties.setReaderTaskExecutor(new VirtualThreadTaskExecutor("pulsar-reader-")); } this.propertiesMapper.customizeReaderContainerProperties(readerContainerProperties); - return new DefaultPulsarReaderContainerFactory<>(pulsarReaderFactory, readerContainerProperties); + DefaultPulsarReaderContainerFactory containerFactory = new DefaultPulsarReaderContainerFactory<>( + pulsarReaderFactory, readerContainerProperties); + containerFactoryCustomizers.customize(containerFactory); + return containerFactory; } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfiguration.java index ea60717f7bdf..6716f86eb016 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfiguration.java @@ -188,4 +188,11 @@ PulsarTopicBuilder pulsarTopicBuilder() { this.properties.getDefaults().getTopic().getNamespace()); } + @Bean + @ConditionalOnMissingBean + PulsarContainerFactoryCustomizers pulsarContainerFactoryCustomizers( + ObjectProvider> customizers) { + return new PulsarContainerFactoryCustomizers(customizers.orderedStream().toList()); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizer.java new file mode 100644 index 000000000000..17e3de79b45d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2024 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.autoconfigure.pulsar; + +import org.springframework.pulsar.config.PulsarContainerFactory; + +/** + * Callback interface that can be implemented by beans wishing to customize a + * {@link PulsarContainerFactory} before it is fully initialized, in particular to tune + * its configuration. + * + * @param the type of the {@link PulsarContainerFactory} + * @author Chris Bono + * @since 3.4.0 + */ +@FunctionalInterface +public interface PulsarContainerFactoryCustomizer> { + + /** + * Customize the container factory. + * @param containerFactory the {@code PulsarContainerFactory} to customize + */ + void customize(T containerFactory); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java new file mode 100644 index 000000000000..82bd14889883 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2024 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.autoconfigure.pulsar; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.util.LambdaSafe; +import org.springframework.pulsar.config.PulsarContainerFactory; +import org.springframework.pulsar.core.PulsarConsumerFactory; + +/** + * Invokes the available {@link PulsarContainerFactoryCustomizer} instances in the context + * for a given {@link PulsarConsumerFactory}. + * + * @author Chris Bono + * @since 3.4.0 + */ +public class PulsarContainerFactoryCustomizers { + + private final List> customizers; + + public PulsarContainerFactoryCustomizers(List> customizers) { + this.customizers = (customizers != null) ? new ArrayList<>(customizers) : Collections.emptyList(); + } + + /** + * Customize the specified {@link PulsarContainerFactory}. Locates all + * {@link PulsarContainerFactoryCustomizer} beans able to handle the specified + * instance and invoke {@link PulsarContainerFactoryCustomizer#customize} on them. + * @param the type of container factory + * @param containerFactory the container factory to customize + * @return the customized container factory + */ + @SuppressWarnings("unchecked") + public > T customize(T containerFactory) { + LambdaSafe.callbacks(PulsarContainerFactoryCustomizer.class, this.customizers, containerFactory) + .withLogger(PulsarContainerFactoryCustomizers.class) + .invoke((customizer) -> customizer.customize(containerFactory)); + return containerFactory; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java index 5ca96e70579a..45d8d3037212 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java @@ -164,12 +164,15 @@ private void applyMessageConsumerBuilderCustomizers(List reactivePulsarListenerContainerFactory( ReactivePulsarConsumerFactory reactivePulsarConsumerFactory, SchemaResolver schemaResolver, - TopicResolver topicResolver) { + TopicResolver topicResolver, PulsarContainerFactoryCustomizers containerFactoryCustomizers) { ReactivePulsarContainerProperties containerProperties = new ReactivePulsarContainerProperties<>(); containerProperties.setSchemaResolver(schemaResolver); containerProperties.setTopicResolver(topicResolver); this.propertiesMapper.customizeContainerProperties(containerProperties); - return new DefaultReactivePulsarListenerContainerFactory<>(reactivePulsarConsumerFactory, containerProperties); + DefaultReactivePulsarListenerContainerFactory containerFactory = new DefaultReactivePulsarListenerContainerFactory<>( + reactivePulsarConsumerFactory, containerProperties); + containerFactoryCustomizers.customize(containerFactory); + return containerFactory; } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java index d8d30e942e32..6a467810fc79 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -72,6 +73,7 @@ import org.springframework.pulsar.core.SchemaResolver; import org.springframework.pulsar.core.TopicResolver; import org.springframework.pulsar.listener.PulsarContainerProperties.TransactionSettings; +import org.springframework.pulsar.reactive.config.DefaultReactivePulsarListenerContainerFactory; import org.springframework.pulsar.transaction.PulsarAwareTransactionManager; import org.springframework.test.util.ReflectionTestUtils; @@ -585,6 +587,44 @@ void whenTransactionEnabledFalseListenerContainerShouldNotUseTransactions() { }); } + @Test + void whenHasUserDefinedCustomizersAppliesInCorrectOrder() { + this.contextRunner.withUserConfiguration(ListenerContainerFactoryCustomizersConfig.class) + .run((context) -> assertThat(context).getBean(ConcurrentPulsarListenerContainerFactory.class) + .hasFieldOrPropertyWithValue("containerProperties.subscriptionName", ":bar:foo")); + } + + @TestConfiguration(proxyBeanMethods = false) + static class ListenerContainerFactoryCustomizersConfig { + + @Bean + @Order(50) + PulsarContainerFactoryCustomizer> customizerIgnored() { + return (__) -> { + throw new RuntimeException("should-not-have-matched"); + }; + } + + @Bean + @Order(200) + PulsarContainerFactoryCustomizer> customizerFoo() { + return (containerFactory) -> appendToSubscriptionName(containerFactory, ":foo"); + } + + @Bean + @Order(100) + PulsarContainerFactoryCustomizer> customizerBar() { + return (containerFactory) -> appendToSubscriptionName(containerFactory, ":bar"); + } + + private void appendToSubscriptionName(ConcurrentPulsarListenerContainerFactory containerFactory, + String valueToAppend) { + String name = Objects.toString(containerFactory.getContainerProperties().getSubscriptionName(), ""); + containerFactory.getContainerProperties().setSubscriptionName(name.concat(valueToAppend)); + } + + } + } @Nested @@ -617,7 +657,7 @@ void hasNoTopicBuilderWhenTopicDefaultsAreDisabled() { } @Test - void whenHasUserDefinedCustomizersAppliesInCorrectOrder() { + void whenHasUserDefinedReaderBuilderCustomizersAppliesInCorrectOrder() { this.contextRunner.withPropertyValues("spring.pulsar.reader.name=fromPropsCustomizer") .withUserConfiguration(ReaderBuilderCustomizersConfig.class) .run((context) -> { @@ -654,6 +694,13 @@ void whenVirtualThreadsAreEnabledOnJava20AndEarlierReaderShouldNotUseVirtualThre }); } + @Test + void whenHasUserDefinedFactoryCustomizersAppliesInCorrectOrder() { + this.contextRunner.withUserConfiguration(ReaderContainerFactoryCustomizersConfig.class) + .run((context) -> assertThat(context).getBean(DefaultPulsarReaderContainerFactory.class) + .hasFieldOrPropertyWithValue("containerProperties.readerListener", ":bar:foo")); + } + @TestConfiguration(proxyBeanMethods = false) static class ReaderBuilderCustomizersConfig { @@ -671,6 +718,37 @@ ReaderBuilderCustomizer customizerBar() { } + @TestConfiguration(proxyBeanMethods = false) + static class ReaderContainerFactoryCustomizersConfig { + + @Bean + @Order(50) + PulsarContainerFactoryCustomizer> customizerIgnored() { + return (__) -> { + throw new RuntimeException("should-not-have-matched"); + }; + } + + @Bean + @Order(200) + PulsarContainerFactoryCustomizer> customizerFoo() { + return (containerFactory) -> appendToReaderListener(containerFactory, ":foo"); + } + + @Bean + @Order(100) + PulsarContainerFactoryCustomizer> customizerBar() { + return (containerFactory) -> appendToReaderListener(containerFactory, ":bar"); + } + + private void appendToReaderListener(DefaultPulsarReaderContainerFactory containerFactory, + String valueToAppend) { + String name = Objects.toString(containerFactory.getContainerProperties().getReaderListener(), ""); + containerFactory.getContainerProperties().setReaderListener(name.concat(valueToAppend)); + } + + } + } @Nested diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfigurationTests.java index c61777862e43..a0763714ca5c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarConfigurationTests.java @@ -86,6 +86,15 @@ void whenHasUserDefinedConnectionDetailsBeanDoesNotAutoConfigureBean() { .isSameAs(customConnectionDetails)); } + @Test + void whenHasUserDefinedContainerFactoryCustomizersBeanDoesNotAutoConfigureBean() { + PulsarContainerFactoryCustomizers customizers = mock(PulsarContainerFactoryCustomizers.class); + this.contextRunner + .withBean("customContainerFactoryCustomizers", PulsarContainerFactoryCustomizers.class, () -> customizers) + .run((context) -> assertThat(context).getBean(PulsarContainerFactoryCustomizers.class) + .isSameAs(customizers)); + } + @Nested class ClientTests { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java new file mode 100644 index 000000000000..dfc290745a63 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012-2024 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.autoconfigure.pulsar; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; + +import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; +import org.springframework.pulsar.config.DefaultPulsarReaderContainerFactory; +import org.springframework.pulsar.config.ListenerContainerFactory; +import org.springframework.pulsar.config.PulsarContainerFactory; +import org.springframework.pulsar.config.PulsarListenerContainerFactory; +import org.springframework.pulsar.core.PulsarConsumerFactory; +import org.springframework.pulsar.listener.PulsarContainerProperties; +import org.springframework.pulsar.reactive.config.DefaultReactivePulsarListenerContainerFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Unit tests for {@link PulsarContainerFactoryCustomizers}. + * + * @author Chris Bono + */ +class PulsarContainerFactoryCustomizersTests { + + @Test + void customizeWithNullCustomizersShouldDoNothing() { + PulsarContainerFactory containerFactory = mock(PulsarContainerFactory.class); + new PulsarContainerFactoryCustomizers(null).customize(containerFactory); + BDDMockito.verifyNoInteractions(containerFactory); + } + + @SuppressWarnings("unchecked") + @Test + void customizeSimplePulsarContainerFactory() { + PulsarContainerFactoryCustomizers customizers = new PulsarContainerFactoryCustomizers( + Collections.singletonList(new SimplePulsarContainerFactoryCustomizer())); + PulsarContainerProperties containerProperties = new PulsarContainerProperties(); + ConcurrentPulsarListenerContainerFactory pulsarContainerFactory = new ConcurrentPulsarListenerContainerFactory<>( + mock(PulsarConsumerFactory.class), containerProperties); + customizers.customize(pulsarContainerFactory); + assertThat(pulsarContainerFactory.getContainerProperties().getSubscriptionName()).isEqualTo("my-subscription"); + } + + @Test + void customizeShouldCheckGeneric() { + List> list = new ArrayList<>(); + list.add(new TestCustomizer<>()); + list.add(new TestPulsarListenersContainerFactoryCustomizer()); + list.add(new TestConcurrentPulsarListenerContainerFactoryCustomizer()); + PulsarContainerFactoryCustomizers customizers = new PulsarContainerFactoryCustomizers(list); + + customizers.customize(mock(PulsarContainerFactory.class)); + assertThat(list.get(0).getCount()).isOne(); + assertThat(list.get(1).getCount()).isZero(); + assertThat(list.get(2).getCount()).isZero(); + + customizers.customize(mock(ConcurrentPulsarListenerContainerFactory.class)); + assertThat(list.get(0).getCount()).isEqualTo(2); + assertThat(list.get(1).getCount()).isOne(); + assertThat(list.get(2).getCount()).isOne(); + + customizers.customize(mock(DefaultReactivePulsarListenerContainerFactory.class)); + assertThat(list.get(0).getCount()).isEqualTo(3); + assertThat(list.get(1).getCount()).isEqualTo(2); + assertThat(list.get(2).getCount()).isOne(); + + customizers.customize(mock(DefaultPulsarReaderContainerFactory.class)); + assertThat(list.get(0).getCount()).isEqualTo(4); + assertThat(list.get(1).getCount()).isEqualTo(2); + assertThat(list.get(2).getCount()).isOne(); + } + + static class SimplePulsarContainerFactoryCustomizer + implements PulsarContainerFactoryCustomizer> { + + @Override + public void customize(ConcurrentPulsarListenerContainerFactory containerFactory) { + containerFactory.getContainerProperties().setSubscriptionName("my-subscription"); + } + + } + + /** + * Test customizer that will match all {@link PulsarListenerContainerFactory}. + */ + static class TestCustomizer> implements PulsarContainerFactoryCustomizer { + + private int count; + + @Override + public void customize(T pulsarContainerFactory) { + this.count++; + } + + int getCount() { + return this.count; + } + + } + + /** + * Test customizer that will match both + * {@link ConcurrentPulsarListenerContainerFactory} and + * {@link DefaultReactivePulsarListenerContainerFactory} as they both extend + * {@link ListenerContainerFactory}. + */ + static class TestPulsarListenersContainerFactoryCustomizer extends TestCustomizer> { + + } + + /** + * Test customizer that will match only + * {@link ConcurrentPulsarListenerContainerFactory}. + */ + static class TestConcurrentPulsarListenerContainerFactoryCustomizer + extends TestCustomizer> { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java index 0ecb9e85e9fb..9b2d58d0004e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Supplier; import com.github.benmanes.caffeine.cache.Caffeine; @@ -45,6 +46,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; +import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.core.DefaultSchemaResolver; import org.springframework.pulsar.core.DefaultTopicResolver; import org.springframework.pulsar.core.PulsarAdministration; @@ -382,6 +384,44 @@ void injectsExpectedBeans() { }); } + @Test + void whenHasUserDefinedFactoryCustomizersAppliesInCorrectOrder() { + this.contextRunner.withUserConfiguration(ListenerContainerFactoryCustomizersConfig.class) + .run((context) -> assertThat(context).getBean(DefaultReactivePulsarListenerContainerFactory.class) + .hasFieldOrPropertyWithValue("containerProperties.subscriptionName", ":bar:foo")); + } + + @TestConfiguration(proxyBeanMethods = false) + static class ListenerContainerFactoryCustomizersConfig { + + @Bean + @Order(50) + PulsarContainerFactoryCustomizer> customizerIgnored() { + return (__) -> { + throw new RuntimeException("should-not-have-matched"); + }; + } + + @Bean + @Order(200) + PulsarContainerFactoryCustomizer> customizerFoo() { + return (containerFactory) -> appendToSubscriptionName(containerFactory, ":foo"); + } + + @Bean + @Order(100) + PulsarContainerFactoryCustomizer> customizerBar() { + return (containerFactory) -> appendToSubscriptionName(containerFactory, ":bar"); + } + + private void appendToSubscriptionName(DefaultReactivePulsarListenerContainerFactory containerFactory, + String valueToAppend) { + String name = Objects.toString(containerFactory.getContainerProperties().getSubscriptionName(), ""); + containerFactory.getContainerProperties().setSubscriptionName(name.concat(valueToAppend)); + } + + } + } @Nested diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc index 28272fdee130..bc6a8c6e3837 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc @@ -150,11 +150,11 @@ include-code::MyBean[] Spring Boot auto-configuration provides all the components necessary for `PulsarListener`, such as the `PulsarListenerContainerFactory` and the consumer factory it uses to construct the underlying Pulsar consumers. You can configure these components by specifying any of the `spring.pulsar.listener.\*` and `spring.pulsar.consumer.*` prefixed application properties. -If you need more control over the consumer factory configuration, consider registering one or more `ConsumerBuilderCustomizer` beans. +If you need more control over the configuration of the consumer factory used by the container factory to create consumers, consider registering one or more `ConsumerBuilderCustomizer` beans. These customizers are applied to all consumers created by the factory, and therefore all `@PulsarListener` instances. You can also customize a single listener by setting the `consumerCustomizer` attribute of the `@PulsarListener` annotation. - +If you need more control over the actual container factory configuration, consider registering one or more `PulsarContainerFactoryCustomizer>` beans. [[messaging.pulsar.receiving-reactive]] == Receiving a Message Reactively @@ -167,11 +167,11 @@ include-code::MyBean[] Spring Boot auto-configuration provides all the components necessary for `ReactivePulsarListener`, such as the `ReactivePulsarListenerContainerFactory` and the consumer factory it uses to construct the underlying reactive Pulsar consumers. You can configure these components by specifying any of the `spring.pulsar.listener.\*` and `spring.pulsar.consumer.*` prefixed application properties. -If you need more control over the consumer factory configuration, consider registering one or more `ReactiveMessageConsumerBuilderCustomizer` beans. +If you need more control over the configuration of the consumer factory used by the container factory to create consumers, consider registering one or more `ReactiveMessageConsumerBuilderCustomizer` beans. These customizers are applied to all consumers created by the factory, and therefore all `@ReactivePulsarListener` instances. You can also customize a single listener by setting the `consumerCustomizer` attribute of the `@ReactivePulsarListener` annotation. - +If you need more control over the actual container factory configuration, consider registering one or more `PulsarContainerFactoryCustomizer>` beans. [[messaging.pulsar.reading]] == Reading a Message @@ -187,10 +187,11 @@ include-code::MyBean[] The `@PulsarReader` relies on a `PulsarReaderFactory` to create the underlying Pulsar reader. Spring Boot auto-configuration provides this reader factory which can be customized by setting any of the `spring.pulsar.reader.*` prefixed application properties. -If you need more control over the reader factory configuration, consider registering one or more `ReaderBuilderCustomizer` beans. +If you need more control over the configuration of the reader factory used by the container factory to create readers, consider registering one or more `ReaderBuilderCustomizer` beans. These customizers are applied to all readers created by the factory, and therefore all `@PulsarReader` instances. You can also customize a single listener by setting the `readerCustomizer` attribute of the `@PulsarReader` annotation. +If you need more control over the actual container factory configuration, consider registering one or more `PulsarContainerFactoryCustomizer>` beans. [[messaging.pulsar.reading-reactive]] From 5b25a37a36d99c174234bcf9fb815e44662bf833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Sep 2024 12:01:07 +0200 Subject: [PATCH 110/271] Polish "Add Pulsar container factory customizer infrastructure" See gh-42182 --- .../pulsar/PulsarAutoConfiguration.java | 6 +++--- .../pulsar/PulsarAutoConfigurationTests.java | 19 ++++++++++--------- ...ulsarContainerFactoryCustomizersTests.java | 8 ++++---- .../PulsarReactiveAutoConfigurationTests.java | 10 +++++----- .../reference/pages/messaging/pulsar.adoc | 8 ++++---- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java index d5cf598b2038..a38a95f5a5db 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java @@ -178,7 +178,7 @@ private void applyConsumerBuilderCustomizers(List> ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( PulsarConsumerFactory pulsarConsumerFactory, SchemaResolver schemaResolver, TopicResolver topicResolver, ObjectProvider pulsarTransactionManager, - PulsarContainerFactoryCustomizers containerFactoryCustomizers, Environment environment) { + Environment environment, PulsarContainerFactoryCustomizers containerFactoryCustomizers) { PulsarContainerProperties containerProperties = new PulsarContainerProperties(); containerProperties.setSchemaResolver(schemaResolver); containerProperties.setTopicResolver(topicResolver); @@ -218,8 +218,8 @@ private void applyReaderBuilderCustomizers(List> cust @Bean @ConditionalOnMissingBean(name = "pulsarReaderContainerFactory") DefaultPulsarReaderContainerFactory pulsarReaderContainerFactory(PulsarReaderFactory pulsarReaderFactory, - SchemaResolver schemaResolver, PulsarContainerFactoryCustomizers containerFactoryCustomizers, - Environment environment) { + SchemaResolver schemaResolver, Environment environment, + PulsarContainerFactoryCustomizers containerFactoryCustomizers) { PulsarReaderContainerProperties readerContainerProperties = new PulsarReaderContainerProperties(); readerContainerProperties.setSchemaResolver(schemaResolver); if (Threading.VIRTUAL.isActive(environment)) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java index 6a467810fc79..fdc10771c1f9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -600,8 +599,8 @@ static class ListenerContainerFactoryCustomizersConfig { @Bean @Order(50) PulsarContainerFactoryCustomizer> customizerIgnored() { - return (__) -> { - throw new RuntimeException("should-not-have-matched"); + return (containerFactory) -> { + throw new IllegalStateException("should-not-have-matched"); }; } @@ -619,8 +618,9 @@ PulsarContainerFactoryCustomizer> cu private void appendToSubscriptionName(ConcurrentPulsarListenerContainerFactory containerFactory, String valueToAppend) { - String name = Objects.toString(containerFactory.getContainerProperties().getSubscriptionName(), ""); - containerFactory.getContainerProperties().setSubscriptionName(name.concat(valueToAppend)); + String subscriptionName = containerFactory.getContainerProperties().getSubscriptionName(); + String updatedValue = (subscriptionName != null) ? subscriptionName + valueToAppend : valueToAppend; + containerFactory.getContainerProperties().setSubscriptionName(updatedValue); } } @@ -724,8 +724,8 @@ static class ReaderContainerFactoryCustomizersConfig { @Bean @Order(50) PulsarContainerFactoryCustomizer> customizerIgnored() { - return (__) -> { - throw new RuntimeException("should-not-have-matched"); + return (containerFactory) -> { + throw new IllegalStateException("should-not-have-matched"); }; } @@ -743,8 +743,9 @@ PulsarContainerFactoryCustomizer> customi private void appendToReaderListener(DefaultPulsarReaderContainerFactory containerFactory, String valueToAppend) { - String name = Objects.toString(containerFactory.getContainerProperties().getReaderListener(), ""); - containerFactory.getContainerProperties().setReaderListener(name.concat(valueToAppend)); + Object readerListener = containerFactory.getContainerProperties().getReaderListener(); + String updatedValue = (readerListener != null) ? readerListener + valueToAppend : valueToAppend; + containerFactory.getContainerProperties().setReaderListener(updatedValue); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java index dfc290745a63..8b03635db225 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizersTests.java @@ -21,7 +21,6 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.mockito.BDDMockito; import org.springframework.pulsar.config.ConcurrentPulsarListenerContainerFactory; import org.springframework.pulsar.config.DefaultPulsarReaderContainerFactory; @@ -33,10 +32,11 @@ import org.springframework.pulsar.reactive.config.DefaultReactivePulsarListenerContainerFactory; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; /** - * Unit tests for {@link PulsarContainerFactoryCustomizers}. + * Tests for {@link PulsarContainerFactoryCustomizers}. * * @author Chris Bono */ @@ -46,11 +46,11 @@ class PulsarContainerFactoryCustomizersTests { void customizeWithNullCustomizersShouldDoNothing() { PulsarContainerFactory containerFactory = mock(PulsarContainerFactory.class); new PulsarContainerFactoryCustomizers(null).customize(containerFactory); - BDDMockito.verifyNoInteractions(containerFactory); + then(containerFactory).shouldHaveNoInteractions(); } - @SuppressWarnings("unchecked") @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) void customizeSimplePulsarContainerFactory() { PulsarContainerFactoryCustomizers customizers = new PulsarContainerFactoryCustomizers( Collections.singletonList(new SimplePulsarContainerFactoryCustomizer())); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java index 9b2d58d0004e..933b9a27174e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfigurationTests.java @@ -19,7 +19,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Supplier; import com.github.benmanes.caffeine.cache.Caffeine; @@ -397,8 +396,8 @@ static class ListenerContainerFactoryCustomizersConfig { @Bean @Order(50) PulsarContainerFactoryCustomizer> customizerIgnored() { - return (__) -> { - throw new RuntimeException("should-not-have-matched"); + return (containerFactory) -> { + throw new IllegalStateException("should-not-have-matched"); }; } @@ -416,8 +415,9 @@ PulsarContainerFactoryCustomizer containerFactory, String valueToAppend) { - String name = Objects.toString(containerFactory.getContainerProperties().getSubscriptionName(), ""); - containerFactory.getContainerProperties().setSubscriptionName(name.concat(valueToAppend)); + String subscriptionName = containerFactory.getContainerProperties().getSubscriptionName(); + String updatedValue = (subscriptionName != null) ? subscriptionName + valueToAppend : valueToAppend; + containerFactory.getContainerProperties().setSubscriptionName(updatedValue); } } diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc index bc6a8c6e3837..f758ba85eb58 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/messaging/pulsar.adoc @@ -150,11 +150,11 @@ include-code::MyBean[] Spring Boot auto-configuration provides all the components necessary for `PulsarListener`, such as the `PulsarListenerContainerFactory` and the consumer factory it uses to construct the underlying Pulsar consumers. You can configure these components by specifying any of the `spring.pulsar.listener.\*` and `spring.pulsar.consumer.*` prefixed application properties. -If you need more control over the configuration of the consumer factory used by the container factory to create consumers, consider registering one or more `ConsumerBuilderCustomizer` beans. +If you need more control over the configuration of the consumer factory, consider registering one or more `ConsumerBuilderCustomizer` beans. These customizers are applied to all consumers created by the factory, and therefore all `@PulsarListener` instances. You can also customize a single listener by setting the `consumerCustomizer` attribute of the `@PulsarListener` annotation. -If you need more control over the actual container factory configuration, consider registering one or more `PulsarContainerFactoryCustomizer>` beans. +If you need more control over the actual container factory configuration, consider registering one or more `PulsarContainerFactoryCustomizer>` beans. [[messaging.pulsar.receiving-reactive]] == Receiving a Message Reactively @@ -167,7 +167,7 @@ include-code::MyBean[] Spring Boot auto-configuration provides all the components necessary for `ReactivePulsarListener`, such as the `ReactivePulsarListenerContainerFactory` and the consumer factory it uses to construct the underlying reactive Pulsar consumers. You can configure these components by specifying any of the `spring.pulsar.listener.\*` and `spring.pulsar.consumer.*` prefixed application properties. -If you need more control over the configuration of the consumer factory used by the container factory to create consumers, consider registering one or more `ReactiveMessageConsumerBuilderCustomizer` beans. +If you need more control over the configuration of the consumer factory, consider registering one or more `ReactiveMessageConsumerBuilderCustomizer` beans. These customizers are applied to all consumers created by the factory, and therefore all `@ReactivePulsarListener` instances. You can also customize a single listener by setting the `consumerCustomizer` attribute of the `@ReactivePulsarListener` annotation. @@ -187,7 +187,7 @@ include-code::MyBean[] The `@PulsarReader` relies on a `PulsarReaderFactory` to create the underlying Pulsar reader. Spring Boot auto-configuration provides this reader factory which can be customized by setting any of the `spring.pulsar.reader.*` prefixed application properties. -If you need more control over the configuration of the reader factory used by the container factory to create readers, consider registering one or more `ReaderBuilderCustomizer` beans. +If you need more control over the configuration of the reader factory, consider registering one or more `ReaderBuilderCustomizer` beans. These customizers are applied to all readers created by the factory, and therefore all `@PulsarReader` instances. You can also customize a single listener by setting the `readerCustomizer` attribute of the `@PulsarReader` annotation. From a5367b52c27d14eabb9af2f553651b1c68451975 Mon Sep 17 00:00:00 2001 From: arefbehboudi Date: Mon, 9 Sep 2024 16:13:35 +0330 Subject: [PATCH 111/271] Polish See gh-42192 --- .../java/org/springframework/boot/json/JsonValueWriter.java | 5 +---- .../boot/logging/log4j2/Log4J2LoggingSystem.java | 4 +--- .../reactive/ApplicationContextServerWebExchangeMatcher.java | 2 +- .../security/servlet/ApplicationContextRequestMatcher.java | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java index be0535f7a9a5..00194b550431 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java @@ -103,12 +103,9 @@ else if (ObjectUtils.isArray(value)) { else if (value instanceof Map map) { writeObject(map::forEach); } - else if (value instanceof Number) { + else if (value instanceof Number || value instanceof Boolean) { append(value.toString()); } - else if (value instanceof Boolean) { - append(Boolean.TRUE.equals(value) ? "true" : "false"); - } else { writeString(value); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index 21d4d47a3971..694c8f5cba40 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -238,9 +238,7 @@ protected void loadConfiguration(LoggingInitializationContext initializationCont private void load(LoggingInitializationContext initializationContext, String location, LogFile logFile) { List overrides = getOverrides(initializationContext); - if (initializationContext != null) { - applySystemProperties(initializationContext.getEnvironment(), logFile); - } + applySystemProperties(initializationContext.getEnvironment(), logFile); loadConfiguration(location, logFile, overrides); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java index 88ea38ca5614..11dda128d247 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java @@ -29,7 +29,7 @@ /** * {@link ApplicationContext} backed {@link ServerWebExchangeMatcher}. Can work directly * with the {@link ApplicationContext}, obtain an existing bean or - * {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) create a new bean} + * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} * that is autowired in the usual way. * * @param the type of the context that the match method actually needs to use. Can be diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java index 0f2d9e3858ec..d96253c707d7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java @@ -30,7 +30,7 @@ /** * {@link ApplicationContext} backed {@link RequestMatcher}. Can work directly with the * {@link ApplicationContext}, obtain an existing bean or - * {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) create a new bean} + * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} * that is autowired in the usual way. * * @param the type of the context that the match method actually needs to use. Can be From ece5c6fe9e613831d2f0b2dd67ee4f7aa3828c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Sep 2024 12:42:23 +0200 Subject: [PATCH 112/271] Polish contribution See gh-42192 --- .../ApplicationContextServerWebExchangeMatcher.java | 6 +++--- .../security/servlet/ApplicationContextRequestMatcher.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java index 11dda128d247..685c49ae1bd3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -29,8 +29,8 @@ /** * {@link ApplicationContext} backed {@link ServerWebExchangeMatcher}. Can work directly * with the {@link ApplicationContext}, obtain an existing bean or - * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} - * that is autowired in the usual way. + * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} that is + * autowired in the usual way. * * @param the type of the context that the match method actually needs to use. Can be * an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java index d96253c707d7..2005f5b6f20b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -30,8 +30,8 @@ /** * {@link ApplicationContext} backed {@link RequestMatcher}. Can work directly with the * {@link ApplicationContext}, obtain an existing bean or - * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} - * that is autowired in the usual way. + * {@link AutowireCapableBeanFactory#createBean(Class) create a new bean} that is + * autowired in the usual way. * * @param the type of the context that the match method actually needs to use. Can be * an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class) From 74a9d11d1b3221a6966e586cd08f5a91577ba19c Mon Sep 17 00:00:00 2001 From: Samuel Lissner Date: Mon, 26 Aug 2024 17:14:21 +0200 Subject: [PATCH 113/271] Add Graylog Extended Log Format (GELF) for structured logging See gh-42158 --- .../reference/pages/features/logging.adoc | 48 ++++++ ...tendedLogFormatStructuredLogFormatter.java | 162 ++++++++++++++++++ .../logging/log4j2/StructuredLogLayout.java | 3 + ...tendedLogFormatStructuredLogFormatter.java | 160 +++++++++++++++++ .../logging/logback/StructuredLogEncoder.java | 5 + .../structured/CommonStructuredLogFormat.java | 6 + .../GraylogExtendedLogFormatService.java | 72 ++++++++ ...itional-spring-configuration-metadata.json | 28 ++- ...dLogFormatStructuredLogFormatterTests.java | 92 ++++++++++ ...dLogFormatStructuredLogFormatterTests.java | 95 ++++++++++ .../logback/StructuredLogEncoderTests.java | 2 +- .../GraylogExtendedLogFormatServiceTests.java | 76 ++++++++ 12 files changed, 747 insertions(+), 2 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java 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 f609f5762917..9041e89692aa 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 @@ -445,6 +445,7 @@ Structured logging is a technique where the log output is written in a well-defi Spring Boot supports structured logging and has support for the following JSON formats out of the box: * xref:#features.logging.structured.ecs[Elastic Common Schema (ECS)] +* xref:#features.logging.structured.gelf[Graylog Extended Log Format (GELF)] * xref:#features.logging.structured.logstash[Logstash] To enable structured logging, set the property configprop:logging.structured.format.console[] (for console output) or configprop:logging.structured.format.file[] (for file output) to the id of the format you want to use. @@ -492,6 +493,53 @@ logging: NOTE: configprop:logging.structured.ecs.service.name[] will default to configprop:spring.application.name[] if not specified. +NOTE: configprop:logging.structured.ecs.service.version[] will default to configprop:spring.application.version[] if not specified. + + + +[[features.logging.structured.gelf]] +=== Graylog Extended Log Format (GELF) +https://go2docs.graylog.org/current/getting_in_log_data/gelf.html[Graylog Extended Log Format] is a JSON based logging format for the Graylog log analytics platform. + +To enable the Graylog Extended Log Format, set the appropriate `format` property to `gelf`: + +[configprops,yaml] +---- +logging: + structured: + format: + console: gelf + file: gelf +---- + +A log line looks like this: + +[source,json] +---- +{"version":"1.1","short_message":"Hello structured logging!","timestamp":1.725530750186E9,"level":6,"_level_name":"INFO","_process_pid":9086,"_process_thread_name":"main","host":"spring-boot-gelf","_log_logger":"com.slissner.springbootgelf.ExampleLogger","_userId":"1","_testkey_testmessage":"test"} +---- + +This format also adds every key value pair contained in the MDC to the JSON object. +You can also use the https://www.slf4j.org/manual.html#fluent[SLF4J fluent logging API] to add key value pairs to the logged JSON object with the https://www.slf4j.org/apidocs/org/slf4j/spi/LoggingEventBuilder.html#addKeyValue(java.lang.String,java.lang.Object)[addKeyValue] method. + +The `service` values can be customized using `logging.structured.gelf.service` properties: + +[configprops,yaml] +---- +logging: + structured: + gelf: + service: + name: MyService + version: 1.0 + environment: Production + node-name: Primary +---- + +NOTE: configprop:logging.structured.gelf.service.name[] will default to configprop:spring.application.name[] if not specified. + +NOTE: configprop:logging.structured.gelf.service.version[] will default to configprop:spring.application.version[] if not specified. + [[features.logging.structured.logstash]] diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java new file mode 100644 index 000000000000..270de27f5cae --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -0,0 +1,162 @@ +/* + * Copyright 2012-2024 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.logging.log4j2; + +import java.math.BigDecimal; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.impl.ThrowableProxy; +import org.apache.logging.log4j.core.net.Severity; +import org.apache.logging.log4j.core.time.Instant; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.util.ReadOnlyStringMap; + +import org.springframework.boot.json.JsonWriter; +import org.springframework.boot.logging.structured.CommonStructuredLogFormat; +import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; +import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter; +import org.springframework.boot.logging.structured.StructuredLogFormatter; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * Log4j2 {@link StructuredLogFormatter} for + * {@link CommonStructuredLogFormat#GRAYLOG_EXTENDED_LOG_FORMAT}. Supports GELF version + * 1.1. + * + * @author Samuel Lissner + */ +class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructuredLogFormatter { + + /** + * Allowed characters in field names are any word character (letter, number, + * underscore), dashes and dots. + */ + private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w\\.\\-]*$"); + + /** + * Every field been sent and prefixed with an underscore "_" will be treated as an + * additional field. + */ + private static final String ADDITIONAL_FIELD_PREFIX = "_"; + + /** + * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server + * nodes omit this field automatically. + */ + private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("_id"); + + /** + * Default format to be used for the `full_message` property when there is a throwable + * present in the log event. + */ + private static final String DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT = "%s%n%n%s"; + + GraylogExtendedLogFormatStructuredLogFormatter(Environment environment) { + super((members) -> jsonMembers(environment, members)); + } + + private static void jsonMembers(Environment environment, JsonWriter.Members members) { + members.add("version", "1.1"); + + // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are + // ignoring this here. + members.add("short_message", LogEvent::getMessage).as(Message::getFormattedMessage); + + members.add("timestamp", LogEvent::getInstant) + .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); + members.add("level", GraylogExtendedLogFormatStructuredLogFormatter::convertLevel); + members.add("_level_name", LogEvent::getLevel).as(Level::name); + + members.add("_process_pid", environment.getProperty("spring.application.pid", Long.class)) + .when(Objects::nonNull); + members.add("_process_thread_name", LogEvent::getThreadName); + + GraylogExtendedLogFormatService.get(environment).jsonMembers(members); + + members.add("_log_logger", LogEvent::getLoggerName); + + members.from(LogEvent::getContextData) + .whenNot(ReadOnlyStringMap::isEmpty) + .usingPairs((contextData, pairs) -> contextData + .forEach((key, value) -> pairs.accept(makeAdditionalFieldName(key), value))); + + members.add().whenNotNull(LogEvent::getThrownProxy).usingMembers((eventMembers) -> { + final Function throwableProxyGetter = LogEvent::getThrownProxy; + + eventMembers.add("full_message", + GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable); + eventMembers.add("_error_type", throwableProxyGetter.andThen(ThrowableProxy::getThrowable)) + .whenNotNull() + .as(ObjectUtils::nullSafeClassName); + eventMembers.add("_error_stack_trace", + throwableProxyGetter.andThen(ThrowableProxy::getExtendedStackTraceAsString)); + eventMembers.add("_error_message", throwableProxyGetter.andThen(ThrowableProxy::getMessage)); + }); + } + + /** + * GELF requires "seconds since UNIX epoch with optional decimal places for + * milliseconds". To comply with this requirement, we format a POSIX timestamp + * with millisecond precision as e.g. "1725459730385" -> "1725459730.385" + * @param timeStamp the timestamp of the log message. Note it is not the standard Java + * `Instant` type but {@link org.apache.logging.log4j.core.time} + * @return the timestamp formatted as string with millisecond precision + */ + private static double formatTimeStamp(final Instant timeStamp) { + return new BigDecimal(timeStamp.getEpochMillisecond()).movePointLeft(3).doubleValue(); + } + + /** + * Converts the log4j2 event level to the Syslog event level code. + * @param event the log event + * @return an integer representing the syslog log level code + * @see Severity class from Log4j2 which contains the conversion logic + */ + private static int convertLevel(final LogEvent event) { + return Severity.getSeverity(event.getLevel()).getCode(); + } + + private static String formatFullMessageWithThrowable(final LogEvent event) { + return String.format(DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT, event.getMessage().getFormattedMessage(), + event.getThrownProxy().getExtendedStackTraceAsString()); + } + + private static String makeAdditionalFieldName(String fieldName) { + Assert.notNull(fieldName, "fieldName must not be null"); + Assert.isTrue(FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches(), + () -> String.format("fieldName must be a valid according to GELF standard. [fieldName=%s]", fieldName)); + Assert.isTrue(!ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName), () -> String.format( + "fieldName must not be an illegal additional field key according to GELF standard. [fieldName=%s]", + fieldName)); + + if (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) { + // No need to prepend the `ADDITIONAL_FIELD_PREFIX` in case the caller already + // has prepended the prefix. + return fieldName; + } + + return ADDITIONAL_FIELD_PREFIX + fieldName; + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredLogLayout.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredLogLayout.java index bdb99a9fd8b7..5e4b49fbdf1f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredLogLayout.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/StructuredLogLayout.java @@ -105,6 +105,9 @@ private void addCommonFormatters(CommonFormatters commonFormatters) { commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA, (instantiator) -> new ElasticCommonSchemaStructuredLogFormatter( instantiator.getArg(Environment.class))); + commonFormatters.add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT, + (instantiator) -> new GraylogExtendedLogFormatStructuredLogFormatter( + instantiator.getArg(Environment.class))); commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, (instantiator) -> new LogstashStructuredLogFormatter()); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java new file mode 100644 index 000000000000..b9f1d1fe62bd --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -0,0 +1,160 @@ +/* + * Copyright 2012-2024 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.logging.logback; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import ch.qos.logback.classic.pattern.ThrowableProxyConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.util.LevelToSyslogSeverity; +import org.slf4j.event.KeyValuePair; + +import org.springframework.boot.json.JsonWriter; +import org.springframework.boot.json.JsonWriter.PairExtractor; +import org.springframework.boot.logging.structured.CommonStructuredLogFormat; +import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; +import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter; +import org.springframework.boot.logging.structured.StructuredLogFormatter; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * Logback {@link StructuredLogFormatter} for + * {@link CommonStructuredLogFormat#GRAYLOG_EXTENDED_LOG_FORMAT}. Supports GELF version + * 1.1. + * + * @author Samuel Lissner + */ +class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructuredLogFormatter { + + /** + * Allowed characters in field names are any word character (letter, number, + * underscore), dashes and dots. + */ + private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w\\.\\-]*$"); + + /** + * Every field been sent and prefixed with an underscore "_" will be treated as an + * additional field. + */ + private static final String ADDITIONAL_FIELD_PREFIX = "_"; + + /** + * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server + * nodes omit this field automatically. + */ + private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("_id"); + + /** + * Default format to be used for the `full_message` property when there is a throwable + * present in the log event. + */ + private static final String DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT = "%s%n%n%s"; + + private static final PairExtractor keyValuePairExtractor = PairExtractor + .of((pair) -> makeAdditionalFieldName(pair.key), (pair) -> pair.value); + + GraylogExtendedLogFormatStructuredLogFormatter(Environment environment, + ThrowableProxyConverter throwableProxyConverter) { + super((members) -> jsonMembers(environment, throwableProxyConverter, members)); + } + + private static void jsonMembers(Environment environment, ThrowableProxyConverter throwableProxyConverter, + JsonWriter.Members members) { + members.add("version", "1.1"); + + // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are + // ignoring this here. + members.add("short_message", ILoggingEvent::getFormattedMessage); + + members.add("timestamp", ILoggingEvent::getTimeStamp) + .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); + members.add("level", LevelToSyslogSeverity::convert); + members.add("_level_name", ILoggingEvent::getLevel); + + members.add("_process_pid", environment.getProperty("spring.application.pid", Long.class)) + .when(Objects::nonNull); + members.add("_process_thread_name", ILoggingEvent::getThreadName); + + GraylogExtendedLogFormatService.get(environment).jsonMembers(members); + + members.add("_log_logger", ILoggingEvent::getLoggerName); + + members.addMapEntries(mapMDCProperties(ILoggingEvent::getMDCPropertyMap)); + + members.from(ILoggingEvent::getKeyValuePairs) + .whenNotEmpty() + .usingExtractedPairs(Iterable::forEach, keyValuePairExtractor); + + members.add().whenNotNull(ILoggingEvent::getThrowableProxy).usingMembers((throwableMembers) -> { + throwableMembers.add("full_message", + (event) -> formatFullMessageWithThrowable(throwableProxyConverter, event)); + throwableMembers.add("_error_type", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getClassName); + throwableMembers.add("_error_stack_trace", throwableProxyConverter::convert); + throwableMembers.add("_error_message", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getMessage); + }); + } + + /** + * GELF requires "seconds since UNIX epoch with optional decimal places for + * milliseconds". To comply with this requirement, we format a POSIX timestamp + * with millisecond precision as e.g. "1725459730385" -> "1725459730.385" + * @param timeStamp the timestamp of the log message + * @return the timestamp formatted as string with millisecond precision + */ + private static double formatTimeStamp(final long timeStamp) { + return new BigDecimal(timeStamp).movePointLeft(3).doubleValue(); + } + + private static String formatFullMessageWithThrowable(final ThrowableProxyConverter throwableProxyConverter, + ILoggingEvent event) { + return String.format(DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT, event.getFormattedMessage(), + throwableProxyConverter.convert(event)); + } + + private static Function> mapMDCProperties( + Function> MDCPropertyMapGetter) { + return MDCPropertyMapGetter.andThen((mdc) -> mdc.entrySet() + .stream() + .collect(Collectors.toMap((entry) -> makeAdditionalFieldName(entry.getKey()), Map.Entry::getValue))); + } + + private static String makeAdditionalFieldName(String fieldName) { + Assert.notNull(fieldName, "fieldName must not be null"); + Assert.isTrue(FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches(), + () -> String.format("fieldName must be a valid according to GELF standard. [fieldName=%s]", fieldName)); + Assert.isTrue(!ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName), () -> String.format( + "fieldName must not be an illegal additional field key according to GELF standard. [fieldName=%s]", + fieldName)); + + if (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) { + // No need to prepend the `ADDITIONAL_FIELD_PREFIX` in case the caller already + // has prepended the prefix. + return fieldName; + } + + return ADDITIONAL_FIELD_PREFIX + fieldName; + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java index b8f1d15cb69d..5b3c9d964e84 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java @@ -82,6 +82,11 @@ private void addCommonFormatters(CommonFormatters commonFormatter commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA, (instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(instantiator.getArg(Environment.class), instantiator.getArg(ThrowableProxyConverter.class))); + commonFormatters + .add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT, + (instantiator) -> new GraylogExtendedLogFormatStructuredLogFormatter( + instantiator.getArg(Environment.class), + instantiator.getArg(ThrowableProxyConverter.class))); commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, (instantiator) -> new LogstashStructuredLogFormatter( instantiator.getArg(ThrowableProxyConverter.class))); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/CommonStructuredLogFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/CommonStructuredLogFormat.java index e4a92ebd325b..acba34b7cd68 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/CommonStructuredLogFormat.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/CommonStructuredLogFormat.java @@ -31,6 +31,12 @@ public enum CommonStructuredLogFormat { */ ELASTIC_COMMON_SCHEMA("ecs"), + /** + * Graylog + * Extended Log Format (GELF) log format. + */ + GRAYLOG_EXTENDED_LOG_FORMAT("gelf"), + /** * The Logstash diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java new file mode 100644 index 000000000000..75216bb1adf7 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012-2024 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.logging.structured; + +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.json.JsonWriter; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +/** + * Service details for Graylog Extended Log Format structured logging. + * + * @param name the application name + * @param version the version of the application + * @param environment the name of the environment the application is running in + * @param nodeName the name of the node the application is running on + * @author Samuel Lissner + * @since 3.4.0 + */ +public record GraylogExtendedLogFormatService(String name, String version, String environment, String nodeName) { + + static final GraylogExtendedLogFormatService NONE = new GraylogExtendedLogFormatService(null, null, null, null); + + private GraylogExtendedLogFormatService withDefaults(Environment environment) { + String name = withFallbackProperty(environment, this.name, "spring.application.name"); + String version = withFallbackProperty(environment, this.version, "spring.application.version"); + return new GraylogExtendedLogFormatService(name, version, this.environment, this.nodeName); + } + + private String withFallbackProperty(Environment environment, String value, String property) { + return (!StringUtils.hasLength(value)) ? environment.getProperty(property) : value; + } + + /** + * Add {@link JsonWriter} members for the service. + * @param members the members to add to + */ + public void jsonMembers(JsonWriter.Members members) { + // note "host" is a field name prescribed by GELF + members.add("host", this::name).whenHasLength(); + members.add("_service_version", this::version).whenHasLength(); + members.add("_service_environment", this::environment).whenHasLength(); + members.add("_service_node_name", this::nodeName).whenHasLength(); + } + + /** + * Return a new {@link GraylogExtendedLogFormatService} from bound from properties in + * the given {@link Environment}. + * @param environment the source environment + * @return a new {@link GraylogExtendedLogFormatService} instance + */ + public static GraylogExtendedLogFormatService get(Environment environment) { + return Binder.get(environment) + .bind("logging.structured.gelf.service", GraylogExtendedLogFormatService.class) + .orElse(NONE) + .withDefaults(environment); + } +} diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index bc864ac75878..44c11cc162b5 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -242,7 +242,7 @@ { "name": "logging.structured.ecs.service.version", "type": "java.lang.String", - "description": "Structured ECS service version." + "description": "Structured ECS service version (defaults to 'spring.application.version')." }, { "name": "logging.structured.format.console", @@ -254,6 +254,26 @@ "type": "java.lang.String", "description": "Structured logging format for output to a file. Must be either a format id or a fully qualified class name." }, + { + "name": "logging.structured.gelf.service.environment", + "type": "java.lang.String", + "description": "Structured GELF service environment." + }, + { + "name": "logging.structured.gelf.service.name", + "type": "java.lang.String", + "description": "Structured GELF service name (defaults to 'spring.application.name')." + }, + { + "name": "logging.structured.gelf.service.node-name", + "type": "java.lang.String", + "description": "Structured GELF service node name." + }, + { + "name": "logging.structured.gelf.service.version", + "type": "java.lang.String", + "description": "Structured GELF service version (defaults to 'spring.application.version')." + }, { "name": "logging.threshold.console", "type": "java.lang.String", @@ -628,6 +648,9 @@ { "value": "ecs" }, + { + "value": "gelf" + }, { "value": "logstash" } @@ -647,6 +670,9 @@ { "value": "ecs" }, + { + "value": "gelf" + }, { "value": "logstash" } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java new file mode 100644 index 000000000000..11f29846aca9 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2024 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.logging.log4j2; + +import java.util.Map; + +import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap; +import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraylogExtendedLogFormatStructuredLogFormatter}. + * + * @author Samuel Lissner + */ +class GraylogExtendedLogFormatStructuredLogFormatterTests extends AbstractStructuredLoggingTests { + + private GraylogExtendedLogFormatStructuredLogFormatter formatter; + + @BeforeEach + void setUp() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.structured.gelf.service.name", "name"); + environment.setProperty("logging.structured.gelf.service.version", "1.0.0"); + environment.setProperty("logging.structured.gelf.service.environment", "test"); + environment.setProperty("logging.structured.gelf.service.node-name", "node-1"); + environment.setProperty("spring.application.pid", "1"); + this.formatter = new GraylogExtendedLogFormatStructuredLogFormatter(environment); + } + + @Test + void shouldFormat() { + MutableLogEvent event = createEvent(); + event.setContextData(new JdkMapAdapterStringMap(Map.of("mdc-1", "mdc-v-1"), true)); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.000D, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_service_environment", "test", "_service_node_name", "node-1", + "_log_logger", "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1")); + } + + @Test + void shouldFormatException() { + MutableLogEvent event = createEvent(); + event.setThrown(new RuntimeException("Boom")); + + String json = this.formatter.format(event); + Map deserialized = deserialize(json); + + String fullMessage = (String) deserialized.get("full_message"); + String stackTrace = (String) deserialized.get("_error_stack_trace"); + + assertThat(fullMessage).startsWith( + """ + message + + java.lang.RuntimeException: Boom + \tat org.springframework.boot.logging.log4j2.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException"""); + assertThat(stackTrace).startsWith( + """ + java.lang.RuntimeException: Boom + \tat org.springframework.boot.logging.log4j2.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException"""); + + assertThat(deserialized) + .containsAllEntriesOf(map("_error_type", "java.lang.RuntimeException", "_error_message", "Boom")); + assertThat(json).contains( + """ + message\\n\\njava.lang.RuntimeException: Boom\\n\\tat org.springframework.boot.logging.log4j2.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException"""); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java new file mode 100644 index 000000000000..fe1df90cf9bc --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012-2024 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.logging.logback; + +import java.util.Collections; +import java.util.Map; + +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableProxy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraylogExtendedLogFormatStructuredLogFormatter}. + * + * @author Samuel Lissner + */ +class GraylogExtendedLogFormatStructuredLogFormatterTests extends AbstractStructuredLoggingTests { + + private GraylogExtendedLogFormatStructuredLogFormatter formatter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.structured.gelf.service.name", "name"); + environment.setProperty("logging.structured.gelf.service.version", "1.0.0"); + environment.setProperty("logging.structured.gelf.service.environment", "test"); + environment.setProperty("logging.structured.gelf.service.node-name", "node-1"); + environment.setProperty("spring.application.pid", "1"); + this.formatter = new GraylogExtendedLogFormatStructuredLogFormatter(environment, getThrowableProxyConverter()); + } + + @Test + void shouldFormat() { + LoggingEvent event = createEvent(); + event.setMDCPropertyMap(Map.of("mdc-1", "mdc-v-1")); + event.setKeyValuePairs(keyValuePairs("kv-1", "kv-v-1")); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.000D, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_service_environment", "test", "_service_node_name", "node-1", + "_log_logger", "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1", "_kv-1", "kv-v-1")); + } + + @Test + void shouldFormatException() { + LoggingEvent event = createEvent(); + event.setMDCPropertyMap(Collections.emptyMap()); + event.setThrowableProxy(new ThrowableProxy(new RuntimeException("Boom"))); + + String json = this.formatter.format(event); + Map deserialized = deserialize(json); + String fullMessage = (String) deserialized.get("full_message"); + String stackTrace = (String) deserialized.get("_error_stack_trace"); + + assertThat(fullMessage).startsWith( + "message\n\njava.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException" + .formatted()); + + assertThat(deserialized) + .containsAllEntriesOf(map("_error_type", "java.lang.RuntimeException", "_error_message", "Boom")); + + assertThat(stackTrace).startsWith( + "java.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException" + .formatted()); + assertThat(json).contains( + "java.lang.RuntimeException: Boom%n\\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException" + .formatted() + .replace("\n", "\\n") + .replace("\r", "\\r")); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/StructuredLogEncoderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/StructuredLogEncoderTests.java index 3928f7e9521c..f24543c92352 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/StructuredLogEncoderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/StructuredLogEncoderTests.java @@ -134,7 +134,7 @@ void shouldFailIfNoCommonOrCustomFormatIsSet() { this.encoder.start(); }) .withMessageContaining( - "Unknown format 'does-not-exist'. Values can be a valid fully-qualified class name or one of the common formats: [ecs, logstash]"); + "Unknown format 'does-not-exist'. Values can be a valid fully-qualified class name or one of the common formats: [ecs, gelf, logstash]"); } private String encode(LoggingEvent event) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java new file mode 100644 index 000000000000..5dd7fa9e458f --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2024 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.logging.structured; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.json.JsonWriter; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GraylogExtendedLogFormatService}. + * + * @author Samuel Lissner + */ +class GraylogExtendedLogFormatServiceTests { + + @Test + void getBindsFromEnvironment() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.structured.gelf.service.name", "spring"); + environment.setProperty("logging.structured.gelf.service.version", "1.2.3"); + environment.setProperty("logging.structured.gelf.service.environment", "prod"); + environment.setProperty("logging.structured.gelf.service.node-name", "boot"); + GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", "1.2.3", "prod", "boot")); + } + + @Test + void getWhenNoServiceNameUsesApplicationName() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("spring.application.name", "spring"); + GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", null, null, null)); + } + + @Test + void getWhenNoServiceVersionUsesApplicationVersion() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("spring.application.version", "1.2.3"); + GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, "1.2.3", null, null)); + } + + @Test + void getWhenNoPropertiesToBind() { + MockEnvironment environment = new MockEnvironment(); + GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, null, null, null)); + } + + @Test + void addToJsonMembersCreatesValidJson() { + GraylogExtendedLogFormatService service = new GraylogExtendedLogFormatService("spring", "1.2.3", "prod", + "boot"); + JsonWriter writer = JsonWriter.of(service::jsonMembers); + assertThat(writer.writeToString(service)).isEqualTo("{\"host\":\"spring\",\"_service_version\":\"1.2.3\"," + + "\"_service_environment\":\"prod\",\"_service_node_name\":\"boot\"}"); + } + +} From b5e7302031224ae67e6dac8ecd4dfea2b116fb07 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 10 Sep 2024 10:50:01 +0200 Subject: [PATCH 114/271] Polish "Add Graylog Extended Log Format (GELF) for structured logging" See gh-42158 --- .../reference/pages/features/logging.adoc | 4 +- ...tendedLogFormatStructuredLogFormatter.java | 73 +++++++--------- ...tendedLogFormatStructuredLogFormatter.java | 85 +++++++------------ .../GraylogExtendedLogFormatService.java | 10 +-- ...itional-spring-configuration-metadata.json | 10 --- ...dLogFormatStructuredLogFormatterTests.java | 68 +++++++++++++-- .../log4j2/StructuredLoggingLayoutTests.java | 2 +- ...dLogFormatStructuredLogFormatterTests.java | 69 +++++++++++++-- .../GraylogExtendedLogFormatServiceTests.java | 16 ++-- 9 files changed, 194 insertions(+), 143 deletions(-) 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 9041e89692aa..cc3ae220d7f0 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 @@ -516,7 +516,7 @@ A log line looks like this: [source,json] ---- -{"version":"1.1","short_message":"Hello structured logging!","timestamp":1.725530750186E9,"level":6,"_level_name":"INFO","_process_pid":9086,"_process_thread_name":"main","host":"spring-boot-gelf","_log_logger":"com.slissner.springbootgelf.ExampleLogger","_userId":"1","_testkey_testmessage":"test"} +{"version":"1.1","short_message":"No active profile set, falling back to 1 default profile: \"default\"","timestamp":1725958035.857,"level":6,"_level_name":"INFO","_process_pid":47649,"_process_thread_name":"main","_log_logger":"org.example.Application"} ---- This format also adds every key value pair contained in the MDC to the JSON object. @@ -532,8 +532,6 @@ logging: service: name: MyService version: 1.0 - environment: Production - node-name: Primary ---- NOTE: configprop:logging.structured.gelf.service.name[] will default to configprop:spring.application.name[] if not specified. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java index 270de27f5cae..8a31b50b9972 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -19,23 +19,26 @@ import java.math.BigDecimal; import java.util.Objects; import java.util.Set; -import java.util.function.Function; +import java.util.function.BiConsumer; import java.util.regex.Pattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.impl.ThrowableProxy; import org.apache.logging.log4j.core.net.Severity; import org.apache.logging.log4j.core.time.Instant; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.springframework.boot.json.JsonWriter; +import org.springframework.boot.json.JsonWriter.WritableJson; import org.springframework.boot.logging.structured.CommonStructuredLogFormat; import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter; import org.springframework.boot.logging.structured.StructuredLogFormatter; import org.springframework.core.env.Environment; +import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -45,14 +48,17 @@ * 1.1. * * @author Samuel Lissner + * @author Moritz Halbritter */ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructuredLogFormatter { + private static final Log logger = LogFactory.getLog(GraylogExtendedLogFormatStructuredLogFormatter.class); + /** * Allowed characters in field names are any word character (letter, number, * underscore), dashes and dots. */ - private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w\\.\\-]*$"); + private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$"); /** * Every field been sent and prefixed with an underscore "_" will be treated as an @@ -64,13 +70,7 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server * nodes omit this field automatically. */ - private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("_id"); - - /** - * Default format to be used for the `full_message` property when there is a throwable - * present in the log event. - */ - private static final String DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT = "%s%n%n%s"; + private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("id", "_id"); GraylogExtendedLogFormatStructuredLogFormatter(Environment environment) { super((members) -> jsonMembers(environment, members)); @@ -78,40 +78,30 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure private static void jsonMembers(Environment environment, JsonWriter.Members members) { members.add("version", "1.1"); - // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are // ignoring this here. members.add("short_message", LogEvent::getMessage).as(Message::getFormattedMessage); - members.add("timestamp", LogEvent::getInstant) .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); members.add("level", GraylogExtendedLogFormatStructuredLogFormatter::convertLevel); members.add("_level_name", LogEvent::getLevel).as(Level::name); - members.add("_process_pid", environment.getProperty("spring.application.pid", Long.class)) .when(Objects::nonNull); members.add("_process_thread_name", LogEvent::getThreadName); - GraylogExtendedLogFormatService.get(environment).jsonMembers(members); - members.add("_log_logger", LogEvent::getLoggerName); - members.from(LogEvent::getContextData) .whenNot(ReadOnlyStringMap::isEmpty) .usingPairs((contextData, pairs) -> contextData - .forEach((key, value) -> pairs.accept(makeAdditionalFieldName(key), value))); - + .forEach((key, value) -> createAdditionalField(key, value, pairs))); members.add().whenNotNull(LogEvent::getThrownProxy).usingMembers((eventMembers) -> { - final Function throwableProxyGetter = LogEvent::getThrownProxy; - eventMembers.add("full_message", GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable); - eventMembers.add("_error_type", throwableProxyGetter.andThen(ThrowableProxy::getThrowable)) + eventMembers.add("_error_type", (event) -> event.getThrownProxy().getThrowable()) .whenNotNull() .as(ObjectUtils::nullSafeClassName); - eventMembers.add("_error_stack_trace", - throwableProxyGetter.andThen(ThrowableProxy::getExtendedStackTraceAsString)); - eventMembers.add("_error_message", throwableProxyGetter.andThen(ThrowableProxy::getMessage)); + eventMembers.add("_error_stack_trace", (event) -> event.getThrownProxy().getExtendedStackTraceAsString()); + eventMembers.add("_error_message", (event) -> event.getThrownProxy().getMessage()); }); } @@ -123,8 +113,8 @@ private static void jsonMembers(Environment environment, JsonWriter.Members out.append(new BigDecimal(timeStamp.getEpochMillisecond()).movePointLeft(3).toPlainString()); } /** @@ -133,30 +123,27 @@ private static double formatTimeStamp(final Instant timeStamp) { * @return an integer representing the syslog log level code * @see Severity class from Log4j2 which contains the conversion logic */ - private static int convertLevel(final LogEvent event) { + private static int convertLevel(LogEvent event) { return Severity.getSeverity(event.getLevel()).getCode(); } - private static String formatFullMessageWithThrowable(final LogEvent event) { - return String.format(DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT, event.getMessage().getFormattedMessage(), - event.getThrownProxy().getExtendedStackTraceAsString()); + private static String formatFullMessageWithThrowable(LogEvent event) { + return event.getMessage().getFormattedMessage() + "\n\n" + + event.getThrownProxy().getExtendedStackTraceAsString(); } - private static String makeAdditionalFieldName(String fieldName) { + private static void createAdditionalField(String fieldName, Object value, BiConsumer pairs) { Assert.notNull(fieldName, "fieldName must not be null"); - Assert.isTrue(FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches(), - () -> String.format("fieldName must be a valid according to GELF standard. [fieldName=%s]", fieldName)); - Assert.isTrue(!ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName), () -> String.format( - "fieldName must not be an illegal additional field key according to GELF standard. [fieldName=%s]", - fieldName)); - - if (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) { - // No need to prepend the `ADDITIONAL_FIELD_PREFIX` in case the caller already - // has prepended the prefix. - return fieldName; + if (!FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches()) { + logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", fieldName)); + return; } - - return ADDITIONAL_FIELD_PREFIX + fieldName; + if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName)) { + logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", fieldName)); + return; + } + String key = (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) ? fieldName : ADDITIONAL_FIELD_PREFIX + fieldName; + pairs.accept(key, value); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java index b9f1d1fe62bd..4d9df6a8149c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -17,27 +17,28 @@ package org.springframework.boot.logging.logback; import java.math.BigDecimal; -import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Function; +import java.util.function.BiConsumer; import java.util.regex.Pattern; -import java.util.stream.Collectors; import ch.qos.logback.classic.pattern.ThrowableProxyConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.util.LevelToSyslogSeverity; -import org.slf4j.event.KeyValuePair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.boot.json.JsonWriter; -import org.springframework.boot.json.JsonWriter.PairExtractor; +import org.springframework.boot.json.JsonWriter.WritableJson; import org.springframework.boot.logging.structured.CommonStructuredLogFormat; import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter; import org.springframework.boot.logging.structured.StructuredLogFormatter; import org.springframework.core.env.Environment; +import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; /** * Logback {@link StructuredLogFormatter} for @@ -45,14 +46,17 @@ * 1.1. * * @author Samuel Lissner + * @author Moritz Halbritter */ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructuredLogFormatter { + private static final Log logger = LogFactory.getLog(GraylogExtendedLogFormatStructuredLogFormatter.class); + /** * Allowed characters in field names are any word character (letter, number, * underscore), dashes and dots. */ - private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w\\.\\-]*$"); + private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$"); /** * Every field been sent and prefixed with an underscore "_" will be treated as an @@ -64,16 +68,7 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server * nodes omit this field automatically. */ - private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("_id"); - - /** - * Default format to be used for the `full_message` property when there is a throwable - * present in the log event. - */ - private static final String DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT = "%s%n%n%s"; - - private static final PairExtractor keyValuePairExtractor = PairExtractor - .of((pair) -> makeAdditionalFieldName(pair.key), (pair) -> pair.value); + private static final Set ADDITIONAL_FIELD_ILLEGAL_KEYS = Set.of("id", "_id"); GraylogExtendedLogFormatStructuredLogFormatter(Environment environment, ThrowableProxyConverter throwableProxyConverter) { @@ -83,30 +78,25 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure private static void jsonMembers(Environment environment, ThrowableProxyConverter throwableProxyConverter, JsonWriter.Members members) { members.add("version", "1.1"); - // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are // ignoring this here. members.add("short_message", ILoggingEvent::getFormattedMessage); - members.add("timestamp", ILoggingEvent::getTimeStamp) .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); members.add("level", LevelToSyslogSeverity::convert); members.add("_level_name", ILoggingEvent::getLevel); - members.add("_process_pid", environment.getProperty("spring.application.pid", Long.class)) .when(Objects::nonNull); members.add("_process_thread_name", ILoggingEvent::getThreadName); - GraylogExtendedLogFormatService.get(environment).jsonMembers(members); - members.add("_log_logger", ILoggingEvent::getLoggerName); - - members.addMapEntries(mapMDCProperties(ILoggingEvent::getMDCPropertyMap)); - + members.from(ILoggingEvent::getMDCPropertyMap) + .when((mdc) -> !CollectionUtils.isEmpty(mdc)) + .usingPairs((mdc, pairs) -> mdc.forEach((key, value) -> createAdditionalField(key, value, pairs))); members.from(ILoggingEvent::getKeyValuePairs) - .whenNotEmpty() - .usingExtractedPairs(Iterable::forEach, keyValuePairExtractor); - + .when((keyValuePairs) -> !CollectionUtils.isEmpty(keyValuePairs)) + .usingPairs((keyValuePairs, pairs) -> keyValuePairs + .forEach((keyValuePair) -> createAdditionalField(keyValuePair.key, keyValuePair.value, pairs))); members.add().whenNotNull(ILoggingEvent::getThrowableProxy).usingMembers((throwableMembers) -> { throwableMembers.add("full_message", (event) -> formatFullMessageWithThrowable(throwableProxyConverter, event)); @@ -123,38 +113,27 @@ private static void jsonMembers(Environment environment, ThrowableProxyConverter * @param timeStamp the timestamp of the log message * @return the timestamp formatted as string with millisecond precision */ - private static double formatTimeStamp(final long timeStamp) { - return new BigDecimal(timeStamp).movePointLeft(3).doubleValue(); + private static WritableJson formatTimeStamp(long timeStamp) { + return (out) -> out.append(new BigDecimal(timeStamp).movePointLeft(3).toPlainString()); } - private static String formatFullMessageWithThrowable(final ThrowableProxyConverter throwableProxyConverter, + private static String formatFullMessageWithThrowable(ThrowableProxyConverter throwableProxyConverter, ILoggingEvent event) { - return String.format(DEFAULT_FULL_MESSAGE_WITH_THROWABLE_FORMAT, event.getFormattedMessage(), - throwableProxyConverter.convert(event)); + return event.getFormattedMessage() + "\n\n" + throwableProxyConverter.convert(event); } - private static Function> mapMDCProperties( - Function> MDCPropertyMapGetter) { - return MDCPropertyMapGetter.andThen((mdc) -> mdc.entrySet() - .stream() - .collect(Collectors.toMap((entry) -> makeAdditionalFieldName(entry.getKey()), Map.Entry::getValue))); - } - - private static String makeAdditionalFieldName(String fieldName) { - Assert.notNull(fieldName, "fieldName must not be null"); - Assert.isTrue(FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches(), - () -> String.format("fieldName must be a valid according to GELF standard. [fieldName=%s]", fieldName)); - Assert.isTrue(!ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName), () -> String.format( - "fieldName must not be an illegal additional field key according to GELF standard. [fieldName=%s]", - fieldName)); - - if (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) { - // No need to prepend the `ADDITIONAL_FIELD_PREFIX` in case the caller already - // has prepended the prefix. - return fieldName; + private static void createAdditionalField(String key, Object value, BiConsumer pairs) { + Assert.notNull(key, "fieldName must not be null"); + if (!FIELD_NAME_VALID_PATTERN.matcher(key).matches()) { + logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", key)); + return; } - - return ADDITIONAL_FIELD_PREFIX + fieldName; + if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(key)) { + logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", key)); + return; + } + String keyWithPrefix = (key.startsWith(ADDITIONAL_FIELD_PREFIX)) ? key : ADDITIONAL_FIELD_PREFIX + key; + pairs.accept(keyWithPrefix, value); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java index 75216bb1adf7..bbe854a1a984 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java @@ -26,19 +26,17 @@ * * @param name the application name * @param version the version of the application - * @param environment the name of the environment the application is running in - * @param nodeName the name of the node the application is running on * @author Samuel Lissner * @since 3.4.0 */ -public record GraylogExtendedLogFormatService(String name, String version, String environment, String nodeName) { +public record GraylogExtendedLogFormatService(String name, String version) { - static final GraylogExtendedLogFormatService NONE = new GraylogExtendedLogFormatService(null, null, null, null); + static final GraylogExtendedLogFormatService NONE = new GraylogExtendedLogFormatService(null, null); private GraylogExtendedLogFormatService withDefaults(Environment environment) { String name = withFallbackProperty(environment, this.name, "spring.application.name"); String version = withFallbackProperty(environment, this.version, "spring.application.version"); - return new GraylogExtendedLogFormatService(name, version, this.environment, this.nodeName); + return new GraylogExtendedLogFormatService(name, version); } private String withFallbackProperty(Environment environment, String value, String property) { @@ -53,8 +51,6 @@ public void jsonMembers(JsonWriter.Members members) { // note "host" is a field name prescribed by GELF members.add("host", this::name).whenHasLength(); members.add("_service_version", this::version).whenHasLength(); - members.add("_service_environment", this::environment).whenHasLength(); - members.add("_service_node_name", this::nodeName).whenHasLength(); } /** diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 44c11cc162b5..db3716f03d49 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -254,21 +254,11 @@ "type": "java.lang.String", "description": "Structured logging format for output to a file. Must be either a format id or a fully qualified class name." }, - { - "name": "logging.structured.gelf.service.environment", - "type": "java.lang.String", - "description": "Structured GELF service environment." - }, { "name": "logging.structured.gelf.service.name", "type": "java.lang.String", "description": "Structured GELF service name (defaults to 'spring.application.name')." }, - { - "name": "logging.structured.gelf.service.node-name", - "type": "java.lang.String", - "description": "Structured GELF service node name." - }, { "name": "logging.structured.gelf.service.version", "type": "java.lang.String", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java index 11f29846aca9..5a777888e227 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatterTests.java @@ -22,7 +22,10 @@ import org.apache.logging.log4j.core.impl.MutableLogEvent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.testsupport.system.CapturedOutput; +import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; @@ -31,7 +34,9 @@ * Tests for {@link GraylogExtendedLogFormatStructuredLogFormatter}. * * @author Samuel Lissner + * @author Moritz Halbritter */ +@ExtendWith(OutputCaptureExtension.class) class GraylogExtendedLogFormatStructuredLogFormatterTests extends AbstractStructuredLoggingTests { private GraylogExtendedLogFormatStructuredLogFormatter formatter; @@ -41,8 +46,6 @@ void setUp() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("logging.structured.gelf.service.name", "name"); environment.setProperty("logging.structured.gelf.service.version", "1.0.0"); - environment.setProperty("logging.structured.gelf.service.environment", "test"); - environment.setProperty("logging.structured.gelf.service.node-name", "node-1"); environment.setProperty("spring.application.pid", "1"); this.formatter = new GraylogExtendedLogFormatStructuredLogFormatter(environment); } @@ -54,23 +57,72 @@ void shouldFormat() { String json = this.formatter.format(event); assertThat(json).endsWith("\n"); Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf( + map("version", "1.1", "host", "name", "timestamp", 1719910193.0, "level", 6, "_level_name", "INFO", + "_process_pid", 1, "_process_thread_name", "main", "_service_version", "1.0.0", "_log_logger", + "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1")); + } + + @Test + void shouldFormatMillisecondsInTimestamp() { + MutableLogEvent event = createEvent(); + event.setTimeMillis(1719910193123L); + String json = this.formatter.format(event); + assertThat(json).contains("\"timestamp\":1719910193.123"); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", - 1719910193.000D, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", - "_service_version", "1.0.0", "_service_environment", "test", "_service_node_name", "node-1", - "_log_logger", "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1")); + 1719910193.123, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); } @Test - void shouldFormatException() { + void shouldNotAllowInvalidFieldNames(CapturedOutput output) { MutableLogEvent event = createEvent(); - event.setThrown(new RuntimeException("Boom")); + event.setContextData(new JdkMapAdapterStringMap(Map.of("/", "value"), true)); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.0, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); + assertThat(output).contains("'/' is not a valid field name according to GELF standard"); + } + @Test + void shouldNotAllowIllegalFieldNames(CapturedOutput output) { + MutableLogEvent event = createEvent(); + event.setContextData(new JdkMapAdapterStringMap(Map.of("id", "1"), true)); String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.0, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); + assertThat(output).contains("'id' is an illegal field name according to GELF standard"); + } + + @Test + void shouldNotAddDoubleUnderscoreToCustomFields() { + MutableLogEvent event = createEvent(); + event.setContextData(new JdkMapAdapterStringMap(Map.of("_custom", "value"), true)); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf( + map("version", "1.1", "host", "name", "timestamp", 1719910193.0, "level", 6, "_level_name", "INFO", + "_process_pid", 1, "_process_thread_name", "main", "_service_version", "1.0.0", "_log_logger", + "org.example.Test", "short_message", "message", "_custom", "value")); + } + @Test + void shouldFormatException() { + MutableLogEvent event = createEvent(); + event.setThrown(new RuntimeException("Boom")); + String json = this.formatter.format(event); + Map deserialized = deserialize(json); String fullMessage = (String) deserialized.get("full_message"); String stackTrace = (String) deserialized.get("_error_stack_trace"); - assertThat(fullMessage).startsWith( """ message diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/StructuredLoggingLayoutTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/StructuredLoggingLayoutTests.java index 64e56d6a7756..cf688e167d71 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/StructuredLoggingLayoutTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/StructuredLoggingLayoutTests.java @@ -110,7 +110,7 @@ void shouldCheckTypeArgumentWithRawType() { void shouldFailIfNoCommonOrCustomFormatIsSet() { assertThatIllegalArgumentException().isThrownBy(() -> newBuilder().setFormat("does-not-exist").build()) .withMessageContaining("Unknown format 'does-not-exist'. " - + "Values can be a valid fully-qualified class name or one of the common formats: [ecs, logstash]"); + + "Values can be a valid fully-qualified class name or one of the common formats: [ecs, gelf, logstash]"); } private Builder newBuilder() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java index fe1df90cf9bc..39ab27c638f1 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java @@ -23,7 +23,10 @@ import ch.qos.logback.classic.spi.ThrowableProxy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.testsupport.system.CapturedOutput; +import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; @@ -32,7 +35,9 @@ * Tests for {@link GraylogExtendedLogFormatStructuredLogFormatter}. * * @author Samuel Lissner + * @author Moritz Halbritter */ +@ExtendWith(OutputCaptureExtension.class) class GraylogExtendedLogFormatStructuredLogFormatterTests extends AbstractStructuredLoggingTests { private GraylogExtendedLogFormatStructuredLogFormatter formatter; @@ -44,8 +49,6 @@ void setUp() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("logging.structured.gelf.service.name", "name"); environment.setProperty("logging.structured.gelf.service.version", "1.0.0"); - environment.setProperty("logging.structured.gelf.service.environment", "test"); - environment.setProperty("logging.structured.gelf.service.node-name", "node-1"); environment.setProperty("spring.application.pid", "1"); this.formatter = new GraylogExtendedLogFormatStructuredLogFormatter(environment, getThrowableProxyConverter()); } @@ -58,10 +61,63 @@ void shouldFormat() { String json = this.formatter.format(event); assertThat(json).endsWith("\n"); Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf( + map("version", "1.1", "host", "name", "timestamp", 1719910193.0, "level", 6, "_level_name", "INFO", + "_process_pid", 1, "_process_thread_name", "main", "_service_version", "1.0.0", "_log_logger", + "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1", "_kv-1", "kv-v-1")); + } + + @Test + void shouldFormatMillisecondsInTimestamp() { + LoggingEvent event = createEvent(); + event.setTimeStamp(1719910193123L); + event.setMDCPropertyMap(Collections.emptyMap()); + String json = this.formatter.format(event); + assertThat(json).contains("\"timestamp\":1719910193.123"); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.123, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); + } + + @Test + void shouldNotAllowInvalidFieldNames(CapturedOutput output) { + LoggingEvent event = createEvent(); + event.setMDCPropertyMap(Map.of("/", "value")); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", + 1719910193.0, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); + assertThat(output).contains("'/' is not a valid field name according to GELF standard"); + } + + @Test + void shouldNotAllowIllegalFieldNames(CapturedOutput output) { + LoggingEvent event = createEvent(); + event.setMDCPropertyMap(Map.of("id", "1")); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("version", "1.1", "host", "name", "timestamp", - 1719910193.000D, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", - "_service_version", "1.0.0", "_service_environment", "test", "_service_node_name", "node-1", - "_log_logger", "org.example.Test", "short_message", "message", "_mdc-1", "mdc-v-1", "_kv-1", "kv-v-1")); + 1719910193.0, "level", 6, "_level_name", "INFO", "_process_pid", 1, "_process_thread_name", "main", + "_service_version", "1.0.0", "_log_logger", "org.example.Test", "short_message", "message")); + assertThat(output).contains("'id' is an illegal field name according to GELF standard"); + } + + @Test + void shouldNotAddDoubleUnderscoreToCustomFields() { + LoggingEvent event = createEvent(); + event.setMDCPropertyMap(Map.of("_custom", "value")); + String json = this.formatter.format(event); + assertThat(json).endsWith("\n"); + Map deserialized = deserialize(json); + assertThat(deserialized).containsExactlyInAnyOrderEntriesOf( + map("version", "1.1", "host", "name", "timestamp", 1719910193.0, "level", 6, "_level_name", "INFO", + "_process_pid", 1, "_process_thread_name", "main", "_service_version", "1.0.0", "_log_logger", + "org.example.Test", "short_message", "message", "_custom", "value")); } @Test @@ -69,16 +125,13 @@ void shouldFormatException() { LoggingEvent event = createEvent(); event.setMDCPropertyMap(Collections.emptyMap()); event.setThrowableProxy(new ThrowableProxy(new RuntimeException("Boom"))); - String json = this.formatter.format(event); Map deserialized = deserialize(json); String fullMessage = (String) deserialized.get("full_message"); String stackTrace = (String) deserialized.get("_error_stack_trace"); - assertThat(fullMessage).startsWith( "message\n\njava.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException" .formatted()); - assertThat(deserialized) .containsAllEntriesOf(map("_error_type", "java.lang.RuntimeException", "_error_message", "Boom")); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java index 5dd7fa9e458f..b80e9c9946d0 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatServiceTests.java @@ -35,10 +35,8 @@ void getBindsFromEnvironment() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("logging.structured.gelf.service.name", "spring"); environment.setProperty("logging.structured.gelf.service.version", "1.2.3"); - environment.setProperty("logging.structured.gelf.service.environment", "prod"); - environment.setProperty("logging.structured.gelf.service.node-name", "boot"); GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); - assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", "1.2.3", "prod", "boot")); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", "1.2.3")); } @Test @@ -46,7 +44,7 @@ void getWhenNoServiceNameUsesApplicationName() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("spring.application.name", "spring"); GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); - assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", null, null, null)); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService("spring", null)); } @Test @@ -54,23 +52,21 @@ void getWhenNoServiceVersionUsesApplicationVersion() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("spring.application.version", "1.2.3"); GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); - assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, "1.2.3", null, null)); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, "1.2.3")); } @Test void getWhenNoPropertiesToBind() { MockEnvironment environment = new MockEnvironment(); GraylogExtendedLogFormatService service = GraylogExtendedLogFormatService.get(environment); - assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, null, null, null)); + assertThat(service).isEqualTo(new GraylogExtendedLogFormatService(null, null)); } @Test void addToJsonMembersCreatesValidJson() { - GraylogExtendedLogFormatService service = new GraylogExtendedLogFormatService("spring", "1.2.3", "prod", - "boot"); + GraylogExtendedLogFormatService service = new GraylogExtendedLogFormatService("spring", "1.2.3"); JsonWriter writer = JsonWriter.of(service::jsonMembers); - assertThat(writer.writeToString(service)).isEqualTo("{\"host\":\"spring\",\"_service_version\":\"1.2.3\"," - + "\"_service_environment\":\"prod\",\"_service_node_name\":\"boot\"}"); + assertThat(writer.writeToString(service)).isEqualTo("{\"host\":\"spring\",\"_service_version\":\"1.2.3\"}"); } } From 2f8fc5cb054b6000253dbbd0c3c28804fd229f6d Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Sat, 31 Aug 2024 21:37:06 +0300 Subject: [PATCH 115/271] Add consistent scope support for ConfigurationProperties beans See gh-42073 --- .../ConfigurationPropertiesBeanRegistrar.java | 25 ++++++-- ...igurationPropertiesBeanRegistrarTests.java | 41 ++++++++++++ .../ConfigurationPropertiesTests.java | 62 ++++++++++++++++++- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java index ac209a29de5c..458ac8d874f4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java @@ -16,12 +16,19 @@ package org.springframework.boot.context.properties; +import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; +import org.springframework.context.annotation.AnnotationScopeMetadataResolver; +import org.springframework.context.annotation.ScopeMetadata; +import org.springframework.context.annotation.ScopeMetadataResolver; +import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; @@ -38,6 +45,8 @@ */ final class ConfigurationPropertiesBeanRegistrar { + private static final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); + private final BeanDefinitionRegistry registry; private final BeanFactory beanFactory; @@ -75,17 +84,25 @@ private void registerBeanDefinition(String beanName, Class type, MergedAnnotation annotation) { Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on '" + type.getName() + "'."); - this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type)); + BeanDefinitionReaderUtils.registerBeanDefinition(createBeanDefinition(beanName, type), this.registry); } - private BeanDefinition createBeanDefinition(String beanName, Class type) { + private BeanDefinitionHolder createBeanDefinition(String beanName, Class type) { BindMethod bindMethod = ConfigurationPropertiesBean.deduceBindMethod(type); RootBeanDefinition definition = new RootBeanDefinition(type); BindMethodAttribute.set(definition, bindMethod); if (bindMethod == BindMethod.VALUE_OBJECT) { definition.setInstanceSupplier(() -> ConstructorBound.from(this.beanFactory, beanName, type)); } - return definition; + ScopeMetadata metadata = scopeMetadataResolver.resolveScopeMetadata(new AnnotatedGenericBeanDefinition(type)); + definition.setScope(metadata.getScopeName()); + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition, beanName); + ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); + if (scopedProxyMode.equals(ScopedProxyMode.NO)) { + return definitionHolder; + } + boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); + return ScopedProxyUtils.createScopedProxy(definitionHolder, this.registry, proxyTargetClass); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java index 66989ada160f..a6c33ed7d0ff 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java @@ -20,12 +20,15 @@ import org.junit.jupiter.api.Test; +import org.springframework.aop.scope.ScopedProxyFactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -44,12 +47,38 @@ class ConfigurationPropertiesBeanRegistrarTests { private final ConfigurationPropertiesBeanRegistrar registrar = new ConfigurationPropertiesBeanRegistrar( this.registry); + @Test + void registerScopedBeanDefinition() { + String beanName = "scopedbeancp-" + ScopedBeanConfigurationProperties.class.getName(); + this.registrar.register(ScopedBeanConfigurationProperties.class); + BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName); + assertThat(beanDefinition).isNotNull(); + assertThat(beanDefinition.getBeanClassName()).isEqualTo(ScopedBeanConfigurationProperties.class.getName()); + assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); + } + + @Test + void registerScopedBeanDefinitionWithProxyMode() { + String beanName = "scopedbeancp-" + ProxyScopedBeanConfigurationProperties.class.getName(); + this.registrar.register(ProxyScopedBeanConfigurationProperties.class); + BeanDefinition proxiedBeanDefinition = this.registry.getBeanDefinition(beanName); + assertThat(proxiedBeanDefinition).isNotNull(); + assertThat(proxiedBeanDefinition.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName()); + String targetBeanName = (String) proxiedBeanDefinition.getPropertyValues().get("targetBeanName"); + assertThat(targetBeanName).isNotNull(); + BeanDefinition beanDefinition = this.registry.getBeanDefinition(targetBeanName); + assertThat(beanDefinition).isNotNull(); + assertThat(beanDefinition.getBeanClassName()).isEqualTo(ProxyScopedBeanConfigurationProperties.class.getName()); + assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); + } + @Test void registerWhenNotAlreadyRegisteredAddBeanDefinition() { String beanName = "beancp-" + BeanConfigurationProperties.class.getName(); this.registrar.register(BeanConfigurationProperties.class); BeanDefinition definition = this.registry.getBeanDefinition(beanName); assertThat(definition).isNotNull(); + assertThat(definition.getScope()).isEqualTo(BeanDefinition.SCOPE_SINGLETON); assertThat(definition.getBeanClassName()).isEqualTo(BeanConfigurationProperties.class.getName()); } @@ -99,6 +128,18 @@ static class BeanConfigurationProperties { } + @ConfigurationProperties(prefix = "scopedbeancp") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + static class ScopedBeanConfigurationProperties { + + } + + @ConfigurationProperties(prefix = "scopedbeancp") + @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = BeanDefinition.SCOPE_PROTOTYPE) + static class ProxyScopedBeanConfigurationProperties { + + } + static class NoAnnotationConfigurationProperties { } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java index 449d3fb48dc0..5c2ba0e6725e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java @@ -33,6 +33,7 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.UUID; import jakarta.annotation.PostConstruct; import jakarta.validation.Valid; @@ -48,6 +49,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -1243,6 +1245,24 @@ void loadWhenBindingToConstructorParametersWithConversionToCustomListImplementat "b"); } + @Test + void loadPrototypeScopedProperties() { + load(PrototypeScopePropertiesConfiguration.class); + PrototypeScopeProperties p1 = this.context.getBean(PrototypeScopeProperties.class); + PrototypeScopeProperties p2 = this.context.getBean(PrototypeScopeProperties.class); + assertThat(p1.id).isNotNull(); + assertThat(p2.id).isNotNull(); + assertThat(p1.id).isNotEqualTo(p2.id); + } + + @Test + void loadProxyScopedProperties() { + load(ProxyScopePropertiesConfiguration.class, "name=test"); + ProxyScopeProperties p = this.context.getBean(ProxyScopeProperties.class); + assertThat(p.name).isEqualTo("test"); + assertThat(p.getName()).isEqualTo("test"); + } + @Test void loadWhenBindingToJavaBeanWithConversionToCustomListImplementation() { load(SetterBoundCustomListPropertiesConfiguration.class, "test.values=a,b"); @@ -1493,12 +1513,52 @@ static PropertySourcesPlaceholderConfigurer configurer2() { } + @EnableConfigurationProperties(PrototypeScopeProperties.class) + @Configuration(proxyBeanMethods = false) + static class PrototypeScopePropertiesConfiguration { + + } + + @ConfigurationProperties + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + static class PrototypeScopeProperties { + + private final String id = UUID.randomUUID().toString(); + + String getId() { + return this.id; + } + + } + + @EnableConfigurationProperties(ProxyScopeProperties.class) + @Configuration(proxyBeanMethods = false) + static class ProxyScopePropertiesConfiguration { + + } + + @ConfigurationProperties + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + static class ProxyScopeProperties { + + private String name; + + String getName() { + return this.name; + } + + void setName(String name) { + this.name = name; + } + + } + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties static class PrototypePropertiesConfiguration { @Bean - @Scope("prototype") + @Scope(BeanDefinition.SCOPE_PROTOTYPE) @ConfigurationProperties("example") PrototypeBean prototypeBean() { return new PrototypeBean(); From 23d76a05e73dba1a91a70ab47b4e0be5ddf44040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 10 Sep 2024 16:55:18 +0200 Subject: [PATCH 116/271] Polish "Add consistent scope support for ConfigurationProperties beans" See gh-42073 --- .../ConfigurationPropertiesBeanRegistrar.java | 15 ++- ...igurationPropertiesBeanRegistrarTests.java | 76 +++++++------- ...igurationPropertiesScanRegistrarTests.java | 28 +++--- .../ConfigurationPropertiesTests.java | 99 +++++++------------ ...ConfigurationPropertiesRegistrarTests.java | 12 +-- ...nfigurationPropertiesBeanRegistrarTests.kt | 32 +++--- 6 files changed, 127 insertions(+), 135 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java index 458ac8d874f4..82c3a8bcbe7c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; import org.springframework.context.annotation.AnnotationScopeMetadataResolver; import org.springframework.context.annotation.ScopeMetadata; @@ -88,21 +88,26 @@ private void registerBeanDefinition(String beanName, Class type, } private BeanDefinitionHolder createBeanDefinition(String beanName, Class type) { + GenericBeanDefinition definition = new AnnotatedGenericBeanDefinition(type); BindMethod bindMethod = ConfigurationPropertiesBean.deduceBindMethod(type); - RootBeanDefinition definition = new RootBeanDefinition(type); BindMethodAttribute.set(definition, bindMethod); if (bindMethod == BindMethod.VALUE_OBJECT) { definition.setInstanceSupplier(() -> ConstructorBound.from(this.beanFactory, beanName, type)); } - ScopeMetadata metadata = scopeMetadataResolver.resolveScopeMetadata(new AnnotatedGenericBeanDefinition(type)); + ScopeMetadata metadata = scopeMetadataResolver.resolveScopeMetadata(definition); definition.setScope(metadata.getScopeName()); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition, beanName); + return applyScopedProxyMode(metadata, definitionHolder, this.registry); + } + + static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, + BeanDefinitionRegistry registry) { ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); if (scopedProxyMode.equals(ScopedProxyMode.NO)) { - return definitionHolder; + return definition; } boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); - return ScopedProxyUtils.createScopedProxy(definitionHolder, this.registry, proxyTargetClass); + return ScopedProxyUtils.createScopedProxy(definition, registry, proxyTargetClass); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java index a6c33ed7d0ff..3680387a69bf 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -25,7 +25,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; @@ -47,38 +46,12 @@ class ConfigurationPropertiesBeanRegistrarTests { private final ConfigurationPropertiesBeanRegistrar registrar = new ConfigurationPropertiesBeanRegistrar( this.registry); - @Test - void registerScopedBeanDefinition() { - String beanName = "scopedbeancp-" + ScopedBeanConfigurationProperties.class.getName(); - this.registrar.register(ScopedBeanConfigurationProperties.class); - BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName); - assertThat(beanDefinition).isNotNull(); - assertThat(beanDefinition.getBeanClassName()).isEqualTo(ScopedBeanConfigurationProperties.class.getName()); - assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); - } - - @Test - void registerScopedBeanDefinitionWithProxyMode() { - String beanName = "scopedbeancp-" + ProxyScopedBeanConfigurationProperties.class.getName(); - this.registrar.register(ProxyScopedBeanConfigurationProperties.class); - BeanDefinition proxiedBeanDefinition = this.registry.getBeanDefinition(beanName); - assertThat(proxiedBeanDefinition).isNotNull(); - assertThat(proxiedBeanDefinition.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName()); - String targetBeanName = (String) proxiedBeanDefinition.getPropertyValues().get("targetBeanName"); - assertThat(targetBeanName).isNotNull(); - BeanDefinition beanDefinition = this.registry.getBeanDefinition(targetBeanName); - assertThat(beanDefinition).isNotNull(); - assertThat(beanDefinition.getBeanClassName()).isEqualTo(ProxyScopedBeanConfigurationProperties.class.getName()); - assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); - } - @Test void registerWhenNotAlreadyRegisteredAddBeanDefinition() { String beanName = "beancp-" + BeanConfigurationProperties.class.getName(); this.registrar.register(BeanConfigurationProperties.class); BeanDefinition definition = this.registry.getBeanDefinition(beanName); assertThat(definition).isNotNull(); - assertThat(definition.getScope()).isEqualTo(BeanDefinition.SCOPE_SINGLETON); assertThat(definition.getBeanClassName()).isEqualTo(BeanConfigurationProperties.class.getName()); } @@ -104,7 +77,7 @@ void registerWhenValueObjectRegistersValueObjectBeanDefinition() { String beanName = "valuecp-" + ValueObjectConfigurationProperties.class.getName(); this.registrar.register(ValueObjectConfigurationProperties.class); BeanDefinition definition = this.registry.getBeanDefinition(beanName); - assertThat(definition).satisfies(configurationPropertiesBeanDefinition(BindMethod.VALUE_OBJECT)); + assertThat(definition).satisfies(hasBindMethodAttribute(BindMethod.VALUE_OBJECT)); } @Test @@ -112,12 +85,45 @@ void registerWhenNotValueObjectRegistersRootBeanDefinitionWithJavaBeanBindMethod String beanName = MultiConstructorBeanConfigurationProperties.class.getName(); this.registrar.register(MultiConstructorBeanConfigurationProperties.class); BeanDefinition definition = this.registry.getBeanDefinition(beanName); - assertThat(definition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); + assertThat(definition).satisfies(hasBindMethodAttribute(BindMethod.JAVA_BEAN)); } - private Consumer configurationPropertiesBeanDefinition(BindMethod bindMethod) { + @Test + void registerWhenNoScopeUsesSingleton() { + String beanName = "beancp-" + BeanConfigurationProperties.class.getName(); + this.registrar.register(BeanConfigurationProperties.class); + BeanDefinition definition = this.registry.getBeanDefinition(beanName); + assertThat(definition).isNotNull(); + assertThat(definition.getScope()).isEqualTo(BeanDefinition.SCOPE_SINGLETON); + } + + @Test + void registerScopedBeanDefinition() { + String beanName = "beancp-" + ScopedBeanConfigurationProperties.class.getName(); + this.registrar.register(ScopedBeanConfigurationProperties.class); + BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName); + assertThat(beanDefinition).isNotNull(); + assertThat(beanDefinition.getBeanClassName()).isEqualTo(ScopedBeanConfigurationProperties.class.getName()); + assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); + } + + @Test + void registerScopedBeanDefinitionWithProxyMode() { + String beanName = "beancp-" + ProxyScopedBeanConfigurationProperties.class.getName(); + this.registrar.register(ProxyScopedBeanConfigurationProperties.class); + BeanDefinition proxiedBeanDefinition = this.registry.getBeanDefinition(beanName); + assertThat(proxiedBeanDefinition).isNotNull(); + assertThat(proxiedBeanDefinition.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName()); + String targetBeanName = (String) proxiedBeanDefinition.getPropertyValues().get("targetBeanName"); + assertThat(targetBeanName).isNotNull(); + BeanDefinition beanDefinition = this.registry.getBeanDefinition(targetBeanName); + assertThat(beanDefinition).isNotNull(); + assertThat(beanDefinition.getBeanClassName()).isEqualTo(ProxyScopedBeanConfigurationProperties.class.getName()); + assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); + } + + private Consumer hasBindMethodAttribute(BindMethod bindMethod) { return (definition) -> { - assertThat(definition).isExactlyInstanceOf(RootBeanDefinition.class); assertThat(definition.hasAttribute(BindMethod.class.getName())).isTrue(); assertThat(definition.getAttribute(BindMethod.class.getName())).isEqualTo(bindMethod); }; @@ -128,14 +134,14 @@ static class BeanConfigurationProperties { } - @ConfigurationProperties(prefix = "scopedbeancp") + @ConfigurationProperties(prefix = "beancp") @Scope(BeanDefinition.SCOPE_PROTOTYPE) static class ScopedBeanConfigurationProperties { } - @ConfigurationProperties(prefix = "scopedbeancp") - @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = BeanDefinition.SCOPE_PROTOTYPE) + @ConfigurationProperties(prefix = "beancp") + @Scope(scopeName = BeanDefinition.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS) static class ProxyScopedBeanConfigurationProperties { } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrarTests.java index 66f3148f1065..1bdbab3d041c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -17,13 +17,13 @@ package org.springframework.boot.context.properties; import java.io.IOException; +import java.util.Locale; import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; import org.springframework.boot.context.properties.scan.combined.c.CombinedConfiguration; import org.springframework.boot.context.properties.scan.combined.d.OtherCombinedConfiguration; @@ -56,9 +56,9 @@ void registerBeanDefinitionsShouldScanForConfigurationProperties() throws IOExce "foo-org.springframework.boot.context.properties.scan.valid.ConfigurationPropertiesScanConfiguration$FooProperties"); BeanDefinition barDefinition = this.beanFactory.getBeanDefinition( "bar-org.springframework.boot.context.properties.scan.valid.ConfigurationPropertiesScanConfiguration$BarProperties"); - assertThat(bingDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); - assertThat(fooDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); - assertThat(barDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.VALUE_OBJECT)); + assertThat(bingDefinition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); + assertThat(fooDefinition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); + assertThat(barDefinition).satisfies(hasBindMethod(BindMethod.VALUE_OBJECT)); } @Test @@ -67,9 +67,12 @@ void scanWhenBeanDefinitionExistsShouldSkip() throws IOException { beanFactory.setAllowBeanDefinitionOverriding(false); this.registrar.registerBeanDefinitions( getAnnotationMetadata(ConfigurationPropertiesScanConfiguration.TestConfiguration.class), beanFactory); - BeanDefinition fooDefinition = beanFactory.getBeanDefinition( - "foo-org.springframework.boot.context.properties.scan.valid.ConfigurationPropertiesScanConfiguration$FooProperties"); - assertThat(fooDefinition).isExactlyInstanceOf(RootBeanDefinition.class); + assertThat(beanFactory.containsBeanDefinition( + "foo-org.springframework.boot.context.properties.scan.valid.ConfigurationPropertiesScanConfiguration$FooProperties")) + .isTrue(); + assertThat(beanFactory.getBeanDefinitionNames()) + .filteredOn((name) -> name.toLowerCase(Locale.ENGLISH).contains("fooproperties")) + .hasSize(1); } @Test @@ -88,11 +91,11 @@ void scanWhenBasePackagesAndBasePackageClassesProvidedShouldUseThat() throws IOE "b.first-org.springframework.boot.context.properties.scan.valid.b.BScanConfiguration$BFirstProperties"); BeanDefinition bSecondDefinition = beanFactory.getBeanDefinition( "b.second-org.springframework.boot.context.properties.scan.valid.b.BScanConfiguration$BSecondProperties"); - assertThat(aDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); + assertThat(aDefinition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); // Constructor injection - assertThat(bFirstDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.VALUE_OBJECT)); + assertThat(bFirstDefinition).satisfies(hasBindMethod(BindMethod.VALUE_OBJECT)); // Post-processing injection - assertThat(bSecondDefinition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); + assertThat(bSecondDefinition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); } @Test @@ -112,9 +115,8 @@ void scanWhenOtherComponentAnnotationPresentShouldSkipType() throws IOException assertThat(beanFactory.getBeanDefinitionCount()).isZero(); } - private Consumer configurationPropertiesBeanDefinition(BindMethod bindMethod) { + private Consumer hasBindMethod(BindMethod bindMethod) { return (definition) -> { - assertThat(definition).isExactlyInstanceOf(RootBeanDefinition.class); assertThat(definition.hasAttribute(BindMethod.class.getName())).isTrue(); assertThat(definition.getAttribute(BindMethod.class.getName())).isEqualTo(bindMethod); }; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java index 5c2ba0e6725e..392a7804bf3f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java @@ -33,7 +33,6 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; -import java.util.UUID; import jakarta.annotation.PostConstruct; import jakarta.validation.Valid; @@ -586,13 +585,21 @@ void loadWhenHasUnboundElementsFromSystemEnvironmentShouldNotThrowException() { } @Test - void loadShouldSupportRebindableConfigurationProperties() { - // gh-9160 + void loadShouldSupportRebindableConfigurationPropertiesRegisteredAsBean() { + testRebindableConfigurationProperties(PrototypePropertiesBeanConfiguration.class); + } + + @Test + void loadShouldSupportRebindableConfigurationPropertiesRegisteredUsingRegistrar() { + testRebindableConfigurationProperties(PrototypePropertiesRegistrarConfiguration.class); + } + + void testRebindableConfigurationProperties(Class configurationClass) { MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); Map source = new LinkedHashMap<>(); source.put("example.one", "foo"); sources.addFirst(new MapPropertySource("test-source", source)); - this.context.register(PrototypePropertiesConfiguration.class); + this.context.register(configurationClass); this.context.refresh(); PrototypeBean first = this.context.getBean(PrototypeBean.class); assertThat(first.getOne()).isEqualTo("foo"); @@ -604,12 +611,24 @@ void loadShouldSupportRebindableConfigurationProperties() { } @Test - void loadWhenHasPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationProperties() { + void loadWhenHasPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationPropertiesRegisteredAsBean() { + testPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationProperties( + PrototypePropertiesBeanConfiguration.class); + } + + @Test + void loadWhenHasPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationPropertiesRegisteredUsingRegistrar() { + testPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationProperties( + PrototypePropertiesRegistrarConfiguration.class); + } + + void testPropertySourcesPlaceholderConfigurerShouldSupportRebindableConfigurationProperties( + Class configurationClass) { MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); Map source = new LinkedHashMap<>(); source.put("example.one", "foo"); sources.addFirst(new MapPropertySource("test-source", source)); - this.context.register(PrototypePropertiesConfiguration.class); + this.context.register(configurationClass); this.context.register(PropertySourcesPlaceholderConfigurer.class); this.context.refresh(); PrototypeBean first = this.context.getBean(PrototypeBean.class); @@ -1245,24 +1264,6 @@ void loadWhenBindingToConstructorParametersWithConversionToCustomListImplementat "b"); } - @Test - void loadPrototypeScopedProperties() { - load(PrototypeScopePropertiesConfiguration.class); - PrototypeScopeProperties p1 = this.context.getBean(PrototypeScopeProperties.class); - PrototypeScopeProperties p2 = this.context.getBean(PrototypeScopeProperties.class); - assertThat(p1.id).isNotNull(); - assertThat(p2.id).isNotNull(); - assertThat(p1.id).isNotEqualTo(p2.id); - } - - @Test - void loadProxyScopedProperties() { - load(ProxyScopePropertiesConfiguration.class, "name=test"); - ProxyScopeProperties p = this.context.getBean(ProxyScopeProperties.class); - assertThat(p.name).isEqualTo("test"); - assertThat(p.getName()).isEqualTo("test"); - } - @Test void loadWhenBindingToJavaBeanWithConversionToCustomListImplementation() { load(SetterBoundCustomListPropertiesConfiguration.class, "test.values=a,b"); @@ -1513,56 +1514,28 @@ static PropertySourcesPlaceholderConfigurer configurer2() { } - @EnableConfigurationProperties(PrototypeScopeProperties.class) @Configuration(proxyBeanMethods = false) - static class PrototypeScopePropertiesConfiguration { - - } - - @ConfigurationProperties - @Scope(BeanDefinition.SCOPE_PROTOTYPE) - static class PrototypeScopeProperties { - - private final String id = UUID.randomUUID().toString(); + @EnableConfigurationProperties + static class PrototypePropertiesBeanConfiguration { - String getId() { - return this.id; + @Bean + @Scope(BeanDefinition.SCOPE_PROTOTYPE) + @ConfigurationProperties("example") + PrototypeBean prototypeBean() { + return new PrototypeBean(); } } - @EnableConfigurationProperties(ProxyScopeProperties.class) @Configuration(proxyBeanMethods = false) - static class ProxyScopePropertiesConfiguration { + @EnableConfigurationProperties(PrototypeBeanProperties.class) + static class PrototypePropertiesRegistrarConfiguration { } - @ConfigurationProperties + @ConfigurationProperties("example") @Scope(BeanDefinition.SCOPE_PROTOTYPE) - static class ProxyScopeProperties { - - private String name; - - String getName() { - return this.name; - } - - void setName(String name) { - this.name = name; - } - - } - - @Configuration(proxyBeanMethods = false) - @EnableConfigurationProperties - static class PrototypePropertiesConfiguration { - - @Bean - @Scope(BeanDefinition.SCOPE_PROTOTYPE) - @ConfigurationProperties("example") - PrototypeBean prototypeBean() { - return new PrototypeBean(); - } + static class PrototypeBeanProperties extends PrototypeBean { } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrarTests.java index 7fd7bb7a5a3c..63e9167534d9 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -23,7 +23,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; import org.springframework.core.type.AnnotationMetadata; @@ -57,7 +56,7 @@ void typeWithDefaultConstructorShouldRegisterRootBeanDefinition() { register(TestConfiguration.class); BeanDefinition definition = this.beanFactory .getBeanDefinition("foo-" + getClass().getName() + "$FooProperties"); - assertThat(definition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); + assertThat(definition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); } @Test @@ -65,7 +64,7 @@ void constructorBoundPropertiesShouldRegisterConfigurationPropertiesBeanDefiniti register(TestConfiguration.class); BeanDefinition definition = this.beanFactory .getBeanDefinition("bar-" + getClass().getName() + "$BarProperties"); - assertThat(definition).satisfies(configurationPropertiesBeanDefinition(BindMethod.VALUE_OBJECT)); + assertThat(definition).satisfies(hasBindMethod(BindMethod.VALUE_OBJECT)); } @Test @@ -73,7 +72,7 @@ void typeWithMultipleConstructorsShouldRegisterGenericBeanDefinition() { register(TestConfiguration.class); BeanDefinition definition = this.beanFactory .getBeanDefinition("bing-" + getClass().getName() + "$BingProperties"); - assertThat(definition).satisfies(configurationPropertiesBeanDefinition(BindMethod.JAVA_BEAN)); + assertThat(definition).satisfies(hasBindMethod(BindMethod.JAVA_BEAN)); } @Test @@ -99,9 +98,8 @@ void registrationWithNoTypeShouldNotRegisterAnything() { } } - private Consumer configurationPropertiesBeanDefinition(BindMethod bindMethod) { + private Consumer hasBindMethod(BindMethod bindMethod) { return (definition) -> { - assertThat(definition).isExactlyInstanceOf(RootBeanDefinition.class); assertThat(definition.hasAttribute(BindMethod.class.getName())).isTrue(); assertThat(definition.getAttribute(BindMethod.class.getName())).isEqualTo(bindMethod); }; diff --git a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/context/properties/KotlinConfigurationPropertiesBeanRegistrarTests.kt b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/context/properties/KotlinConfigurationPropertiesBeanRegistrarTests.kt index cc50d38eda96..4b941250bc49 100644 --- a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/context/properties/KotlinConfigurationPropertiesBeanRegistrarTests.kt +++ b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/context/properties/KotlinConfigurationPropertiesBeanRegistrarTests.kt @@ -1,9 +1,24 @@ +/* + * Copyright 2012-2024 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.context.properties import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.beans.factory.support.DefaultListableBeanFactory -import org.springframework.beans.factory.support.RootBeanDefinition import org.springframework.boot.context.properties.bind.BindMethod /** @@ -19,15 +34,7 @@ class KotlinConfigurationPropertiesBeanRegistrarTests { private val registrar = ConfigurationPropertiesBeanRegistrar(beanFactory) @Test - fun `type with default constructor should register root bean definition`() { - this.registrar.register(FooProperties::class.java) - val beanDefinition = this.beanFactory.getBeanDefinition( - "foo-org.springframework.boot.context.properties.KotlinConfigurationPropertiesBeanRegistrarTests\$FooProperties") - assertThat(beanDefinition).isExactlyInstanceOf(RootBeanDefinition::class.java) - } - - @Test - fun `type with primary constructor and no autowired should register configuration properties bean definition`() { + fun `type with primary constructor and no autowired should register value object configuration properties`() { this.registrar.register(BarProperties::class.java) val beanDefinition = this.beanFactory.getBeanDefinition( "bar-org.springframework.boot.context.properties.KotlinConfigurationPropertiesBeanRegistrarTests\$BarProperties") @@ -36,11 +43,12 @@ class KotlinConfigurationPropertiesBeanRegistrarTests { } @Test - fun `type with no primary constructor should register root bean definition`() { + fun `type with no primary constructor should register java bean configuration properties`() { this.registrar.register(BingProperties::class.java) val beanDefinition = this.beanFactory.getBeanDefinition( "bing-org.springframework.boot.context.properties.KotlinConfigurationPropertiesBeanRegistrarTests\$BingProperties") - assertThat(beanDefinition).isExactlyInstanceOf(RootBeanDefinition::class.java) + assertThat(beanDefinition.hasAttribute(BindMethod::class.java.name)).isTrue() + assertThat(beanDefinition.getAttribute(BindMethod::class.java.name)).isEqualTo(BindMethod.JAVA_BEAN) } @ConfigurationProperties(prefix = "foo") From bee3158d91616485f812c93cd7f7e78302893b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 11 Sep 2024 06:45:38 +0200 Subject: [PATCH 117/271] Use Antora links for Spring Data reference doc Closes gh-42203 --- spring-boot-project/spring-boot-docs/build.gradle | 10 ++++++++-- .../src/docs/asciidoc/attributes.adoc | 14 ++++++++------ .../src/docs/asciidoc/data/nosql.adoc | 10 +++++----- .../src/docs/asciidoc/data/sql.adoc | 8 ++++---- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index dba2031dd9b1..d69f86ecc754 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -330,13 +330,19 @@ tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { "spring-batch-version": versionConstraints["org.springframework.batch:spring-batch-core"], "spring-batch-version-antora": toAntoraVersion(versionConstraints["org.springframework.batch:spring-batch-core"]), "spring-boot-version": project.version, + "spring-data-cassandra-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-cassandra"]), "spring-data-commons-version": versionConstraints["org.springframework.data:spring-data-commons"], - "spring-data-couchbase-version": versionConstraints["org.springframework.data:spring-data-couchbase"], + "spring-data-couchbase-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-couchbase"]), + "spring-data-elasticsearch-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-elasticsearch"]), "spring-data-jdbc-version": versionConstraints["org.springframework.data:spring-data-jdbc"], + "spring-data-jdbc-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-jdbc"]), "spring-data-jpa-version": versionConstraints["org.springframework.data:spring-data-jpa"], + "spring-data-jpa-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-jpa"]), + "spring-data-ldap-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-ldap"]), "spring-data-mongodb-version": versionConstraints["org.springframework.data:spring-data-mongodb"], - "spring-data-neo4j-version": versionConstraints["org.springframework.data:spring-data-neo4j"], + "spring-data-neo4j-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-neo4j"]), "spring-data-r2dbc-version": versionConstraints["org.springframework.data:spring-data-r2dbc"], + "spring-data-r2dbc-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-r2dbc"]), "spring-data-rest-version": versionConstraints["org.springframework.data:spring-data-rest-core"], "spring-framework-version": versionConstraints["org.springframework:spring-core"], "spring-framework-version-antora": toAntoraVersion(versionConstraints["org.springframework:spring-core"]), diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc index 8e2ef2e3fe79..10903baaffe5 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc @@ -59,25 +59,27 @@ :spring-batch-docs: https://docs.spring.io/spring-batch/reference/{spring-batch-version-antora} :spring-data: https://spring.io/projects/spring-data :spring-data-cassandra: https://spring.io/projects/spring-data-cassandra +:spring-data-cassandra-docs: https://docs.spring.io/spring-data/cassandra/reference/{spring-data-cassandra-version-antora} :spring-data-commons-api: https://docs.spring.io/spring-data/commons/docs/{spring-data-commons-version}/api/org/springframework/data :spring-data-couchbase: https://spring.io/projects/spring-data-couchbase -:spring-data-couchbase-docs: https://docs.spring.io/spring-data/couchbase/docs/{spring-data-couchbase-version}/reference/html/ +:spring-data-couchbase-docs: https://docs.spring.io/spring-data/couchbase/reference/{spring-data-couchbase-version-antora} :spring-data-elasticsearch: https://spring.io/projects/spring-data-elasticsearch -:spring-data-elasticsearch-docs: https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/ +:spring-data-elasticsearch-docs: https://docs.spring.io/spring-data/elasticsearch/reference/{spring-data-elasticsearch-version-antora} :spring-data-envers: https://spring.io/projects/spring-data-envers :spring-data-gemfire: https://spring.io/projects/spring-data-gemfire :spring-data-geode: https://spring.io/projects/spring-data-geode +:spring-data-jdbc-docs: https://docs.spring.io/spring-data/relational/reference/{spring-data-jdbc-version-antora} :spring-data-jpa: https://spring.io/projects/spring-data-jpa :spring-data-jpa-api: https://docs.spring.io/spring-data/jpa/docs/{spring-data-jpa-version}/api/org/springframework/data/jpa -:spring-data-jpa-docs: https://docs.spring.io/spring-data/jpa/reference/{spring-data-jpa-version}/ -:spring-data-jdbc-docs: https://docs.spring.io/spring-data/jdbc/docs/{spring-data-jdbc-version}/reference/html/ +:spring-data-jpa-docs: https://docs.spring.io/spring-data/jpa/reference/{spring-data-jpa-version-antora} :spring-data-ldap: https://spring.io/projects/spring-data-ldap +:spring-data-ldap-docs: https://docs.spring.io/spring-data/ldap/reference/{spring-data-ldap-version-antora} :spring-data-mongodb: https://spring.io/projects/spring-data-mongodb :spring-data-mongodb-api: https://docs.spring.io/spring-data/mongodb/docs/{spring-data-mongodb-version}/api/org/springframework/data/mongodb :spring-data-neo4j: https://spring.io/projects/spring-data-neo4j -:spring-data-neo4j-docs: https://docs.spring.io/spring-data/neo4j/docs/{spring-data-neo4j-version}/reference/html/ +:spring-data-neo4j-docs: https://docs.spring.io/spring-data/neo4j/reference/{spring-data-neo4j-version-antora} :spring-data-r2dbc-api: https://docs.spring.io/spring-data/r2dbc/docs/{spring-data-r2dbc-version}/api/org/springframework/data/r2dbc -:spring-data-r2dbc-docs: https://docs.spring.io/spring-data/r2dbc/docs/{spring-data-r2dbc-version}/reference/html/ +:spring-data-r2dbc-docs: https://docs.spring.io/spring-data/relational/reference/{spring-data-r2dbc-version-antora} :spring-data-redis: https://spring.io/projects/spring-data-redis :spring-data-rest-api: https://docs.spring.io/spring-data/rest/docs/{spring-data-rest-version}/api/org/springframework/data/rest :spring-framework: https://spring.io/projects/spring-framework diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc index fc99ae1f5390..af715d8ac644 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc @@ -259,7 +259,7 @@ Each will be called in order with the `ConfigBuilder` that is used to build the [[data.nosql.neo4j.repositories]] ==== Spring Data Neo4j Repositories Spring Data includes repository support for Neo4j. -For complete details of Spring Data Neo4j, see the {spring-data-neo4j-docs}[reference documentation]. +For complete details of Spring Data Neo4j, see the {spring-data-neo4j-docs}/[reference documentation]. Spring Data Neo4j shares the common infrastructure with Spring Data JPA as many other Spring Data modules do. You could take the JPA example from earlier and define `City` as Spring Data Neo4j `@Node` rather than JPA `@Entity` and the repository abstraction works in the same way, as shown in the following example: @@ -386,7 +386,7 @@ Repositories and documents are found through scanning. By default, the <> are scanned. You can customize the locations to look for repositories and documents by using `@EnableElasticsearchRepositories` and `@EntityScan` respectively. -TIP: For complete details of Spring Data Elasticsearch, see the {spring-data-elasticsearch-docs}[reference documentation]. +TIP: For complete details of Spring Data Elasticsearch, see the {spring-data-elasticsearch-docs}/[reference documentation]. Spring Boot supports both classic and reactive Elasticsearch repositories, using the `ElasticsearchRestTemplate` or `ReactiveElasticsearchTemplate` beans. Most likely those beans are auto-configured by Spring Boot given the required dependencies are present. @@ -501,7 +501,7 @@ Repositories and entities are found through scanning. By default, the <> are scanned. You can customize the locations to look for repositories and entities by using `@EnableCassandraRepositories` and `@EntityScan` respectively. -TIP: For complete details of Spring Data Cassandra, see the https://docs.spring.io/spring-data/cassandra/docs/[reference documentation]. +TIP: For complete details of Spring Data Cassandra, see the {spring-data-cassandra-docs}/[reference documentation]. @@ -555,7 +555,7 @@ Repositories and documents are found through scanning. By default, the <> are scanned. You can customize the locations to look for repositories and documents by using `@EnableCouchbaseRepositories` and `@EntityScan` respectively. -For complete details of Spring Data Couchbase, see the {spring-data-couchbase-docs}[reference documentation]. +For complete details of Spring Data Couchbase, see the {spring-data-couchbase-docs}/[reference documentation]. You can inject an auto-configured `CouchbaseTemplate` instance as you would with any other Spring Bean, provided a `CouchbaseClientFactory` bean is available. This happens when a `Cluster` is available, as described above, and a bucket name has been specified: @@ -625,7 +625,7 @@ Repositories and documents are found through scanning. By default, the <> are scanned. You can customize the locations to look for repositories and documents by using `@EnableLdapRepositories` and `@EntityScan` respectively. -For complete details of Spring Data LDAP, see the https://docs.spring.io/spring-data/ldap/docs/1.0.x/reference/html/[reference documentation]. +For complete details of Spring Data LDAP, see the {spring-data-ldap-docs}/[reference documentation]. You can also inject an auto-configured `LdapTemplate` instance as you would with any other Spring Bean, as shown in the following example: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc index b5746f819a60..90c18fbaaca1 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/sql.adoc @@ -248,7 +248,7 @@ For JPA components (such as converters) that are created as Spring beans, use `O ==== TIP: We have barely scratched the surface of Spring Data JPA. -For complete details, see the {spring-data-jpa-docs}[Spring Data JPA reference documentation]. +For complete details, see the {spring-data-jpa-docs}/[Spring Data JPA reference documentation]. @@ -260,7 +260,7 @@ To use Spring Data Envers, make sure your repository extends from `RevisionRepos include::code:CountryRepository[] -NOTE: For more details, check the {spring-data-jpa-docs}/#envers[Spring Data Envers reference documentation]. +NOTE: For more details, check the {spring-data-jpa-docs}/envers.html[Spring Data Envers reference documentation]. @@ -312,7 +312,7 @@ Spring Boot will auto-configure Spring Data's JDBC repositories when the necessa They can be added to your project with a single dependency on `spring-boot-starter-data-jdbc`. If necessary, you can take control of Spring Data JDBC's configuration by adding the `@EnableJdbcRepositories` annotation or an `AbstractJdbcConfiguration` subclass to your application. -TIP: For complete details of Spring Data JDBC, see the {spring-data-jdbc-docs}[reference documentation]. +TIP: For complete details of Spring Data JDBC, see the {spring-data-jdbc-docs}/[reference documentation]. @@ -522,4 +522,4 @@ The following example shows a typical Spring Data repository interface definitio include::code:CityRepository[] -TIP: We have barely scratched the surface of Spring Data R2DBC. For complete details, see the {spring-data-r2dbc-docs}[Spring Data R2DBC reference documentation]. +TIP: We have barely scratched the surface of Spring Data R2DBC. For complete details, see the {spring-data-r2dbc-docs}/[Spring Data R2DBC reference documentation]. From 81853d4ae4eb4bba12d83d27bfd57612bc0028b9 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 10 Sep 2024 22:08:57 -0700 Subject: [PATCH 118/271] Use early static registration of EventPublishingContextWrapper in tests Add `OpenTelemetryEventPublisherBeansTestExecutionListener` JUnit class to automatically trigger early addition of the `ContextStorage` wrapper. The listener has also been updated with a static `addWrapper()` method that can be called directly for other test frameworks. Closes gh-42005 --- .../build.gradle | 1 + ...entPublisherBeansApplicationListener.java} | 92 +++++++++++-------- ...ntPublisherBeansTestExecutionListener.java | 38 ++++++++ ...it.platform.launcher.TestExecutionListener | 1 + .../main/resources/META-INF/spring.factories | 2 +- .../BaggagePropagationIntegrationTests.java | 5 +- ...TestExecutionListenerIntegrationTests.java | 52 +++++++++++ ...elemetryTracingAutoConfigurationTests.java | 9 +- 8 files changed, 152 insertions(+), 48 deletions(-) rename spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/{OpenTelemetryEventPublisherApplicationListener.java => OpenTelemetryEventPublisherBeansApplicationListener.java} (64%) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansTestExecutionListener.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublishingContextWrapperBeansTestExecutionListenerIntegrationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index 6ae4cf452814..0671717c0dd2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -106,6 +106,7 @@ dependencies { optional("org.hibernate.orm:hibernate-micrometer") optional("org.hibernate.validator:hibernate-validator") optional("org.influxdb:influxdb-java") + optional("org.junit.platform:junit-platform-launcher") optional("org.liquibase:liquibase-core") { exclude group: "javax.xml.bind", module: "jaxb-api" } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherApplicationListener.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansApplicationListener.java similarity index 64% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherApplicationListener.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansApplicationListener.java index eac080a07b92..d43cf33428e5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherApplicationListener.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansApplicationListener.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.UnaryOperator; import io.micrometer.tracing.otel.bridge.EventPublishingContextWrapper; import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher; @@ -42,11 +41,17 @@ /** * {@link ApplicationListener} to add an OpenTelemetry {@link ContextStorage} wrapper for * {@link EventPublisher} bean support. A single {@link ContextStorage} wrapper is added - * as early as possible then updated with {@link EventPublisher} beans as needed. + * on the {@link ApplicationStartingEvent} then updated with {@link EventPublisher} beans + * as needed. + *

+ * The {@link #addWrapper()} method may also be called directly if the + * {@link ApplicationStartingEvent} isn't called early enough or isn't fired. * * @author Phillip Webb + * @since 3.4.0 + * @see OpenTelemetryEventPublisherBeansTestExecutionListener */ -class OpenTelemetryEventPublisherApplicationListener implements GenericApplicationListener { +public class OpenTelemetryEventPublisherBeansApplicationListener implements GenericApplicationListener { private static final boolean OTEL_CONTEXT_PRESENT = ClassUtils.isPresent("io.opentelemetry.context.ContextStorage", null); @@ -54,6 +59,8 @@ class OpenTelemetryEventPublisherApplicationListener implements GenericApplicati private static final boolean MICROMETER_OTEL_PRESENT = ClassUtils .isPresent("io.micrometer.tracing.otel.bridge.OtelTracer", null); + private static final AtomicBoolean added = new AtomicBoolean(); + @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; @@ -69,11 +76,11 @@ public boolean supportsEventType(ResolvableType eventType) { @Override public void onApplicationEvent(ApplicationEvent event) { - if (!OTEL_CONTEXT_PRESENT || !MICROMETER_OTEL_PRESENT) { + if (!isInstallable()) { return; } if (event instanceof ApplicationStartingEvent) { - EventPublisherBeansContextWrapper.addWrapperIfNecessary(); + addWrapper(); } if (event instanceof ContextRefreshedEvent contextRefreshedEvent) { ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); @@ -83,92 +90,105 @@ public void onApplicationEvent(ApplicationEvent event) { .stream() .map(EventPublishingContextWrapper::new) .toList(); - EventPublisherBeansContextWrapper.instance.put(applicationContext, publishers); + Wrapper.instance.put(applicationContext, publishers); } if (event instanceof ContextClosedEvent contextClosedEvent) { - EventPublisherBeansContextWrapper.instance.remove(contextClosedEvent.getApplicationContext()); + Wrapper.instance.remove(contextClosedEvent.getApplicationContext()); } } /** - * The single {@link ContextStorage} wrapper that delegates to {@link EventPublisher} - * beans. + * {@link ContextStorage#addWrapper(java.util.function.Function) Add} the + * {@link ContextStorage} wrapper to ensure that {@link EventPublisher} are propagated + * correctly. */ - static class EventPublisherBeansContextWrapper implements UnaryOperator { + public static void addWrapper() { + if (isInstallable() && added.compareAndSet(false, true)) { + Wrapper.instance.addWrapper(); + } + } + + private static boolean isInstallable() { + return OTEL_CONTEXT_PRESENT && MICROMETER_OTEL_PRESENT; + } - private static final AtomicBoolean added = new AtomicBoolean(); + /** + * Single instance class used to add the wrapper and manage the {@link EventPublisher} + * beans. + */ + static final class Wrapper { - private static final EventPublisherBeansContextWrapper instance = new EventPublisherBeansContextWrapper(); + static Wrapper instance = new Wrapper(); - private final MultiValueMap publishers = new LinkedMultiValueMap<>(); + private final MultiValueMap beans = new LinkedMultiValueMap<>(); - private volatile ContextStorage delegate; + private volatile ContextStorage storageDelegate; - static void addWrapperIfNecessary() { - if (added.compareAndSet(false, true)) { - ContextStorage.addWrapper(instance); - } + private Wrapper() { } - @Override - public ContextStorage apply(ContextStorage contextStorage) { - return new EventPublisherBeansContextStorage(contextStorage); + private void addWrapper() { + ContextStorage.addWrapper(Storage::new); } void put(ApplicationContext applicationContext, List publishers) { synchronized (this) { - this.publishers.addAll(applicationContext, publishers); - this.delegate = null; + this.beans.addAll(applicationContext, publishers); + this.storageDelegate = null; } } void remove(ApplicationContext applicationContext) { synchronized (this) { - this.publishers.remove(applicationContext); - this.delegate = null; + this.beans.remove(applicationContext); + this.storageDelegate = null; } } - private ContextStorage getDelegate(ContextStorage parent) { - ContextStorage delegate = this.delegate; + ContextStorage getStorageDelegate(ContextStorage parent) { + ContextStorage delegate = this.storageDelegate; if (delegate == null) { synchronized (this) { delegate = parent; - for (List publishers : this.publishers.values()) { + for (List publishers : this.beans.values()) { for (EventPublishingContextWrapper publisher : publishers) { delegate = publisher.apply(delegate); } } + this.storageDelegate = delegate; } } return delegate; } /** - * The wrapped {@link ContextStorage} that delegates to the - * {@link EventPublisherBeansContextWrapper}. + * {@link ContextStorage} that delegates to the {@link EventPublisher} beans. */ - class EventPublisherBeansContextStorage implements ContextStorage { + class Storage implements ContextStorage { private final ContextStorage parent; - EventPublisherBeansContextStorage(ContextStorage wrapped) { - this.parent = wrapped; + Storage(ContextStorage parent) { + this.parent = parent; } @Override public Scope attach(Context toAttach) { - return getDelegate(this.parent).attach(toAttach); + return getDelegate().attach(toAttach); } @Override public Context current() { - return getDelegate(this.parent).current(); + return getDelegate().current(); } @Override public Context root() { - return getDelegate(this.parent).root(); + return getDelegate().root(); + } + + private ContextStorage getDelegate() { + return getStorageDelegate(this.parent); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansTestExecutionListener.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansTestExecutionListener.java new file mode 100644 index 000000000000..f364a6a9e238 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublisherBeansTestExecutionListener.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2024 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.actuate.autoconfigure.tracing; + +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; + +/** + * JUnit {@link TestExecutionListener} to ensure + * {@link OpenTelemetryEventPublisherBeansApplicationListener#addWrapper()} is called as + * early as possible. + * + * @author Phillip Webb + * @since 3.4.0 + * @see OpenTelemetryEventPublisherBeansApplicationListener + */ +public class OpenTelemetryEventPublisherBeansTestExecutionListener implements TestExecutionListener { + + @Override + public void executionStarted(TestIdentifier testIdentifier) { + OpenTelemetryEventPublisherBeansApplicationListener.addWrapper(); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener new file mode 100644 index 000000000000..284014dde279 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener @@ -0,0 +1 @@ +org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherBeansTestExecutionListener diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index f2ae3549ace2..143d99335558 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -13,4 +13,4 @@ org.springframework.boot.actuate.autoconfigure.tracing.LogCorrelationEnvironment # Application Listeners org.springframework.context.ApplicationListener=\ -org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherApplicationListener +org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherBeansApplicationListener diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java index 6fa45e12f15b..f7663b90f537 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/BaggagePropagationIntegrationTests.java @@ -31,7 +31,6 @@ import org.slf4j.MDC; import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherApplicationListener.EventPublisherBeansContextWrapper; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ForkedClassPath; @@ -58,7 +57,7 @@ class BaggagePropagationIntegrationTests { @BeforeEach @AfterEach void setup() { - EventPublisherBeansContextWrapper.addWrapperIfNecessary(); + OpenTelemetryEventPublisherBeansApplicationListener.addWrapper(); MDC.clear(); } @@ -291,7 +290,7 @@ static class OtelApplicationContextInitializer @Override public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.addApplicationListener(new OpenTelemetryEventPublisherApplicationListener()); + applicationContext.addApplicationListener(new OpenTelemetryEventPublisherBeansApplicationListener()); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublishingContextWrapperBeansTestExecutionListenerIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublishingContextWrapperBeansTestExecutionListenerIntegrationTests.java new file mode 100644 index 000000000000..2daee77b4c77 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryEventPublishingContextWrapperBeansTestExecutionListenerIntegrationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2024 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.actuate.autoconfigure.tracing; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.function.Function; + +import io.opentelemetry.context.ContextStorage; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherBeansApplicationListener.Wrapper.Storage; +import org.springframework.boot.testsupport.classpath.ForkedClassPath; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Integration tests for {@link OpenTelemetryEventPublisherBeansTestExecutionListener}. + * + * @author Phillip Webb + */ +@ForkedClassPath +class OpenTelemetryEventPublishingContextWrapperBeansTestExecutionListenerIntegrationTests { + + private final ContextStorage parent = mock(ContextStorage.class); + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + void wrapperIsInstalled() throws Exception { + Class wrappersClass = Class.forName("io.opentelemetry.context.ContextStorageWrappers"); + Method getWrappersMethod = wrappersClass.getDeclaredMethod("getWrappers"); + getWrappersMethod.setAccessible(true); + List wrappers = (List) getWrappersMethod.invoke(null); + assertThat(wrappers).anyMatch((function) -> function.apply(this.parent) instanceof Storage); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java index 87bed95c17f3..c38e2fc11313 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/OpenTelemetryTracingAutoConfigurationTests.java @@ -54,14 +54,12 @@ import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import org.assertj.core.api.InstanceOfAssertFactories; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryEventPublisherApplicationListener.EventPublisherBeansContextWrapper; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.annotation.Configurations; import org.springframework.boot.test.context.FilteredClassLoader; @@ -93,11 +91,6 @@ class OpenTelemetryTracingAutoConfigurationTests { org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration.class, OpenTelemetryTracingAutoConfiguration.class)); - @BeforeAll - static void addWrapper() { - EventPublisherBeansContextWrapper.addWrapperIfNecessary(); - } - @Test void shouldSupplyBeans() { this.contextRunner.run((context) -> { @@ -354,7 +347,7 @@ void shouldUseReplacementForDeprecatedVersion() { } private void initializeOpenTelemetry(ConfigurableApplicationContext context) { - context.addApplicationListener(new OpenTelemetryEventPublisherApplicationListener()); + context.addApplicationListener(new OpenTelemetryEventPublisherBeansApplicationListener()); Span.current(); } From 13fd0f98d1aa42a6723efcf7793d7cf432551210 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 11 Sep 2024 10:20:02 +0200 Subject: [PATCH 119/271] Upgrade to Native Build Tools Plugin 0.10.3 Closes gh-42205 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1df263340053..a7cb98e6ccbb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ jacksonVersion=2.17.2 junitJupiterVersion=5.10.3 kotlinVersion=1.9.25 mavenVersion=3.9.4 -nativeBuildToolsVersion=0.10.2 +nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.1.13-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.28 From 915cb12c644113510fba6d188041341934649b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 11 Sep 2024 11:05:37 +0200 Subject: [PATCH 120/271] Fix broken Spring GraphQL links Closes gh-42207 --- .../src/docs/asciidoc/features/testing.adoc | 4 ++-- .../src/docs/asciidoc/web/spring-graphql.adoc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc index a05cc18214e2..225a0d9c6d7b 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc @@ -484,14 +484,14 @@ Spring GraphQL offers a dedicated testing support module; you'll need to add it } ---- -This testing module ships the {spring-graphql-docs}/#testing-graphqltester[GraphQlTester]. +This testing module ships the {spring-graphql-docs}/testing.html#testing.graphqltester[GraphQlTester]. The tester is heavily used in test, so be sure to become familiar with using it. There are `GraphQlTester` variants and Spring Boot will auto-configure them depending on the type of tests: * the `ExecutionGraphQlServiceTester` performs tests on the server side, without a client nor a transport * the `HttpGraphQlTester` performs tests with a client that connects to a server, with or without a live server -Spring Boot helps you to test your {spring-graphql-docs}/#controllers[Spring GraphQL Controllers] with the `@GraphQlTest` annotation. +Spring Boot helps you to test your {spring-graphql-docs}/controllers.html[Spring GraphQL Controllers] with the `@GraphQlTest` annotation. `@GraphQlTest` auto-configures the Spring GraphQL infrastructure, without any transport nor server being involved. This limits scanned beans to `@Controller`, `RuntimeWiringConfigurer`, `JsonComponent`, `Converter`, `GenericConverter`, `DataFetcherExceptionResolver`, `Instrumentation` and `GraphQlSourceBuilderCustomizer`. Regular `@Component` and `@ConfigurationProperties` beans are not scanned when the `@GraphQlTest` annotation is used. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc index 656edbeaadfa..25308dd3ce1b 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc @@ -54,9 +54,9 @@ If you wish to not expose information about the schema, you can disable introspe === GraphQL RuntimeWiring The GraphQL Java `RuntimeWiring.Builder` can be used to register custom scalar types, directives, type resolvers, `DataFetcher`, and more. You can declare `RuntimeWiringConfigurer` beans in your Spring config to get access to the `RuntimeWiring.Builder`. -Spring Boot detects such beans and adds them to the {spring-graphql-docs}/#execution-graphqlsource[GraphQlSource builder]. +Spring Boot detects such beans and adds them to the {spring-graphql-docs}/request-execution.html#execution.graphqlsource[GraphQlSource builder]. -Typically, however, applications will not implement `DataFetcher` directly and will instead create {spring-graphql-docs}/#controllers[annotated controllers]. +Typically, however, applications will not implement `DataFetcher` directly and will instead create {spring-graphql-docs}/controllers.html[annotated controllers]. Spring Boot will automatically detect `@Controller` classes with annotated handler methods and register those as ``DataFetcher``s. Here's a sample implementation for our greeting query with a `@Controller` class: @@ -67,7 +67,7 @@ include::code:GreetingController[] [[web.graphql.data-query]] === Querydsl and QueryByExample Repositories Support Spring Data offers support for both Querydsl and QueryByExample repositories. -Spring GraphQL can {spring-graphql-docs}/#data[configure Querydsl and QueryByExample repositories as `DataFetcher`]. +Spring GraphQL can {spring-graphql-docs}/data.html[configure Querydsl and QueryByExample repositories as `DataFetcher`]. Spring Data repositories annotated with `@GraphQlRepository` and extending one of: @@ -98,7 +98,7 @@ The GraphQL WebSocket endpoint is off by default. To enable it: * For a WebFlux application, no additional dependency is required * For both, the configprop:spring.graphql.websocket.path[] application property must be set -Spring GraphQL provides a {spring-graphql-docs}/#web-interception[Web Interception] model. +Spring GraphQL provides a {spring-graphql-docs}/transports.html#server.interception[Web Interception] model. This is quite useful for retrieving information from an HTTP request header and set it in the GraphQL context or fetching information from the same context and writing it to a response header. With Spring Boot, you can declare a `WebInterceptor` bean to have it registered with the web transport. @@ -138,7 +138,7 @@ include::code:RSocketGraphQlClientExample[tag=request] [[web.graphql.exception-handling]] === Exception Handling Spring GraphQL enables applications to register one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially. -The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}/#execution-exceptions[Spring GraphQL exception handling documentation]. +The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}/controllers.html#controllers.exception-handler[Spring GraphQL exception handling documentation]. Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and register them with the `GraphQlSource.Builder`. From 2d89cf539cbd8e77c23fdeec6620a678e16db9bf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 10:34:14 +0100 Subject: [PATCH 121/271] Upgrade to Develocity Conventions 0.0.21 Closes gh-42210 --- settings.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/settings.gradle b/settings.gradle index 72c6ed0c6104..369b1d18e21c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,8 +19,7 @@ pluginManagement { } plugins { - id "com.gradle.develocity" version "3.17.5" - id "io.spring.develocity.conventions" version "0.0.19" + id "io.spring.develocity.conventions" version "0.0.21" } rootProject.name="spring-boot-build" From 0130ca17f39cde872178d8f5691dce4d10816319 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:16:38 +0100 Subject: [PATCH 122/271] Upgrade to Artemis 2.37.0 Closes gh-42213 --- 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 e883780dbc86..79a767e788cc 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -54,7 +54,7 @@ bom { releaseNotes("https://github.com/eclipse-ee4j/angus-mail/releases/tag/{version}") } } - library("Artemis", "2.36.0") { + library("Artemis", "2.37.0") { group("org.apache.activemq") { imports = [ "artemis-bom" From 3e2d9c07c5eaef2b412874441767c1c56ba32638 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:16:42 +0100 Subject: [PATCH 123/271] Upgrade to Zipkin Reporter 3.4.1 Closes gh-42214 --- 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 79a767e788cc..510782419ebd 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -103,7 +103,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Zipkin Reporter", "3.4.0") { + library("Zipkin Reporter", "3.4.1") { group("io.zipkin.reporter2") { imports = [ "zipkin-reporter-bom" From df556304679ee6c9716915996dd41d2a7c318baf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:16:46 +0100 Subject: [PATCH 124/271] Upgrade to Byte Buddy 1.15.1 Closes gh-42215 --- 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 510782419ebd..0c6f2e0dd919 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -136,7 +136,7 @@ bom { releaseNotes("https://github.com/mojohaus/build-helper-maven-plugin/releases/tag/{version}") } } - library("Byte Buddy", "1.14.19") { + library("Byte Buddy", "1.15.1") { group("net.bytebuddy") { modules = [ "byte-buddy", From 3dafc4570742e5bbcb9dd9b78455593a8cdad488 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:16:51 +0100 Subject: [PATCH 125/271] Upgrade to Commons Lang3 3.17.0 Closes gh-42216 --- 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 0c6f2e0dd919..243cf9f6d45c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -229,7 +229,7 @@ bom { site("https://commons.apache.org/proper/commons-dbcp") } } - library("Commons Lang3", "3.16.0") { + library("Commons Lang3", "3.17.0") { group("org.apache.commons") { modules = [ "commons-lang3" From 8c9666b32811fdd18a4434c67ec84579450fe6f2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:16:56 +0100 Subject: [PATCH 126/271] Upgrade to Elasticsearch Client 8.15.1 Closes gh-42217 --- 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 243cf9f6d45c..e02eac969ac8 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -334,7 +334,7 @@ bom { releaseNotes("https://github.com/ehcache/ehcache3/releases/tag/v{version}") } } - library("Elasticsearch Client", "8.15.0") { + library("Elasticsearch Client", "8.15.1") { group("org.elasticsearch.client") { modules = [ "elasticsearch-rest-client" { From ee48e83153f925bf14782f429553493c65482a87 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:01 +0100 Subject: [PATCH 127/271] Upgrade to Flyway 10.17.3 Closes gh-42218 --- .../boot/autoconfigure/flyway/FlywayPropertiesTests.java | 2 +- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java index 17fccbfcfdb4..a79740949aea 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java @@ -126,7 +126,7 @@ void expectedPropertiesAreManaged() { ignoreProperties(configuration, "resolversAsClassNames", "callbacksAsClassNames", "driver", "modernConfig", "currentResolvedEnvironment", "reportFilename", "reportEnabled", "workingDirectory", "cachedDataSources", "cachedResolvedEnvironments", "currentEnvironmentName", "allEnvironments", - "environmentProvisionMode"); + "environmentProvisionMode", "provisionMode"); // Handled by the conversion service ignoreProperties(configuration, "baselineVersionAsString", "encodingAsString", "locationsAsStrings", "targetAsString"); diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index e02eac969ac8..b0ecfed20612 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -354,7 +354,7 @@ bom { releaseNotes("https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-{version}.html") } } - library("Flyway", "10.17.1") { + library("Flyway", "10.17.3") { group("org.flywaydb") { modules = [ "flyway-commandline", From 9df090094fe22af2291adbbbb2ab07aea93a5772 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:05 +0100 Subject: [PATCH 128/271] Upgrade to Infinispan 15.0.8.Final Closes gh-42219 --- 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 b0ecfed20612..66d91cf8a976 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -602,7 +602,7 @@ bom { ] } } - library("Infinispan", "15.0.7.Final") { + library("Infinispan", "15.0.8.Final") { group("org.infinispan") { imports = [ "infinispan-bom" From 276a33c37d34930bfd2a8f9bfbe97ece8b4504b2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:09 +0100 Subject: [PATCH 129/271] Upgrade to Jakarta Servlet JSP JSTL 3.0.2 Closes gh-42220 --- 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 66d91cf8a976..0a7658e1a81e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -756,7 +756,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Jakarta Servlet JSP JSTL", "3.0.1") { + library("Jakarta Servlet JSP JSTL", "3.0.2") { group("jakarta.servlet.jsp.jstl") { modules = [ "jakarta.servlet.jsp.jstl-api" From 477c2c45814aecbf526007dadba477eb887ee3b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:14 +0100 Subject: [PATCH 130/271] Upgrade to JBoss Logging 3.6.1.Final Closes gh-42221 --- 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 0a7658e1a81e..cf121fe4e2f9 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -870,7 +870,7 @@ bom { ] } } - library("JBoss Logging", "3.6.0.Final") { + library("JBoss Logging", "3.6.1.Final") { group("org.jboss.logging") { modules = [ "jboss-logging" From add55e91da254f8c0fb32f82f09e30a784b889fa Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:18 +0100 Subject: [PATCH 131/271] Upgrade to Jedis 5.1.5 Closes gh-42222 --- 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 cf121fe4e2f9..4cb9f22b6add 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -884,7 +884,7 @@ bom { ] } } - library("Jedis", "5.0.2") { + library("Jedis", "5.1.5") { group("redis.clients") { modules = [ "jedis" From 0c9e1837447236177696356ad26e71e12fbf62c3 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:23 +0100 Subject: [PATCH 132/271] Upgrade to Jetty Reactive HTTPClient 4.0.7 Closes gh-42223 --- 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 4cb9f22b6add..1b8f564f13f2 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -906,7 +906,7 @@ bom { releaseNotes("https://github.com/eclipse-ee4j/jersey/releases/tag/{version}") } } - library("Jetty Reactive HTTPClient", "4.0.6") { + library("Jetty Reactive HTTPClient", "4.0.7") { group("org.eclipse.jetty") { modules = [ "jetty-reactive-httpclient" From 6dfc7d9c74e5eb76173662fb3339d00136906555 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:27 +0100 Subject: [PATCH 133/271] Upgrade to Jetty 12.0.13 Closes gh-42224 --- 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 1b8f564f13f2..78963d00ac45 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -913,7 +913,7 @@ bom { ] } } - library("Jetty", "12.0.12") { + library("Jetty", "12.0.13") { group("org.eclipse.jetty.ee10") { imports = [ "jetty-ee10-bom" From 173915df3df16f8b9b6844fc078c3cac9cc1a58d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:32 +0100 Subject: [PATCH 134/271] Upgrade to Lettuce 6.4.0.RELEASE Closes gh-42225 --- 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 78963d00ac45..e0b2fe839ddb 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1104,7 +1104,7 @@ bom { releaseNotes("https://github.com/Kotlin/kotlinx.serialization/releases/tag/v{version}") } } - library("Lettuce", "6.3.2.RELEASE") { + library("Lettuce", "6.4.0.RELEASE") { group("io.lettuce") { modules = [ "lettuce-core" From 9f7d10a388f1da308deafb206170f6f4da5eec5d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:36 +0100 Subject: [PATCH 135/271] Upgrade to Liquibase 4.29.2 Closes gh-42226 --- 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 e0b2fe839ddb..75897ce44ee3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1116,7 +1116,7 @@ bom { releaseNotes("https://github.com/lettuce-io/lettuce-core/releases/tag/{version}") } } - library("Liquibase", "4.29.1") { + library("Liquibase", "4.29.2") { group("org.liquibase") { modules = [ "liquibase-cdi", From 5a3e65c33612e526e929036cd1acd6a134c8f604 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:40 +0100 Subject: [PATCH 136/271] Upgrade to Log4j2 2.24.0 Closes gh-42227 --- 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 75897ce44ee3..e0356ba2b1d6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1131,7 +1131,7 @@ bom { releaseNotes("https://github.com/liquibase/liquibase/releases/tag/v{version}") } } - library("Log4j2", "2.23.1") { + library("Log4j2", "2.24.0") { group("org.apache.logging.log4j") { imports = [ "log4j-bom" From 07347465abfe61ac3099188122ed27348c568f97 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:45 +0100 Subject: [PATCH 137/271] Upgrade to Logback 1.5.8 Closes gh-42228 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- .../boot/logging/logback/LogbackLoggingSystemTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index e0356ba2b1d6..d37c027a8740 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1142,7 +1142,7 @@ bom { releaseNotes("https://github.com/apache/logging-log4j2/releases/tag/rel%2F{version}") } } - library("Logback", "1.5.7") { + library("Logback", "1.5.8") { group("ch.qos.logback") { modules = [ "logback-classic", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index bac881abb9ba..642fc388ef80 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -647,7 +647,7 @@ void logbackDebugPropertyIsHonored(CapturedOutput output) { LogFile logFile = getLogFile(file.getPath(), null); initialize(this.initializationContext, null, logFile); assertThat(output).contains("LevelChangePropagator") - .contains("SizeAndTimeBasedFNATP") + .contains("SizeAndTimeBasedFileNamingAndTriggeringPolicy") .contains("DebugLogbackConfigurator"); } finally { From e5858e5d40833a6197b9ced79719fe15de5c00d0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:49 +0100 Subject: [PATCH 138/271] Upgrade to Maven Dependency Plugin 3.8.0 Closes gh-42229 --- 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 d37c027a8740..4c58f601f359 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1203,7 +1203,7 @@ bom { ] } } - library("Maven Dependency Plugin", "3.7.1") { + library("Maven Dependency Plugin", "3.8.0") { group("org.apache.maven.plugins") { plugins = [ "maven-dependency-plugin" From 965cc24d9192d7c210edf07cdb082da339970f10 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:54 +0100 Subject: [PATCH 139/271] Upgrade to Maven Failsafe Plugin 3.5.0 Closes gh-42230 --- 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 4c58f601f359..dbba4484a0ed 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1224,7 +1224,7 @@ bom { ] } } - library("Maven Failsafe Plugin", "3.4.0") { + library("Maven Failsafe Plugin", "3.5.0") { group("org.apache.maven.plugins") { plugins = [ "maven-failsafe-plugin" From 9341afb888bb6c653becc514558b11d2dc1930df Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:17:58 +0100 Subject: [PATCH 140/271] Upgrade to Maven Help Plugin 3.5.0 Closes gh-42231 --- 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 dbba4484a0ed..f988e09a0f19 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1231,7 +1231,7 @@ bom { ] } } - library("Maven Help Plugin", "3.4.1") { + library("Maven Help Plugin", "3.5.0") { group("org.apache.maven.plugins") { plugins = [ "maven-help-plugin" From bd96e20a29dec146931f38668fbc8445b31fc102 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:02 +0100 Subject: [PATCH 141/271] Upgrade to Maven Invoker Plugin 3.8.0 Closes gh-42232 --- 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 f988e09a0f19..cb606541fc6d 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1245,7 +1245,7 @@ bom { ] } } - library("Maven Invoker Plugin", "3.7.0") { + library("Maven Invoker Plugin", "3.8.0") { group("org.apache.maven.plugins") { plugins = [ "maven-invoker-plugin" From 53554278bcb3ba43f11d9829e9277a711d4e01f6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:07 +0100 Subject: [PATCH 142/271] Upgrade to Maven Javadoc Plugin 3.10.0 Closes gh-42233 --- 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 cb606541fc6d..2fd2cb3d2892 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1259,7 +1259,7 @@ bom { ] } } - library("Maven Javadoc Plugin", "3.8.0") { + library("Maven Javadoc Plugin", "3.10.0") { group("org.apache.maven.plugins") { plugins = [ "maven-javadoc-plugin" From e178e34f4f9caca9c1558c8fc5c1c7e896b43954 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:11 +0100 Subject: [PATCH 143/271] Upgrade to Maven Surefire Plugin 3.5.0 Closes gh-42234 --- 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 2fd2cb3d2892..3fd16cb475f9 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1287,7 +1287,7 @@ bom { ] } } - library("Maven Surefire Plugin", "3.4.0") { + library("Maven Surefire Plugin", "3.5.0") { group("org.apache.maven.plugins") { plugins = [ "maven-surefire-plugin" From fda09d5a35be83a3d2274f6e519749efaa5a025c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:16 +0100 Subject: [PATCH 144/271] Upgrade to Mockito 5.13.0 Closes gh-42235 --- 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 3fd16cb475f9..8d88456fc29f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1336,7 +1336,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/tracing/releases/tag/v{version}") } } - library("Mockito", "5.12.0") { + library("Mockito", "5.13.0") { group("org.mockito") { imports = [ "mockito-bom" From dc301d33e8d4b5f32bfc7583c5f818cd9ba10518 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:20 +0100 Subject: [PATCH 145/271] Upgrade to MongoDB 5.1.4 Closes gh-42236 --- 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 8d88456fc29f..b45bbf3216d6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1347,7 +1347,7 @@ bom { releaseNotes("https://github.com/mockito/mockito/releases/tag/v{version}") } } - library("MongoDB", "5.1.3") { + library("MongoDB", "5.1.4") { group("org.mongodb") { modules = [ "bson", From 8b4d66d10e9370e40b9643a320f1fb0785b21fd9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:25 +0100 Subject: [PATCH 146/271] Upgrade to MSSQL JDBC 12.8.1.jre11 Closes gh-42237 --- 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 b45bbf3216d6..33ce78adafdc 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1363,7 +1363,7 @@ bom { releaseNotes("https://github.com/mongodb/mongo-java-driver/releases/tag/r{version}") } } - library("MSSQL JDBC", "12.8.0.jre11") { + library("MSSQL JDBC", "12.8.1.jre11") { prohibit { endsWith(".jre8") because "we want to use the jre11 version" From bddda107e0c3022fdf5c13f9e1e921665880e4fd Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:29 +0100 Subject: [PATCH 147/271] Upgrade to Netty 4.1.113.Final Closes gh-42238 --- 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 33ce78adafdc..1f8271578fcc 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1427,7 +1427,7 @@ bom { releaseNotes("https://github.com/neo4j/neo4j-java-driver/releases/tag/{version}") } } - library("Netty", "4.1.112.Final") { + library("Netty", "4.1.113.Final") { group("io.netty") { imports = [ "netty-bom" From 6692e15001565ba93526d9dc7b3efdd350e2de38 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:34 +0100 Subject: [PATCH 148/271] Upgrade to Postgresql 42.7.4 Closes gh-42239 --- 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 1f8271578fcc..50eeae46bcd8 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1509,7 +1509,7 @@ bom { ] } } - library("Postgresql", "42.7.3") { + library("Postgresql", "42.7.4") { group("org.postgresql") { modules = [ "postgresql" From a6afb64d66fa021d02a0f1fb8ef99d4001cc9d0a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:38 +0100 Subject: [PATCH 149/271] Upgrade to R2DBC MySQL 1.3.0 Closes gh-42240 --- 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 50eeae46bcd8..6f72c82193a3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1618,7 +1618,7 @@ bom { ] } } - library("R2DBC MySQL", "1.1.3") { + library("R2DBC MySQL", "1.3.0") { group("io.asyncer") { modules = [ "r2dbc-mysql" From 608f23543017d3fde1b6ff16492b47cc53d7d3ed Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:39 +0100 Subject: [PATCH 150/271] Upgrade to Reactor Bom 2024.0.0-M6 Closes gh-42139 --- 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 6f72c82193a3..e864f13bb835 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1695,7 +1695,7 @@ bom { ] } } - library("Reactor Bom", "2024.0.0-SNAPSHOT") { + library("Reactor Bom", "2024.0.0-M6") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From c50976a142a52710850dafb3371236c096bdfd62 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:43 +0100 Subject: [PATCH 151/271] Upgrade to Selenium 4.24.0 Closes gh-42241 --- 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 e864f13bb835..5654a0d15f64 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1835,7 +1835,7 @@ bom { ] } } - library("Selenium", "4.23.1") { + library("Selenium", "4.24.0") { group("org.seleniumhq.selenium") { imports = [ "selenium-bom" From f84f045a0d56e55ab2af9e3e377e964f368ba9cd Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:47 +0100 Subject: [PATCH 152/271] Upgrade to SnakeYAML 2.3 Closes gh-42242 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 675d8c6aae81..3a45d566589c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.2.0-SNAPSHOT springFramework60xVersion=6.0.23 tomcatVersion=10.1.28 -snakeYamlVersion=2.2 +snakeYamlVersion=2.3 kotlin.stdlib.default.dependency=false From a3a6b25b817706ced56a99de0393d20049628b82 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:52 +0100 Subject: [PATCH 153/271] Upgrade to Spring HATEOAS 2.4.0-M1 Closes gh-42243 --- 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 5654a0d15f64..2ddb7a1ac0f8 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1985,7 +1985,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-graphql/releases/tag/v{version}") } } - library("Spring HATEOAS", "2.3.2") { + library("Spring HATEOAS", "2.4.0-M1") { considerSnapshots() group("org.springframework.hateoas") { modules = [ From a456a0c208aa4e08b1cacac2985a1b2cbeed7626 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:18:56 +0100 Subject: [PATCH 154/271] Upgrade to Tomcat 10.1.29 Closes gh-42244 --- gradle.properties | 2 +- .../springframework/boot/web/embedded/tomcat/TldPatterns.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3a45d566589c..53e5294ed454 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.2.0-SNAPSHOT springFramework60xVersion=6.0.23 -tomcatVersion=10.1.28 +tomcatVersion=10.1.29 snakeYamlVersion=2.3 kotlin.stdlib.default.dependency=false diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java index eeb73d715be1..69ab0f7854ba 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java @@ -106,6 +106,7 @@ final class TldPatterns { skipPatterns.add("tagsoup-*.jar"); skipPatterns.add("tomcat-api.jar"); skipPatterns.add("tomcat-coyote.jar"); + skipPatterns.add("tomcat-coyote-ffm.jar"); skipPatterns.add("tomcat-dbcp.jar"); skipPatterns.add("tomcat-i18n-*.jar"); skipPatterns.add("tomcat-jdbc.jar"); From 1073c3bf89d53f636668da3495d1fe33e5c62485 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:19:58 +0100 Subject: [PATCH 155/271] Upgrade to Micrometer 1.14.0-M3 Closes gh-42137 --- 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 2ddb7a1ac0f8..a89bf9f6c83e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1301,7 +1301,7 @@ bom { ] } } - library("Micrometer", "1.14.0-SNAPSHOT") { + library("Micrometer", "1.14.0-M3") { considerSnapshots() group("io.micrometer") { modules = [ From 080e43ee3628329239b6f61ee2ee3291a0aec7a4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:19:59 +0100 Subject: [PATCH 156/271] Upgrade to Micrometer Tracing 1.4.0-M3 Closes gh-42138 --- 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 a89bf9f6c83e..a13a8969ea29 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1321,7 +1321,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.4.0-SNAPSHOT") { + library("Micrometer Tracing", "1.4.0-M3") { considerSnapshots() group("io.micrometer") { imports = [ From 74f3f8188c688791906915d531e5e41658387142 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 11:21:32 +0100 Subject: [PATCH 157/271] Remove workaround for LOG4J2-3618 Closes gh-41906 --- .../logging/log4j2/Log4J2LoggingSystemTests.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index 77982dc5ef97..dd7446367260 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -24,7 +24,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Handler; import java.util.logging.Level; @@ -43,7 +42,6 @@ import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry; import org.apache.logging.log4j.jul.Log4jBridgeHandler; import org.apache.logging.log4j.util.PropertiesUtil; -import org.apache.logging.log4j.util.PropertySource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,7 +62,6 @@ import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.core.env.Environment; import org.springframework.mock.env.MockEnvironment; -import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -110,7 +107,6 @@ void setup() { this.configuration = loggerContext.getConfiguration(); this.loggingSystem.cleanUp(); this.logger = LogManager.getLogger(getClass()); - cleanUpPropertySources(); } @AfterEach @@ -119,19 +115,9 @@ void cleanUp() { LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false); loggerContext.stop(); loggerContext.start(((Reconfigurable) this.configuration).reconfigure()); - cleanUpPropertySources(); PluginRegistry.getInstance().clear(); } - @SuppressWarnings("unchecked") - private void cleanUpPropertySources() { // https://issues.apache.org/jira/browse/LOG4J2-3618 - PropertiesUtil properties = PropertiesUtil.getProperties(); - Object environment = ReflectionTestUtils.getField(properties, "environment"); - Set sources = (Set) ReflectionTestUtils.getField(environment, "sources"); - sources.removeIf((candidate) -> candidate instanceof SpringEnvironmentPropertySource - || candidate instanceof SpringBootPropertySource); - } - @Test void noFile(CapturedOutput output) { this.loggingSystem.beforeInitialize(); From ec91b4c8b8cc9f80e64407bb3b16dd1b4da8216f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:02 +0100 Subject: [PATCH 158/271] Upgrade to Infinispan 14.0.31.Final Closes gh-42245 --- 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 272295df9e5c..b9e26f478b36 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -470,7 +470,7 @@ bom { ] } } - library("Infinispan", "14.0.30.Final") { + library("Infinispan", "14.0.31.Final") { group("org.infinispan") { imports = [ "infinispan-bom" From 216c69259e923a17cc74ec103741aad2a27b9633 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:07 +0100 Subject: [PATCH 159/271] Upgrade to Jakarta Servlet JSP JSTL 3.0.2 Closes gh-42246 --- 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 b9e26f478b36..d138c64d376c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -554,7 +554,7 @@ bom { ] } } - library("Jakarta Servlet JSP JSTL", "3.0.1") { + library("Jakarta Servlet JSP JSTL", "3.0.2") { group("jakarta.servlet.jsp.jstl") { modules = [ "jakarta.servlet.jsp.jstl-api" From a6c93825409d4440776be359aee29f83f0866293 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:11 +0100 Subject: [PATCH 160/271] Upgrade to Jetty Reactive HTTPClient 4.0.7 Closes gh-42247 --- 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 d138c64d376c..7cbb2946107b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -680,7 +680,7 @@ bom { ] } } - library("Jetty Reactive HTTPClient", "4.0.6") { + library("Jetty Reactive HTTPClient", "4.0.7") { group("org.eclipse.jetty") { modules = [ "jetty-reactive-httpclient" From d8748ec35d8d00bdead4d116d772eca268e1ecf0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:16 +0100 Subject: [PATCH 161/271] Upgrade to Jetty 12.0.13 Closes gh-42248 --- 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 7cbb2946107b..b1b2f0f96447 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -687,7 +687,7 @@ bom { ] } } - library("Jetty", "12.0.12") { + library("Jetty", "12.0.13") { group("org.eclipse.jetty.ee10") { imports = [ "jetty-ee10-bom" From 6bced8fe56cf6d882ca0481634722050746c99fc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:16 +0100 Subject: [PATCH 162/271] Upgrade to Micrometer 1.12.10 Closes gh-42121 --- 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 b1b2f0f96447..94d17f35c577 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1004,7 +1004,7 @@ bom { ] } } - library("Micrometer", "1.12.10-SNAPSHOT") { + library("Micrometer", "1.12.10") { considerSnapshots() group("io.micrometer") { modules = [ From 4676ad4f4b0b63893c0de62d48b6b39ccfa90b5a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:17 +0100 Subject: [PATCH 163/271] Upgrade to Micrometer Tracing 1.2.10 Closes gh-42122 --- 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 94d17f35c577..e69af6752e0a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1017,7 +1017,7 @@ bom { ] } } - library("Micrometer Tracing", "1.2.10-SNAPSHOT") { + library("Micrometer Tracing", "1.2.10") { considerSnapshots() group("io.micrometer") { imports = [ From 7d22d7f9cf0a158dac8c86506a023c7dd9ce12bc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:22 +0100 Subject: [PATCH 164/271] Upgrade to MongoDB 4.11.4 Closes gh-42249 --- 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 e69af6752e0a..901259e97256 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1032,7 +1032,7 @@ bom { ] } } - library("MongoDB", "4.11.3") { + library("MongoDB", "4.11.4") { group("org.mongodb") { modules = [ "bson", From e1724277af6184ce33614a1f80d9f710e112052c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:26 +0100 Subject: [PATCH 165/271] Upgrade to Netty 4.1.113.Final Closes gh-42250 --- 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 901259e97256..64664e06ce86 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1091,7 +1091,7 @@ bom { ] } } - library("Netty", "4.1.112.Final") { + library("Netty", "4.1.113.Final") { group("io.netty") { imports = [ "netty-bom" From c08c5ae5ed468bd783e7c3b56be93c52a310482f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:26 +0100 Subject: [PATCH 166/271] Upgrade to Reactor Bom 2023.0.10 Closes gh-42123 --- 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 64664e06ce86..a68cf3ef2a52 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1391,7 +1391,7 @@ bom { ] } } - library("Reactor Bom", "2023.0.10-SNAPSHOT") { + library("Reactor Bom", "2023.0.10") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From e65cc54ec47c62e365771596222b8574c9032d2c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:05:31 +0100 Subject: [PATCH 167/271] Upgrade to Tomcat 10.1.29 Closes gh-42251 --- gradle.properties | 2 +- .../springframework/boot/web/embedded/tomcat/TldPatterns.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2407ea66eca2..5f97dcf7a773 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,6 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.9.28 springFrameworkVersion=6.1.13-SNAPSHOT springFramework60xVersion=6.0.23 -tomcatVersion=10.1.28 +tomcatVersion=10.1.29 kotlin.stdlib.default.dependency=false diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java index eeb73d715be1..69ab0f7854ba 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java @@ -106,6 +106,7 @@ final class TldPatterns { skipPatterns.add("tagsoup-*.jar"); skipPatterns.add("tomcat-api.jar"); skipPatterns.add("tomcat-coyote.jar"); + skipPatterns.add("tomcat-coyote-ffm.jar"); skipPatterns.add("tomcat-dbcp.jar"); skipPatterns.add("tomcat-i18n-*.jar"); skipPatterns.add("tomcat-jdbc.jar"); From b63f78c4fd4e8ac27a6d7f05ea4a689347d53d66 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:26:51 +0100 Subject: [PATCH 168/271] Upgrade to Zipkin Reporter 3.4.1 Closes gh-42252 --- 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 33ff87a4e546..8534ed5a7bbb 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -103,7 +103,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Zipkin Reporter", "3.4.0") { + library("Zipkin Reporter", "3.4.1") { group("io.zipkin.reporter2") { imports = [ "zipkin-reporter-bom" From ad78f8f82337cc2752821d04eb8a293d75d1a8ed Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:26:56 +0100 Subject: [PATCH 169/271] Upgrade to Infinispan 15.0.8.Final Closes gh-42253 --- 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 8534ed5a7bbb..55d22f497849 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -601,7 +601,7 @@ bom { ] } } - library("Infinispan", "15.0.7.Final") { + library("Infinispan", "15.0.8.Final") { group("org.infinispan") { imports = [ "infinispan-bom" From ef63f1799f442ea823da5375aaea845feb843a65 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:00 +0100 Subject: [PATCH 170/271] Upgrade to Jakarta Servlet JSP JSTL 3.0.2 Closes gh-42254 --- 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 55d22f497849..a29fd63ed751 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -747,7 +747,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Jakarta Servlet JSP JSTL", "3.0.1") { + library("Jakarta Servlet JSP JSTL", "3.0.2") { group("jakarta.servlet.jsp.jstl") { modules = [ "jakarta.servlet.jsp.jstl-api" From a7f74aae36558d84040c96d0c7dcfc98dfbac36a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:05 +0100 Subject: [PATCH 171/271] Upgrade to Jetty Reactive HTTPClient 4.0.7 Closes gh-42255 --- 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 a29fd63ed751..2583914cd4d5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -889,7 +889,7 @@ bom { releaseNotes("https://github.com/eclipse-ee4j/jersey/releases/tag/{version}") } } - library("Jetty Reactive HTTPClient", "4.0.6") { + library("Jetty Reactive HTTPClient", "4.0.7") { group("org.eclipse.jetty") { modules = [ "jetty-reactive-httpclient" From 9794dc3480e2dfc7188d6b6162b2e41a3cd7630f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:10 +0100 Subject: [PATCH 172/271] Upgrade to Jetty 12.0.13 Closes gh-42256 --- 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 2583914cd4d5..adc9eda6001c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -896,7 +896,7 @@ bom { ] } } - library("Jetty", "12.0.12") { + library("Jetty", "12.0.13") { group("org.eclipse.jetty.ee10") { imports = [ "jetty-ee10-bom" From 6c2fa2fab06ac7c6f68a9d37f004a25d7cc0a88f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:15 +0100 Subject: [PATCH 173/271] Upgrade to Logback 1.5.8 Closes gh-42257 --- spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- .../boot/logging/logback/LogbackLoggingSystemTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index adc9eda6001c..2877e70deaa1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1121,7 +1121,7 @@ bom { releaseNotes("https://github.com/apache/logging-log4j2/releases/tag/rel%2F{version}") } } - library("Logback", "1.5.7") { + library("Logback", "1.5.8") { group("ch.qos.logback") { modules = [ "logback-classic", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 0c978d8789ad..65adc53a10fb 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -646,7 +646,7 @@ void logbackDebugPropertyIsHonored(CapturedOutput output) { LogFile logFile = getLogFile(file.getPath(), null); initialize(this.initializationContext, null, logFile); assertThat(output).contains("LevelChangePropagator") - .contains("SizeAndTimeBasedFNATP") + .contains("SizeAndTimeBasedFileNamingAndTriggeringPolicy") .contains("DebugLogbackConfigurator"); } finally { From b570c030441990d236dfd37105e41fec04923330 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:16 +0100 Subject: [PATCH 174/271] Upgrade to Micrometer 1.13.4 Closes gh-42129 --- 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 2877e70deaa1..f7163da9943f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1280,7 +1280,7 @@ bom { ] } } - library("Micrometer", "1.13.4-SNAPSHOT") { + library("Micrometer", "1.13.4") { considerSnapshots() group("io.micrometer") { modules = [ From a08d173b9958c4bacc626e23d55f6a2e51479733 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:16 +0100 Subject: [PATCH 175/271] Upgrade to Micrometer Tracing 1.3.4 Closes gh-42130 --- 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 f7163da9943f..9a65b72088be 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1300,7 +1300,7 @@ bom { releaseNotes("https://github.com/micrometer-metrics/micrometer/releases/tag/v{version}") } } - library("Micrometer Tracing", "1.3.4-SNAPSHOT") { + library("Micrometer Tracing", "1.3.4") { considerSnapshots() group("io.micrometer") { imports = [ From 2975661b41f0ca8520db3f56c3769b0e8b8d31a7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:21 +0100 Subject: [PATCH 176/271] Upgrade to MSSQL JDBC 12.6.4.jre11 Closes gh-42258 --- 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 9a65b72088be..6eb82c29115e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1342,7 +1342,7 @@ bom { releaseNotes("https://github.com/mongodb/mongo-java-driver/releases/tag/r{version}") } } - library("MSSQL JDBC", "12.6.3.jre11") { + library("MSSQL JDBC", "12.6.4.jre11") { prohibit { endsWith(".jre8") because "we want to use the jre11 version" From a253a24228c3886eb44335843e60f9b8be50a99f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:25 +0100 Subject: [PATCH 177/271] Upgrade to Netty 4.1.113.Final Closes gh-42259 --- 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 6eb82c29115e..4f83eacd096a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1410,7 +1410,7 @@ bom { releaseNotes("https://github.com/neo4j/neo4j-java-driver/releases/tag/{version}") } } - library("Netty", "4.1.112.Final") { + library("Netty", "4.1.113.Final") { group("io.netty") { imports = [ "netty-bom" From 11bd835b5a44a5b5ec6798ab8aced70328d8701f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:30 +0100 Subject: [PATCH 178/271] Upgrade to Postgresql 42.7.4 Closes gh-42260 --- 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 4f83eacd096a..9e5cbfa7653b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1518,7 +1518,7 @@ bom { ] } } - library("Postgresql", "42.7.3") { + library("Postgresql", "42.7.4") { group("org.postgresql") { modules = [ "postgresql" From 1503b8db8fcf8bd5d5dc87dbdc779a3b1ff90793 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:30 +0100 Subject: [PATCH 179/271] Upgrade to Reactor Bom 2023.0.10 Closes gh-42131 --- 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 9e5cbfa7653b..e50d1b812c7c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1704,7 +1704,7 @@ bom { ] } } - library("Reactor Bom", "2023.0.10-SNAPSHOT") { + library("Reactor Bom", "2023.0.10") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { From b2edefe982c75af7dd958a88bce15076c4a1dc29 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 13:27:35 +0100 Subject: [PATCH 180/271] Upgrade to Tomcat 10.1.29 Closes gh-42261 --- gradle.properties | 2 +- .../springframework/boot/web/embedded/tomcat/TldPatterns.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a7cb98e6ccbb..2341ef0c79a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.1.13-SNAPSHOT springFramework60xVersion=6.0.23 -tomcatVersion=10.1.28 +tomcatVersion=10.1.29 snakeYamlVersion=2.2 kotlin.stdlib.default.dependency=false diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java index eeb73d715be1..69ab0f7854ba 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TldPatterns.java @@ -106,6 +106,7 @@ final class TldPatterns { skipPatterns.add("tagsoup-*.jar"); skipPatterns.add("tomcat-api.jar"); skipPatterns.add("tomcat-coyote.jar"); + skipPatterns.add("tomcat-coyote-ffm.jar"); skipPatterns.add("tomcat-dbcp.jar"); skipPatterns.add("tomcat-i18n-*.jar"); skipPatterns.add("tomcat-jdbc.jar"); From 504fd62472b74936b2c75fdff1f459fe555ca003 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 14:02:55 +0100 Subject: [PATCH 181/271] Update the link to Log4j2's system properties documentation Closes gh-42262 --- .../spring-boot-docs/src/docs/asciidoc/features/logging.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc index 1d90d2d771cd..d4cc383ced2c 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc @@ -554,7 +554,7 @@ NOTE: The lookup key should be specified in kebab case (such as `my.property-nam [[features.logging.log4j2-extensions.environment-property-source]] ==== Log4j2 System Properties -Log4j2 supports a number of https://logging.apache.org/log4j/2.x/manual/configuration.html#SystemProperties[System Properties] that can be used to configure various items. +Log4j2 supports a number of https://logging.apache.org/log4j/2.x/manual/systemproperties.html[System Properties] that can be used to configure various items. For example, the `log4j2.skipJansi` system property can be used to configure if the `ConsoleAppender` will try to use a https://github.com/fusesource/jansi[Jansi] output stream on Windows. All system properties that are loaded after the Log4j2 initialization can be obtained from the Spring `Environment`. From 545e370d9a4fec42181228d7a5ec2caa974dee88 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 11 Sep 2024 15:12:40 +0100 Subject: [PATCH 182/271] Declare use of testResultsOverview build service Closes gh-42265 --- .../boot/build/testing/TestFailuresPlugin.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/testing/TestFailuresPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/testing/TestFailuresPlugin.java index f9ebc52f784e..77f7dcf70071 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/testing/TestFailuresPlugin.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/testing/TestFailuresPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -40,9 +40,10 @@ public void apply(Project project) { .getSharedServices() .registerIfAbsent("testResultsOverview", TestResultsOverview.class, (spec) -> { }); - project.getTasks() - .withType(Test.class, - (test) -> test.addTestListener(new FailureRecordingTestListener(testResultsOverview, test))); + project.getTasks().withType(Test.class, (test) -> { + test.usesService(testResultsOverview); + test.addTestListener(new FailureRecordingTestListener(testResultsOverview, test)); + }); } private final class FailureRecordingTestListener implements TestListener { From 5bddca850a38a2dedcee8298229d5360e0785b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 11 Sep 2024 10:40:24 +0200 Subject: [PATCH 183/271] Link to major.minor versions of Spring projects This commit updates the doc build process to link to the latest doc for a given generation, rather than a specific version. This applies to both the reference guide and the aggregated Javadoc. Closes gh-42196 --- .../spring-boot-docs/build.gradle | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index d69f86ecc754..1715916ef0a3 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -232,12 +232,18 @@ task aggregatedJavadoc(type: Javadoc) { } doFirst { def versionConstraints = dependencyVersions.versionConstraints + def toMajorMinorVersion = version -> { + String formatted = version.split("\\.").take(2).join('.') + '.x' + return version.endsWith("-SNAPSHOT") ? formatted + "-SNAPSHOT" : formatted + } + def springFrameworkVersion = toMajorMinorVersion(versionConstraints["org.springframework:spring-core"]) + def springSecurityVersion = toMajorMinorVersion(versionConstraints["org.springframework.security:spring-security-core"]) def tomcatVersion = "${versionConstraints["org.apache.tomcat:tomcat-annotations-api"]}" def tomcatDocsVersion = tomcatVersion.substring(0, tomcatVersion.lastIndexOf(".")); options.links = [ "https://docs.oracle.com/en/java/javase/17/docs/api/", - "https://docs.spring.io/spring-framework/docs/${versionConstraints["org.springframework:spring-core"]}/javadoc-api/", - "https://docs.spring.io/spring-security/site/docs/${versionConstraints["org.springframework.security:spring-security-core"]}/api/", + "https://docs.spring.io/spring-framework/docs/${springFrameworkVersion}/javadoc-api/", + "https://docs.spring.io/spring-security/site/docs/${springSecurityVersion}/api/", "https://jakarta.ee/specifications/platform/9/apidocs/", "https://tomcat.apache.org/tomcat-${tomcatDocsVersion}-doc/api/", ] as String[] @@ -317,8 +323,14 @@ tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { } doFirst { def versionConstraints = dependencyVersions.versionConstraints + def extractMajorMinor = version -> version.split("\\.").take(2).join('.') + def toMajorMinorVersion = version -> { + String formatted = extractMajorMinor(version) + '.x' + return version.endsWith("-SNAPSHOT") ? formatted + "-SNAPSHOT" : formatted + } + def toSpringDataVersion = version -> extractMajorMinor(version) + '.x' def toAntoraVersion = version -> { - String formatted = version.split("\\.").take(2).join('.') + String formatted = extractMajorMinor(version) return version.endsWith("-SNAPSHOT") ? formatted + "-SNAPSHOT" : formatted } attributes "hibernate-version": versionConstraints["org.hibernate.orm:hibernate-core"].split("\\.").take(2).join('.'), @@ -326,33 +338,33 @@ tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { "jooq-version": versionConstraints["org.jooq:jooq"], "lettuce-version": versionConstraints["io.lettuce:lettuce-core"], "native-build-tools-version": nativeBuildToolsVersion, - "spring-amqp-version": versionConstraints["org.springframework.amqp:spring-amqp"], - "spring-batch-version": versionConstraints["org.springframework.batch:spring-batch-core"], + "spring-amqp-version": toMajorMinorVersion(versionConstraints["org.springframework.amqp:spring-amqp"]), + "spring-batch-version": toMajorMinorVersion(versionConstraints["org.springframework.batch:spring-batch-core"]), "spring-batch-version-antora": toAntoraVersion(versionConstraints["org.springframework.batch:spring-batch-core"]), "spring-boot-version": project.version, "spring-data-cassandra-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-cassandra"]), - "spring-data-commons-version": versionConstraints["org.springframework.data:spring-data-commons"], + "spring-data-commons-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-commons"]), "spring-data-couchbase-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-couchbase"]), "spring-data-elasticsearch-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-elasticsearch"]), - "spring-data-jdbc-version": versionConstraints["org.springframework.data:spring-data-jdbc"], + "spring-data-jdbc-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-jdbc"]), "spring-data-jdbc-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-jdbc"]), - "spring-data-jpa-version": versionConstraints["org.springframework.data:spring-data-jpa"], + "spring-data-jpa-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-jpa"]), "spring-data-jpa-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-jpa"]), "spring-data-ldap-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-ldap"]), - "spring-data-mongodb-version": versionConstraints["org.springframework.data:spring-data-mongodb"], + "spring-data-mongodb-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-mongodb"]), "spring-data-neo4j-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-neo4j"]), - "spring-data-r2dbc-version": versionConstraints["org.springframework.data:spring-data-r2dbc"], + "spring-data-r2dbc-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-r2dbc"]), "spring-data-r2dbc-version-antora": toAntoraVersion(versionConstraints["org.springframework.data:spring-data-r2dbc"]), - "spring-data-rest-version": versionConstraints["org.springframework.data:spring-data-rest-core"], - "spring-framework-version": versionConstraints["org.springframework:spring-core"], + "spring-data-rest-version": toSpringDataVersion(versionConstraints["org.springframework.data:spring-data-rest-core"]), + "spring-framework-version": toMajorMinorVersion(versionConstraints["org.springframework:spring-core"]), "spring-framework-version-antora": toAntoraVersion(versionConstraints["org.springframework:spring-core"]), "spring-graphql-version-antora": toAntoraVersion(versionConstraints["org.springframework.graphql:spring-graphql"]), "spring-integration-version-antora": toAntoraVersion(versionConstraints["org.springframework.integration:spring-integration-core"]), - "spring-kafka-version": versionConstraints["org.springframework.kafka:spring-kafka"], - "spring-pulsar-version": versionConstraints["org.springframework.pulsar:spring-pulsar"], + "spring-kafka-version": toMajorMinorVersion(versionConstraints["org.springframework.kafka:spring-kafka"]), + "spring-pulsar-version": toMajorMinorVersion(versionConstraints["org.springframework.pulsar:spring-pulsar"]), "spring-security-version-antora": toAntoraVersion(versionConstraints["org.springframework.security:spring-security-core"]), "spring-authorization-server-version-antora": toAntoraVersion(versionConstraints["org.springframework.security:spring-security-oauth2-authorization-server"]), - "spring-webservices-version": versionConstraints["org.springframework.ws:spring-ws-core"], + "spring-webservices-version": toMajorMinorVersion(versionConstraints["org.springframework.ws:spring-ws-core"]), "tomcat-version": tomcatVersion.split("\\.").take(2).join('.'), "remote-spring-application-output": runRemoteSpringApplicationExample.outputs.files.singleFile, "spring-application-output": runSpringApplicationExample.outputs.files.singleFile, From 3f8079a01d8000679322732c11c271f1112b5bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 11 Sep 2024 19:04:46 +0200 Subject: [PATCH 184/271] Upgrade to Neo4j Java Driver 5.24.0 Closes gh-42271 --- 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 4bd7c735b871..1ce6346ab768 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1410,7 +1410,7 @@ bom { ] } } - library("Neo4j Java Driver", "5.23.0") { + library("Neo4j Java Driver", "5.24.0") { alignWith { version { from "org.springframework.data:spring-data-neo4j" From 94fc6b4fe1b37fb424d072c3d9bccacf5372f95c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 11 Sep 2024 12:53:25 -0700 Subject: [PATCH 185/271] Polish formatting --- .../docs/antora/modules/how-to/pages/class-data-sharing.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc index 591b359b0b08..69672185c2fd 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/class-data-sharing.adoc @@ -5,6 +5,7 @@ This section includes information about using Class Data Sharing (CDS) with Spri For an overview of Spring Boot support for CDS, see xref:reference:packaging/class-data-sharing.adoc[]. + [[howto.class-data-sharing.buildpacks]] == Packaging an Application Using CDS and Buildpacks @@ -15,12 +16,16 @@ This will cause the buildpack to do a training run of the application, save the The Paketo Buildpack for Spring Boot https://github.com/paketo-buildpacks/spring-boot?tab=readme-ov-file#configuration[documentation] has information on other configuration options that can be enabled with builder environment variables, like `CDS_TRAINING_JAVA_TOOL_OPTIONS` that allows to override the default `JAVA_TOOL_OPTIONS`, only for the CDS training run. + + [[howto.class-data-sharing.dockerfiles]] == Packaging an Application Using CDS and Dockerfiles If you don't want to use Cloud Native Buildpacks, it is also possible to use CDS with a `Dockerfile`. For more information about that, please see the xref:reference:packaging/container-images/dockerfiles.adoc#packaging.container-images.dockerfiles.cds[Dockerfiles reference documentation]. + + [[howto.class-data-sharing.training-run-configuration]] == Preventing Remote Services Interaction During the Training Run From 06e3f37afd0198b3e23997073853aae4ca05973e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 11 Sep 2024 13:56:25 -0700 Subject: [PATCH 186/271] Polish --- .../opentelemetry/otlp/package-info.java | 2 +- .../reference/pages/features/logging.adoc | 2 + .../ConfigurationPropertiesBeanRegistrar.java | 9 ++- ...tendedLogFormatStructuredLogFormatter.java | 69 +++++++++++-------- ...tendedLogFormatStructuredLogFormatter.java | 64 ++++++++++------- .../logging/logback/StructuredLogEncoder.java | 34 ++++++--- .../GraylogExtendedLogFormatService.java | 2 +- ...dLogFormatStructuredLogFormatterTests.java | 1 - 8 files changed, 109 insertions(+), 74 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java index 519a94919dd6..ecdcd3f16b7f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. 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 fca2cdee10fa..0dba5146f276 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 @@ -454,6 +454,7 @@ To enable structured logging, set the property configprop:logging.structured.for [[features.logging.structured.ecs]] === Elastic Common Schema + https://www.elastic.co/guide/en/ecs/8.11/ecs-reference.html[Elastic Common Schema] is a JSON based logging format. To enable the Elastic Common Schema log format, set the appropriate `format` property to `ecs`: @@ -499,6 +500,7 @@ NOTE: configprop:logging.structured.ecs.service.version[] will default to config [[features.logging.structured.gelf]] === Graylog Extended Log Format (GELF) + https://go2docs.graylog.org/current/getting_in_log_data/gelf.html[Graylog Extended Log Format] is a JSON based logging format for the Graylog log analytics platform. To enable the Graylog Extended Log Format, set the appropriate `format` property to `gelf`: diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java index 82c3a8bcbe7c..ed5add358a95 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java @@ -102,12 +102,11 @@ private BeanDefinitionHolder createBeanDefinition(String beanName, Class type static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { - ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); - if (scopedProxyMode.equals(ScopedProxyMode.NO)) { - return definition; + ScopedProxyMode mode = metadata.getScopedProxyMode(); + if (mode != ScopedProxyMode.NO) { + return ScopedProxyUtils.createScopedProxy(definition, registry, mode == ScopedProxyMode.TARGET_CLASS); } - boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); - return ScopedProxyUtils.createScopedProxy(definition, registry, proxyTargetClass); + return definition; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java index 8a31b50b9972..5138b50353f3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -32,6 +32,7 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.springframework.boot.json.JsonWriter; +import org.springframework.boot.json.JsonWriter.Members; import org.springframework.boot.json.JsonWriter.WritableJson; import org.springframework.boot.logging.structured.CommonStructuredLogFormat; import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; @@ -41,6 +42,7 @@ import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Log4j2 {@link StructuredLogFormatter} for @@ -60,12 +62,6 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure */ private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$"); - /** - * Every field been sent and prefixed with an underscore "_" will be treated as an - * additional field. - */ - private static final String ADDITIONAL_FIELD_PREFIX = "_"; - /** * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server * nodes omit this field automatically. @@ -78,9 +74,8 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure private static void jsonMembers(Environment environment, JsonWriter.Members members) { members.add("version", "1.1"); - // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are - // ignoring this here. - members.add("short_message", LogEvent::getMessage).as(Message::getFormattedMessage); + members.add("short_message", LogEvent::getMessage) + .as(GraylogExtendedLogFormatStructuredLogFormatter::getMessageText); members.add("timestamp", LogEvent::getInstant) .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); members.add("level", GraylogExtendedLogFormatStructuredLogFormatter::convertLevel); @@ -92,25 +87,23 @@ private static void jsonMembers(Environment environment, JsonWriter.Members contextData - .forEach((key, value) -> createAdditionalField(key, value, pairs))); - members.add().whenNotNull(LogEvent::getThrownProxy).usingMembers((eventMembers) -> { - eventMembers.add("full_message", - GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable); - eventMembers.add("_error_type", (event) -> event.getThrownProxy().getThrowable()) - .whenNotNull() - .as(ObjectUtils::nullSafeClassName); - eventMembers.add("_error_stack_trace", (event) -> event.getThrownProxy().getExtendedStackTraceAsString()); - eventMembers.add("_error_message", (event) -> event.getThrownProxy().getMessage()); - }); + .usingPairs(GraylogExtendedLogFormatStructuredLogFormatter::createAdditionalFields); + members.add() + .whenNotNull(LogEvent::getThrownProxy) + .usingMembers(GraylogExtendedLogFormatStructuredLogFormatter::throwableMembers); + } + + private static String getMessageText(Message message) { + // Always return text as a blank message will lead to a error as of Graylog v6 + String formattedMessage = message.getFormattedMessage(); + return (!StringUtils.hasText(formattedMessage)) ? "(blank)" : formattedMessage; } /** * GELF requires "seconds since UNIX epoch with optional decimal places for * milliseconds". To comply with this requirement, we format a POSIX timestamp * with millisecond precision as e.g. "1725459730385" -> "1725459730.385" - * @param timeStamp the timestamp of the log message. Note it is not the standard Java - * `Instant` type but {@link org.apache.logging.log4j.core.time} + * @param timeStamp the timestamp of the log message. * @return the timestamp formatted as string with millisecond precision */ private static WritableJson formatTimeStamp(Instant timeStamp) { @@ -127,23 +120,39 @@ private static int convertLevel(LogEvent event) { return Severity.getSeverity(event.getLevel()).getCode(); } + private static void throwableMembers(Members members) { + members.add("full_message", GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable); + members.add("_error_type", (event) -> event.getThrownProxy().getThrowable()) + .whenNotNull() + .as(ObjectUtils::nullSafeClassName); + members.add("_error_stack_trace", (event) -> event.getThrownProxy().getExtendedStackTraceAsString()); + members.add("_error_message", (event) -> event.getThrownProxy().getMessage()); + } + private static String formatFullMessageWithThrowable(LogEvent event) { return event.getMessage().getFormattedMessage() + "\n\n" + event.getThrownProxy().getExtendedStackTraceAsString(); } - private static void createAdditionalField(String fieldName, Object value, BiConsumer pairs) { - Assert.notNull(fieldName, "fieldName must not be null"); - if (!FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches()) { - logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", fieldName)); + private static void createAdditionalFields(ReadOnlyStringMap contextData, BiConsumer pairs) { + contextData.forEach((name, value) -> createAdditionalField(name, value, pairs)); + } + + private static void createAdditionalField(String name, Object value, BiConsumer pairs) { + Assert.notNull(name, "fieldName must not be null"); + if (!FIELD_NAME_VALID_PATTERN.matcher(name).matches()) { + logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", name)); return; } - if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName)) { - logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", fieldName)); + if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(name)) { + logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", name)); return; } - String key = (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) ? fieldName : ADDITIONAL_FIELD_PREFIX + fieldName; - pairs.accept(key, value); + pairs.accept(asAdditionalFieldName(name), value); + } + + private static Object asAdditionalFieldName(String name) { + return (!name.startsWith("_")) ? "_" + name : name; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java index 4d9df6a8149c..b2ddd304d194 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatter.java @@ -17,6 +17,7 @@ package org.springframework.boot.logging.logback; import java.math.BigDecimal; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; @@ -28,8 +29,10 @@ import ch.qos.logback.classic.util.LevelToSyslogSeverity; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.slf4j.event.KeyValuePair; import org.springframework.boot.json.JsonWriter; +import org.springframework.boot.json.JsonWriter.Members; import org.springframework.boot.json.JsonWriter.WritableJson; import org.springframework.boot.logging.structured.CommonStructuredLogFormat; import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService; @@ -39,6 +42,7 @@ import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; /** * Logback {@link StructuredLogFormatter} for @@ -58,12 +62,6 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure */ private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$"); - /** - * Every field been sent and prefixed with an underscore "_" will be treated as an - * additional field. - */ - private static final String ADDITIONAL_FIELD_PREFIX = "_"; - /** * Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server * nodes omit this field automatically. @@ -78,9 +76,8 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure private static void jsonMembers(Environment environment, ThrowableProxyConverter throwableProxyConverter, JsonWriter.Members members) { members.add("version", "1.1"); - // note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are - // ignoring this here. - members.add("short_message", ILoggingEvent::getFormattedMessage); + members.add("short_message", ILoggingEvent::getFormattedMessage) + .as(GraylogExtendedLogFormatStructuredLogFormatter::getMessageText); members.add("timestamp", ILoggingEvent::getTimeStamp) .as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp); members.add("level", LevelToSyslogSeverity::convert); @@ -95,15 +92,15 @@ private static void jsonMembers(Environment environment, ThrowableProxyConverter .usingPairs((mdc, pairs) -> mdc.forEach((key, value) -> createAdditionalField(key, value, pairs))); members.from(ILoggingEvent::getKeyValuePairs) .when((keyValuePairs) -> !CollectionUtils.isEmpty(keyValuePairs)) - .usingPairs((keyValuePairs, pairs) -> keyValuePairs - .forEach((keyValuePair) -> createAdditionalField(keyValuePair.key, keyValuePair.value, pairs))); - members.add().whenNotNull(ILoggingEvent::getThrowableProxy).usingMembers((throwableMembers) -> { - throwableMembers.add("full_message", - (event) -> formatFullMessageWithThrowable(throwableProxyConverter, event)); - throwableMembers.add("_error_type", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getClassName); - throwableMembers.add("_error_stack_trace", throwableProxyConverter::convert); - throwableMembers.add("_error_message", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getMessage); - }); + .usingPairs(GraylogExtendedLogFormatStructuredLogFormatter::createAdditionalField); + members.add() + .whenNotNull(ILoggingEvent::getThrowableProxy) + .usingMembers((throwableMembers) -> throwableMembers(throwableMembers, throwableProxyConverter)); + } + + private static String getMessageText(String formattedMessage) { + // Always return text as a blank message will lead to a error as of Graylog v6 + return (!StringUtils.hasText(formattedMessage)) ? "(blank)" : formattedMessage; } /** @@ -117,23 +114,38 @@ private static WritableJson formatTimeStamp(long timeStamp) { return (out) -> out.append(new BigDecimal(timeStamp).movePointLeft(3).toPlainString()); } + private static void throwableMembers(Members members, + ThrowableProxyConverter throwableProxyConverter) { + members.add("full_message", (event) -> formatFullMessageWithThrowable(throwableProxyConverter, event)); + members.add("_error_type", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getClassName); + members.add("_error_stack_trace", throwableProxyConverter::convert); + members.add("_error_message", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getMessage); + } + private static String formatFullMessageWithThrowable(ThrowableProxyConverter throwableProxyConverter, ILoggingEvent event) { return event.getFormattedMessage() + "\n\n" + throwableProxyConverter.convert(event); } - private static void createAdditionalField(String key, Object value, BiConsumer pairs) { - Assert.notNull(key, "fieldName must not be null"); - if (!FIELD_NAME_VALID_PATTERN.matcher(key).matches()) { - logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", key)); + private static void createAdditionalField(List keyValuePairs, BiConsumer pairs) { + keyValuePairs.forEach((keyValuePair) -> createAdditionalField(keyValuePair.key, keyValuePair.value, pairs)); + } + + private static void createAdditionalField(String name, Object value, BiConsumer pairs) { + Assert.notNull(name, "fieldName must not be null"); + if (!FIELD_NAME_VALID_PATTERN.matcher(name).matches()) { + logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", name)); return; } - if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(key)) { - logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", key)); + if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(name)) { + logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", name)); return; } - String keyWithPrefix = (key.startsWith(ADDITIONAL_FIELD_PREFIX)) ? key : ADDITIONAL_FIELD_PREFIX + key; - pairs.accept(keyWithPrefix, value); + pairs.accept(asAdditionalFieldName(name), value); + } + + private static Object asAdditionalFieldName(String name) { + return (!name.startsWith("_")) ? "_" + name : name; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java index 5b3c9d964e84..746251e1e02e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/StructuredLogEncoder.java @@ -28,6 +28,7 @@ import org.springframework.boot.logging.structured.StructuredLogFormatter; import org.springframework.boot.logging.structured.StructuredLogFormatterFactory; import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters; +import org.springframework.boot.util.Instantiator; import org.springframework.boot.util.Instantiator.AvailableParameters; import org.springframework.core.env.Environment; import org.springframework.util.Assert; @@ -79,16 +80,29 @@ private void addAvailableParameters(AvailableParameters availableParameters) { } private void addCommonFormatters(CommonFormatters commonFormatters) { - commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA, - (instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(instantiator.getArg(Environment.class), - instantiator.getArg(ThrowableProxyConverter.class))); - commonFormatters - .add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT, - (instantiator) -> new GraylogExtendedLogFormatStructuredLogFormatter( - instantiator.getArg(Environment.class), - instantiator.getArg(ThrowableProxyConverter.class))); - commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, (instantiator) -> new LogstashStructuredLogFormatter( - instantiator.getArg(ThrowableProxyConverter.class))); + commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA, this::createEcsFormatter); + commonFormatters.add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT, this::createGraylogFormatter); + commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, this::createLogstashFormatter); + } + + private StructuredLogFormatter createEcsFormatter( + Instantiator> instantiator) { + Environment environment = instantiator.getArg(Environment.class); + ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class); + return new ElasticCommonSchemaStructuredLogFormatter(environment, throwableProxyConverter); + } + + private StructuredLogFormatter createGraylogFormatter( + Instantiator> instantiator) { + Environment environment = instantiator.getArg(Environment.class); + ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class); + return new GraylogExtendedLogFormatStructuredLogFormatter(environment, throwableProxyConverter); + } + + private StructuredLogFormatter createLogstashFormatter( + Instantiator> instantiator) { + ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class); + return new LogstashStructuredLogFormatter(throwableProxyConverter); } @Override diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java index bbe854a1a984..8e10f4831e56 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java @@ -48,7 +48,6 @@ private String withFallbackProperty(Environment environment, String value, Strin * @param members the members to add to */ public void jsonMembers(JsonWriter.Members members) { - // note "host" is a field name prescribed by GELF members.add("host", this::name).whenHasLength(); members.add("_service_version", this::version).whenHasLength(); } @@ -65,4 +64,5 @@ public static GraylogExtendedLogFormatService get(Environment environment) { .orElse(NONE) .withDefaults(environment); } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java index 39ab27c638f1..2b2c039d73a2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/GraylogExtendedLogFormatStructuredLogFormatterTests.java @@ -134,7 +134,6 @@ void shouldFormatException() { .formatted()); assertThat(deserialized) .containsAllEntriesOf(map("_error_type", "java.lang.RuntimeException", "_error_message", "Boom")); - assertThat(stackTrace).startsWith( "java.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException" .formatted()); From 2e28d2642da1f81b7fcef597ff304a6294ea2895 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 11 Sep 2024 21:00:58 -0700 Subject: [PATCH 187/271] Polish --- .../context/properties/BoundConfigurationProperties.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/BoundConfigurationProperties.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/BoundConfigurationProperties.java index a41f8e43d8b5..9e88322cfe60 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/BoundConfigurationProperties.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/BoundConfigurationProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -72,10 +72,8 @@ public Map getAll() { * @return a {@link BoundConfigurationProperties} or {@code null} */ public static BoundConfigurationProperties get(ApplicationContext context) { - if (!context.containsBeanDefinition(BEAN_NAME)) { - return null; - } - return context.getBean(BEAN_NAME, BoundConfigurationProperties.class); + return (!context.containsBeanDefinition(BEAN_NAME)) ? null + : context.getBean(BEAN_NAME, BoundConfigurationProperties.class); } static void register(BeanDefinitionRegistry registry) { From 8628f7334fdf67ac54d79a57a6c4bdb44e6f68d4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 27 Jun 2024 17:25:36 -0700 Subject: [PATCH 188/271] Ensure `@AutoConfigureTestDatabase` does not replace test databases Update `@AutoConfigureTestDatabase` support so that by default test databases are not replaced. Fixes gh-35253 --- .../build/architecture/ArchitectureCheck.java | 14 ++- .../container/ContainerImageMetadata.java | 66 +++++++++++ .../autoconfigure/container/package-info.java | 20 ++++ .../ContainerImageMetadataTests.java | 82 +++++++++++++ ...ServiceConnectionsApplicationListener.java | 8 +- .../build.gradle | 5 + ...DatabaseDockerComposeIntegrationTests.java | 95 +++++++++++++++ ...DynamicPropertySourceIntegrationTests.java | 74 ++++++++++++ ...tabaseNonTestDatabaseIntegrationTests.java | 82 +++++++++++++ ...baseServiceConnectionIntegrationTests.java | 70 +++++++++++ .../resources/postgres-compose.yaml | 9 ++ .../jdbc/AutoConfigureTestDatabase.java | 21 +++- .../jdbc/TestDatabaseAutoConfiguration.java | 112 ++++++++++++++++-- .../context/ContainerFieldsImporter.java | 5 +- .../ConnectionDetailsRegistrar.java | 5 +- .../connection/ContainerConnectionSource.java | 8 ++ 16 files changed, 654 insertions(+), 22 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadataTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDockerComposeIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDynamicPropertySourceIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseServiceConnectionIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/resources/postgres-compose.yaml diff --git a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java index 0131943efb94..b41c573dd92f 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java @@ -168,21 +168,23 @@ private ArchRule allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoPa .and() .haveRawReturnType( Predicates.assignableTo("org.springframework.beans.factory.config.BeanFactoryPostProcessor")) - .should(haveNoParameters()) + .should(onlyInjectEnvironment()) .andShould() .beStatic() .allowEmptyShould(true); } - private ArchCondition haveNoParameters() { - return new ArchCondition<>("have no parameters") { + private ArchCondition onlyInjectEnvironment() { + return new ArchCondition<>("only inject Environment") { @Override public void check(JavaMethod item, ConditionEvents events) { List parameters = item.getParameters(); - if (!parameters.isEmpty()) { - events - .add(SimpleConditionEvent.violated(item, item.getDescription() + " should have no parameters")); + for (JavaParameter parameter : parameters) { + if (!"org.springframework.core.env.Environment".equals(parameter.getType().getName())) { + events.add(SimpleConditionEvent.violated(item, + item.getDescription() + " should only inject Environment")); + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java new file mode 100644 index 000000000000..9a92d9c58c62 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadata.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2024 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.autoconfigure.container; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.core.AttributeAccessor; + +/** + * Metadata about a container image that can be added to an {@link AttributeAccessor}. + * Primarily designed to be attached to {@link BeanDefinition BeanDefinitions} created in + * support of Testcontainers or Docker Compose. + * + * @param imageName the contaimer image name or {@code null} if the image name is not yet + * known + * @author Phillip Webb + * @since 3.4.0 + */ +public record ContainerImageMetadata(String imageName) { + + static final String NAME = ContainerImageMetadata.class.getName(); + + /** + * Add this container image metadata to the given attributes. + * @param attributes the attributes to add the metadata to + */ + public void addTo(AttributeAccessor attributes) { + if (attributes != null) { + attributes.setAttribute(NAME, this); + } + } + + /** + * Return {@code true} if {@link ContainerImageMetadata} has been added to the given + * attributes. + * @param attributes the attributes to check + * @return if metadata is present + */ + public static boolean isPresent(AttributeAccessor attributes) { + return getFrom(attributes) != null; + } + + /** + * Return {@link ContainerImageMetadata} from the given attributes or {@code null} if + * no metadata has been added. + * @param attributes the attributes + * @return the metadata or {@code null} + */ + public static ContainerImageMetadata getFrom(AttributeAccessor attributes) { + return (attributes != null) ? (ContainerImageMetadata) attributes.getAttribute(NAME) : null; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java new file mode 100644 index 000000000000..0dda84287abf --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/container/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2024 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. + */ + +/** + * Support classes related to auto-configuration involving containers. + */ +package org.springframework.boot.autoconfigure.container; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadataTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadataTests.java new file mode 100644 index 000000000000..28822d8dac73 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/container/ContainerImageMetadataTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2024 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.autoconfigure.container; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.AttributeAccessor; +import org.springframework.core.AttributeAccessorSupport; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ContainerImageMetadata}. + * + * @author Phillip Webb + */ +class ContainerImageMetadataTests { + + private ContainerImageMetadata metadata = new ContainerImageMetadata("test"); + + private AttributeAccessor attributes = new AttributeAccessorSupport() { + + }; + + @Test + void addToWhenAttributesIsNullDoesNothing() { + this.metadata.addTo(null); + } + + @Test + void addToAddsMetadata() { + this.metadata.addTo(this.attributes); + assertThat(this.attributes.getAttribute(ContainerImageMetadata.NAME)).isSameAs(this.metadata); + } + + @Test + void isPresentWhenPresentReturnsTrue() { + this.metadata.addTo(this.attributes); + assertThat(ContainerImageMetadata.isPresent(this.attributes)).isTrue(); + } + + @Test + void isPresentWhenNotPresentReturnsFalse() { + assertThat(ContainerImageMetadata.isPresent(this.attributes)).isFalse(); + } + + @Test + void isPresentWhenNullAttributesReturnsFalse() { + assertThat(ContainerImageMetadata.isPresent(null)).isFalse(); + } + + @Test + void getFromWhenPresentReturnsMetadata() { + this.metadata.addTo(this.attributes); + assertThat(ContainerImageMetadata.getFrom(this.attributes)).isSameAs(this.metadata); + } + + @Test + void getFromWhenNotPresentReturnsNull() { + assertThat(ContainerImageMetadata.getFrom(this.attributes)).isNull(); + } + + @Test + void getFromWhenNullAttributesReturnsNull() { + assertThat(ContainerImageMetadata.getFrom(null)).isNull(); + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java index 03efb04de0c8..4cf5135d26ed 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/DockerComposeServiceConnectionsApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.container.ContainerImageMetadata; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories; import org.springframework.boot.docker.compose.core.RunningService; @@ -77,10 +78,13 @@ private void registerConnectionDetails(BeanDefinitionRegistry registry, List void register(BeanDefinitionRegistry registry, RunningService runningService, Class connectionDetailsType, ConnectionDetails connectionDetails) { + ContainerImageMetadata containerMetadata = new ContainerImageMetadata(runningService.image().toString()); String beanName = getBeanName(runningService, connectionDetailsType); Class beanType = (Class) connectionDetails.getClass(); Supplier beanSupplier = () -> (T) connectionDetails; - registry.registerBeanDefinition(beanName, new RootBeanDefinition(beanType, beanSupplier)); + RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType, beanSupplier); + containerMetadata.addTo(beanDefinition); + registry.registerBeanDefinition(beanName, beanDefinition); } private String getBeanName(RunningService runningService, Class connectionDetailsType) { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle index 2c37870ce14d..1c8684fe2a04 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle @@ -13,18 +13,23 @@ dependencies { api(project(":spring-boot-project:spring-boot-test")) api(project(":spring-boot-project:spring-boot-autoconfigure")) + dockerTestImplementation(project(":spring-boot-project:spring-boot-docker-compose")) dockerTestImplementation(project(":spring-boot-project:spring-boot-testcontainers")) dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker")) + dockerTestImplementation("com.zaxxer:HikariCP") dockerTestImplementation("io.projectreactor:reactor-test") dockerTestImplementation("com.redis:testcontainers-redis") + dockerTestImplementation("com.h2database:h2") dockerTestImplementation("org.assertj:assertj-core") dockerTestImplementation("org.junit.jupiter:junit-jupiter") + dockerTestImplementation("org.postgresql:postgresql") dockerTestImplementation("org.testcontainers:cassandra") dockerTestImplementation("org.testcontainers:couchbase") dockerTestImplementation("org.testcontainers:elasticsearch") dockerTestImplementation("org.testcontainers:junit-jupiter") dockerTestImplementation("org.testcontainers:mongodb") dockerTestImplementation("org.testcontainers:neo4j") + dockerTestImplementation("org.testcontainers:postgresql") dockerTestImplementation("org.testcontainers:testcontainers") dockerTestRuntimeOnly("io.lettuce:lettuce-core") diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDockerComposeIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDockerComposeIntegrationTests.java new file mode 100644 index 000000000000..79a23e4163b5 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDockerComposeIntegrationTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012-2024 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.jdbc; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabaseDockerComposeIntegrationTests.SetupDockerCompose; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link AutoConfigureTestDatabase} with Docker Compose. + * + * @author Phillip Webb + */ +@SpringBootTest +@ContextConfiguration(initializers = SetupDockerCompose.class) +@AutoConfigureTestDatabase +@OverrideAutoConfiguration(enabled = false) +@DisabledIfDockerUnavailable +class AutoConfigureTestDatabaseDockerComposeIntegrationTests { + + @Autowired + private DataSource dataSource; + + @Test + void dataSourceIsNotReplaced() { + assertThat(this.dataSource).isInstanceOf(HikariDataSource.class).isNotInstanceOf(EmbeddedDatabase.class); + } + + @Configuration + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class Config { + + } + + static class SetupDockerCompose implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + try { + Path composeFile = Files.createTempFile("", "-postgres-compose"); + String composeFileContent = new ClassPathResource("postgres-compose.yaml") + .getContentAsString(StandardCharsets.UTF_8) + .replace("{imageName}", TestImage.POSTGRESQL.toString()); + Files.writeString(composeFile, composeFileContent); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext, + "spring.docker.compose.skip.in-tests=false", "spring.docker.compose.stop.command=down", + "spring.docker.compose.file=" + composeFile.toAbsolutePath().toString()); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDynamicPropertySourceIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDynamicPropertySourceIntegrationTests.java new file mode 100644 index 000000000000..c18f27702ca8 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseDynamicPropertySourceIntegrationTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2024 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.jdbc; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link AutoConfigureTestDatabase} with Testcontainers and a + * {@link DynamicPropertySource @DynamicPropertySource}. + * + * @author Phillip Webb + */ +@SpringBootTest +@AutoConfigureTestDatabase +@Testcontainers(disabledWithoutDocker = true) +@OverrideAutoConfiguration(enabled = false) +class AutoConfigureTestDatabaseDynamicPropertySourceIntegrationTests { + + @Container + static PostgreSQLContainer postgres = TestImage.container(PostgreSQLContainer.class); + + @Autowired + private DataSource dataSource; + + @DynamicPropertySource + static void jdbcProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + } + + @Test + void dataSourceIsNotReplaced() { + assertThat(this.dataSource).isInstanceOf(HikariDataSource.class).isNotInstanceOf(EmbeddedDatabase.class); + } + + @Configuration + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class Config { + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests.java new file mode 100644 index 000000000000..463d47159dba --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2024 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.jdbc; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests.SetupDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link AutoConfigureTestDatabase} with Docker Compose. + * + * @author Phillip Webb + */ +@SpringBootTest +@ContextConfiguration(initializers = SetupDatabase.class) +@AutoConfigureTestDatabase +@OverrideAutoConfiguration(enabled = false) +@DisabledIfDockerUnavailable +class AutoConfigureTestDatabaseNonTestDatabaseIntegrationTests { + + @Container + static PostgreSQLContainer postgres = TestImage.container(PostgreSQLContainer.class); + + @Autowired + private DataSource dataSource; + + @Test + void dataSourceIsReplaced() { + assertThat(this.dataSource).isInstanceOf(EmbeddedDatabase.class); + } + + @Configuration + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class Config { + + } + + static class SetupDatabase implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + postgres.start(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext, + "spring.datasource.url=" + postgres.getJdbcUrl()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseServiceConnectionIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseServiceConnectionIntegrationTests.java new file mode 100644 index 000000000000..d37448d2dcc4 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseServiceConnectionIntegrationTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2024 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.jdbc; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test for {@link AutoConfigureTestDatabase} with Testcontainers and a + * {@link ServiceConnection @ServiceConnection}. + * + * @author Phillip Webb + */ +@SpringBootTest +@AutoConfigureTestDatabase(replace = Replace.NON_TEST) +@Testcontainers(disabledWithoutDocker = true) +@OverrideAutoConfiguration(enabled = false) +class AutoConfigureTestDatabaseServiceConnectionIntegrationTests { + + @Container + @ServiceConnection + static PostgreSQLContainer postgres = TestImage.container(PostgreSQLContainer.class); + + @Autowired + private DataSource dataSource; + + @Test + void dataSourceIsNotReplaced() { + assertThat(this.dataSource).isInstanceOf(HikariDataSource.class).isNotInstanceOf(EmbeddedDatabase.class); + } + + @Configuration + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class Config { + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/resources/postgres-compose.yaml b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/resources/postgres-compose.yaml new file mode 100644 index 000000000000..cb721c823b23 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/resources/postgres-compose.yaml @@ -0,0 +1,9 @@ +services: + database: + image: '{imageName}' + ports: + - '5432' + environment: + - 'POSTGRES_USER=myuser' + - 'POSTGRES_DB=mydatabase' + - 'POSTGRES_PASSWORD=secret' diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java index 9bed2317ed5a..7947e569937f 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -26,10 +26,12 @@ import javax.sql.DataSource; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.container.ContainerImageMetadata; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping; import org.springframework.context.annotation.Primary; +import org.springframework.test.context.DynamicPropertySource; /** * Annotation that can be applied to a test class to configure a test database to use @@ -54,7 +56,7 @@ * @return the type of existing DataSource to replace */ @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE) - Replace replace() default Replace.ANY; + Replace replace() default Replace.NON_TEST; /** * The type of connection to be established when {@link #replace() replacing} the @@ -69,6 +71,21 @@ */ enum Replace { + /** + * Replace the DataSource bean unless it is auto-configured and connecting to a + * test database. The following types of connections are considered test + * databases: + *

    + *
  • Any bean definition that includes {@link ContainerImageMetadata} (including + * {@code @ServiceConnection} annotated Testcontainer databases, and connections + * created using Docker Compose)
  • + *
  • Any connection configured using a {@code spring.datasource.url} backed by a + * {@link DynamicPropertySource @DynamicPropertySource}
  • + *
+ * @since 3.4.0 + */ + NON_TEST, + /** * Replace the DataSource bean whether it was auto-configured or manually defined. */ diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java index c0c28e124cc3..fa1f07bf42f7 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -16,7 +16,9 @@ package org.springframework.boot.test.autoconfigure.jdbc; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.sql.DataSource; @@ -28,6 +30,8 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -37,8 +41,17 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.container.ContainerImageMetadata; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.bind.BoundPropertiesTrackingBindHandler; +import org.springframework.boot.context.properties.source.ConfigurationProperty; +import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; +import org.springframework.boot.origin.PropertySourceOrigin; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Role; @@ -47,6 +60,8 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.type.MethodMetadata; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.util.Assert; @@ -63,25 +78,48 @@ public class TestDatabaseAutoConfiguration { @Bean - @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "AUTO_CONFIGURED") - @ConditionalOnMissingBean - public DataSource dataSource(Environment environment) { - return new EmbeddedDataSourceFactory(environment).getEmbeddedDatabase(); + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "NON_TEST", + matchIfMissing = true) + static EmbeddedDataSourceBeanFactoryPostProcessor nonTestEmbeddedDataSourceBeanFactoryPostProcessor( + Environment environment) { + return new EmbeddedDataSourceBeanFactoryPostProcessor(environment, Replace.NON_TEST); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "ANY", - matchIfMissing = true) - static EmbeddedDataSourceBeanFactoryPostProcessor embeddedDataSourceBeanFactoryPostProcessor() { - return new EmbeddedDataSourceBeanFactoryPostProcessor(); + @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "ANY") + static EmbeddedDataSourceBeanFactoryPostProcessor embeddedDataSourceBeanFactoryPostProcessor( + Environment environment) { + return new EmbeddedDataSourceBeanFactoryPostProcessor(environment, Replace.ANY); + } + + @Bean + @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "AUTO_CONFIGURED") + @ConditionalOnMissingBean + public DataSource dataSource(Environment environment) { + return new EmbeddedDataSourceFactory(environment).getEmbeddedDatabase(); } @Order(Ordered.LOWEST_PRECEDENCE) static class EmbeddedDataSourceBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { + private static final ConfigurationPropertyName DATASOURCE_URL_PROPERTY = ConfigurationPropertyName + .of("spring.datasource.url"); + + private static final String DYNAMIC_VALUES_PROPERTY_SOURCE_CLASS = "org.springframework.test.context.support.DynamicValuesPropertySource"; + private static final Log logger = LogFactory.getLog(EmbeddedDataSourceBeanFactoryPostProcessor.class); + private final Environment environment; + + private final Replace replace; + + EmbeddedDataSourceBeanFactoryPostProcessor(Environment environment, Replace replace) { + this.environment = environment; + this.replace = replace; + } + @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { if (AotDetector.useGeneratedArtifacts()) { @@ -98,7 +136,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) private void process(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory) { BeanDefinitionHolder holder = getDataSourceBeanDefinition(beanFactory); - if (holder != null) { + if (holder != null && isReplaceable(beanFactory, holder)) { String beanName = holder.getBeanName(); boolean primary = holder.getBeanDefinition().isPrimary(); logger.info("Replacing '" + beanName + "' DataSource bean with " + (primary ? "primary " : "") @@ -135,6 +173,60 @@ private BeanDefinitionHolder getDataSourceBeanDefinition(ConfigurableListableBea return null; } + private boolean isReplaceable(ConfigurableListableBeanFactory beanFactory, BeanDefinitionHolder holder) { + if (this.replace == Replace.NON_TEST) { + return !isAutoConfigured(holder) || !isConnectingToTestDatabase(beanFactory); + } + return true; + } + + private boolean isAutoConfigured(BeanDefinitionHolder holder) { + if (holder.getBeanDefinition() instanceof AnnotatedBeanDefinition annotatedBeanDefinition) { + MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata(); + return (factoryMethodMetadata != null) && (factoryMethodMetadata.getDeclaringClassName() + .startsWith("org.springframework.boot.autoconfigure.")); + } + return false; + } + + private boolean isConnectingToTestDatabase(ConfigurableListableBeanFactory beanFactory) { + return isUsingTestServiceConnection(beanFactory) || isUsingDynamicPropertySournce(); + } + + private boolean isUsingTestServiceConnection(ConfigurableListableBeanFactory beanFactory) { + for (String beanName : beanFactory.getBeanNamesForType(JdbcConnectionDetails.class)) { + try { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + if (ContainerImageMetadata.isPresent(beanDefinition)) { + return true; + } + } + catch (NoSuchBeanDefinitionException ex) { + // Ignore + } + } + return false; + } + + private boolean isUsingDynamicPropertySournce() { + List bound = new ArrayList<>(); + Binder.get(this.environment, new BoundPropertiesTrackingBindHandler(bound::add)) + .bind(DATASOURCE_URL_PROPERTY, Bindable.of(String.class)); + return (!bound.isEmpty()) ? isBoundToDynamicValuesPropertySource(bound.get(0)) : false; + } + + private boolean isBoundToDynamicValuesPropertySource(ConfigurationProperty configurationProperty) { + if (configurationProperty.getOrigin() instanceof PropertySourceOrigin origin) { + return isDynamicValuesPropertySource(origin.getPropertySource()); + } + return false; + } + + private boolean isDynamicValuesPropertySource(PropertySource propertySource) { + return propertySource != null + && DYNAMIC_VALUES_PROPERTY_SOURCE_CLASS.equals(propertySource.getClass().getName()); + } + } static class EmbeddedDataSourceFactoryBean implements FactoryBean, EnvironmentAware, InitializingBean { diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/context/ContainerFieldsImporter.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/context/ContainerFieldsImporter.java index 95a8fce48ff3..a6909d890b83 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/context/ContainerFieldsImporter.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/context/ContainerFieldsImporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -24,6 +24,7 @@ import org.testcontainers.containers.Container; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.autoconfigure.container.ContainerImageMetadata; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -65,7 +66,9 @@ private Container getContainer(Field field) { } private void registerBeanDefinition(BeanDefinitionRegistry registry, Field field, Container container) { + ContainerImageMetadata containerMetadata = new ContainerImageMetadata(container.getDockerImageName()); TestcontainerFieldBeanDefinition beanDefinition = new TestcontainerFieldBeanDefinition(field, container); + containerMetadata.addTo(beanDefinition); String beanName = generateBeanName(field); registry.registerBeanDefinition(beanName, beanDefinition); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ConnectionDetailsRegistrar.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ConnectionDetailsRegistrar.java index 318ef6a5f91d..491d61c5d6af 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ConnectionDetailsRegistrar.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ConnectionDetailsRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -32,6 +32,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.container.ContainerImageMetadata; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactoryNotFoundException; @@ -100,12 +101,14 @@ private void registerBeanDefinition(BeanDefinitionRegistry registry, Contain Arrays.asList(existingBeans)))); return; } + ContainerImageMetadata containerMetadata = new ContainerImageMetadata(source.getContainerImageName()); String beanName = getBeanName(source, connectionDetails); Class beanType = (Class) connectionDetails.getClass(); Supplier beanSupplier = () -> (T) connectionDetails; logger.debug(LogMessage.of(() -> "Registering '%s' for %s".formatted(beanName, source))); RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType, beanSupplier); beanDefinition.setAttribute(ServiceConnection.class.getName(), true); + containerMetadata.addTo(beanDefinition); registry.registerBeanDefinition(beanName, beanDefinition); } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java index da480628d6e3..06a09e4e4a87 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java @@ -52,6 +52,8 @@ public final class ContainerConnectionSource> implements private final Class containerType; + private final String containerImageName; + private final String connectionName; private final Set> connectionDetailsTypes; @@ -63,6 +65,7 @@ public final class ContainerConnectionSource> implements this.beanNameSuffix = beanNameSuffix; this.origin = origin; this.containerType = containerType; + this.containerImageName = containerImageName; this.connectionName = getOrDeduceConnectionName(annotation.getString("name"), containerImageName); this.connectionDetailsTypes = Set.of(annotation.getClassArray("type")); this.containerSupplier = containerSupplier; @@ -73,6 +76,7 @@ public final class ContainerConnectionSource> implements this.beanNameSuffix = beanNameSuffix; this.origin = origin; this.containerType = containerType; + this.containerImageName = containerImageName; this.connectionName = getOrDeduceConnectionName(annotation.name(), containerImageName); this.connectionDetailsTypes = Set.of(annotation.type()); this.containerSupplier = containerSupplier; @@ -136,6 +140,10 @@ public Origin getOrigin() { return this.origin; } + String getContainerImageName() { + return this.containerImageName; + } + String getConnectionName() { return this.connectionName; } From 2bfd784d7baef889fab89ab406f9fcb762b81c6d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 12 Sep 2024 08:17:43 +0100 Subject: [PATCH 189/271] Revert "Prevent duplicate DynamicPropertyRegistry beans" This partially reverts commit 4d4b189cce3af9328861038b7dae954744e4cef2. The changes to main code are no longer needed as Framework's test context framework no longer defines a DynamicPropertyRegistry bean. The changes to test code have been kept to verify that @SpringBootTest and TestcontainersPropertySourceAutoConfiguration continue to work in combination. Closes gh-42275 --- ...ainersPropertySourceAutoConfiguration.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfiguration.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfiguration.java index 64c1fcaf36f6..e6219f2d2d4e 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceAutoConfiguration.java @@ -16,10 +16,6 @@ package org.springframework.boot.testcontainers.properties; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.ConfigurableApplicationContext; @@ -43,27 +39,9 @@ public class TestcontainersPropertySourceAutoConfiguration { TestcontainersPropertySourceAutoConfiguration() { } - @Bean - static RemoveTestDynamicPropertyRegistryBeanPostProcessor removeTestDynamicPropertyRegistryBeanPostProcessor() { - return new RemoveTestDynamicPropertyRegistryBeanPostProcessor(); - } - @Bean static DynamicPropertyRegistry dynamicPropertyRegistry(ConfigurableApplicationContext applicationContext) { return TestcontainersPropertySource.attach(applicationContext); } - static class RemoveTestDynamicPropertyRegistryBeanPostProcessor implements BeanFactoryPostProcessor { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - if (beanFactory instanceof DefaultSingletonBeanRegistry singletonBeanRegistry) { - singletonBeanRegistry - .destroySingleton("org.springframework.test.context.support.DynamicPropertiesContextCustomizer" - + ".dynamicPropertyRegistry"); - } - } - - } - } From bf1ef30818ade928a49a183ce834921e9a8ef1a2 Mon Sep 17 00:00:00 2001 From: arefbehboudi Date: Wed, 11 Sep 2024 19:26:49 +0330 Subject: [PATCH 190/271] Polish See gh-42268 --- .../java/org/springframework/boot/SpringBootBanner.java | 7 ++----- .../ParentContextApplicationContextInitializer.java | 1 - .../cloud/CloudFoundryVcapEnvironmentPostProcessor.java | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringBootBanner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringBootBanner.java index e455786e9627..4807ef4855ab 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringBootBanner.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringBootBanner.java @@ -48,11 +48,8 @@ public void printBanner(Environment environment, Class sourceClass, PrintStre printStream.println(); printStream.println(BANNER); String version = String.format(" (v%s)", SpringBootVersion.getVersion()); - StringBuilder padding = new StringBuilder(); - while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) { - padding.append(" "); - } - printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(), + String padding = " ".repeat(Math.max(0, STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length()))); + printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version)); printStream.println(); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java index ecaee4c79a5c..257e25ed2451 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/builder/ParentContextApplicationContextInitializer.java @@ -83,7 +83,6 @@ public void onApplicationEvent(ContextRefreshedEvent event) { /** * {@link ApplicationEvent} fired when a parent context is available. */ - @SuppressWarnings("serial") public static class ParentContextAvailableEvent extends ApplicationEvent { public ParentContextAvailableEvent(ConfigurableApplicationContext applicationContext) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java index 9aa390f1f6c9..d973135936d1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java @@ -213,10 +213,7 @@ else if (value instanceof Collection collection) { else if (value instanceof String) { properties.put(name, value); } - else if (value instanceof Number) { - properties.put(name, value.toString()); - } - else if (value instanceof Boolean) { + else if (value instanceof Number || value instanceof Boolean) { properties.put(name, value.toString()); } else { From 206c28704fa6a797a24300555558235c300312b1 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 12 Sep 2024 14:19:03 +0200 Subject: [PATCH 191/271] Put registration id in validation error message Closes gh-42278 --- .../security/oauth2/client/OAuth2ClientProperties.java | 7 ++++--- .../oauth2/client/OAuth2ClientPropertiesTests.java | 2 +- 2 files changed, 5 insertions(+), 4 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 22effd124982..36fa40eeda44 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 @@ -31,6 +31,7 @@ * @author Phillip Webb * @author Artsiom Yudovin * @author MyeongHyeon Lee + * @author Moritz Halbritter * @since 2.0.0 */ @ConfigurationProperties(prefix = "spring.security.oauth2.client") @@ -60,12 +61,12 @@ public void afterPropertiesSet() { } public void validate() { - getRegistration().values().forEach(this::validateRegistration); + getRegistration().forEach(this::validateRegistration); } - private void validateRegistration(Registration registration) { + private void validateRegistration(String id, Registration registration) { if (!StringUtils.hasText(registration.getClientId())) { - throw new IllegalStateException("Client id must not be empty."); + throw new IllegalStateException("Client id of registration '%s' must not be empty.".formatted(id)); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesTests.java index fd04c1b14fc3..7e12b24b18e8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesTests.java @@ -37,7 +37,7 @@ void clientIdAbsentThrowsException() { registration.setProvider("google"); this.properties.getRegistration().put("foo", registration); assertThatIllegalStateException().isThrownBy(this.properties::validate) - .withMessageContaining("Client id must not be empty."); + .withMessageContaining("Client id of registration 'foo' must not be empty."); } @Test From 4bb513d7f099e8b8603b8916d030093aac696f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:06:42 +0200 Subject: [PATCH 192/271] Upgrade to Spring Framework 6.1.13 Closes gh-42125 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5f97dcf7a773..271babef7578 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ junitJupiterVersion=5.10.3 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.9.28 -springFrameworkVersion=6.1.13-SNAPSHOT +springFrameworkVersion=6.1.13 springFramework60xVersion=6.0.23 tomcatVersion=10.1.29 From a8104944476ed3c2955243e11d98265fe7ce39ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:06:46 +0200 Subject: [PATCH 193/271] Upgrade to Spring HATEOAS 2.2.5 Closes gh-42281 --- 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 a68cf3ef2a52..1efc90d866f1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1610,7 +1610,7 @@ bom { ] } } - library("Spring HATEOAS", "2.2.4") { + library("Spring HATEOAS", "2.2.5") { considerSnapshots() group("org.springframework.hateoas") { modules = [ From 05a93c1ba96b51edab15debe4c288c3339ab4697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:13:39 +0200 Subject: [PATCH 194/271] Upgrade to Spring Framework 6.1.13 Closes gh-42133 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2341ef0c79a4..e025dcda9903 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ junitJupiterVersion=5.10.3 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 -springFrameworkVersion=6.1.13-SNAPSHOT +springFrameworkVersion=6.1.13 springFramework60xVersion=6.0.23 tomcatVersion=10.1.29 snakeYamlVersion=2.2 From 37cd9e7949450d8d78025d79ee429f35ba4cc3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:13:43 +0200 Subject: [PATCH 195/271] Upgrade to Spring HATEOAS 2.3.3 Closes gh-42282 --- spring-boot-project/spring-boot-dependencies/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index fddf8d08381c..b3339832e098 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2001,7 +2001,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-graphql/releases/tag/v{version}") } } - library("Spring HATEOAS", "2.3.2") { + library("Spring HATEOAS", "2.3.3") { considerSnapshots() group("org.springframework.hateoas") { modules = [ @@ -2171,7 +2171,7 @@ bom { .formatted(version.forMajorMinorGeneration()) } docs { version -> "https://docs.spring.io/spring-ws/docs/%s/reference/html" .formatted(version.forMajorMinorGeneration()) } - releaseNotes("https://github.com/spring-projects/spring-ws/releases/tag/v{version}") + releaseNotes("https://github.com/spring-projects/spring-ws/releases/tag/vversion}") } } library("SQLite JDBC", "3.45.3.0") { From f213ba4e49d1fd91bb34c0c37eb4f1a062b1f0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:30:51 +0200 Subject: [PATCH 196/271] Upgrade to Spring Framework 6.2.0-RC1 Closes gh-42144 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 53e5294ed454..fb57a27b10e2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ junitJupiterVersion=5.11.0 kotlinVersion=1.9.25 mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 -springFrameworkVersion=6.2.0-SNAPSHOT +springFrameworkVersion=6.2.0-RC1 springFramework60xVersion=6.0.23 tomcatVersion=10.1.29 snakeYamlVersion=2.3 From af346900931b97640baa4de163880b6b692bc5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 12 Sep 2024 15:30:56 +0200 Subject: [PATCH 197/271] Upgrade to Spring HATEOAS 2.4.0-RC1 Closes gh-42283 --- 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 1ce6346ab768..60b51b1b3135 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1992,7 +1992,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-graphql/releases/tag/v{version}") } } - library("Spring HATEOAS", "2.4.0-M1") { + library("Spring HATEOAS", "2.4.0-RC1") { considerSnapshots() group("org.springframework.hateoas") { modules = [ From 3723a9e04037ced775637730eb0231394033af42 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 12 Sep 2024 13:02:23 -0700 Subject: [PATCH 198/271] Support Testcontainer JDBC URLs with `Replace.NON_TEST` Update `TestDatabaseAutoConfiguration` so that Testcontainer JDBC URLs are also detected when using `Replace.NON_TEST`. Closes gh-35253 --- ...TestcontainersJdbcUrlIntegrationTests.java | 80 +++++++++++++++++++ .../jdbc/AutoConfigureTestDatabase.java | 2 + .../jdbc/TestDatabaseAutoConfiguration.java | 20 ++++- .../boot/testsupport/container/TestImage.java | 4 + 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests.java diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests.java new file mode 100644 index 000000000000..c9771a947d0a --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/dockerTest/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012-2024 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.jdbc; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests.InitializeDatasourceUrl; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test for {@link AutoConfigureTestDatabase} with Testcontainers and a + * {@link ServiceConnection @ServiceConnection}. + * + * @author Phillip Webb + */ +@SpringBootTest +@ContextConfiguration(initializers = InitializeDatasourceUrl.class) +@AutoConfigureTestDatabase(replace = Replace.NON_TEST) +@Testcontainers(disabledWithoutDocker = true) +@OverrideAutoConfiguration(enabled = false) +class AutoConfigureTestDatabaseTestcontainersJdbcUrlIntegrationTests { + + @Autowired + private DataSource dataSource; + + @Test + void dataSourceIsNotReplaced() { + assertThat(this.dataSource).isInstanceOf(HikariDataSource.class).isNotInstanceOf(EmbeddedDatabase.class); + } + + @Configuration + @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + static class Config { + + } + + static class InitializeDatasourceUrl implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext, + "spring.datasource.url=jdbc:tc:postgis:" + TestImage.POSTGRESQL.getTag() + ":///"); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java index 7947e569937f..4283a1ca8be5 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.java @@ -81,6 +81,8 @@ enum Replace { * created using Docker Compose) *
  • Any connection configured using a {@code spring.datasource.url} backed by a * {@link DynamicPropertySource @DynamicPropertySource}
  • + *
  • Any connection configured using a {@code spring.datasource.url} with the + * Testcontainers JDBC syntax
  • * * @since 3.4.0 */ diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java index fa1f07bf42f7..2c6c3ebe4719 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/TestDatabaseAutoConfiguration.java @@ -107,6 +107,8 @@ static class EmbeddedDataSourceBeanFactoryPostProcessor implements BeanDefinitio private static final ConfigurationPropertyName DATASOURCE_URL_PROPERTY = ConfigurationPropertyName .of("spring.datasource.url"); + private static final Bindable BINDABLE_STRING = Bindable.of(String.class); + private static final String DYNAMIC_VALUES_PROPERTY_SOURCE_CLASS = "org.springframework.test.context.support.DynamicValuesPropertySource"; private static final Log logger = LogFactory.getLog(EmbeddedDataSourceBeanFactoryPostProcessor.class); @@ -190,7 +192,7 @@ private boolean isAutoConfigured(BeanDefinitionHolder holder) { } private boolean isConnectingToTestDatabase(ConfigurableListableBeanFactory beanFactory) { - return isUsingTestServiceConnection(beanFactory) || isUsingDynamicPropertySournce(); + return isUsingTestServiceConnection(beanFactory) || isUsingTestDatasourceUrl(); } private boolean isUsingTestServiceConnection(ConfigurableListableBeanFactory beanFactory) { @@ -208,11 +210,16 @@ private boolean isUsingTestServiceConnection(ConfigurableListableBeanFactory bea return false; } - private boolean isUsingDynamicPropertySournce() { + private boolean isUsingTestDatasourceUrl() { List bound = new ArrayList<>(); Binder.get(this.environment, new BoundPropertiesTrackingBindHandler(bound::add)) - .bind(DATASOURCE_URL_PROPERTY, Bindable.of(String.class)); - return (!bound.isEmpty()) ? isBoundToDynamicValuesPropertySource(bound.get(0)) : false; + .bind(DATASOURCE_URL_PROPERTY, BINDABLE_STRING); + return (!bound.isEmpty()) ? isUsingTestDatasourceUrl(bound.get(0)) : false; + } + + private boolean isUsingTestDatasourceUrl(ConfigurationProperty configurationProperty) { + return isBoundToDynamicValuesPropertySource(configurationProperty) + || isTestcontainersUrl(configurationProperty); } private boolean isBoundToDynamicValuesPropertySource(ConfigurationProperty configurationProperty) { @@ -227,6 +234,11 @@ private boolean isDynamicValuesPropertySource(PropertySource propertySource) && DYNAMIC_VALUES_PROPERTY_SOURCE_CLASS.equals(propertySource.getClass().getName()); } + private boolean isTestcontainersUrl(ConfigurationProperty configurationProperty) { + Object value = configurationProperty.getValue(); + return (value != null) && value.toString().startsWith("jdbc:tc:"); + } + } static class EmbeddedDataSourceFactoryBean implements FactoryBean, EnvironmentAware, InitializingBean { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java index d8fb5feb71b3..4e3b773f1a0a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java @@ -343,6 +343,10 @@ private > C createContainer(Class containerClass) { } } + public String getTag() { + return this.tag; + } + @Override public String toString() { return (this.tag != null) ? this.name + ":" + this.tag : this.name; From 03e7be3ccf8d0d876c65c246914ecbc00cdeee80 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 12 Sep 2024 13:27:00 -0700 Subject: [PATCH 199/271] Document that spring.jmx.enabled is not for third-party libraries Closes gh-42272 --- .../springframework/boot/autoconfigure/jmx/JmxProperties.java | 2 +- .../spring-boot-docs/src/docs/asciidoc/actuator/jmx.adoc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java index 4c4141432b30..371071efb865 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxProperties.java @@ -29,7 +29,7 @@ public class JmxProperties { /** - * Expose management beans to the JMX domain. + * Expose Spring's management beans to the JMX domain. */ private boolean enabled = false; diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/jmx.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/jmx.adoc index ad3a52b2a439..e50027bf668b 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/jmx.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/jmx.adoc @@ -9,6 +9,9 @@ Any of your beans that are annotated with Spring JMX annotations (`@ManagedResou If your platform provides a standard `MBeanServer`, Spring Boot uses that and defaults to the VM `MBeanServer`, if necessary. If all that fails, a new `MBeanServer` is created. +NOTE: `spring.jmx.enabled` affects only the management beans provided by Spring. +Enabling management beans provided by other libraries (for example Log4j2 or Quartz) is independent. + See the {spring-boot-autoconfigure-module-code}/jmx/JmxAutoConfiguration.java[`JmxAutoConfiguration`] class for more details. By default, Spring Boot also exposes management endpoints as JMX MBeans under the `org.springframework.boot` domain. From ee10425b6d605b7671eeb31acb65273924ce8589 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Thu, 12 Sep 2024 17:26:09 +0800 Subject: [PATCH 200/271] Add tests to ensure private constructor is not used for binding See gh-42277 --- .../DefaultBindConstructorProviderTests.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProviderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProviderTests.java index 160773e52cef..99ebc054eb39 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProviderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/DefaultBindConstructorProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -33,6 +33,7 @@ * * @author Phillip Webb * @author Madhura Bhave + * @author Yanming Zhou */ class DefaultBindConstructorProviderTests { @@ -92,6 +93,12 @@ void getBindConstructorWhenHasTwoConstructorsWithBothConstructorBindingThrowsExc .withMessageContaining("has more than one @ConstructorBinding"); } + @Test + void getBindConstructorWhenIsTypeWithPrivateConstructorReturnsNull() { + Constructor constructor = this.provider.getBindConstructor(TypeWithPrivateConstructor.class, false); + assertThat(constructor).isNull(); + } + @Test void getBindConstructorWhenIsMemberTypeWithPrivateConstructorReturnsNull() { Constructor constructor = this.provider.getBindConstructor(MemberTypeWithPrivateConstructor.Member.class, @@ -224,6 +231,13 @@ static class TwoConstructorsWithBothConstructorBinding { } + static final class TypeWithPrivateConstructor { + + private TypeWithPrivateConstructor(Environment environment) { + } + + } + static class MemberTypeWithPrivateConstructor { static final class Member { From 0215da06dfd61702f29d146d5e9e941c9ba60e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 11:17:58 +0200 Subject: [PATCH 201/271] Upgrade to Groovy 4.0.23 Closes gh-42291 --- 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 1efc90d866f1..4ccc709d9c36 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -344,7 +344,7 @@ bom { ] } } - library("Groovy", "4.0.22") { + library("Groovy", "4.0.23") { group("org.apache.groovy") { imports = [ "groovy-bom" From 831cbedb31096a9a0c9b11e130943ae654c6bdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 11:20:54 +0200 Subject: [PATCH 202/271] Upgrade to Groovy 4.0.23 Closes gh-42292 --- 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 a2c772fb2c3e..756f97f1a832 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -444,7 +444,7 @@ bom { releaseNotes("https://github.com/graphql-java/graphql-java/releases/tag/v{version}") } } - library("Groovy", "4.0.22") { + library("Groovy", "4.0.23") { group("org.apache.groovy") { imports = [ "groovy-bom" From d6bfdbd90e1027aa4882dc55ff1c18ab4dbbf03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 14:33:28 +0200 Subject: [PATCH 203/271] Upgrade to Flyway 10.18.0 Closes gh-42295 --- .../boot/autoconfigure/flyway/FlywayAutoConfiguration.java | 1 + .../boot/autoconfigure/flyway/FlywayProperties.java | 3 +++ .../boot/autoconfigure/flyway/FlywayPropertiesTests.java | 1 + spring-boot-project/spring-boot-dependencies/build.gradle | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index d04d240f5eb6..d07ceeef606e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -225,6 +225,7 @@ private void applyConnectionDetails(FlywayConnectionDetails connectionDetails, D * @param configuration the configuration * @param properties the properties */ + @SuppressWarnings("removal") private void configureProperties(FluentConfiguration configuration, FlywayProperties properties) { // NOTE: Using method references in the mapper methods can break // back-compatibility (see gh-38164) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java index ad9c25864a6e..370dc85e5b11 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java @@ -597,10 +597,13 @@ public void setCleanDisabled(boolean cleanDisabled) { this.cleanDisabled = cleanDisabled; } + @Deprecated(since = "3.4.0", forRemoval = true) + @DeprecatedConfigurationProperty(since = "3.4.0") public boolean isCleanOnValidationError() { return this.cleanOnValidationError; } + @Deprecated(since = "3.4.0", forRemoval = true) public void setCleanOnValidationError(boolean cleanOnValidationError) { this.cleanOnValidationError = cleanOnValidationError; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java index a79740949aea..fbf15285972b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java @@ -46,6 +46,7 @@ class FlywayPropertiesTests { @Test + @SuppressWarnings("removal") void defaultValuesAreConsistent() { FlywayProperties properties = new FlywayProperties(); Configuration configuration = new FluentConfiguration(); diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index c4f18da8d0f1..414154197f27 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -354,7 +354,7 @@ bom { releaseNotes("https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-{version}.html") } } - library("Flyway", "10.17.3") { + library("Flyway", "10.18.0") { group("org.flywaydb") { modules = [ "flyway-commandline", From c65d26fb8173240eaa4eb502d038fd1f5a376104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 14:33:32 +0200 Subject: [PATCH 204/271] Upgrade to Groovy 4.0.23 Closes gh-42296 --- 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 414154197f27..21076d05c28d 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -445,7 +445,7 @@ bom { releaseNotes("https://github.com/graphql-java/graphql-java/releases/tag/v{version}") } } - library("Groovy", "4.0.22") { + library("Groovy", "4.0.23") { group("org.apache.groovy") { imports = [ "groovy-bom" From baff7a5c6424c00077e8723ff1d9d8141ed2d893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 14:33:37 +0200 Subject: [PATCH 205/271] Upgrade to HttpCore5 5.3 Closes gh-42297 --- 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 21076d05c28d..f556aaaa1eee 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -593,7 +593,7 @@ bom { ] } } - library("HttpCore5", "5.2.5") { + library("HttpCore5", "5.3") { group("org.apache.httpcomponents.core5") { modules = [ "httpcore5", From 535ec4c14a49086041056b2c52dcab43c390ed3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 14:33:41 +0200 Subject: [PATCH 206/271] Upgrade to OpenTelemetry 1.42.1 Closes gh-42298 --- 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 f556aaaa1eee..070c294a2c60 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1445,7 +1445,7 @@ bom { ] } } - library("OpenTelemetry", "1.41.0") { + library("OpenTelemetry", "1.42.1") { group("io.opentelemetry") { imports = [ "opentelemetry-bom" From d3861bcaefc0846ffa195fc59a5dad7549f291bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 15:45:58 +0200 Subject: [PATCH 207/271] Upgrade to Spring Data Bom 2023.1.10 Closes gh-42124 --- 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 4ccc709d9c36..3abac2435419 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1583,7 +1583,7 @@ bom { ] } } - library("Spring Data Bom", "2023.1.10-SNAPSHOT") { + library("Spring Data Bom", "2023.1.10") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 99d4e5cbf80c3fe92701ab9b943eb1400325d76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 15:47:29 +0200 Subject: [PATCH 208/271] Upgrade to Spring Data Bom 2024.0.4 Closes gh-42132 --- 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 756f97f1a832..f5cf324fc225 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1954,7 +1954,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2024.0.4-SNAPSHOT") { + library("Spring Data Bom", "2024.0.4") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 4c6d1de28482f52fcdf83f5807bfb04e1f9f94c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 15:50:33 +0200 Subject: [PATCH 209/271] Upgrade to Spring Data Bom 2024.1.0-M1 Closes gh-42143 --- 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 070c294a2c60..71fbf8e5775d 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1945,7 +1945,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-batch/releases/tag/v{version}") } } - library("Spring Data Bom", "2024.1.0-SNAPSHOT") { + library("Spring Data Bom", "2024.1.0-M1") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { From 988e083fef4eab3690c72976d95dfebec7bad39c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 13 Sep 2024 15:10:40 +0100 Subject: [PATCH 210/271] Make sure that generateAntoraYml runs when attributes have changed Closes gh-42300 --- .../boot/build/AntoraConventions.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java index a05289a4a220..841afc509985 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java @@ -37,6 +37,7 @@ import org.gradle.api.Project; import org.gradle.api.logging.LogLevel; import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.TaskContainer; @@ -118,8 +119,7 @@ private void configureGenerateAntoraYmlTask(Project project, GenerateAntoraYmlTa generateAntoraYmlTask.setProperty("outputFile", new File(project.getBuildDir(), "generated/docs/antora-yml/antora.yml")); generateAntoraYmlTask.setProperty("yml", getDefaultYml(project)); - generateAntoraYmlTask.doFirst((task) -> generateAntoraYmlTask.getAsciidocAttributes() - .putAll(project.provider(() -> getAsciidocAttributes(project, dependencyVersionsTask)))); + generateAntoraYmlTask.getAsciidocAttributes().putAll(getAsciidocAttributes(project, dependencyVersionsTask)); } private Map getDefaultYml(Project project) { @@ -138,12 +138,14 @@ private void configureGenerateAntoraYmlTask(Project project, GenerateAntoraYmlTa return defaultYml; } - private Map getAsciidocAttributes(Project project, + private Provider> getAsciidocAttributes(Project project, ExtractVersionConstraints dependencyVersionsTask) { - BomExtension bom = (BomExtension) project.project(DEPENDENCIES_PATH).getExtensions().getByName("bom"); - Map dependencyVersions = dependencyVersionsTask.getVersionConstraints(); - AntoraAsciidocAttributes attributes = new AntoraAsciidocAttributes(project, bom, dependencyVersions); - return attributes.get(); + return project.provider(() -> { + BomExtension bom = (BomExtension) project.project(DEPENDENCIES_PATH).getExtensions().getByName("bom"); + Map dependencyVersions = dependencyVersionsTask.getVersionConstraints(); + AntoraAsciidocAttributes attributes = new AntoraAsciidocAttributes(project, bom, dependencyVersions); + return attributes.get(); + }); } private void configureAntoraTask(Project project, AntoraTask antoraTask, NpmInstallTask npmInstallTask, From e6e7357303e4e94503a8ce2d977fd25069421c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Fri, 13 Sep 2024 16:24:43 +0200 Subject: [PATCH 211/271] Upgrade to Undertow 2.3.17.Final Closes gh-42302 --- spring-boot-project/spring-boot-dependencies/build.gradle | 6 +----- .../undertow/UndertowServletWebServerFactoryTests.java | 7 +++++++ .../server/AbstractServletWebServerFactoryTests.java | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 3abac2435419..a242ca7813d6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1765,11 +1765,7 @@ bom { ] } } - library("Undertow", "2.3.13.Final") { - prohibit { - versionRange "[2.3.14.Final,2.3.15.Final]" - because "it contains a regression (https://issues.redhat.com/browse/UNDERTOW-2420)" - } + library("Undertow", "2.3.17.Final") { group("io.undertow") { modules = [ "undertow-core", diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java index 3c8a05cd6a94..ec7812ee82d8 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java @@ -243,6 +243,13 @@ void whenServerIsShuttingDownARequestOnAnIdleConnectionAreRejectedWithServiceUna this.webServer.stop(); } + @Test + @Override + @Disabled("https://issues.redhat.com/browse/UNDERTOW-2420") + protected void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception { + super.portClashOfSecondaryConnectorResultsInPortInUseException(); + } + @Test @Override @Disabled("Restart after stop is not supported with Undertow") diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index f59f916aad64..c8081a9ab48a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -1094,7 +1094,7 @@ protected void portClashOfPrimaryConnectorResultsInPortInUseException() throws E } @Test - void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception { + protected void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception { doWithBlockedPort((port) -> { assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> { AbstractServletWebServerFactory factory = getFactory(); From 44be2e11d96cc8b8b75fd00a9f9c4b160b4273f8 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Fri, 13 Sep 2024 10:24:14 +0800 Subject: [PATCH 212/271] Add common definition annotations support for ConfigurationProperties Update `` to ensure that common bean definition annotations, such as `@Lazy`, `@Primary` and `@Fallback`, are applied. See gh-42289 --- .../ConfigurationPropertiesBeanRegistrar.java | 6 ++++-- ...nfigurationPropertiesBeanRegistrarTests.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java index ed5add358a95..0831d13cb7ec 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java @@ -23,8 +23,8 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; +import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.AnnotationScopeMetadataResolver; import org.springframework.context.annotation.ScopeMetadata; import org.springframework.context.annotation.ScopeMetadataResolver; @@ -42,6 +42,7 @@ * * @author Madhura Bhave * @author Phillip Webb + * @author Yanming Zhou */ final class ConfigurationPropertiesBeanRegistrar { @@ -88,7 +89,8 @@ private void registerBeanDefinition(String beanName, Class type, } private BeanDefinitionHolder createBeanDefinition(String beanName, Class type) { - GenericBeanDefinition definition = new AnnotatedGenericBeanDefinition(type); + AnnotatedGenericBeanDefinition definition = new AnnotatedGenericBeanDefinition(type); + AnnotationConfigUtils.processCommonDefinitionAnnotations(definition); BindMethod bindMethod = ConfigurationPropertiesBean.deduceBindMethod(type); BindMethodAttribute.set(definition, bindMethod); if (bindMethod == BindMethod.VALUE_OBJECT) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java index 3680387a69bf..4379dd14548e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.context.properties.bind.BindMethod; +import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; @@ -38,6 +39,7 @@ * @author Madhura Bhave * @author Stephane Nicoll * @author Phillip Webb + * @author Yanming Zhou */ class ConfigurationPropertiesBeanRegistrarTests { @@ -122,6 +124,15 @@ void registerScopedBeanDefinitionWithProxyMode() { assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE); } + @Test + void registerBeanDefinitionWithCommonDefinitionAnnotations() { + String beanName = "beancp-" + PrimaryConfigurationProperties.class.getName(); + this.registrar.register(PrimaryConfigurationProperties.class); + BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName); + assertThat(beanDefinition).isNotNull(); + assertThat(beanDefinition.isPrimary()).isEqualTo(true); + } + private Consumer hasBindMethodAttribute(BindMethod bindMethod) { return (definition) -> { assertThat(definition.hasAttribute(BindMethod.class.getName())).isTrue(); @@ -146,6 +157,12 @@ static class ProxyScopedBeanConfigurationProperties { } + @ConfigurationProperties(prefix = "beancp") + @Primary + static class PrimaryConfigurationProperties { + + } + static class NoAnnotationConfigurationProperties { } From 9bcfc72307eb9cd6d1cf0e08eb55c97204e3e0b9 Mon Sep 17 00:00:00 2001 From: mushroom528 Date: Sat, 14 Sep 2024 15:55:03 +0900 Subject: [PATCH 213/271] Replace Configuration fully qualified name by constant See gh-42311 --- .../ConfigurationMetadataAnnotationProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 4c8b0fcb1bb4..b5241d9426fd 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -69,7 +69,7 @@ ConfigurationMetadataAnnotationProcessor.REST_CONTROLLER_ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.SERVLET_ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.WEB_ENDPOINT_ANNOTATION, - "org.springframework.context.annotation.Configuration" }) + ConfigurationMetadataAnnotationProcessor.CONFIGURATION_ANNOTATION }) public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor { static final String ADDITIONAL_METADATA_LOCATIONS_OPTION = "org.springframework.boot.configurationprocessor.additionalMetadataLocations"; @@ -104,6 +104,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String AUTO_CONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration"; + static final String CONFIGURATION_ANNOTATION = "org.springframework.context.annotation.Configuration"; + private static final Set SUPPORTED_OPTIONS = Collections .unmodifiableSet(Collections.singleton(ADDITIONAL_METADATA_LOCATIONS_OPTION)); From 242803d59b2323c24e57ee13f3bbf3fc917469e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 09:01:36 +0200 Subject: [PATCH 214/271] Polish "Replace Configuration fully qualified name by constant" See gh-42311 --- ...nfigurationMetadataAnnotationProcessor.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index b5241d9426fd..a6b084a8cc7f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -61,15 +61,15 @@ * @author Scott Frederick * @since 1.2.0 */ -@SupportedAnnotationTypes({ ConfigurationMetadataAnnotationProcessor.AUTO_CONFIGURATION_ANNOTATION, - ConfigurationMetadataAnnotationProcessor.CONFIGURATION_PROPERTIES_ANNOTATION, +@SupportedAnnotationTypes({ ConfigurationMetadataAnnotationProcessor.CONFIGURATION_PROPERTIES_ANNOTATION, + ConfigurationMetadataAnnotationProcessor.AUTO_CONFIGURATION_ANNOTATION, + ConfigurationMetadataAnnotationProcessor.CONFIGURATION_ANNOTATION, ConfigurationMetadataAnnotationProcessor.CONTROLLER_ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.JMX_ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.REST_CONTROLLER_ENDPOINT_ANNOTATION, ConfigurationMetadataAnnotationProcessor.SERVLET_ENDPOINT_ANNOTATION, - ConfigurationMetadataAnnotationProcessor.WEB_ENDPOINT_ANNOTATION, - ConfigurationMetadataAnnotationProcessor.CONFIGURATION_ANNOTATION }) + ConfigurationMetadataAnnotationProcessor.WEB_ENDPOINT_ANNOTATION }) public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor { static final String ADDITIONAL_METADATA_LOCATIONS_OPTION = "org.springframework.boot.configurationprocessor.additionalMetadataLocations"; @@ -86,6 +86,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String DEFAULT_VALUE_ANNOTATION = "org.springframework.boot.context.properties.bind.DefaultValue"; + static final String AUTO_CONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration"; + + static final String CONFIGURATION_ANNOTATION = "org.springframework.context.annotation.Configuration"; + static final String CONTROLLER_ENDPOINT_ANNOTATION = "org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint"; static final String ENDPOINT_ANNOTATION = "org.springframework.boot.actuate.endpoint.annotation.Endpoint"; @@ -102,10 +106,6 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String NAME_ANNOTATION = "org.springframework.boot.context.properties.bind.Name"; - static final String AUTO_CONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration"; - - static final String CONFIGURATION_ANNOTATION = "org.springframework.context.annotation.Configuration"; - private static final Set SUPPORTED_OPTIONS = Collections .unmodifiableSet(Collections.singleton(ADDITIONAL_METADATA_LOCATIONS_OPTION)); From 3f9f0490a63b19456a614a435c171995549e597d Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Mon, 9 Sep 2024 12:43:19 +0300 Subject: [PATCH 215/271] Use DataSource.unwrap to get routing data source This commit uses DataSource.isWrapperFor and DataSource.unwrap to detect if a DataSource is an AbstractRoutingDataSource. Previously, it relied on instanceof which does not account for cases where the datasource has been proxied. See gh-42313 --- ...rceHealthContributorAutoConfiguration.java | 35 +++- ...althContributorAutoConfigurationTests.java | 149 +++++++++++++++++- 2 files changed, 177 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java index 966296cd00f6..02bd94f88a50 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.jdbc; +import java.sql.SQLException; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -88,7 +89,7 @@ public HealthContributor dbHealthContributor(Map dataSources if (dataSourceHealthIndicatorProperties.isIgnoreRoutingDataSources()) { Map filteredDatasources = dataSources.entrySet() .stream() - .filter((e) -> !(e.getValue() instanceof AbstractRoutingDataSource)) + .filter((e) -> !isAbstractRoutingDataSource(e.getValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return createContributor(filteredDatasources); } @@ -104,8 +105,9 @@ private HealthContributor createContributor(Map beans) { } private HealthContributor createContributor(DataSource source) { - if (source instanceof AbstractRoutingDataSource routingDataSource) { - return new RoutingDataSourceHealthContributor(routingDataSource, this::createContributor); + if (isAbstractRoutingDataSource(source)) { + return new RoutingDataSourceHealthContributor(unwrapAbstractRoutingDataSource(source), + this::createContributor); } return new DataSourceHealthIndicator(source, getValidationQuery(source)); } @@ -115,6 +117,31 @@ private String getValidationQuery(DataSource source) { return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null; } + private static boolean isAbstractRoutingDataSource(DataSource dataSource) { + if (dataSource instanceof AbstractRoutingDataSource) { + return true; + } + try { + return dataSource.isWrapperFor(AbstractRoutingDataSource.class); + } + catch (SQLException ex) { + return false; + } + } + + private static AbstractRoutingDataSource unwrapAbstractRoutingDataSource(DataSource dataSource) { + if (dataSource instanceof AbstractRoutingDataSource routingDataSource) { + return routingDataSource; + } + try { + return dataSource.unwrap(AbstractRoutingDataSource.class); + } + catch (SQLException ex) { + throw new IllegalStateException( + "DataSource '%s' failed to unwrap '%s'".formatted(dataSource, AbstractRoutingDataSource.class), ex); + } + } + /** * {@link CompositeHealthContributor} used for {@link AbstractRoutingDataSource} beans * where the overall health is composed of a {@link DataSourceHealthIndicator} for diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java index 6945cb71bbbf..3badcb91fdf5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -16,13 +16,22 @@ package org.springframework.boot.actuate.autoconfigure.jdbc; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.ConnectionBuilder; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.ShardingKeyBuilder; import java.util.HashMap; import java.util.Map; +import java.util.logging.Logger; import javax.sql.DataSource; import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration.RoutingDataSourceHealthContributor; import org.springframework.boot.actuate.health.CompositeHealthContributor; @@ -87,6 +96,19 @@ void runWithRoutingAndEmbeddedDataSourceShouldIncludeRoutingDataSource() { }); } + @Test + void runWithProxyBeanPostProcessorRoutingAndEmbeddedDataSourceShouldIncludeRoutingDataSource() { + this.contextRunner + .withUserConfiguration(ProxyDataSourceBeanPostProcessor.class, EmbeddedDataSourceConfiguration.class, + RoutingDataSourceConfig.class) + .run((context) -> { + CompositeHealthContributor composite = context.getBean(CompositeHealthContributor.class); + assertThat(composite.getContributor("dataSource")).isInstanceOf(DataSourceHealthIndicator.class); + assertThat(composite.getContributor("routingDataSource")) + .isInstanceOf(RoutingDataSourceHealthContributor.class); + }); + } + @Test void runWithRoutingAndEmbeddedDataSourceShouldNotIncludeRoutingDataSourceWhenIgnored() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, RoutingDataSourceConfig.class) @@ -98,6 +120,19 @@ void runWithRoutingAndEmbeddedDataSourceShouldNotIncludeRoutingDataSourceWhenIgn }); } + @Test + void runWithProxyBeanPostProcessorAndRoutingAndEmbeddedDataSourceShouldNotIncludeRoutingDataSourceWhenIgnored() { + this.contextRunner + .withUserConfiguration(ProxyDataSourceBeanPostProcessor.class, EmbeddedDataSourceConfiguration.class, + RoutingDataSourceConfig.class) + .withPropertyValues("management.health.db.ignore-routing-datasources:true") + .run((context) -> { + assertThat(context).doesNotHaveBean(CompositeHealthContributor.class); + assertThat(context).hasSingleBean(DataSourceHealthIndicator.class); + assertThat(context).doesNotHaveBean(RoutingDataSourceHealthContributor.class); + }); + } + @Test void runWithOnlyRoutingDataSourceShouldIncludeRoutingDataSourceWithComposedIndicators() { this.contextRunner.withUserConfiguration(RoutingDataSourceConfig.class).run((context) -> { @@ -112,6 +147,23 @@ void runWithOnlyRoutingDataSourceShouldIncludeRoutingDataSourceWithComposedIndic }); } + @Test + void runWithProxyBeanPostProcessorAndRoutingDataSourceShouldIncludeRoutingDataSourceWithComposedIndicators() { + this.contextRunner.withUserConfiguration(ProxyDataSourceBeanPostProcessor.class, RoutingDataSourceConfig.class) + .run((context) -> { + assertThat(context).hasSingleBean(RoutingDataSourceHealthContributor.class); + RoutingDataSourceHealthContributor routingHealthContributor = context + .getBean(RoutingDataSourceHealthContributor.class); + assertThat(routingHealthContributor.getContributor("one")) + .isInstanceOf(DataSourceHealthIndicator.class); + assertThat(routingHealthContributor.getContributor("two")) + .isInstanceOf(DataSourceHealthIndicator.class); + assertThat(routingHealthContributor.iterator()).toIterable() + .extracting("name") + .containsExactlyInAnyOrder("one", "two"); + }); + } + @Test void runWithOnlyRoutingDataSourceShouldCrashWhenIgnored() { this.contextRunner.withUserConfiguration(RoutingDataSourceConfig.class) @@ -121,6 +173,15 @@ void runWithOnlyRoutingDataSourceShouldCrashWhenIgnored() { .hasRootCauseInstanceOf(IllegalArgumentException.class)); } + @Test + void runWithProxyBeanPostProcessorAndOnlyRoutingDataSourceShouldCrashWhenIgnored() { + this.contextRunner.withUserConfiguration(ProxyDataSourceBeanPostProcessor.class, RoutingDataSourceConfig.class) + .withPropertyValues("management.health.db.ignore-routing-datasources:true") + .run((context) -> assertThat(context).hasFailed() + .getFailure() + .hasRootCauseInstanceOf(IllegalArgumentException.class)); + } + @Test void runWithValidationQueryPropertyShouldUseCustomQuery() { this.contextRunner @@ -177,30 +238,112 @@ DataSource testDataSource() { static class RoutingDataSourceConfig { @Bean - AbstractRoutingDataSource routingDataSource() { + AbstractRoutingDataSource routingDataSource() throws SQLException { Map dataSources = new HashMap<>(); dataSources.put("one", mock(DataSource.class)); dataSources.put("two", mock(DataSource.class)); AbstractRoutingDataSource routingDataSource = mock(AbstractRoutingDataSource.class); + given(routingDataSource.isWrapperFor(AbstractRoutingDataSource.class)).willReturn(true); + given(routingDataSource.unwrap(AbstractRoutingDataSource.class)).willReturn(routingDataSource); given(routingDataSource.getResolvedDataSources()).willReturn(dataSources); return routingDataSource; } } + static class ProxyDataSourceBeanPostProcessor implements BeanPostProcessor { + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof DataSource dataSource) { + return new ProxyDataSource(dataSource); + } + return bean; + } + + } + @Configuration(proxyBeanMethods = false) static class NullKeyRoutingDataSourceConfig { @Bean - AbstractRoutingDataSource routingDataSource() { + AbstractRoutingDataSource routingDataSource() throws Exception { Map dataSources = new HashMap<>(); dataSources.put(null, mock(DataSource.class)); dataSources.put("one", mock(DataSource.class)); AbstractRoutingDataSource routingDataSource = mock(AbstractRoutingDataSource.class); + given(routingDataSource.isWrapperFor(AbstractRoutingDataSource.class)).willReturn(true); + given(routingDataSource.unwrap(AbstractRoutingDataSource.class)).willReturn(routingDataSource); given(routingDataSource.getResolvedDataSources()).willReturn(dataSources); return routingDataSource; } } + static class ProxyDataSource implements DataSource { + + private final DataSource dataSource; + + ProxyDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + this.dataSource.setLogWriter(out); + } + + @Override + public Connection getConnection() throws SQLException { + return this.dataSource.getConnection(); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return this.dataSource.getConnection(username, password); + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return this.dataSource.getLogWriter(); + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + this.dataSource.setLoginTimeout(seconds); + } + + @Override + public int getLoginTimeout() throws SQLException { + return this.dataSource.getLoginTimeout(); + } + + @Override + public ConnectionBuilder createConnectionBuilder() throws SQLException { + return this.dataSource.createConnectionBuilder(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return this.dataSource.getParentLogger(); + } + + @Override + public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { + return this.dataSource.createShardingKeyBuilder(); + } + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class iface) throws SQLException { + return iface.isInstance(this) ? (T) this : this.dataSource.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return (iface.isInstance(this) || this.dataSource.isWrapperFor(iface)); + } + + } + } From 78a140ae258a2d2e499a2c1965ad45b83295d067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 09:29:04 +0200 Subject: [PATCH 216/271] Polish "Use DataSource.unwrap to get routing data source" See gh-42313 --- ...rceHealthContributorAutoConfiguration.java | 14 ++- ...althContributorAutoConfigurationTests.java | 87 +++---------------- 2 files changed, 20 insertions(+), 81 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java index 02bd94f88a50..a94172671e92 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java @@ -89,7 +89,7 @@ public HealthContributor dbHealthContributor(Map dataSources if (dataSourceHealthIndicatorProperties.isIgnoreRoutingDataSources()) { Map filteredDatasources = dataSources.entrySet() .stream() - .filter((e) -> !isAbstractRoutingDataSource(e.getValue())) + .filter((e) -> !isRoutingDataSource(e.getValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return createContributor(filteredDatasources); } @@ -105,9 +105,8 @@ private HealthContributor createContributor(Map beans) { } private HealthContributor createContributor(DataSource source) { - if (isAbstractRoutingDataSource(source)) { - return new RoutingDataSourceHealthContributor(unwrapAbstractRoutingDataSource(source), - this::createContributor); + if (isRoutingDataSource(source)) { + return new RoutingDataSourceHealthContributor(extractRoutingDataSource(source), this::createContributor); } return new DataSourceHealthIndicator(source, getValidationQuery(source)); } @@ -117,7 +116,7 @@ private String getValidationQuery(DataSource source) { return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null; } - private static boolean isAbstractRoutingDataSource(DataSource dataSource) { + private static boolean isRoutingDataSource(DataSource dataSource) { if (dataSource instanceof AbstractRoutingDataSource) { return true; } @@ -129,7 +128,7 @@ private static boolean isAbstractRoutingDataSource(DataSource dataSource) { } } - private static AbstractRoutingDataSource unwrapAbstractRoutingDataSource(DataSource dataSource) { + private static AbstractRoutingDataSource extractRoutingDataSource(DataSource dataSource) { if (dataSource instanceof AbstractRoutingDataSource routingDataSource) { return routingDataSource; } @@ -137,8 +136,7 @@ private static AbstractRoutingDataSource unwrapAbstractRoutingDataSource(DataSou return dataSource.unwrap(AbstractRoutingDataSource.class); } catch (SQLException ex) { - throw new IllegalStateException( - "DataSource '%s' failed to unwrap '%s'".formatted(dataSource, AbstractRoutingDataSource.class), ex); + throw new IllegalStateException("Failed to unwrap AbstractRoutingDataSource from " + dataSource, ex); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java index 3badcb91fdf5..bcbf8dc752ba 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java @@ -16,15 +16,9 @@ package org.springframework.boot.actuate.autoconfigure.jdbc; -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.ConnectionBuilder; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.ShardingKeyBuilder; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; import javax.sql.DataSource; @@ -256,11 +250,24 @@ static class ProxyDataSourceBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof DataSource dataSource) { - return new ProxyDataSource(dataSource); + return proxyDataSource(dataSource); } return bean; } + private static DataSource proxyDataSource(DataSource dataSource) { + try { + DataSource mock = mock(DataSource.class); + given(mock.isWrapperFor(AbstractRoutingDataSource.class)) + .willReturn(dataSource instanceof AbstractRoutingDataSource); + given(mock.unwrap(AbstractRoutingDataSource.class)).willAnswer((invocation) -> dataSource); + return mock; + } + catch (SQLException ex) { + throw new IllegalStateException(ex); + } + } + } @Configuration(proxyBeanMethods = false) @@ -280,70 +287,4 @@ AbstractRoutingDataSource routingDataSource() throws Exception { } - static class ProxyDataSource implements DataSource { - - private final DataSource dataSource; - - ProxyDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } - - @Override - public void setLogWriter(PrintWriter out) throws SQLException { - this.dataSource.setLogWriter(out); - } - - @Override - public Connection getConnection() throws SQLException { - return this.dataSource.getConnection(); - } - - @Override - public Connection getConnection(String username, String password) throws SQLException { - return this.dataSource.getConnection(username, password); - } - - @Override - public PrintWriter getLogWriter() throws SQLException { - return this.dataSource.getLogWriter(); - } - - @Override - public void setLoginTimeout(int seconds) throws SQLException { - this.dataSource.setLoginTimeout(seconds); - } - - @Override - public int getLoginTimeout() throws SQLException { - return this.dataSource.getLoginTimeout(); - } - - @Override - public ConnectionBuilder createConnectionBuilder() throws SQLException { - return this.dataSource.createConnectionBuilder(); - } - - @Override - public Logger getParentLogger() throws SQLFeatureNotSupportedException { - return this.dataSource.getParentLogger(); - } - - @Override - public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { - return this.dataSource.createShardingKeyBuilder(); - } - - @Override - @SuppressWarnings("unchecked") - public T unwrap(Class iface) throws SQLException { - return iface.isInstance(this) ? (T) this : this.dataSource.unwrap(iface); - } - - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - return (iface.isInstance(this) || this.dataSource.isWrapperFor(iface)); - } - - } - } From 4877e4d1e3a65d1ce23c137b9d26a535cacb1908 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 16 Sep 2024 10:59:04 +0200 Subject: [PATCH 217/271] Allow the configuration of active profiles in SpringApplication.Augmented Closes gh-36660 --- .../boot/SpringApplication.java | 23 +++++++++++++--- .../boot/SpringApplicationTests.java | 26 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 005f8e81d3c0..e2b5e731ce62 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -1430,7 +1430,7 @@ public static int exit(ApplicationContext context, ExitCodeGenerator... exitCode */ public static SpringApplication.Augmented from(ThrowingConsumer main) { Assert.notNull(main, "Main must not be null"); - return new Augmented(main, Collections.emptySet()); + return new Augmented(main, Collections.emptySet(), Collections.emptySet()); } /** @@ -1492,9 +1492,12 @@ public static class Augmented { private final Set> sources; - Augmented(ThrowingConsumer main, Set> sources) { + private final Set additionalProfiles; + + Augmented(ThrowingConsumer main, Set> sources, Set additionalProfiles) { this.main = main; this.sources = Set.copyOf(sources); + this.additionalProfiles = additionalProfiles; } /** @@ -1506,7 +1509,20 @@ public static class Augmented { public Augmented with(Class... sources) { LinkedHashSet> merged = new LinkedHashSet<>(this.sources); merged.addAll(Arrays.asList(sources)); - return new Augmented(this.main, merged); + return new Augmented(this.main, merged, this.additionalProfiles); + } + + /** + * Return a new {@link SpringApplication.Augmented} instance with additional + * profiles that should be applied when the application runs. + * @param profiles the profiles that should be applied + * @return a new {@link SpringApplication.Augmented} instance + * @since 3.4.0 + */ + public Augmented withAdditionalProfiles(String... profiles) { + Set merged = new LinkedHashSet<>(this.additionalProfiles); + merged.addAll(Arrays.asList(profiles)); + return new Augmented(this.main, this.sources, merged); } /** @@ -1518,6 +1534,7 @@ public SpringApplication.Running run(String... args) { RunListener runListener = new RunListener(); SpringApplicationHook hook = new SingleUseSpringApplicationHook((springApplication) -> { springApplication.addPrimarySources(this.sources); + springApplication.setAdditionalProfiles(this.additionalProfiles.toArray(String[]::new)); return runListener; }); withHook(hook, () -> this.main.accept(args)); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 2f07827407ab..ccd17f15ae3d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -92,6 +92,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Profile; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SimpleApplicationEventMulticaster; @@ -1409,7 +1410,8 @@ public void contextLoaded(ConfigurableApplicationContext context) { then(listener).should(never()).onApplicationEvent(any(ApplicationFailedEvent.class)); } - @Test // gh-32555 + @Test + // gh-32555 void shouldUseAotInitializer() { SpringApplication application = new SpringApplication(ExampleAotProcessedMainClass.class); application.setWebApplicationType(WebApplicationType.NONE); @@ -1468,6 +1470,17 @@ void fromWithMultipleApplicationsOnlyAppliesAdditionalSourcesOnce() { assertThatNoException().isThrownBy(() -> this.context.getBean(SingleUseAdditionalConfig.class)); } + @Test + void fromAppliesProfiles() { + this.context = SpringApplication.from(ExampleFromMainMethod::main) + .with(ProfileConfig.class) + .withAdditionalProfiles("custom") + .run() + .getApplicationContext(); + assertThat(this.context).isNotNull(); + assertThat(this.context.getBeanProvider(Example.class).getIfAvailable()).isNotNull(); + } + @Test void shouldStartDaemonThreadIfKeepAliveIsEnabled() { SpringApplication application = new SpringApplication(ExampleConfig.class); @@ -2159,4 +2172,15 @@ static class SingleUseAdditionalConfig { } + @Configuration + static class ProfileConfig { + + @Bean + @Profile("custom") + Example example() { + return new Example(); + } + + } + } From b75c2b6529d65cb03a52d9f513b2046ddcdc4d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 14:48:13 +0200 Subject: [PATCH 218/271] Upgrade to Spring Retry 2.0.9 Closes gh-42325 --- 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 a242ca7813d6..f1717bb1ccac 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1662,7 +1662,7 @@ bom { ] } } - library("Spring Retry", "2.0.8") { + library("Spring Retry", "2.0.9") { considerSnapshots() group("org.springframework.retry") { modules = [ From c16c04abfe705b098efe227be0d8673918eaa982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 14:49:22 +0200 Subject: [PATCH 219/271] Upgrade to R2DBC MariaDB 1.2.2 Closes gh-42326 --- 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 a0753450fecc..83b6ceb1dea5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1615,7 +1615,7 @@ bom { ] } } - library("R2DBC MariaDB", "1.2.1") { + library("R2DBC MariaDB", "1.2.2") { group("org.mariadb") { modules = [ "r2dbc-mariadb" From f7cb18c60552bd7dd474594a01fb34308384b710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 14:49:27 +0200 Subject: [PATCH 220/271] Upgrade to Spring Retry 2.0.9 Closes gh-42327 --- 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 83b6ceb1dea5..8b7969c81baf 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2109,7 +2109,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-restdocs/releases/tag/v{version}") } } - library("Spring Retry", "2.0.8") { + library("Spring Retry", "2.0.9") { considerSnapshots() group("org.springframework.retry") { modules = [ From c5cfcfb2fd0006e1674bfadc69a6eb9c389b2748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 14:56:09 +0200 Subject: [PATCH 221/271] Upgrade to R2DBC MariaDB 1.2.2 Closes gh-42328 --- 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 e702463a1dc2..56729f01c66f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1606,7 +1606,7 @@ bom { ] } } - library("R2DBC MariaDB", "1.2.1") { + library("R2DBC MariaDB", "1.2.2") { group("org.mariadb") { modules = [ "r2dbc-mariadb" From 4dbc636c51e09c337841d70f6782e103506b5650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 16 Sep 2024 14:56:13 +0200 Subject: [PATCH 222/271] Upgrade to Spring Retry 2.0.9 Closes gh-42329 --- 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 56729f01c66f..1437ac7a6b63 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2100,7 +2100,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-restdocs/releases/tag/v{version}") } } - library("Spring Retry", "2.0.8") { + library("Spring Retry", "2.0.9") { considerSnapshots() group("org.springframework.retry") { modules = [ From e009581a41ca490b8c9d9d8c17bca96e4143dcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:13:02 +0200 Subject: [PATCH 223/271] Upgrade to Spring Kafka 3.1.9 Closes gh-42127 --- 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 f1717bb1ccac..76cbc6b7e16d 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1626,7 +1626,7 @@ bom { ] } } - library("Spring Kafka", "3.1.9-SNAPSHOT") { + library("Spring Kafka", "3.1.9") { considerSnapshots() group("org.springframework.kafka") { modules = [ From de5f7ad31875ff15a0dd64a9dc8e18e1d8607d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:13:03 +0200 Subject: [PATCH 224/271] Upgrade to Spring Pulsar 1.0.10 Closes gh-42128 --- 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 76cbc6b7e16d..8fcbf5c55be7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1646,7 +1646,7 @@ bom { ] } } - library("Spring Pulsar", "1.0.10-SNAPSHOT") { + library("Spring Pulsar", "1.0.10") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From fc5b80ac64d1f164050d8e70057cdbd955a71d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:13:10 +0200 Subject: [PATCH 225/271] Upgrade to Spring Kafka 3.2.4 Closes gh-42135 --- 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 8b7969c81baf..5b5ff3c7b1f6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2037,7 +2037,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.2.4-SNAPSHOT") { + library("Spring Kafka", "3.2.4") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 36b43f681cda5fba04d2d443339cf8da52c9a51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:13:10 +0200 Subject: [PATCH 226/271] Upgrade to Spring Pulsar 1.1.4 Closes gh-42136 --- 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 5b5ff3c7b1f6..9b20597a28ac 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2075,7 +2075,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.1.4-SNAPSHOT") { + library("Spring Pulsar", "1.1.4") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From f844c64b12f68d1c9807f541a463fcd2180f9ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:14:39 +0200 Subject: [PATCH 227/271] Upgrade to Spring AMQP 3.2.0-M3 Closes gh-42140 --- 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 1437ac7a6b63..f9ce1343bae0 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1894,7 +1894,7 @@ bom { ] } } - library("Spring AMQP", "3.2.0-SNAPSHOT") { + library("Spring AMQP", "3.2.0-M3") { considerSnapshots() group("org.springframework.amqp") { imports = [ From 12b25e52800e7c4a5dc459447db79353013f8fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:14:39 +0200 Subject: [PATCH 228/271] Upgrade to Spring Kafka 3.3.0-M3 Closes gh-42146 --- 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 f9ce1343bae0..8fdcfcd2d610 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2028,7 +2028,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-integration/releases/tag/v{version}") } } - library("Spring Kafka", "3.3.0-SNAPSHOT") { + library("Spring Kafka", "3.3.0-M3") { considerSnapshots() group("org.springframework.kafka") { modules = [ From 01b4ae526ad9e4771983ed2d6a660533d03a703e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:14:40 +0200 Subject: [PATCH 229/271] Upgrade to Spring Pulsar 1.2.0-M2 Closes gh-42147 --- 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 8fdcfcd2d610..197884f8fad5 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2066,7 +2066,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-ldap/releases/tag/{version}") } } - library("Spring Pulsar", "1.2.0-SNAPSHOT") { + library("Spring Pulsar", "1.2.0-M2") { considerSnapshots() group("org.springframework.pulsar") { imports = [ From f057559b1776cbaf6114ef2cf1895863f53e453e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:14:40 +0200 Subject: [PATCH 230/271] Upgrade to Spring Security 6.4.0-M4 Closes gh-42148 --- 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 197884f8fad5..18dfe85e6ac7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2112,7 +2112,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-retry/releases/tag/v{version}") } } - library("Spring Security", "6.4.0-SNAPSHOT") { + library("Spring Security", "6.4.0-M4") { considerSnapshots() group("org.springframework.security") { imports = [ From 6f535adb47c6c4532c2e1a490dae66c6ff97b621 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:48:57 +0000 Subject: [PATCH 231/271] Bump gradle/actions from 4.0.1 to 4.1.0 Bumps [gradle/actions](https://github.com/gradle/actions) from 4.0.1 to 4.1.0. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/16bf8bc8fe830fa669c3c9f914d3eb147c629707...d156388eb19639ec20ade50009f3d199ce1e2808) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] See gh-42331 --- .github/workflows/build-pull-request.yml | 4 ++-- .github/workflows/verify.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index ae132039b12b..04cc8240b133 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -23,9 +23,9 @@ jobs: - name: Check Out uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 + uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - name: Set Up Gradle - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - name: Build env: CI: 'true' diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index f46a9fa33364..01cd02e9737d 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -42,7 +42,7 @@ jobs: - name: Set Up Homebrew uses: Homebrew/actions/setup-homebrew@7657c9512f50e1c35b640971116425935bab3eea - name: Set Up Gradle - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 with: cache-read-only: false - name: Configure Gradle Properties From c6c0923dceb3f2d18d3e7ac1d95e277402445bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 17 Sep 2024 09:19:08 +0200 Subject: [PATCH 232/271] Polish "Bump gradle/actions from 4.0.1 to 4.1.0" See gh-42331 --- .github/actions/prepare-gradle-build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 7ba072319eaf..62e740840e77 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -32,7 +32,7 @@ runs: ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} ${{ inputs.java-toolchain == 'true' && '17' || '' }} - name: Set Up Gradle - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4.0.1 + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 with: cache-read-only: false develocity-access-key: ${{ inputs.develocity-access-key }} From 9294c003b45c7c45807596dc3e6473623d8db66d Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 17 Sep 2024 14:17:52 +0200 Subject: [PATCH 233/271] Remove setting BP_NATIVE_IMAGE Closes gh-32884 --- .../spring-boot-starter-parent/build.gradle | 7 ------- .../gradle-plugin/pages/packaging-oci-image.adoc | 2 +- .../antora/modules/gradle-plugin/pages/reacting.adoc | 2 +- .../boot/gradle/plugin/NativeImagePluginAction.java | 8 -------- .../NativeImagePluginActionIntegrationTests.java | 7 ------- ...tBuildImageIsConfiguredToBuildANativeImage.gradle | 12 ------------ .../image/paketo/PaketoBuilderTests-nativeApp.gradle | 11 ++++------- 7 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests-bootBuildImageIsConfiguredToBuildANativeImage.gradle diff --git a/spring-boot-project/spring-boot-starters/spring-boot-starter-parent/build.gradle b/spring-boot-project/spring-boot-starters/spring-boot-starter-parent/build.gradle index a9a020db4ebb..20b90926610b 100644 --- a/spring-boot-project/spring-boot-starters/spring-boot-starter-parent/build.gradle +++ b/spring-boot-project/spring-boot-starters/spring-boot-starter-parent/build.gradle @@ -265,13 +265,6 @@ publishing.publications.withType(MavenPublication) { plugin { delegate.groupId('org.springframework.boot') delegate.artifactId('spring-boot-maven-plugin') - configuration { - image { - env { - delegate.BP_NATIVE_IMAGE("true") - } - } - } executions { execution { delegate.id('process-aot') diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc index e6523405b147..91f7a079246b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc @@ -152,7 +152,7 @@ Acceptable values are `ALWAYS`, `NEVER`, and `IF_NOT_PRESENT`. | `environment` | | Environment variables that should be passed to the builder. -| Empty or `['BP_NATIVE_IMAGE': 'true']` when {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin] is applied. +| Empty. | `buildpacks` | diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc index 4094068ab6bd..f0dc8039ce74 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc @@ -89,6 +89,6 @@ When the {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin . Configures the GraalVM extension to disable Toolchain detection. . Configures each GraalVM native binary to require GraalVM 22.3 or later. . Configures the `bootJar` task to include the reachability metadata produced by the `collectReachabilityMetadata` task in its jar. -. Configures the `bootBuildImage` task to set `BP_NATIVE_IMAGE` to `true` in its environment. +. Configures the `bootJar` task to add the `Spring-Boot-Native-Processed: true` manifest entry. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/NativeImagePluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/NativeImagePluginAction.java index d7555c4255b2..f46f62493af2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/NativeImagePluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/NativeImagePluginAction.java @@ -32,7 +32,6 @@ import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.SourceSetContainer; -import org.springframework.boot.gradle.tasks.bundling.BootBuildImage; import org.springframework.boot.gradle.tasks.bundling.BootJar; /** @@ -59,7 +58,6 @@ public void execute(Project project) { configureMainNativeBinaryClasspath(project, sourceSets, graalVmExtension); configureTestNativeBinaryClasspath(sourceSets, graalVmExtension); copyReachabilityMetadataToBootJar(project); - configureBootBuildImageToProduceANativeImage(project); configureJarManifestNativeAttribute(project); }); } @@ -102,12 +100,6 @@ private void copyReachabilityMetadataToBootJar(Project project) { .configure((bootJar) -> bootJar.from(project.getTasks().named("collectReachabilityMetadata"))); } - private void configureBootBuildImageToProduceANativeImage(Project project) { - project.getTasks() - .named(SpringBootPlugin.BOOT_BUILD_IMAGE_TASK_NAME, BootBuildImage.class) - .configure((bootBuildImage) -> bootBuildImage.getEnvironment().put("BP_NATIVE_IMAGE", "true")); - } - private void configureJarManifestNativeAttribute(Project project) { project.getTasks() .named(SpringBootPlugin.BOOT_JAR_TASK_NAME, BootJar.class) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests.java index d3f471771bfe..cf9d67951c27 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests.java @@ -88,13 +88,6 @@ void reachabilityMetadataConfigurationFilesFromFileRepositoryAreCopiedToJar() th "META-INF/native-image/org.jline/jline/3.21.0/resource-config.json"); } - @TestTemplate - void bootBuildImageIsConfiguredToBuildANativeImage() { - writeDummySpringApplicationAotProcessorMainClass(); - BuildResult result = this.gradleBuild.build("bootBuildImageConfiguration"); - assertThat(result.getOutput()).contains("BP_NATIVE_IMAGE = true"); - } - @TestTemplate void developmentOnlyDependenciesDoNotAppearInNativeImageClasspath() { writeDummySpringApplicationAotProcessorMainClass(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests-bootBuildImageIsConfiguredToBuildANativeImage.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests-bootBuildImageIsConfiguredToBuildANativeImage.gradle deleted file mode 100644 index 969f40bd1eb1..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests-bootBuildImageIsConfiguredToBuildANativeImage.gradle +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id 'org.springframework.boot' version '{version}' - id 'java' -} - -apply plugin: 'org.graalvm.buildtools.native' - -task('bootBuildImageConfiguration') { - doFirst { - println "BP_NATIVE_IMAGE = ${tasks.getByName('bootBuildImage').environment.get()['BP_NATIVE_IMAGE']}" - } -} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-nativeApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-nativeApp.gradle index ffa36bf5978c..c82f134b9913 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-nativeApp.gradle +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-nativeApp.gradle @@ -28,13 +28,10 @@ bootJar { manifest { attributes( 'Implementation-Version': '1.0.0', - 'Implementation-Title': "Paketo Test" + 'Implementation-Title': 'Paketo Test', + // This shouldn't be necessary. + // See https://github.com/spring-projects/spring-boot/issues/42338 + 'Spring-Boot-Native-Processed': 'true' ) } } - -bootBuildImage { - environment = ['BP_NATIVE_IMAGE': 'true', - //see https://github.com/paketo-buildpacks/native-image/issues/321 - 'BP_NATIVE_IMAGE_BUILD_ARGUMENTS': '-H:-AddAllFileSystemProviders'] -} From cad5dd2b852500df3374a5e0521599978c2910b1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 17 Sep 2024 12:25:54 -0700 Subject: [PATCH 234/271] Use variables for `runs-on` settings See gh-42333 --- .github/workflows/build-and-deploy-snapshot.yml | 2 +- .github/workflows/build-pull-request.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 12 ++++++------ .github/workflows/run-system-tests.yml | 2 +- .github/workflows/verify.yml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-and-deploy-snapshot.yml b/.github/workflows/build-and-deploy-snapshot.yml index f2d5ebeb5a3a..c429887ad691 100644 --- a/.github/workflows/build-and-deploy-snapshot.yml +++ b/.github/workflows/build-and-deploy-snapshot.yml @@ -8,7 +8,7 @@ concurrency: jobs: build-and-deploy-snapshot: name: Build and Deploy Snapshot - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} if: ${{ github.repository == 'spring-projects/spring-boot' }} steps: - name: Check Out Code diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 04cc8240b133..96667f8684b7 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -7,7 +7,7 @@ permissions: jobs: build: name: Build Pull Request - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} if: ${{ github.repository == 'spring-projects/spring-boot' }} steps: - name: Free Disk Space diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 447e2be2e9db..c3a4e83ec526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: - - id: ubuntu-latest + - id: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name: Linux - id: windows-latest name: Windows diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ecd0147c108c..c36467b68692 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ concurrency: jobs: build-and-stage-release: name: Build and Stage Release - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_MEDIUIM || 'ubuntu-latest' }} if: ${{ github.repository == 'spring-projects/spring-boot' }} steps: - name: Check Out Code @@ -51,7 +51,7 @@ jobs: needs: - build-and-stage-release - verify - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Check Out Code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -68,7 +68,7 @@ jobs: needs: - build-and-stage-release - sync-to-maven-central - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Set up JFrog CLI uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 @@ -81,7 +81,7 @@ jobs: needs: - build-and-stage-release - sync-to-maven-central - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Check Out Code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -97,7 +97,7 @@ jobs: needs: - build-and-stage-release - sync-to-maven-central - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Check Out Code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -115,7 +115,7 @@ jobs: - promote-release - publish-gradle-plugin - publish-to-sdkman - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Check Out Code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 diff --git a/.github/workflows/run-system-tests.yml b/.github/workflows/run-system-tests.yml index 927437ee3feb..43641898b1be 100644 --- a/.github/workflows/run-system-tests.yml +++ b/.github/workflows/run-system-tests.yml @@ -8,7 +8,7 @@ concurrency: jobs: run-system-tests: name: 'Java ${{ matrix.java.version}}' - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} if: ${{ github.repository == 'spring-projects/spring-boot' }} strategy: matrix: diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 01cd02e9737d..ca8b9fb09ef9 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -21,7 +21,7 @@ on: jobs: verify: name: Verify - runs-on: ubuntu-latest + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} steps: - name: Check Out Release Verification Tests uses: actions/checkout@v4 From 9726ddfe42388f301071c943fa51449abd2d3778 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 17 Sep 2024 12:10:14 -0700 Subject: [PATCH 235/271] Add 'spring.build-type' property See gh-42333 --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 271babef7578..86ec44ff312c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ version=3.2.10-SNAPSHOT +spring.build-type=oss org.gradle.caching=true org.gradle.parallel=true From 5324c646e18062abf3ceb7fbd2006b00d6779e31 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 17 Sep 2024 12:55:22 -0700 Subject: [PATCH 236/271] Update merge script to support commercial repository See gh-42333 --- git/hooks/forward-merge | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git/hooks/forward-merge b/git/hooks/forward-merge index 14872f472746..9666d350d46b 100755 --- a/git/hooks/forward-merge +++ b/git/hooks/forward-merge @@ -112,12 +112,16 @@ message_file=ARGV[0] forward_merges = find_forward_merges(message_file) exit 0 unless forward_merges -$log.debug "Loading config from ~/.spring-boot/forward_merge.yml" +$log.debug "Loading config from ~/.spring-boot/forward-merge.yml" config = YAML.load_file(File.join(Dir.home, '.spring-boot', 'forward-merge.yml')) username = config['github']['credentials']['username'] password = config['github']['credentials']['password'] dry_run = config['dry_run'] -repository = 'spring-projects/spring-boot' + +gradleProperties = IO.read('gradle.properties') +springBuildType = gradleProperties.match(/^spring\.build-type\s?=\s?(.*)$/) +repository = (springBuildType && springBuildType[1] != 'oss') ? "spring-projects/spring-boot-#{springBuildType[1]}" : "spring-projects/spring-boot"; +$log.debug "Targeting repository #{repository}" forward_merges.each do |forward_merge| existing_issue = get_issue(username, password, repository, forward_merge.issue) From c37f786914342e8781548f4cb0d7cfdd46b94894 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:19:23 +0100 Subject: [PATCH 237/271] Upgrade to Spring Integration 6.2.9 Closes gh-42126 --- 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 8fcbf5c55be7..450066fc7490 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1618,7 +1618,7 @@ bom { ] } } - library("Spring Integration", "6.2.9-SNAPSHOT") { + library("Spring Integration", "6.2.9") { considerSnapshots() group("org.springframework.integration") { imports = [ From ef5bc91b35f6dce0101fd6ef6ca5df26a7137520 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:19:28 +0100 Subject: [PATCH 238/271] Upgrade to Tomcat 10.1.30 Closes gh-42344 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 86ec44ff312c..dbc63ad70574 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,6 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.9.28 springFrameworkVersion=6.1.13 springFramework60xVersion=6.0.23 -tomcatVersion=10.1.29 +tomcatVersion=10.1.30 kotlin.stdlib.default.dependency=false From 3706dced231a64e65ce77eba4765c33c95a18ee0 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:48:02 +0100 Subject: [PATCH 239/271] Upgrade to Spring Integration 6.3.4 Closes gh-42134 --- 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 9b20597a28ac..7315c938feea 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2020,7 +2020,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "6.3.4-SNAPSHOT") { + library("Spring Integration", "6.3.4") { considerSnapshots() group("org.springframework.integration") { imports = [ From 5ff86ea49f688497df293b786641967104a564e7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:48:08 +0100 Subject: [PATCH 240/271] Upgrade to Tomcat 10.1.30 Closes gh-42346 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d8f7d00b4734..5dd8b0065247 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.1.13 springFramework60xVersion=6.0.23 -tomcatVersion=10.1.29 +tomcatVersion=10.1.30 snakeYamlVersion=2.2 kotlin.stdlib.default.dependency=false From 59fb90ea0c6d0ad78c85d950cd89c842461e689e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:57:35 +0100 Subject: [PATCH 241/271] Upgrade to Hibernate 6.6.1.Final Closes gh-42349 --- 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 18dfe85e6ac7..1700303d2300 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -500,7 +500,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "6.6.0.Final") { + library("Hibernate", "6.6.1.Final") { group("org.hibernate.orm") { modules = [ "hibernate-agroal", From 6fc881e5653a658c6953ea40f9eebf6a50d6ee62 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:57:40 +0100 Subject: [PATCH 242/271] Upgrade to Rabbit AMQP Client 5.22.0 Closes gh-42350 --- 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 1700303d2300..0eb2c6f18eb2 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1667,7 +1667,7 @@ bom { javadoc("https://r2dbc.io/spec/{version}/api") } } - library("Rabbit AMQP Client", "5.21.0") { + library("Rabbit AMQP Client", "5.22.0") { group("com.rabbitmq") { modules = [ "amqp-client" From 32917323f98cf147dd7e529d3312ff2979eeeaaf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:57:40 +0100 Subject: [PATCH 243/271] Upgrade to Spring Authorization Server 1.4.0-M2 Closes gh-42141 --- 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 0eb2c6f18eb2..dc95eb512c42 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1911,7 +1911,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-amqp/releases/tag/v{version}") } } - library("Spring Authorization Server", "1.4.0-SNAPSHOT") { + library("Spring Authorization Server", "1.4.0-M2") { considerSnapshots() group("org.springframework.security") { modules = [ From fe90ef0d1331a65032504f25883ebcf2cde9948e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:57:41 +0100 Subject: [PATCH 244/271] Upgrade to Spring Integration 6.4.0-M3 Closes gh-42145 --- 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 dc95eb512c42..5b7355816799 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2011,7 +2011,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-hateoas/releases/tag/{version}") } } - library("Spring Integration", "6.4.0-SNAPSHOT") { + library("Spring Integration", "6.4.0-M3") { considerSnapshots() group("org.springframework.integration") { imports = [ From 7e096aac605c68c3fa34c6cf30f40714dff15d00 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 08:57:42 +0100 Subject: [PATCH 245/271] Upgrade to Tomcat 10.1.30 Closes gh-42347 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a9e039d27ad2..c53b8c7a0a93 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.3 springFrameworkVersion=6.2.0-RC1 springFramework60xVersion=6.0.23 -tomcatVersion=10.1.29 +tomcatVersion=10.1.30 snakeYamlVersion=2.3 kotlin.stdlib.default.dependency=false From 6d8a421114f0180267384b3ccdcb9a9c2e9aab2b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 09:22:39 +0100 Subject: [PATCH 246/271] Update CI to use Java 23 GA Closes gh-42345 --- .github/actions/build/action.yml | 5 +++++ .github/actions/prepare-gradle-build/action.yml | 8 ++++++-- .github/workflows/ci.yml | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index c1ff664f6932..39adc65be1c7 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -13,6 +13,10 @@ inputs: required: false default: 'false' description: 'Whether a Java toolchain should be used' + java-distribution: + required: false + default: 'liberica' + description: 'The distribution of Java to use' publish: required: false default: 'false' @@ -37,6 +41,7 @@ runs: java-version: ${{ inputs.java-version }} java-early-access: ${{ inputs.java-early-access }} java-toolchain: ${{ inputs.java-toolchain }} + java-distribution: ${{ inputs.java-distribution }} - name: Build id: build if: ${{ inputs.publish == 'false' }} diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 62e740840e77..4c24776ddbc2 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -8,11 +8,15 @@ inputs: java-early-access: required: false default: 'false' - description: 'Whether the Java version is in early access' + description: 'Whether the Java version is in early access. When true, forces java-distribution to temurin' java-toolchain: required: false default: 'false' description: 'Whether a Java toolchain should be used' + java-distribution: + required: false + default: 'liberica' + description: 'The distribution of Java to use' develocity-access-key: required: false description: 'The access key for authentication with ge.spring.io' @@ -27,7 +31,7 @@ runs: - name: Set Up Java uses: actions/setup-java@v4 with: - distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || 'liberica' }} + distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || inputs.java-distribution }} java-version: | ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} ${{ inputs.java-toolchain == 'true' && '17' || '' }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3a4e83ec526..0d28650b6343 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - version: 22 toolchain: true - version: 23 - early-access: true + distribution: oracle toolchain: true exclude: - os: @@ -49,6 +49,7 @@ jobs: java-version: ${{ matrix.java.version }} java-early-access: ${{ matrix.java.early-access || 'false' }} java-toolchain: ${{ matrix.java.toolchain }} + java-distribution: ${{ matrix.java.distribution }} develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }} - name: Send Notification uses: ./.github/actions/send-notification From dc72e3853b795716eb7a7c75d2ccfcbee5a8dee8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 09:29:33 +0100 Subject: [PATCH 247/271] Try to ensure that distribution always has a non-empty value See gh-42345 --- .github/actions/prepare-gradle-build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml index 4c24776ddbc2..5404276dd71e 100644 --- a/.github/actions/prepare-gradle-build/action.yml +++ b/.github/actions/prepare-gradle-build/action.yml @@ -31,7 +31,7 @@ runs: - name: Set Up Java uses: actions/setup-java@v4 with: - distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || inputs.java-distribution }} + distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }} java-version: | ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} ${{ inputs.java-toolchain == 'true' && '17' || '' }} From 7eaf6d1a96615e4a1582355f07e6140b39967ae2 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 09:49:28 +0100 Subject: [PATCH 248/271] Prohibit upgrades to Kotlin Coroutines 1.9 Closes gh-42348 --- spring-boot-project/spring-boot-dependencies/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 5b7355816799..cc54c4d2edf4 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1079,6 +1079,10 @@ bom { } } library("Kotlin Coroutines", "1.8.1") { + prohibit { + versionRange "[1.9.0,)" + because "it requires Kotlin 2" + } group("org.jetbrains.kotlinx") { imports = [ "kotlinx-coroutines-bom" From a0d1c10d8aa35c37d77850b8b7ef1b7d9f399002 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Wed, 18 Sep 2024 00:30:20 +0900 Subject: [PATCH 249/271] Polish See gh-42340 --- .../autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java | 1 + .../boot/autoconfigure/graphql/GraphQlProperties.java | 2 +- .../autoconfigure/liquibase/LiquibaseAutoConfiguration.java | 2 +- .../springframework/boot/devtools/restart/MainMethodTests.java | 2 +- .../src/docs/antora/modules/reference/pages/using/devtools.adoc | 2 +- ...eKafkaContainerConnectionDetailsFactoryIntegrationTests.java | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java index fca8478ced74..b9fe808709a3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/r2dbc/R2dbcObservationAutoConfiguration.java @@ -50,6 +50,7 @@ public class R2dbcObservationAutoConfiguration { /** * {@code @Order} value of the observation customizer. + * @since 3.4.0 */ public static final int R2DBC_PROXY_OBSERVATION_CUSTOMIZER_ORDER = 0; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java index 62e81f68a50c..b39692f83f07 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java @@ -220,7 +220,7 @@ public static class Websocket { /** * Maximum idle period before a server keep-alive ping is sent to client. */ - private Duration keepAlive = null; + private Duration keepAlive; public String getPath() { return this.path; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index e82a99b479de..66db73109712 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -182,7 +182,7 @@ static class CustomizerConfiguration { @Bean @ConditionalOnBean(Customizer.class) - SpringLiquibaseCustomizer customizerSpringLiquibaseCustomizer(Customizer customizer) { + SpringLiquibaseCustomizer springLiquibaseCustomizer(Customizer customizer) { return (springLiquibase) -> springLiquibase.setCustomizer(customizer); } diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/MainMethodTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/MainMethodTests.java index b8fce3b1a317..758c3f95c7ef 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/MainMethodTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/MainMethodTests.java @@ -66,7 +66,7 @@ void nestedMainMethod() throws Exception { } @Test // gh-39733 - void vaiJarLauncher() throws Exception { + void viaJarLauncher() throws Exception { FakeJarLauncher.action = (args) -> Valid.main(args); MainMethod method = new TestThread(FakeJarLauncher::main).test(); Method expectedMain = Valid.class.getMethod("main", String[].class); 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 80524e5b2add..db9540af56b6 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 @@ -243,7 +243,7 @@ If this causes issues, you can diagnose the problem by using the `spring.devtool By default, any open project in your IDE is loaded with the "`restart`" classloader, and any regular `.jar` file is loaded with the "`base`" classloader. The same is true if you use `mvn spring-boot:run` or `gradle bootRun`: the project containing your `@SpringBootApplication` is loaded with the "`restart`" classloader, and everything else with the "`base`" classloader. The classpath is printed on the console when you start the app, which can help to identify any problematic entries. -Classes used reflectively, especially annotations, can be loaded into the parent (fixed) classloader on startup before the application classes which uses them, and this might lead to them not being detected by Spring in the application. +Classes used reflectively, especially annotations, can be loaded into the parent (fixed) classloader on startup before the application classes which use them, and this might lead to them not being detected by Spring in the application. You can instruct Spring Boot to load parts of your project with a different classloader by creating a `META-INF/spring-devtools.properties` file. The `spring-devtools.properties` file can contain properties prefixed with `restart.exclude` and `restart.include`. diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java index 516b854d7d8b..d759a62a1c37 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java @@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link ConfluentKafkaContainerConnectionDetailsFactory}. + * Tests for {@link ApacheKafkaContainerConnectionDetailsFactory}. * * @author Moritz Halbritter * @author Andy Wilkinson From c1bd5bdc8bd6feab884e690ea36fd897718f3ac6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 10:30:09 +0100 Subject: [PATCH 250/271] Upgrade to Spring Batch 5.2.0-M1 Closes gh-42142 --- 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 cc54c4d2edf4..bba48e6fad01 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1932,7 +1932,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-authorization-server/releases/tag/{version}") } } - library("Spring Batch", "5.2.0-SNAPSHOT") { + library("Spring Batch", "5.2.0-M1") { considerSnapshots() group("org.springframework.batch") { imports = [ From 713afae013f16617ae5a342883f3b019b3c6b3e4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 11:53:11 +0100 Subject: [PATCH 251/271] Accommodate next execution time being unknown A task's next execution time is unknown if, for example, it's currently running. When it's unknown the nextExecution.time will be missing from the json describing the task. This commit updates the documentation test to accommodate this possibility. Closes gh-42351 --- .../ScheduledTasksEndpointDocumentationTests.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java index 59f31a211258..5e8e7c833d1c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java @@ -63,8 +63,7 @@ void scheduledTasks() { fieldWithPath("cron.[].expression").description("Cron expression."), fieldWithPath("fixedDelay").description("Fixed delay tasks, if any."), targetFieldWithPrefix("fixedDelay.[]."), initialDelayWithPrefix("fixedDelay.[]."), - nextExecutionWithPrefix("fixedDelay.[].") - .description("Time of the next scheduled execution."), + nextExecutionWithPrefix("fixedDelay.[]."), fieldWithPath("fixedDelay.[].interval") .description("Interval, in milliseconds, between the end of the last" + " execution and the start of the next."), @@ -72,9 +71,7 @@ void scheduledTasks() { targetFieldWithPrefix("fixedRate.[]."), fieldWithPath("fixedRate.[].interval") .description("Interval, in milliseconds, between the start of each execution."), - initialDelayWithPrefix("fixedRate.[]."), - nextExecutionWithPrefix("fixedRate.[].") - .description("Time of the next scheduled execution."), + initialDelayWithPrefix("fixedRate.[]."), nextExecutionWithPrefix("fixedRate.[]."), fieldWithPath("custom").description("Tasks with custom triggers, if any."), targetFieldWithPrefix("custom.[]."), fieldWithPath("custom.[].trigger").description("Trigger for the task.")) @@ -93,7 +90,10 @@ private FieldDescriptor initialDelayWithPrefix(String prefix) { } private FieldDescriptor nextExecutionWithPrefix(String prefix) { - return fieldWithPath(prefix + "nextExecution.time").description("Time of the next scheduled execution."); + return fieldWithPath(prefix + "nextExecution.time") + .description("Time of the next scheduled execution, if known.") + .type(JsonFieldType.STRING) + .optional(); } private FieldDescriptor[] lastExecution() { From 1240c59482b36cba7686d6ae0bc1142a7e77803c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 17 Sep 2024 20:06:23 +0100 Subject: [PATCH 252/271] Only configure plugin classpath where it's needed When spring-boot-gradle-plugin is using GradleRunner, it needs to be configured with a custom plugin classpath to account for the fact that our Gradle plugin is on the classpath of the system classloader but some of the other plugins would only be available on a Gradle-created classloader. This imbalance cause class loading problems as code in spring-boot-gradle-plugin can't see types at runtime that are only available on the Gradle-created classloader. To overcome this, we need to configure the GradleRunner with a custom plugin classpath that contains both spring-boot-gradle-plugin and all of the other plugins that are used in its various integration tests. Previously, this was done in GradleBuild that's used by both spring-boot-gradle-plugin and spring-boot-image-tests. This caused a problem as spring-boot-image-tests does not have the above-described problem and trying to correct it did not work leaving tests that use spring-boot-gradle-plugin unable to see other plugins such that the native image plugin. This commit reworks the customization of the plugin classpath so that it's only done in spring-boot-gradle-plugin's integration tests. Closes gh-42338 --- .../spring-boot-gradle-plugin/build.gradle | 11 ++ .../junit/GradleCompatibilityExtension.java | 3 +- .../gradle/junit/GradleMultiDslExtension.java | 5 +- .../KotlinPluginActionIntegrationTests.java | 3 +- .../SpringBootPluginIntegrationTests.java | 3 +- .../testkit/PluginClasspathGradleBuild.java | 109 ++++++++++++++++++ .../build.gradle | 10 +- .../gradle/testkit/GradleBuild.java | 66 +---------- .../spring-boot-image-tests/build.gradle | 1 - 9 files changed, 133 insertions(+), 78 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index e491019a755d..5e8bb3ff5853 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -104,10 +104,21 @@ dependencies { testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) + testImplementation("com.fasterxml.jackson.core:jackson-databind") + testImplementation("com.fasterxml.jackson.module:jackson-module-parameter-names") testImplementation("com.tngtech.archunit:archunit-junit5:0.22.0") + testImplementation("net.java.dev.jna:jna-platform") + testImplementation("org.apache.commons:commons-compress") + testImplementation("org.apache.httpcomponents.client5:httpclient5") testImplementation("org.assertj:assertj-core") + testImplementation("org.graalvm.buildtools:native-gradle-plugin") + testImplementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion") + testImplementation("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinVersion") + testImplementation("org.jetbrains.kotlin:kotlin-daemon-client:$kotlinVersion") testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.mockito:mockito-core") + testImplementation("org.tomlj:tomlj:1.0.0") } gradlePlugin { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java index 5f339a7f8c3d..701b0a489527 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import org.junit.platform.commons.util.AnnotationUtils; +import org.springframework.boot.gradle.testkit.PluginClasspathGradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; import org.springframework.boot.testsupport.gradle.testkit.GradleVersions; @@ -96,7 +97,7 @@ public String getDisplayName(int invocationIndex) { @Override public List getAdditionalExtensions() { - GradleBuild gradleBuild = new GradleBuild().gradleVersion(this.gradleVersion); + GradleBuild gradleBuild = new PluginClasspathGradleBuild().gradleVersion(this.gradleVersion); if (this.configurationCache) { gradleBuild.configurationCache(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java index 8171ff8e5432..41d82960c904 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.extension.TestTemplateInvocationContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.springframework.boot.gradle.testkit.PluginClasspathGradleBuild; import org.springframework.boot.testsupport.gradle.testkit.Dsl; import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; @@ -60,8 +61,8 @@ private static final class DslTestTemplateInvocationContext implements TestTempl @Override public List getAdditionalExtensions() { - GradleBuild gradleBuild = new GradleBuild(this.dsl); - gradleBuild.gradleVersion(GradleVersions.minimumCompatible()); + GradleBuild gradleBuild = new PluginClasspathGradleBuild(this.dsl) + .gradleVersion(GradleVersions.minimumCompatible()); return Arrays.asList(new GradleBuildFieldSetter(gradleBuild), new GradleBuildExtension()); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java index ab32302702fc..b3978b47f705 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.gradle.testkit.PluginClasspathGradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; @@ -42,7 +43,7 @@ @ExtendWith(GradleBuildExtension.class) class KotlinPluginActionIntegrationTests { - GradleBuild gradleBuild = new GradleBuild(); + GradleBuild gradleBuild = new PluginClasspathGradleBuild(); @Test void noKotlinVersionPropertyWithoutKotlinPlugin() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java index 8128321c3872..6fe265491757 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.gradle.testkit.PluginClasspathGradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; @@ -35,7 +36,7 @@ @ExtendWith(GradleBuildExtension.class) class SpringBootPluginIntegrationTests { - final GradleBuild gradleBuild = new GradleBuild(); + final GradleBuild gradleBuild = new PluginClasspathGradleBuild(); @Test @DisabledForJreRange(min = JRE.JAVA_20) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java new file mode 100644 index 000000000000..f5a9e6029eb5 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java @@ -0,0 +1,109 @@ +/* + * Copyright 2012-2024 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.testkit; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.core.Versioned; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; +import com.sun.jna.Platform; +import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; +import org.antlr.v4.runtime.Lexer; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; +import org.apache.hc.core5.http.HttpRequest; +import org.apache.hc.core5.http2.HttpVersionPolicy; +import org.gradle.testkit.runner.GradleRunner; +import org.jetbrains.kotlin.gradle.model.KotlinProject; +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin; +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin; +import org.jetbrains.kotlin.project.model.LanguageSettings; +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion; +import org.tomlj.Toml; + +import org.springframework.asm.ClassVisitor; +import org.springframework.boot.buildpack.platform.build.BuildRequest; +import org.springframework.boot.loader.tools.LaunchScript; +import org.springframework.boot.testsupport.gradle.testkit.Dsl; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; + +/** + * Custom {@link GradleBuild} that configures the + * {@link GradleRunner#withPluginClasspath(Iterable) plugin classpath}. + * + * @author Andy Wilkinson + * @author Scott Frederick + */ +public class PluginClasspathGradleBuild extends GradleBuild { + + public PluginClasspathGradleBuild() { + super(); + } + + public PluginClasspathGradleBuild(Dsl dsl) { + super(dsl); + } + + @Override + public GradleRunner prepareRunner(String... arguments) throws IOException { + return super.prepareRunner(arguments).withPluginClasspath(pluginClasspath()); + } + + private List pluginClasspath() { + return Arrays.asList(new File("bin/main"), new File("build/classes/java/main"), + new File("build/resources/main"), new File(pathOfJarContaining(LaunchScript.class)), + new File(pathOfJarContaining(ClassVisitor.class)), + new File(pathOfJarContaining(DependencyManagementPlugin.class)), + new File(pathOfJarContaining("org.jetbrains.kotlin.cli.common.PropertiesKt")), + new File(pathOfJarContaining(KotlinPlatformJvmPlugin.class)), + new File(pathOfJarContaining(KotlinProject.class)), + new File(pathOfJarContaining(KotlinToolingVersion.class)), + new File(pathOfJarContaining("org.jetbrains.kotlin.daemon.client.KotlinCompilerClient")), + new File(pathOfJarContaining(KotlinCompilerPluginSupportPlugin.class)), + new File(pathOfJarContaining(LanguageSettings.class)), + new File(pathOfJarContaining(ArchiveEntry.class)), new File(pathOfJarContaining(BuildRequest.class)), + new File(pathOfJarContaining(HttpClientConnectionManager.class)), + new File(pathOfJarContaining(HttpRequest.class)), + new File(pathOfJarContaining(HttpVersionPolicy.class)), new File(pathOfJarContaining(Module.class)), + new File(pathOfJarContaining(Versioned.class)), + new File(pathOfJarContaining(ParameterNamesModule.class)), + new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)), + new File(pathOfJarContaining(Toml.class)), new File(pathOfJarContaining(Lexer.class)), + new File(pathOfJarContaining("org.graalvm.buildtools.gradle.NativeImagePlugin")), + new File(pathOfJarContaining("org.graalvm.reachability.GraalVMReachabilityMetadataRepository")), + new File(pathOfJarContaining("org.graalvm.buildtools.utils.SharedConstants"))); + } + + private String pathOfJarContaining(String className) { + try { + return pathOfJarContaining(Class.forName(className)); + } + catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + + private String pathOfJarContaining(Class type) { + return type.getProtectionDomain().getCodeSource().getLocation().getPath(); + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle index c71fca96a3b8..48a3ac40f96d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle @@ -9,15 +9,7 @@ dependencies { compileOnly("org.junit.jupiter:junit-jupiter") implementation(gradleTestKit()) - implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform")) - implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-tools")) implementation("io.spring.gradle:dependency-management-plugin") - implementation("org.graalvm.buildtools:native-gradle-plugin") - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion") - implementation("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinVersion") - implementation("org.jetbrains.kotlin:kotlin-daemon-client:$kotlinVersion") - implementation("org.apache.commons:commons-compress:$commonsCompressVersion") - implementation("org.assertj:assertj-core") + implementation("org.springframework:spring-core") } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java index 20ebe5a95c67..332deba5f826 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java @@ -32,31 +32,11 @@ import java.util.Properties; import java.util.jar.JarFile; -import com.fasterxml.jackson.annotation.JsonView; -import com.fasterxml.jackson.core.Versioned; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; -import com.sun.jna.Platform; -import io.spring.gradle.dependencymanagement.DependencyManagementPlugin; import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension; -import org.antlr.v4.runtime.Lexer; -import org.apache.commons.compress.archivers.ArchiveEntry; -import org.apache.hc.client5.http.io.HttpClientConnectionManager; -import org.apache.hc.core5.http.HttpRequest; -import org.apache.hc.core5.http2.HttpVersionPolicy; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.gradle.util.GradleVersion; -import org.jetbrains.kotlin.gradle.model.KotlinProject; -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin; -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin; -import org.jetbrains.kotlin.project.model.LanguageSettings; -import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion; -import org.tomlj.Toml; - -import org.springframework.asm.ClassVisitor; -import org.springframework.boot.buildpack.platform.build.BuildRequest; -import org.springframework.boot.loader.tools.LaunchScript; + import org.springframework.util.FileCopyUtils; import org.springframework.util.FileSystemUtils; @@ -95,7 +75,7 @@ public GradleBuild() { this(Dsl.GROOVY); } - public GradleBuild(Dsl dsl) { + protected GradleBuild(Dsl dsl) { this.dsl = dsl; } @@ -112,44 +92,6 @@ void after() { FileSystemUtils.deleteRecursively(this.projectDir); } - private List pluginClasspath() { - return Arrays.asList(new File("bin/main"), new File("build/classes/java/main"), - new File("build/resources/main"), new File(pathOfJarContaining(LaunchScript.class)), - new File(pathOfJarContaining(ClassVisitor.class)), - new File(pathOfJarContaining(DependencyManagementPlugin.class)), - new File(pathOfJarContaining("org.jetbrains.kotlin.cli.common.PropertiesKt")), - new File(pathOfJarContaining(KotlinPlatformJvmPlugin.class)), - new File(pathOfJarContaining(KotlinProject.class)), - new File(pathOfJarContaining(KotlinToolingVersion.class)), - new File(pathOfJarContaining("org.jetbrains.kotlin.daemon.client.KotlinCompilerClient")), - new File(pathOfJarContaining(KotlinCompilerPluginSupportPlugin.class)), - new File(pathOfJarContaining(LanguageSettings.class)), - new File(pathOfJarContaining(ArchiveEntry.class)), new File(pathOfJarContaining(BuildRequest.class)), - new File(pathOfJarContaining(HttpClientConnectionManager.class)), - new File(pathOfJarContaining(HttpRequest.class)), - new File(pathOfJarContaining(HttpVersionPolicy.class)), new File(pathOfJarContaining(Module.class)), - new File(pathOfJarContaining(Versioned.class)), - new File(pathOfJarContaining(ParameterNamesModule.class)), - new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)), - new File(pathOfJarContaining(Toml.class)), new File(pathOfJarContaining(Lexer.class)), - new File(pathOfJarContaining("org.graalvm.buildtools.gradle.NativeImagePlugin")), - new File(pathOfJarContaining("org.graalvm.reachability.GraalVMReachabilityMetadataRepository")), - new File(pathOfJarContaining("org.graalvm.buildtools.utils.SharedConstants"))); - } - - private String pathOfJarContaining(String className) { - try { - return pathOfJarContaining(Class.forName(className)); - } - catch (ClassNotFoundException ex) { - throw new IllegalArgumentException(ex); - } - } - - private String pathOfJarContaining(Class type) { - return type.getProtectionDomain().getCodeSource().getLocation().getPath(); - } - public GradleBuild script(String script) { this.script = script.endsWith(this.dsl.getExtension()) ? script : script + this.dsl.getExtension(); return this; @@ -230,9 +172,7 @@ public GradleRunner prepareRunner(String... arguments) throws IOException { if (repository.exists()) { FileSystemUtils.copyRecursively(repository, new File(this.projectDir, "repository")); } - GradleRunner gradleRunner = GradleRunner.create() - .withProjectDir(this.projectDir) - .withPluginClasspath(pluginClasspath()); + GradleRunner gradleRunner = GradleRunner.create().withProjectDir(this.projectDir); if (!this.configurationCache) { // See https://github.com/gradle/gradle/issues/14125 gradleRunner.withDebug(true); diff --git a/spring-boot-system-tests/spring-boot-image-tests/build.gradle b/spring-boot-system-tests/spring-boot-image-tests/build.gradle index 40cd4ee60165..8e11ad60d003 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/build.gradle +++ b/spring-boot-system-tests/spring-boot-image-tests/build.gradle @@ -52,7 +52,6 @@ dependencies { } systemTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) - systemTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin")) systemTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support")) systemTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform")) systemTestImplementation(gradleTestKit()) From f9379f45765d6218d17a85d6e46dec69cd46a2a9 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 18 Mar 2024 13:57:53 -0700 Subject: [PATCH 253/271] Apply nohttp check per-project rather than at root Switch nohttp checks to a convention that is applied per-project rather than at the root. This should help to reduce memory consumption. Closes gh-42332 --- build.gradle | 21 -------- buildSrc/build.gradle | 1 + .../boot/build/ConventionsPlugin.java | 3 +- .../boot/build/NoHttpConventions.java | 54 +++++++++++++++++++ 4 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 buildSrc/src/main/java/org/springframework/boot/build/NoHttpConventions.java diff --git a/build.gradle b/build.gradle index 0795f569d49e..4f490c8e7c19 100644 --- a/build.gradle +++ b/build.gradle @@ -1,29 +1,12 @@ plugins { id "base" id "org.jetbrains.kotlin.jvm" apply false // https://youtrack.jetbrains.com/issue/KT-30276 - id "io.spring.nohttp" version "0.0.11" } description = "Spring Boot Build" defaultTasks 'build' -nohttp { - allowlistFile = project.file("src/nohttp/allowlist.lines") - source.exclude "**/bin/**" - source.exclude "**/build/**" - source.exclude "**/out/**" - source.exclude "**/target/**" - source.exclude "**/.settings/**" - source.exclude "**/.classpath" - source.exclude "**/.project" - source.exclude "spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/export.tar" -} - -check { - dependsOn checkstyleNohttp -} - allprojects { group "org.springframework.boot" @@ -41,7 +24,3 @@ allprojects { resolutionStrategy.cacheChangingModulesFor 0, "minutes" } } - -tasks.named("checkstyleNohttp").configure { - maxHeapSize = "1536m" -} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index f2382ffc1bc1..d8b44f640c68 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -53,6 +53,7 @@ dependencies { implementation("org.springframework:spring-context") implementation("org.springframework:spring-core") implementation("org.springframework:spring-web") + implementation("io.spring.nohttp:nohttp-gradle:0.0.11") testImplementation("org.assertj:assertj-core:${versions.assertj}") testImplementation("org.hamcrest:hamcrest:${versions.hamcrest}") diff --git a/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java index 8a50535a4365..03c5ab14819c 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -43,6 +43,7 @@ public class ConventionsPlugin implements Plugin { @Override public void apply(Project project) { + new NoHttpConventions().apply(project); new JavaConventions().apply(project); new MavenPublishingConventions().apply(project); new AsciidoctorConventions().apply(project); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/NoHttpConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/NoHttpConventions.java new file mode 100644 index 000000000000..7b4847ff2aec --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/NoHttpConventions.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2024 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.build; + +import io.spring.nohttp.gradle.NoHttpCheckstylePlugin; +import io.spring.nohttp.gradle.NoHttpExtension; +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileTree; +import org.gradle.api.plugins.quality.Checkstyle; + +/** + * Conventions that are applied to enforce that no HTTP urls are used. + * + * @author Phillip Webb + */ +public class NoHttpConventions { + + void apply(Project project) { + project.getPluginManager().apply(NoHttpCheckstylePlugin.class); + configureNoHttpExtension(project, project.getExtensions().getByType(NoHttpExtension.class)); + project.getTasks() + .named(NoHttpCheckstylePlugin.CHECKSTYLE_NOHTTP_TASK_NAME, Checkstyle.class) + .configure((task) -> task.getConfigDirectory().set(project.getRootProject().file("src/nohttp"))); + } + + private void configureNoHttpExtension(Project project, NoHttpExtension extension) { + extension.setAllowlistFile(project.getRootProject().file("src/nohttp/allowlist.lines")); + ConfigurableFileTree source = extension.getSource(); + source.exclude("bin/**"); + source.exclude("build/**"); + source.exclude("out/**"); + source.exclude("target/**"); + source.exclude(".settings/**"); + source.exclude(".classpath"); + source.exclude(".project"); + source.exclude(".gradle"); + source.exclude("**/docker/export.tar"); + } + +} From 47134974fc4e267d2168557783739b2cee060cea Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Sep 2024 19:09:54 +0100 Subject: [PATCH 254/271] Revert "Start building against Spring Session 3.4.0 snapshots" This reverts commit 0ce417061257fee46f6c149263d5aa14b362bcea. See gh-42149 --- 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 bba48e6fad01..acc563e882e9 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2133,7 +2133,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-security/releases/tag/{version}") } } - library("Spring Session", "3.4.0-SNAPSHOT") { + library("Spring Session", "3.4.0-M2") { considerSnapshots() prohibit { startsWith(["Apple-", "Bean-", "Corn-", "Dragonfruit-"]) From d7c6589b5e76b8012491d7e7f86dc22d5f7dccb5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 18 Sep 2024 18:11:44 -0700 Subject: [PATCH 255/271] Make `PulsarContainerFactoryCustomizers` package private See gh-42182 --- .../pulsar/PulsarContainerFactoryCustomizers.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java index 82bd14889883..4109086a9760 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarContainerFactoryCustomizers.java @@ -29,13 +29,12 @@ * for a given {@link PulsarConsumerFactory}. * * @author Chris Bono - * @since 3.4.0 */ -public class PulsarContainerFactoryCustomizers { +class PulsarContainerFactoryCustomizers { private final List> customizers; - public PulsarContainerFactoryCustomizers(List> customizers) { + PulsarContainerFactoryCustomizers(List> customizers) { this.customizers = (customizers != null) ? new ArrayList<>(customizers) : Collections.emptyList(); } @@ -48,7 +47,7 @@ public PulsarContainerFactoryCustomizers(List> T customize(T containerFactory) { + > T customize(T containerFactory) { LambdaSafe.callbacks(PulsarContainerFactoryCustomizer.class, this.customizers, containerFactory) .withLogger(PulsarContainerFactoryCustomizers.class) .invoke((customizer) -> customizer.customize(containerFactory)); From ae8315e90aec2e314e32f48c9e697bb11641462f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 07:39:35 +0200 Subject: [PATCH 256/271] Use Liberica for Java 23 CI Closes gh-42354 --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d28650b6343..19b437bf72ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,6 @@ jobs: - version: 22 toolchain: true - version: 23 - distribution: oracle toolchain: true exclude: - os: From c2adc3b6cb508dbff5e62b3a581e08a40cba66b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 07:48:38 +0200 Subject: [PATCH 257/271] Upgrade to Zipkin Reporter 3.4.2 Closes gh-42364 --- 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 7315c938feea..efe1d24c49fe 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -103,7 +103,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Zipkin Reporter", "3.4.1") { + library("Zipkin Reporter", "3.4.2") { group("io.zipkin.reporter2") { imports = [ "zipkin-reporter-bom" From 1b3b3aaef4ed85a9fe688a9b7b5e9b89edd82118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 07:48:42 +0200 Subject: [PATCH 258/271] Upgrade to Hibernate 6.5.3.Final Closes gh-42365 --- 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 efe1d24c49fe..f175f0d60f7a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -499,7 +499,7 @@ bom { releaseNotes("https://github.com/hazelcast/hazelcast/releases/tag/v{version}") } } - library("Hibernate", "6.5.2.Final") { + library("Hibernate", "6.5.3.Final") { group("org.hibernate.orm") { modules = [ "hibernate-agroal", From 97dd01e28a8d6ad54328d0ba2b39fa8591280822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 07:48:46 +0200 Subject: [PATCH 259/271] Upgrade to SendGrid 4.10.3 Closes gh-42366 --- 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 f175f0d60f7a..d347e64f5d02 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1868,7 +1868,7 @@ bom { releaseNotes("https://github.com/SeleniumHQ/htmlunit-driver/releases/tag/htmlunit-driver-{version}") } } - library("SendGrid", "4.10.2") { + library("SendGrid", "4.10.3") { group("com.sendgrid") { modules = [ "sendgrid-java" From ced82a854c98d604d49c94f52edfd4e584538541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 08:15:36 +0200 Subject: [PATCH 260/271] Upgrade to Zipkin Reporter 3.4.2 Closes gh-42367 --- 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 acc563e882e9..81f67bb9c5db 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -103,7 +103,7 @@ bom { .formatted(version.major(), version.minor()) } } } - library("Zipkin Reporter", "3.4.1") { + library("Zipkin Reporter", "3.4.2") { group("io.zipkin.reporter2") { imports = [ "zipkin-reporter-bom" From 8ac3528b891112746797a0fad6798587377299bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 08:15:40 +0200 Subject: [PATCH 261/271] Upgrade to SendGrid 4.10.3 Closes gh-42368 --- 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 81f67bb9c5db..a95593d66785 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1863,7 +1863,7 @@ bom { releaseNotes("https://github.com/SeleniumHQ/htmlunit-driver/releases/tag/htmlunit-driver-{version}") } } - library("SendGrid", "4.10.2") { + library("SendGrid", "4.10.3") { group("com.sendgrid") { modules = [ "sendgrid-java" From f5b6514befd9ce22eab88183c0c77e7f8b541829 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 18 Sep 2024 13:02:23 -0700 Subject: [PATCH 262/271] Allow additional context interfaces to be defined for testing Update `AssertableApplicationContext` and `ApplicationContextRunner` implementations to support additional `ApplicationContext` interfaces. Closes gh-42369 --- .../ApplicationContextAssertProvider.java | 38 +++++++++++++++++-- .../assertj/AssertableApplicationContext.java | 18 ++++++++- ...sertableReactiveWebApplicationContext.java | 20 +++++++++- .../AssertableWebApplicationContext.java | 18 ++++++++- .../AbstractApplicationContextRunner.java | 35 ++++++++++++++--- .../runner/ApplicationContextRunner.java | 20 ++++++++-- .../ReactiveWebApplicationContextRunner.java | 21 ++++++++-- .../runner/WebApplicationContextRunner.java | 20 ++++++++-- .../assertj/AdditionalContextInterface.java | 28 ++++++++++++++ .../AssertableApplicationContextTests.java | 13 ++++++- ...bleReactiveWebApplicationContextTests.java | 13 ++++++- .../AssertableWebApplicationContextTests.java | 13 ++++++- ...AbstractApplicationContextRunnerTests.java | 10 ++++- .../runner/AdditionalContextInterface.java | 28 ++++++++++++++ .../runner/ApplicationContextRunnerTests.java | 14 ++++++- ...ctiveWebApplicationContextRunnerTests.java | 14 ++++++- .../WebApplicationContextRunnerTests.java | 14 ++++++- .../extension/MyExtensionConfiguration.java | 2 +- 18 files changed, 310 insertions(+), 29 deletions(-) create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AdditionalContextInterface.java create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AdditionalContextInterface.java diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssertProvider.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssertProvider.java index f7fdf2122394..8542c5aa675c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssertProvider.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssertProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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,12 +18,14 @@ import java.io.Closeable; import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.function.Supplier; import org.assertj.core.api.AssertProvider; import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * An {@link ApplicationContext} that additionally supports AssertJ style assertions. Can @@ -101,16 +103,46 @@ public interface ApplicationContextAssertProvider * {@link ApplicationContext} or throw an exception if the context fails to start. * @return a {@link ApplicationContextAssertProvider} instance */ - @SuppressWarnings("unchecked") static , C extends ApplicationContext> T get(Class type, Class contextType, Supplier contextSupplier) { + return get(type, contextType, contextSupplier, new Class[0]); + } + + /** + * Factory method to create a new {@link ApplicationContextAssertProvider} instance. + * @param the assert provider type + * @param the context type + * @param type the type of {@link ApplicationContextAssertProvider} required (must be + * an interface) + * @param contextType the type of {@link ApplicationContext} being managed (must be an + * interface) + * @param contextSupplier a supplier that will either return a fully configured + * {@link ApplicationContext} or throw an exception if the context fails to start. + * @param additionalContextInterfaces and additional context interfaces to add to the + * proxy + * @return a {@link ApplicationContextAssertProvider} instance + * @since 3.4.0 + */ + @SuppressWarnings("unchecked") + static , C extends ApplicationContext> T get(Class type, + Class contextType, Supplier contextSupplier, + Class... additionalContextInterfaces) { Assert.notNull(type, "Type must not be null"); Assert.isTrue(type.isInterface(), "Type must be an interface"); Assert.notNull(contextType, "ContextType must not be null"); Assert.isTrue(contextType.isInterface(), "ContextType must be an interface"); - Class[] interfaces = { type, contextType }; + Class[] interfaces = merge(new Class[] { type, contextType }, additionalContextInterfaces); return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new AssertProviderApplicationContextInvocationHandler(contextType, contextSupplier)); } + private static Class[] merge(Class[] classes, Class[] additional) { + if (ObjectUtils.isEmpty(additional)) { + return classes; + } + Class[] result = Arrays.copyOf(classes, classes.length + additional.length); + System.arraycopy(additional, 0, result, classes.length, additional.length); + return result; + } + } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableApplicationContext.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableApplicationContext.java index 9c2a4782f2b1..7cc7fa40b23c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableApplicationContext.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -49,4 +49,20 @@ static AssertableApplicationContext get(Supplier contextSupplier, + Class... additionalContextInterfaces) { + return ApplicationContextAssertProvider.get(AssertableApplicationContext.class, + ConfigurableApplicationContext.class, contextSupplier, additionalContextInterfaces); + } + } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContext.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContext.java index 5af5a9fdd987..c18ec13eb6dc 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContext.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -51,4 +51,22 @@ static AssertableReactiveWebApplicationContext get( ConfigurableReactiveWebApplicationContext.class, contextSupplier); } + /** + * Factory method to create a new {@link AssertableReactiveWebApplicationContext} + * instance. + * @param contextSupplier a supplier that will either return a fully configured + * {@link ConfigurableReactiveWebApplicationContext} or throw an exception if the + * context fails to start. + * @param additionalContextInterfaces and additional context interfaces to add to the + * proxy + * @return a {@link AssertableReactiveWebApplicationContext} instance + * @since 3.4.0 + */ + static AssertableReactiveWebApplicationContext get( + Supplier contextSupplier, + Class... additionalContextInterfaces) { + return ApplicationContextAssertProvider.get(AssertableReactiveWebApplicationContext.class, + ConfigurableReactiveWebApplicationContext.class, contextSupplier, additionalContextInterfaces); + } + } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContext.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContext.java index 96a7d03dd1b2..314b43503bd6 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContext.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -49,4 +49,20 @@ static AssertableWebApplicationContext get(Supplier contextSupplier, + Class... additionalContextInterfaces) { + return ApplicationContextAssertProvider.get(AssertableWebApplicationContext.class, + ConfigurableWebApplicationContext.class, contextSupplier, additionalContextInterfaces); + } + } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java index 635222f8c749..6a7bd6672752 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -106,6 +106,8 @@ */ public abstract class AbstractApplicationContextRunner, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider> { + private static final Class[] NO_ADDITIONAL_CONTEXT_INTERFACES = {}; + private final RunnerConfiguration runnerConfiguration; private final Function, SELF> instanceFactory; @@ -115,13 +117,29 @@ public abstract class AbstractApplicationContextRunner contextFactory, Function, SELF> instanceFactory) { - Assert.notNull(contextFactory, "ContextFactory must not be null"); - Assert.notNull(contextFactory, "RunnerConfiguration must not be null"); - this.runnerConfiguration = new RunnerConfiguration<>(contextFactory); + this(instanceFactory, contextFactory, NO_ADDITIONAL_CONTEXT_INTERFACES); + } + + /** + * Create a new {@link AbstractApplicationContextRunner} instance. + * @param instanceFactory the factory used to create new instance of the runner + * @param contextFactory the factory used to create the actual context + * @param additionalContextInterfaces any additional application context interfaces to + * be added to the application context proxy + * @since 3.4.0 + */ + protected AbstractApplicationContextRunner(Function, SELF> instanceFactory, + Supplier contextFactory, Class... additionalContextInterfaces) { + Assert.notNull(instanceFactory, "'instanceFactory' must not be null"); + Assert.notNull(contextFactory, "'contextFactory' must not be null"); this.instanceFactory = instanceFactory; + this.runnerConfiguration = new RunnerConfiguration<>(contextFactory, additionalContextInterfaces); } /** @@ -386,7 +404,8 @@ private A createAssertableContext(boolean refresh) { ResolvableType resolvableType = ResolvableType.forClass(AbstractApplicationContextRunner.class, getClass()); Class assertType = (Class) resolvableType.resolveGeneric(1); Class contextType = (Class) resolvableType.resolveGeneric(2); - return ApplicationContextAssertProvider.get(assertType, contextType, () -> createAndLoadContext(refresh)); + return ApplicationContextAssertProvider.get(assertType, contextType, () -> createAndLoadContext(refresh), + this.runnerConfiguration.additionalContextInterfaces); } private C createAndLoadContext(boolean refresh) { @@ -472,6 +491,8 @@ protected static final class RunnerConfiguration contextFactory; + private final Class[] additionalContextInterfaces; + private boolean allowBeanDefinitionOverriding = false; private boolean allowCircularReferences = false; @@ -490,12 +511,14 @@ protected static final class RunnerConfiguration configurations = Collections.emptyList(); - private RunnerConfiguration(Supplier contextFactory) { + private RunnerConfiguration(Supplier contextFactory, Class[] additionalContextInterfaces) { this.contextFactory = contextFactory; + this.additionalContextInterfaces = additionalContextInterfaces; } private RunnerConfiguration(RunnerConfiguration source) { this.contextFactory = source.contextFactory; + this.additionalContextInterfaces = source.additionalContextInterfaces; this.allowBeanDefinitionOverriding = source.allowBeanDefinitionOverriding; this.allowCircularReferences = source.allowCircularReferences; this.initializers = source.initializers; diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java index 83de2a500d7c..b7d87ed42a30 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -47,10 +47,24 @@ public ApplicationContextRunner() { /** * Create a new {@link ApplicationContextRunner} instance using the specified * {@code contextFactory} as the underlying source. - * @param contextFactory a supplier that returns a new instance on each call + * @param contextFactory a supplier that returns a new instance on each call be added + * to the application context proxy */ public ApplicationContextRunner(Supplier contextFactory) { - super(contextFactory, ApplicationContextRunner::new); + super(ApplicationContextRunner::new, contextFactory); + } + + /** + * Create a new {@link ApplicationContextRunner} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + * @param additionalContextInterfaces any additional application context interfaces to + * be added to the application context proxy + * @since 3.4.0 + */ + public ApplicationContextRunner(Supplier contextFactory, + Class... additionalContextInterfaces) { + super(ApplicationContextRunner::new, contextFactory, additionalContextInterfaces); } private ApplicationContextRunner(RunnerConfiguration runnerConfiguration) { diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java index 77c99ecc5b99..6274b01bf20f 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -47,10 +47,25 @@ public ReactiveWebApplicationContextRunner() { /** * Create a new {@link ApplicationContextRunner} instance using the specified * {@code contextFactory} as the underlying source. - * @param contextFactory a supplier that returns a new instance on each call + * @param contextFactory a supplier that returns a new instance on each call be added + * to the application context proxy + * @since 3.4.0 */ public ReactiveWebApplicationContextRunner(Supplier contextFactory) { - super(contextFactory, ReactiveWebApplicationContextRunner::new); + super(ReactiveWebApplicationContextRunner::new, contextFactory); + } + + /** + * Create a new {@link ApplicationContextRunner} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + * @param additionalContextInterfaces any additional application context interfaces to + * be added to the application context proxy + * @since 3.4.0 + */ + public ReactiveWebApplicationContextRunner(Supplier contextFactory, + Class... additionalContextInterfaces) { + super(ReactiveWebApplicationContextRunner::new, contextFactory, additionalContextInterfaces); } private ReactiveWebApplicationContextRunner( diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java index fd186a747ac7..5528bdbb818d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -51,10 +51,24 @@ public WebApplicationContextRunner() { /** * Create a new {@link WebApplicationContextRunner} instance using the specified * {@code contextFactory} as the underlying source. - * @param contextFactory a supplier that returns a new instance on each call + * @param contextFactory a supplier that returns a new instance on each call be added + * to the application context proxy */ public WebApplicationContextRunner(Supplier contextFactory) { - super(contextFactory, WebApplicationContextRunner::new); + super(WebApplicationContextRunner::new, contextFactory); + } + + /** + * Create a new {@link WebApplicationContextRunner} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + * @param additionalContextInterfaces any additional application context interfaces to + * be added to the application context proxy + * @since 3.4.0 + */ + public WebApplicationContextRunner(Supplier contextFactory, + Class... additionalContextInterfaces) { + super(WebApplicationContextRunner::new, contextFactory, additionalContextInterfaces); } private WebApplicationContextRunner(RunnerConfiguration configuration) { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AdditionalContextInterface.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AdditionalContextInterface.java new file mode 100644 index 000000000000..0144ffc6164e --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AdditionalContextInterface.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2024 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.context.assertj; + +import org.springframework.context.ApplicationContext; + +/** + * Tests extra interface that can be applied to an {@link ApplicationContext} + * + * @author Phillip Webb + */ +interface AdditionalContextInterface { + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableApplicationContextTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableApplicationContextTests.java index 91189ce2a99c..3ff56807dd49 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableApplicationContextTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; /** * Tests for {@link AssertableApplicationContext}. @@ -38,4 +39,14 @@ void getShouldReturnProxy() { assertThat(context).isInstanceOf(ConfigurableApplicationContext.class); } + @Test + void getWhenHasAdditionalInterfaceShouldReturnProxy() { + AssertableApplicationContext context = AssertableApplicationContext.get( + () -> mock(ConfigurableApplicationContext.class, + withSettings().extraInterfaces(AdditionalContextInterface.class)), + AdditionalContextInterface.class); + assertThat(context).isInstanceOf(ConfigurableApplicationContext.class) + .isInstanceOf(AdditionalContextInterface.class); + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContextTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContextTests.java index b84b776bbd35..0947f9b80eaf 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContextTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; /** * Tests for {@link AssertableReactiveWebApplicationContext}. @@ -38,4 +39,14 @@ void getShouldReturnProxy() { assertThat(context).isInstanceOf(ConfigurableReactiveWebApplicationContext.class); } + @Test + void getWhenHasAdditionalInterfaceShouldReturnProxy() { + AssertableReactiveWebApplicationContext context = AssertableReactiveWebApplicationContext.get( + () -> mock(ConfigurableReactiveWebApplicationContext.class, + withSettings().extraInterfaces(AdditionalContextInterface.class)), + AdditionalContextInterface.class); + assertThat(context).isInstanceOf(ConfigurableReactiveWebApplicationContext.class) + .isInstanceOf(AdditionalContextInterface.class); + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContextTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContextTests.java index 53873f9c47ad..1a2268dbc0a8 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContextTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; /** * Tests for {@link AssertableWebApplicationContext}. @@ -38,4 +39,14 @@ void getShouldReturnProxy() { assertThat(context).isInstanceOf(ConfigurableWebApplicationContext.class); } + @Test + void getWhenHasAdditionalInterfaceShouldReturnProxy() { + ConfigurableWebApplicationContext context = AssertableWebApplicationContext.get( + () -> mock(ConfigurableWebApplicationContext.class, + withSettings().extraInterfaces(AdditionalContextInterface.class)), + AdditionalContextInterface.class); + assertThat(context).isInstanceOf(ConfigurableWebApplicationContext.class) + .isInstanceOf(AdditionalContextInterface.class); + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunnerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunnerTests.java index cd309da53698..d4a38ce5deb4 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunnerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -258,8 +258,16 @@ void prepareDoesNotRefreshContext() { }); } + @Test + void getWirhAdditionalContextInterfaceHasCorrectInstanceOf() { + getWithAdditionalContextInterface() + .run((context) -> assertThat(context).isInstanceOf(AdditionalContextInterface.class)); + } + protected abstract T get(); + protected abstract T getWithAdditionalContextInterface(); + private static void throwCheckedException(String message) throws IOException { throw new IOException(message); } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AdditionalContextInterface.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AdditionalContextInterface.java new file mode 100644 index 000000000000..b66a01715b8e --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/AdditionalContextInterface.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2024 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.context.runner; + +import org.springframework.context.ApplicationContext; + +/** + * Tests extra interface that can be applied to an {@link ApplicationContext} + * + * @author Phillip Webb + */ +interface AdditionalContextInterface { + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ApplicationContextRunnerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ApplicationContextRunnerTests.java index 462d58b9a546..86966fccb739 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ApplicationContextRunnerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ApplicationContextRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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,6 +18,7 @@ import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * Tests for {@link ApplicationContextRunner}. @@ -33,4 +34,15 @@ protected ApplicationContextRunner get() { return new ApplicationContextRunner(); } + @Override + protected ApplicationContextRunner getWithAdditionalContextInterface() { + return new ApplicationContextRunner(TestAnnotationConfigApplicationContext::new, + AdditionalContextInterface.class); + } + + static class TestAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext + implements AdditionalContextInterface { + + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunnerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunnerTests.java index 6d859b7d7891..cf7a66f901db 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunnerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -17,6 +17,7 @@ package org.springframework.boot.test.context.runner; import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; +import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext; import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext; /** @@ -33,4 +34,15 @@ protected ReactiveWebApplicationContextRunner get() { return new ReactiveWebApplicationContextRunner(); } + @Override + protected ReactiveWebApplicationContextRunner getWithAdditionalContextInterface() { + return new ReactiveWebApplicationContextRunner(TestAnnotationConfigReactiveWebApplicationContext::new, + AdditionalContextInterface.class); + } + + static class TestAnnotationConfigReactiveWebApplicationContext extends AnnotationConfigReactiveWebApplicationContext + implements AdditionalContextInterface { + + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/WebApplicationContextRunnerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/WebApplicationContextRunnerTests.java index edf448ff07b0..8d2ede8f98a6 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/WebApplicationContextRunnerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/WebApplicationContextRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.ConfigurableWebApplicationContext; @@ -43,4 +44,15 @@ protected WebApplicationContextRunner get() { return new WebApplicationContextRunner(); } + @Override + protected WebApplicationContextRunner getWithAdditionalContextInterface() { + return new WebApplicationContextRunner(TestAnnotationConfigServletWebApplicationContext::new, + AdditionalContextInterface.class); + } + + static class TestAnnotationConfigServletWebApplicationContext extends AnnotationConfigServletWebApplicationContext + implements AdditionalContextInterface { + + } + } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-extension/src/main/java/smoketest/actuator/extension/MyExtensionConfiguration.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-extension/src/main/java/smoketest/actuator/extension/MyExtensionConfiguration.java index 8fe5193658e0..bec987b68ecc 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-extension/src/main/java/smoketest/actuator/extension/MyExtensionConfiguration.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-extension/src/main/java/smoketest/actuator/extension/MyExtensionConfiguration.java @@ -50,7 +50,7 @@ public MyExtensionWebMvcEndpointHandlerMapping myWebMvcEndpointHandlerMapping( List> filters = Collections .singletonList(new MyExtensionEndpointFilter(environment)); WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(applicationContext, parameterMapper, - endpointMediaTypes, null, invokerAdvisors, filters); + endpointMediaTypes, null, null, invokerAdvisors, filters); Collection endpoints = discoverer.getEndpoints(); return new MyExtensionWebMvcEndpointHandlerMapping(endpoints, endpointMediaTypes, corsConfiguration); } From d72a9d9eb57d9b013da3205eb220ceb388ccb909 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 18 Sep 2024 23:46:55 -0700 Subject: [PATCH 263/271] Allow EndpointRequest to match additional paths Add `toAdditionalPaths(...)` methods on the servlet and reactive `EndpointRequest` classes to support matching of additional paths. A new `AdditionalPathsMapper` interface provides the mappings between endpoint IDs and any additional paths that they might use. The existing `AutoConfiguredHealthEndpointGroups` class has been updated to implement the interface. Auto-configurations have also been updated so that additional health endpoint paths (typically `/livez` and `/readyz`) are permitted when using Spring Security without any custom configuration. Fixes gh-40962 --- .../CloudFoundryWebEndpointDiscoverer.java | 2 +- .../web/WebEndpointAutoConfiguration.java | 6 +- .../AutoConfiguredHealthEndpointGroups.java | 26 +- .../health/HealthEndpointConfiguration.java | 6 +- .../security/reactive/EndpointRequest.java | 266 ++++++++++++------ ...anagementWebSecurityAutoConfiguration.java | 12 +- .../security/servlet/EndpointRequest.java | 203 ++++++++++--- ...anagementWebSecurityAutoConfiguration.java | 19 +- ...oundryWebFluxEndpointIntegrationTests.java | 4 +- ...FoundryMvcWebEndpointIntegrationTests.java | 4 +- ...toConfiguredHealthEndpointGroupsTests.java | 24 +- .../reactive/EndpointRequestTests.java | 89 +++++- ...mentWebSecurityAutoConfigurationTests.java | 29 ++ .../servlet/EndpointRequestTests.java | 90 +++++- ...mentWebSecurityAutoConfigurationTests.java | 65 ++++- .../endpoint/web/AdditionalPathsMapper.java | 43 +++ .../endpoint/web/PathMappedEndpoint.java | 23 +- .../endpoint/web/PathMappedEndpoints.java | 29 +- .../endpoint/web/WebServerNamespace.java | 34 ++- .../web/annotation/DiscoveredWebEndpoint.java | 23 +- .../web/annotation/WebEndpointDiscoverer.java | 35 ++- .../web/PathMappedEndpointsTests.java | 19 +- .../endpoint/web/WebServerNamespaceTests.java | 7 +- .../web/annotation/BaseConfiguration.java | 3 +- .../WebEndpointDiscovererTests.java | 29 +- ...EndpointTestInvocationContextProvider.java | 10 +- 26 files changed, 900 insertions(+), 200 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/AdditionalPathsMapper.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java index c401f5cf7801..4950f22ee466 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscoverer.java @@ -59,7 +59,7 @@ public CloudFoundryWebEndpointDiscoverer(ApplicationContext applicationContext, ParameterValueMapper parameterValueMapper, EndpointMediaTypes endpointMediaTypes, List endpointPathMappers, Collection invokerAdvisors, Collection> filters) { - super(applicationContext, parameterValueMapper, endpointMediaTypes, endpointPathMappers, invokerAdvisors, + super(applicationContext, parameterValueMapper, endpointMediaTypes, endpointPathMappers, null, invokerAdvisors, filters); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java index e3c4e5a4fa46..6ce0eab94f80 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; @@ -81,11 +82,12 @@ public EndpointMediaTypes endpointMediaTypes() { @ConditionalOnMissingBean(WebEndpointsSupplier.class) public WebEndpointDiscoverer webEndpointDiscoverer(ParameterValueMapper parameterValueMapper, EndpointMediaTypes endpointMediaTypes, ObjectProvider endpointPathMappers, + ObjectProvider additionalPathsMappers, ObjectProvider invokerAdvisors, ObjectProvider> filters) { return new WebEndpointDiscoverer(this.applicationContext, parameterValueMapper, endpointMediaTypes, - endpointPathMappers.orderedStream().toList(), invokerAdvisors.orderedStream().toList(), - filters.orderedStream().toList()); + endpointPathMappers.orderedStream().toList(), additionalPathsMappers.orderedStream().toList(), + invokerAdvisors.orderedStream().toList(), filters.orderedStream().toList()); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java index ff47f03252eb..b7c1ff72a3a4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -21,9 +21,11 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.stream.Stream; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; @@ -32,8 +34,12 @@ import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status; +import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.Show; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath; +import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -51,7 +57,7 @@ * @author Phillip Webb * @author Madhura Bhave */ -class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { +class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups, AdditionalPathsMapper { private static final Predicate ALL = (name) -> true; @@ -159,4 +165,20 @@ public HealthEndpointGroup get(String name) { return this.groups.get(name); } + @Override + public List getAdditionalPaths(EndpointId endpointId, WebServerNamespace webServerNamespace) { + if (!HealthEndpoint.ID.equals(endpointId)) { + return null; + } + return streamAllGroups().map(HealthEndpointGroup::getAdditionalPath) + .filter(Objects::nonNull) + .filter((additionalPath) -> additionalPath.hasNamespace(webServerNamespace)) + .map(AdditionalHealthEndpointPath::getValue) + .toList(); + } + + private Stream streamAllGroups() { + return Stream.concat(Stream.of(this.primaryGroup), this.groups.values().stream()); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java index 8badfc736bd7..c4b78bb3b9c7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -73,8 +73,8 @@ HttpCodeStatusMapper healthHttpCodeStatusMapper(HealthEndpointProperties propert } @Bean - @ConditionalOnMissingBean - HealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext, + @ConditionalOnMissingBean(HealthEndpointGroups.class) + AutoConfiguredHealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); } 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 bed3b8f5c068..16e91c6e9063 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 @@ -35,6 +35,7 @@ import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.MergedAnnotation; @@ -43,7 +44,9 @@ import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; @@ -52,6 +55,7 @@ * endpoint locations. * * @author Madhura Bhave + * @author Phillip Webb * @since 2.0.0 */ public final class EndpointRequest { @@ -115,30 +119,129 @@ public static LinksServerWebExchangeMatcher toLinks() { return new LinksServerWebExchangeMatcher(); } + /** + * Returns a matcher that includes additional paths under a {@link WebServerNamespace} + * for the specified {@link Endpoint actuator endpoints}. For example: + *
    +	 * EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, "health")
    +	 * 
    + * @param webServerNamespace the web server namespace + * @param endpoints the endpoints to include + * @return the configured {@link RequestMatcher} + * @since 3.4.0 + */ + public static AdditionalPathsEndpointServerWebExchangeMatcher toAdditionalPaths( + WebServerNamespace webServerNamespace, Class... endpoints) { + return new AdditionalPathsEndpointServerWebExchangeMatcher(webServerNamespace, endpoints); + } + + /** + * Returns a matcher that includes additional paths under a {@link WebServerNamespace} + * for the specified {@link Endpoint actuator endpoints}. For example: + *
    +	 * EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, HealthEndpoint.class)
    +	 * 
    + * @param webServerNamespace the web server namespace + * @param endpoints the endpoints to include + * @return the configured {@link RequestMatcher} + * @since 3.4.0 + */ + public static AdditionalPathsEndpointServerWebExchangeMatcher toAdditionalPaths( + WebServerNamespace webServerNamespace, String... endpoints) { + return new AdditionalPathsEndpointServerWebExchangeMatcher(webServerNamespace, endpoints); + } + /** * Base class for supported request matchers. */ - private abstract static class AbstractWebExchangeMatcher extends ApplicationContextServerWebExchangeMatcher { + private abstract static class AbstractWebExchangeMatcher extends ApplicationContextServerWebExchangeMatcher { - private ManagementPortType managementPortType; + private volatile ServerWebExchangeMatcher delegate; - AbstractWebExchangeMatcher(Class contextClass) { + private volatile ManagementPortType managementPortType; + + AbstractWebExchangeMatcher(Class contextClass) { super(contextClass); } + @Override + protected void initialized(Supplier supplier) { + this.delegate = createDelegate(supplier); + } + + private ServerWebExchangeMatcher createDelegate(Supplier context) { + try { + return createDelegate(context.get()); + } + catch (NoSuchBeanDefinitionException ex) { + return EMPTY_MATCHER; + } + } + + protected abstract ServerWebExchangeMatcher createDelegate(C context); + + protected final List getDelegateMatchers(Set paths) { + return paths.stream().map(this::getDelegateMatcher).collect(Collectors.toCollection(ArrayList::new)); + } + + private PathPatternParserServerWebExchangeMatcher getDelegateMatcher(String path) { + return new PathPatternParserServerWebExchangeMatcher(path + "/**"); + } + + @Override + protected Mono matches(ServerWebExchange exchange, Supplier context) { + return this.delegate.matches(exchange); + } + @Override protected boolean ignoreApplicationContext(ApplicationContext applicationContext) { - if (this.managementPortType == null) { - this.managementPortType = ManagementPortType.get(applicationContext.getEnvironment()); + ManagementPortType managementPortType = this.managementPortType; + if (managementPortType == null) { + managementPortType = ManagementPortType.get(applicationContext.getEnvironment()); + this.managementPortType = managementPortType; + } + return ignoreApplicationContext(applicationContext, managementPortType); + } + + protected boolean ignoreApplicationContext(ApplicationContext applicationContext, + ManagementPortType managementPortType) { + return managementPortType == ManagementPortType.DIFFERENT + && !hasWebServerNamespace(applicationContext, WebServerNamespace.MANAGEMENT); + } + + protected final boolean hasWebServerNamespace(ApplicationContext applicationContext, + WebServerNamespace webServerNamespace) { + if (applicationContext.getParent() == null) { + return WebServerNamespace.SERVER.equals(webServerNamespace); + } + String parentContextId = applicationContext.getParent().getId(); + return applicationContext.getId().equals(parentContextId + ":" + webServerNamespace); + } + + protected final String toString(List endpoints, String emptyValue) { + return (!endpoints.isEmpty()) ? endpoints.stream() + .map(this::getEndpointId) + .map(Object::toString) + .collect(Collectors.joining(", ", "[", "]")) : emptyValue; + } + + protected final EndpointId getEndpointId(Object source) { + if (source instanceof EndpointId endpointId) { + return endpointId; + } + if (source instanceof String string) { + return EndpointId.of(string); } - if (this.managementPortType == ManagementPortType.DIFFERENT) { - if (applicationContext.getParent() == null) { - return true; - } - String managementContextId = applicationContext.getParent().getId() + ":management"; - return !managementContextId.equals(applicationContext.getId()); + if (source instanceof Class) { + return getEndpointId((Class) source); } - return false; + throw new IllegalStateException("Unsupported source " + source); + } + + private EndpointId getEndpointId(Class source) { + MergedAnnotation annotation = MergedAnnotations.from(source).get(Endpoint.class); + Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); + return EndpointId.of(annotation.getString("id")); } } @@ -155,8 +258,6 @@ public static final class EndpointServerWebExchangeMatcher extends AbstractWebEx private final boolean includeLinks; - private volatile ServerWebExchangeMatcher delegate; - private EndpointServerWebExchangeMatcher(boolean includeLinks) { this(Collections.emptyList(), Collections.emptyList(), includeLinks); } @@ -193,48 +294,22 @@ public EndpointServerWebExchangeMatcher excludingLinks() { } @Override - protected void initialized(Supplier pathMappedEndpoints) { - this.delegate = createDelegate(pathMappedEndpoints); - } - - private ServerWebExchangeMatcher createDelegate(Supplier pathMappedEndpoints) { - try { - return createDelegate(pathMappedEndpoints.get()); - } - catch (NoSuchBeanDefinitionException ex) { - return EMPTY_MATCHER; - } - } - - private ServerWebExchangeMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints) { + protected ServerWebExchangeMatcher createDelegate(PathMappedEndpoints endpoints) { Set paths = new LinkedHashSet<>(); if (this.includes.isEmpty()) { - paths.addAll(pathMappedEndpoints.getAllPaths()); + paths.addAll(endpoints.getAllPaths()); } - streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); - streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); + streamPaths(this.includes, endpoints).forEach(paths::add); + streamPaths(this.excludes, endpoints).forEach(paths::remove); List delegateMatchers = getDelegateMatchers(paths); - if (this.includeLinks && StringUtils.hasText(pathMappedEndpoints.getBasePath())) { + if (this.includeLinks && StringUtils.hasText(endpoints.getBasePath())) { delegateMatchers.add(new LinksServerWebExchangeMatcher()); } return new OrServerWebExchangeMatcher(delegateMatchers); } - private Stream streamPaths(List source, PathMappedEndpoints pathMappedEndpoints) { - return source.stream().filter(Objects::nonNull).map(this::getEndpointId).map(pathMappedEndpoints::getPath); - } - - @Override - protected Mono matches(ServerWebExchange exchange, Supplier context) { - return this.delegate.matches(exchange); - } - - private List getDelegateMatchers(Set paths) { - return paths.stream().map(this::getDelegateMatcher).collect(Collectors.toCollection(ArrayList::new)); - } - - private PathPatternParserServerWebExchangeMatcher getDelegateMatcher(String path) { - return new PathPatternParserServerWebExchangeMatcher(path + "/**"); + private Stream streamPaths(List source, PathMappedEndpoints endpoints) { + return source.stream().filter(Objects::nonNull).map(this::getEndpointId).map(endpoints::getPath); } @Override @@ -243,32 +318,6 @@ public String toString() { toString(this.includes, "[*]"), toString(this.excludes, "[]"), this.includeLinks); } - private String toString(List endpoints, String emptyValue) { - return (!endpoints.isEmpty()) ? endpoints.stream() - .map(this::getEndpointId) - .map(Object::toString) - .collect(Collectors.joining(", ", "[", "]")) : emptyValue; - } - - private EndpointId getEndpointId(Object source) { - if (source instanceof EndpointId endpointId) { - return endpointId; - } - if (source instanceof String string) { - return EndpointId.of(string); - } - if (source instanceof Class) { - return getEndpointId((Class) source); - } - throw new IllegalStateException("Unsupported source " + source); - } - - private EndpointId getEndpointId(Class source) { - MergedAnnotation annotation = MergedAnnotations.from(source).get(Endpoint.class); - Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); - return EndpointId.of(annotation.getString("id")); - } - } /** @@ -276,18 +325,12 @@ private EndpointId getEndpointId(Class source) { */ public static final class LinksServerWebExchangeMatcher extends AbstractWebExchangeMatcher { - private volatile ServerWebExchangeMatcher delegate; - private LinksServerWebExchangeMatcher() { super(WebEndpointProperties.class); } @Override - protected void initialized(Supplier properties) { - this.delegate = createDelegate(properties.get()); - } - - private ServerWebExchangeMatcher createDelegate(WebEndpointProperties properties) { + protected ServerWebExchangeMatcher createDelegate(WebEndpointProperties properties) { if (StringUtils.hasText(properties.getBasePath())) { return new OrServerWebExchangeMatcher( new PathPatternParserServerWebExchangeMatcher(properties.getBasePath()), @@ -297,8 +340,67 @@ private ServerWebExchangeMatcher createDelegate(WebEndpointProperties properties } @Override - protected Mono matches(ServerWebExchange exchange, Supplier context) { - return this.delegate.matches(exchange); + public String toString() { + return String.format("LinksServerWebExchangeMatcher"); + } + + } + + /** + * The {@link ServerWebExchangeMatcher} used to match against additional paths for + * {@link Endpoint actuator endpoints}. + */ + public static class AdditionalPathsEndpointServerWebExchangeMatcher + extends AbstractWebExchangeMatcher { + + private final WebServerNamespace webServerNamespace; + + private final List endpoints; + + AdditionalPathsEndpointServerWebExchangeMatcher(WebServerNamespace webServerNamespace, String... endpoints) { + this(webServerNamespace, Arrays.asList((Object[]) endpoints)); + } + + AdditionalPathsEndpointServerWebExchangeMatcher(WebServerNamespace webServerNamespace, Class... endpoints) { + this(webServerNamespace, Arrays.asList((Object[]) endpoints)); + } + + private AdditionalPathsEndpointServerWebExchangeMatcher(WebServerNamespace webServerNamespace, + List endpoints) { + super(PathMappedEndpoints.class); + Assert.notNull(webServerNamespace, "'webServerNamespace' must not be null"); + Assert.notNull(endpoints, "'endpoints' must not be null"); + Assert.notEmpty(endpoints, "'endpoints' must not be empty"); + this.webServerNamespace = webServerNamespace; + this.endpoints = endpoints; + } + + @Override + protected boolean ignoreApplicationContext(ApplicationContext applicationContext, + ManagementPortType managementPortType) { + return !hasWebServerNamespace(applicationContext, this.webServerNamespace); + } + + @Override + protected ServerWebExchangeMatcher createDelegate(PathMappedEndpoints endpoints) { + Set paths = this.endpoints.stream() + .filter(Objects::nonNull) + .map(this::getEndpointId) + .flatMap((endpointId) -> streamAdditionalPaths(endpoints, endpointId)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + List delegateMatchers = getDelegateMatchers(paths); + return (!CollectionUtils.isEmpty(delegateMatchers)) ? new OrServerWebExchangeMatcher(delegateMatchers) + : EMPTY_MATCHER; + } + + private Stream streamAdditionalPaths(PathMappedEndpoints pathMappedEndpoints, EndpointId endpointId) { + return pathMappedEndpoints.getAdditionalPaths(this.webServerNamespace, endpointId).stream(); + } + + @Override + public String toString() { + return String.format("AdditionalPathsEndpointServerWebExchangeMatcher endpoints=%s, webServerNamespace=%s", + toString(this.endpoints, ""), this.webServerNamespace); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java index e9da837d148e..3567604974b0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -40,6 +41,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.web.cors.reactive.PreFlightRequestHandler; import org.springframework.web.cors.reactive.PreFlightRequestWebFilter; @@ -66,7 +68,7 @@ public class ReactiveManagementWebSecurityAutoConfiguration { @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, PreFlightRequestHandler handler) { http.authorizeExchange((exchanges) -> { - exchanges.matchers(EndpointRequest.to(HealthEndpoint.class)).permitAll(); + exchanges.matchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); exchanges.anyExchange().authenticated(); }); PreFlightRequestWebFilter filter = new PreFlightRequestWebFilter(handler); @@ -76,6 +78,14 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, return http.build(); } + private ServerWebExchangeMatcher healthMatcher() { + return EndpointRequest.to(HealthEndpoint.class); + } + + private ServerWebExchangeMatcher additionalHealthPathsMatcher() { + return EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, HealthEndpoint.class); + } + @Bean @ConditionalOnMissingBean({ ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class }) ReactiveAuthenticationManager denyAllAuthenticationManager() { 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 b8a63d0c4cc0..3a85bc894529 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 @@ -35,15 +35,18 @@ import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.WebApplicationContext; @@ -116,6 +119,38 @@ public static LinksRequestMatcher toLinks() { return new LinksRequestMatcher(); } + /** + * Returns a matcher that includes additional paths under a {@link WebServerNamespace} + * for the specified {@link Endpoint actuator endpoints}. For example: + *
    +	 * EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, "health")
    +	 * 
    + * @param webServerNamespace the web server namespace + * @param endpoints the endpoints to include + * @return the configured {@link RequestMatcher} + * @since 3.4.0 + */ + public static AdditionalPathsEndpointRequestMatcher toAdditionalPaths(WebServerNamespace webServerNamespace, + Class... endpoints) { + return new AdditionalPathsEndpointRequestMatcher(webServerNamespace, endpoints); + } + + /** + * Returns a matcher that includes additional paths under a {@link WebServerNamespace} + * for the specified {@link Endpoint actuator endpoints}. For example: + *
    +	 * EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, HealthEndpoint.class)
    +	 * 
    + * @param webServerNamespace the web server namespace + * @param endpoints the endpoints to include + * @return the configured {@link RequestMatcher} + * @since 3.4.0 + */ + public static AdditionalPathsEndpointRequestMatcher toAdditionalPaths(WebServerNamespace webServerNamespace, + String... endpoints) { + return new AdditionalPathsEndpointRequestMatcher(webServerNamespace, endpoints); + } + /** * Base class for supported request matchers. */ @@ -124,7 +159,7 @@ private abstract static class AbstractRequestMatcher private volatile RequestMatcher delegate; - private ManagementPortType managementPortType; + private volatile ManagementPortType managementPortType; AbstractRequestMatcher() { super(WebApplicationContext.class); @@ -132,11 +167,25 @@ private abstract static class AbstractRequestMatcher @Override protected boolean ignoreApplicationContext(WebApplicationContext applicationContext) { - if (this.managementPortType == null) { - this.managementPortType = ManagementPortType.get(applicationContext.getEnvironment()); + ManagementPortType managementPortType = this.managementPortType; + if (managementPortType == null) { + managementPortType = ManagementPortType.get(applicationContext.getEnvironment()); + this.managementPortType = managementPortType; } - return this.managementPortType == ManagementPortType.DIFFERENT - && !WebServerApplicationContext.hasServerNamespace(applicationContext, "management"); + return ignoreApplicationContext(applicationContext, managementPortType); + } + + protected boolean ignoreApplicationContext(WebApplicationContext applicationContext, + ManagementPortType managementPortType) { + return managementPortType == ManagementPortType.DIFFERENT + && !hasWebServerNamespace(applicationContext, WebServerNamespace.MANAGEMENT); + } + + protected final boolean hasWebServerNamespace(ApplicationContext applicationContext, + WebServerNamespace webServerNamespace) { + return WebServerApplicationContext.hasServerNamespace(applicationContext, webServerNamespace.getValue()) + || (webServerNamespace.equals(WebServerNamespace.SERVER) + && !(applicationContext instanceof WebServerApplicationContext)); } @Override @@ -161,6 +210,13 @@ private RequestMatcher createDelegate(WebApplicationContext context) { protected abstract RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory); + protected final List getDelegateMatchers(RequestMatcherFactory requestMatcherFactory, + RequestMatcherProvider matcherProvider, Set paths) { + return paths.stream() + .map((path) -> requestMatcherFactory.antPath(matcherProvider, path, "/**")) + .collect(Collectors.toCollection(ArrayList::new)); + } + protected List getLinksMatchers(RequestMatcherFactory requestMatcherFactory, RequestMatcherProvider matcherProvider, String basePath) { List linksMatchers = new ArrayList<>(); @@ -178,6 +234,32 @@ protected RequestMatcherProvider getRequestMatcherProvider(WebApplicationContext } } + protected final String toString(List endpoints, String emptyValue) { + return (!endpoints.isEmpty()) ? endpoints.stream() + .map(this::getEndpointId) + .map(Object::toString) + .collect(Collectors.joining(", ", "[", "]")) : emptyValue; + } + + protected final EndpointId getEndpointId(Object source) { + if (source instanceof EndpointId endpointId) { + return endpointId; + } + if (source instanceof String string) { + return EndpointId.of(string); + } + if (source instanceof Class sourceClass) { + return getEndpointId(sourceClass); + } + throw new IllegalStateException("Unsupported source " + source); + } + + private EndpointId getEndpointId(Class source) { + MergedAnnotation annotation = MergedAnnotations.from(source).get(Endpoint.class); + Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); + return EndpointId.of(annotation.getString("id")); + } + } /** @@ -228,31 +310,24 @@ public EndpointRequestMatcher excludingLinks() { @Override protected RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory) { - PathMappedEndpoints pathMappedEndpoints = context.getBean(PathMappedEndpoints.class); + PathMappedEndpoints endpoints = context.getBean(PathMappedEndpoints.class); RequestMatcherProvider matcherProvider = getRequestMatcherProvider(context); Set paths = new LinkedHashSet<>(); if (this.includes.isEmpty()) { - paths.addAll(pathMappedEndpoints.getAllPaths()); + paths.addAll(endpoints.getAllPaths()); } - streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); - streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); + streamPaths(this.includes, endpoints).forEach(paths::add); + streamPaths(this.excludes, endpoints).forEach(paths::remove); List delegateMatchers = getDelegateMatchers(requestMatcherFactory, matcherProvider, paths); - String basePath = pathMappedEndpoints.getBasePath(); + String basePath = endpoints.getBasePath(); if (this.includeLinks && StringUtils.hasText(basePath)) { delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory, matcherProvider, basePath)); } return new OrRequestMatcher(delegateMatchers); } - private Stream streamPaths(List source, PathMappedEndpoints pathMappedEndpoints) { - return source.stream().filter(Objects::nonNull).map(this::getEndpointId).map(pathMappedEndpoints::getPath); - } - - private List getDelegateMatchers(RequestMatcherFactory requestMatcherFactory, - RequestMatcherProvider matcherProvider, Set paths) { - return paths.stream() - .map((path) -> requestMatcherFactory.antPath(matcherProvider, path, "/**")) - .collect(Collectors.toCollection(ArrayList::new)); + private Stream streamPaths(List source, PathMappedEndpoints endpoints) { + return source.stream().filter(Objects::nonNull).map(this::getEndpointId).map(endpoints::getPath); } @Override @@ -261,32 +336,6 @@ public String toString() { toString(this.includes, "[*]"), toString(this.excludes, "[]"), this.includeLinks); } - private String toString(List endpoints, String emptyValue) { - return (!endpoints.isEmpty()) ? endpoints.stream() - .map(this::getEndpointId) - .map(Object::toString) - .collect(Collectors.joining(", ", "[", "]")) : emptyValue; - } - - private EndpointId getEndpointId(Object source) { - if (source instanceof EndpointId endpointId) { - return endpointId; - } - if (source instanceof String string) { - return EndpointId.of(string); - } - if (source instanceof Class) { - return getEndpointId((Class) source); - } - throw new IllegalStateException("Unsupported source " + source); - } - - private EndpointId getEndpointId(Class source) { - MergedAnnotation annotation = MergedAnnotations.from(source).get(Endpoint.class); - Assert.state(annotation.isPresent(), () -> "Class " + source + " is not annotated with @Endpoint"); - return EndpointId.of(annotation.getString("id")); - } - } /** @@ -306,6 +355,70 @@ protected RequestMatcher createDelegate(WebApplicationContext context, return EMPTY_MATCHER; } + @Override + public String toString() { + return String.format("LinksRequestMatcher"); + } + + } + + /** + * The request matcher used to match against additional paths for {@link Endpoint + * actuator endpoints}. + */ + public static class AdditionalPathsEndpointRequestMatcher extends AbstractRequestMatcher { + + private final WebServerNamespace webServerNamespace; + + private final List endpoints; + + AdditionalPathsEndpointRequestMatcher(WebServerNamespace webServerNamespace, String... endpoints) { + this(webServerNamespace, Arrays.asList((Object[]) endpoints)); + } + + AdditionalPathsEndpointRequestMatcher(WebServerNamespace webServerNamespace, Class... endpoints) { + this(webServerNamespace, Arrays.asList((Object[]) endpoints)); + } + + private AdditionalPathsEndpointRequestMatcher(WebServerNamespace webServerNamespace, List endpoints) { + Assert.notNull(webServerNamespace, "'webServerNamespace' must not be null"); + Assert.notNull(endpoints, "'endpoints' must not be null"); + Assert.notEmpty(endpoints, "'endpoints' must not be empty"); + this.webServerNamespace = webServerNamespace; + this.endpoints = endpoints; + } + + @Override + protected boolean ignoreApplicationContext(WebApplicationContext applicationContext, + ManagementPortType managementPortType) { + return !hasWebServerNamespace(applicationContext, this.webServerNamespace); + } + + @Override + protected RequestMatcher createDelegate(WebApplicationContext context, + RequestMatcherFactory requestMatcherFactory) { + PathMappedEndpoints endpoints = context.getBean(PathMappedEndpoints.class); + RequestMatcherProvider matcherProvider = getRequestMatcherProvider(context); + Set paths = 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); + return (!CollectionUtils.isEmpty(delegateMatchers)) ? new OrRequestMatcher(delegateMatchers) + : EMPTY_MATCHER; + } + + private Stream streamAdditionalPaths(PathMappedEndpoints pathMappedEndpoints, EndpointId endpointId) { + return pathMappedEndpoints.getAdditionalPaths(this.webServerNamespace, endpointId).stream(); + } + + @Override + public String toString() { + return String.format("AdditionalPathsEndpointRequestMatcher endpoints=%s, webServerNamespace=%s", + toString(this.endpoints, ""), this.webServerNamespace); + } + } /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.java index d6bc5b11a072..704100462f65 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -19,6 +19,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -31,8 +32,10 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.ClassUtils; import static org.springframework.security.config.Customizer.withDefaults; @@ -40,7 +43,7 @@ /** * {@link EnableAutoConfiguration Auto-configuration} for Spring Security when actuator is * on the classpath. It allows unauthenticated access to the {@link HealthEndpoint}. If - * the user specifies their own{@link SecurityFilterChain} bean, this will back-off + * the user specifies their own {@link SecurityFilterChain} bean, this will back-off * completely and the user should specify all the bits that they want to configure as part * of the custom security configuration. * @@ -58,9 +61,9 @@ public class ManagementWebSecurityAutoConfiguration { @Bean @Order(SecurityProperties.BASIC_AUTH_ORDER) - SecurityFilterChain managementSecurityFilterChain(HttpSecurity http) throws Exception { + SecurityFilterChain managementSecurityFilterChain(Environment environment, HttpSecurity http) throws Exception { http.authorizeHttpRequests((requests) -> { - requests.requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll(); + requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); requests.anyRequest().authenticated(); }); if (ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) { @@ -71,4 +74,12 @@ SecurityFilterChain managementSecurityFilterChain(HttpSecurity http) throws Exce return http.build(); } + private RequestMatcher healthMatcher() { + return EndpointRequest.to(HealthEndpoint.class); + } + + private RequestMatcher additionalHealthPathsMatcher() { + return EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, HealthEndpoint.class); + } + } 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 99bb7885735d..7846c80c5cc3 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -262,7 +262,7 @@ WebEndpointDiscoverer webEndpointDiscoverer(ApplicationContext applicationContex EndpointMediaTypes endpointMediaTypes) { ParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper( DefaultConversionService.getSharedInstance()); - return new WebEndpointDiscoverer(applicationContext, parameterMapper, endpointMediaTypes, null, + return new WebEndpointDiscoverer(applicationContext, parameterMapper, endpointMediaTypes, null, null, Collections.emptyList(), Collections.emptyList()); } 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 09c2e72a25a9..01383c6abf69 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -256,7 +256,7 @@ WebEndpointDiscoverer webEndpointDiscoverer(ApplicationContext applicationContex EndpointMediaTypes endpointMediaTypes) { ParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper( DefaultConversionService.getSharedInstance()); - return new WebEndpointDiscoverer(applicationContext, parameterMapper, endpointMediaTypes, null, + return new WebEndpointDiscoverer(applicationContext, parameterMapper, endpointMediaTypes, null, null, Collections.emptyList(), Collections.emptyList()); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java index f967126a9dc7..c00790d80632 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -21,7 +21,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.SecurityContext; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; +import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -333,6 +337,24 @@ void createWhenGroupWithNoShowDetailsOverrideInheritsShowDetails() { }); } + @Test + void getAdditionalPathsReturnsAllAdditionalPaths() { + this.contextRunner + .withPropertyValues("management.endpoint.health.group.a.additional-path=server:/a", + "management.endpoint.health.group.b.additional-path=server:/b", + "management.endpoint.health.group.c.additional-path=management:/c", + "management.endpoint.health.group.d.additional-path=management:/d") + .run((context) -> { + AdditionalPathsMapper additionalPathsMapper = context.getBean(AdditionalPathsMapper.class); + assertThat(additionalPathsMapper.getAdditionalPaths(HealthEndpoint.ID, WebServerNamespace.SERVER)) + .containsExactlyInAnyOrder("/a", "/b"); + assertThat(additionalPathsMapper.getAdditionalPaths(HealthEndpoint.ID, WebServerNamespace.MANAGEMENT)) + .containsExactlyInAnyOrder("/c", "/d"); + assertThat(additionalPathsMapper.getAdditionalPaths(EndpointId.of("other"), WebServerNamespace.SERVER)) + .isNull(); + }); + } + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(HealthEndpointProperties.class) static class AutoConfiguredHealthEndpointGroupsTestConfiguration { 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 e71258a986ab..792ed54ca9c0 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 @@ -18,6 +18,7 @@ import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.assertj.core.api.AssertDelegateTarget; @@ -30,6 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 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.context.support.StaticApplicationContext; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -234,6 +236,13 @@ void toStringWhenIncludedExcludedEndpoints() { assertThat(matcher).hasToString("EndpointRequestMatcher includes=[*], excludes=[bar], includeLinks=false"); } + @Test + void toStringWhenToAdditionalPaths() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, "test"); + assertThat(matcher) + .hasToString("AdditionalPathsEndpointServerWebExchangeMatcher endpoints=[test], webServerNamespace=server"); + } + @Test void toAnyEndpointWhenEndpointPathMappedToRootIsExcludedShouldNotMatchRoot() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint().excluding("root"); @@ -252,6 +261,43 @@ void toEndpointWhenEndpointPathMappedToRootShouldMatchRoot() { assertMatcher.matches("/"); } + @Test + void toAdditionalPathsWithEndpointClassShouldMatchAdditionalPath() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.matches("/additional"); + } + + @Test + void toAdditionalPathsWithEndpointIdShouldMatchAdditionalPath() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, "foo"); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.matches("/additional"); + } + + @Test + void toAdditionalPathsWithEndpointClassShouldNotMatchOtherPaths() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.doesNotMatch("/foo"); + assertMatcher.doesNotMatch("/bar"); + } + + @Test + void toAdditionalPathsWithEndpointClassShouldNotMatchOtherNamespace() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional"))), + WebServerNamespace.MANAGEMENT); + assertMatcher.doesNotMatch("/additional"); + } + private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher) { return assertMatcher(matcher, mockPathMappedEndpoints("/actuator")); } @@ -260,23 +306,20 @@ private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, Str return assertMatcher(matcher, mockPathMappedEndpoints(basePath)); } - private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { - List> endpoints = new ArrayList<>(); - endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo")); - endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar")); - return new PathMappedEndpoints(basePath, () -> endpoints); - } - - private TestEndpoint mockEndpoint(EndpointId id, String rootPath) { - TestEndpoint endpoint = mock(TestEndpoint.class); - given(endpoint.getEndpointId()).willReturn(id); - given(endpoint.getRootPath()).willReturn(rootPath); - return endpoint; + private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, + PathMappedEndpoints pathMappedEndpoints) { + return assertMatcher(matcher, pathMappedEndpoints, null); } private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, - PathMappedEndpoints pathMappedEndpoints) { + PathMappedEndpoints pathMappedEndpoints, WebServerNamespace namespace) { StaticApplicationContext context = new StaticApplicationContext(); + if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { + StaticApplicationContext parentContext = new StaticApplicationContext(); + parentContext.setId("app"); + context.setParent(parentContext); + context.setId(parentContext.getId() + ":" + namespace); + } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); @@ -288,6 +331,26 @@ private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, return assertThat(new RequestMatcherAssert(context, matcher)); } + private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { + List> endpoints = new ArrayList<>(); + endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo")); + endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar")); + return new PathMappedEndpoints(basePath, () -> endpoints); + } + + private TestEndpoint mockEndpoint(EndpointId id, String rootPath) { + return mockEndpoint(id, rootPath, WebServerNamespace.SERVER); + } + + private TestEndpoint mockEndpoint(EndpointId id, String rootPath, WebServerNamespace webServerNamespace, + String... additionalPaths) { + TestEndpoint endpoint = mock(TestEndpoint.class); + given(endpoint.getEndpointId()).willReturn(id); + given(endpoint.getRootPath()).willReturn(rootPath); + given(endpoint.getAdditionalPaths(webServerNamespace)).willReturn(Arrays.asList(additionalPaths)); + return endpoint; + } + static class RequestMatcherAssert implements AssertDelegateTarget { private final StaticApplicationContext context; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfigurationTests.java index 66b39ded5a73..f632721a5608 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfigurationTests.java @@ -79,6 +79,35 @@ void permitAllForHealth() { .run((context) -> assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull()); } + @Test + void withAdditionalPathsOnSamePort() { + this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class) + .withPropertyValues("management.endpoint.health.group.test1.include=*", + "management.endpoint.health.group.test2.include=*", + "management.endpoint.health.group.test1.additional-path=server:/check1", + "management.endpoint.health.group.test2.additional-path=management:/check2") + .run((context) -> { + assertThat(getAuthenticateHeader(context, "/check1")).isNull(); + assertThat(getAuthenticateHeader(context, "/check2").get(0)).contains("Basic realm="); + assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull(); + }); + } + + @Test + void withAdditionalPathsOnDifferentPort() { + this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class) + .withPropertyValues("management.endpoint.health.group.test1.include=*", + "management.endpoint.health.group.test2.include=*", + "management.endpoint.health.group.test1.additional-path=server:/check1", + "management.endpoint.health.group.test2.additional-path=management:/check2", + "management.server.port=0") + .run((context) -> { + assertThat(getAuthenticateHeader(context, "/check1")).isNull(); + assertThat(getAuthenticateHeader(context, "/check2").get(0)).contains("Basic realm="); + assertThat(getAuthenticateHeader(context, "/actuator/health").get(0)).contains("Basic realm="); + }); + } + @Test void securesEverythingElse() { this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class).run((context) -> { 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 d3cf4c26d103..ac07f1d6ef7c 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 @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.autoconfigure.security.servlet; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import jakarta.servlet.http.HttpServletRequest; @@ -24,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; +import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.AdditionalPathsEndpointRequestMatcher; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.EndpointRequestMatcher; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; @@ -31,7 +33,10 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 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.autoconfigure.security.servlet.RequestMatcherProvider; +import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.server.WebServer; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -194,7 +199,7 @@ void endpointRequestMatcherShouldUseCustomRequestMatcherProvider() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher mockRequestMatcher = (request) -> false; RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), - (pattern) -> mockRequestMatcher); + (pattern) -> mockRequestMatcher, null); assertMatcher.doesNotMatch("/foo"); assertMatcher.doesNotMatch("/bar"); } @@ -204,7 +209,7 @@ void linksRequestMatcherShouldUseCustomRequestMatcherProvider() { RequestMatcher matcher = EndpointRequest.toLinks(); RequestMatcher mockRequestMatcher = (request) -> false; RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints("/actuator"), - (pattern) -> mockRequestMatcher); + (pattern) -> mockRequestMatcher, null); assertMatcher.doesNotMatch("/actuator"); } @@ -239,6 +244,13 @@ void toStringWhenIncludedExcludedEndpoints() { assertThat(matcher).hasToString("EndpointRequestMatcher includes=[*], excludes=[bar], includeLinks=false"); } + @Test + void toStringWhenToAdditionalPaths() { + RequestMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, "test"); + assertThat(matcher) + .hasToString("AdditionalPathsEndpointRequestMatcher endpoints=[test], webServerNamespace=server"); + } + @Test void toAnyEndpointWhenEndpointPathMappedToRootIsExcludedShouldNotMatchRoot() { EndpointRequestMatcher matcher = EndpointRequest.toAnyEndpoint().excluding("root"); @@ -257,12 +269,50 @@ void toEndpointWhenEndpointPathMappedToRootShouldMatchRoot() { assertMatcher.matches("/"); } + @Test + void toAdditionalPathsWithEndpointClassShouldMatchAdditionalPath() { + AdditionalPathsEndpointRequestMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.matches("/additional"); + } + + @Test + void toAdditionalPathsWithEndpointIdShouldMatchAdditionalPath() { + AdditionalPathsEndpointRequestMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + "foo"); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.matches("/additional"); + } + + @Test + void toAdditionalPathsWithEndpointClassShouldNotMatchOtherPaths() { + AdditionalPathsEndpointRequestMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional")))); + assertMatcher.doesNotMatch("/foo"); + assertMatcher.doesNotMatch("/bar"); + } + + @Test + void toAdditionalPathsWithEndpointClassShouldNotMatchOtherNamespace() { + AdditionalPathsEndpointRequestMatcher matcher = EndpointRequest.toAdditionalPaths(WebServerNamespace.SERVER, + FooEndpoint.class); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, new PathMappedEndpoints("", + () -> List.of(mockEndpoint(EndpointId.of("foo"), "test", WebServerNamespace.SERVER, "/additional"))), + null, WebServerNamespace.MANAGEMENT); + assertMatcher.doesNotMatch("/additional"); + } + private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { return assertMatcher(matcher, mockPathMappedEndpoints("/actuator")); } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath) { - return assertMatcher(matcher, mockPathMappedEndpoints(basePath), null); + return assertMatcher(matcher, mockPathMappedEndpoints(basePath), null, null); } private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { @@ -273,19 +323,26 @@ private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { } private TestEndpoint mockEndpoint(EndpointId id, String rootPath) { + return mockEndpoint(id, rootPath, WebServerNamespace.SERVER); + } + + private TestEndpoint mockEndpoint(EndpointId id, String rootPath, WebServerNamespace webServerNamespace, + String... additionalPaths) { TestEndpoint endpoint = mock(TestEndpoint.class); given(endpoint.getEndpointId()).willReturn(id); given(endpoint.getRootPath()).willReturn(rootPath); + given(endpoint.getAdditionalPaths(webServerNamespace)).willReturn(Arrays.asList(additionalPaths)); return endpoint; } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints) { - return assertMatcher(matcher, pathMappedEndpoints, null); + return assertMatcher(matcher, pathMappedEndpoints, null, null); } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints, - RequestMatcherProvider matcherProvider) { - StaticWebApplicationContext context = new StaticWebApplicationContext(); + RequestMatcherProvider matcherProvider, WebServerNamespace webServerNamespace) { + StaticWebApplicationContext context = (webServerNamespace != null) + ? new NamedStaticWebApplicationContext(webServerNamespace) : new StaticWebApplicationContext(); context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); @@ -300,6 +357,27 @@ private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEnd return assertThat(new RequestMatcherAssert(context, matcher)); } + static class NamedStaticWebApplicationContext extends StaticWebApplicationContext + implements WebServerApplicationContext { + + private final WebServerNamespace webServerNamespace; + + NamedStaticWebApplicationContext(WebServerNamespace webServerNamespace) { + this.webServerNamespace = webServerNamespace; + } + + @Override + public WebServer getWebServer() { + return null; + } + + @Override + public String getServerNamespace() { + return this.webServerNamespace.getValue(); + } + + } + static class RequestMatcherAssert implements AssertDelegateTarget { private final WebApplicationContext context; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfigurationTests.java index 869ee26aa4af..b60d93ee2dfa 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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,6 +18,7 @@ import java.io.IOException; import java.util.List; +import java.util.function.Supplier; import org.junit.jupiter.api.Test; @@ -36,6 +37,9 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -48,6 +52,7 @@ import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -63,11 +68,17 @@ class ManagementWebSecurityAutoConfigurationTests { private static final String MANAGEMENT_SECURITY_FILTER_CHAIN_BEAN = "managementSecurityFilterChain"; - private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner().withConfiguration( - AutoConfigurations.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class, - InfoEndpointAutoConfiguration.class, EnvironmentEndpointAutoConfiguration.class, - EndpointAutoConfiguration.class, WebMvcAutoConfiguration.class, WebEndpointAutoConfiguration.class, - SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class)); + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(contextSupplier(), + WebServerApplicationContext.class) + .withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class, + HealthEndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, + EnvironmentEndpointAutoConfiguration.class, EndpointAutoConfiguration.class, + WebMvcAutoConfiguration.class, WebEndpointAutoConfiguration.class, SecurityAutoConfiguration.class, + ManagementWebSecurityAutoConfiguration.class)); + + private static Supplier contextSupplier() { + return WebApplicationContextRunner.withMockServletContext(MockWebServerApplicationContext::new); + } @Test void permitAllForHealth() { @@ -159,6 +170,33 @@ void backOffIfRemoteDevToolsSecurityFilterChainIsPresent() { }); } + @Test + void withAdditionalPathsOnSamePort() { + this.contextRunner + .withPropertyValues("management.endpoint.health.group.test1.include=*", + "management.endpoint.health.group.test2.include=*", + "management.endpoint.health.group.test1.additional-path=server:/check1", + "management.endpoint.health.group.test2.additional-path=management:/check2") + .run((context) -> { + assertThat(getResponseStatus(context, "/check1")).isEqualTo(HttpStatus.OK); + assertThat(getResponseStatus(context, "/check2")).isEqualTo(HttpStatus.UNAUTHORIZED); + assertThat(getResponseStatus(context, "/actuator/health")).isEqualTo(HttpStatus.OK); + }); + } + + @Test + void withAdditionalPathsOnDifferentPort() { + this.contextRunner.withPropertyValues("management.endpoint.health.group.test1.include=*", + "management.endpoint.health.group.test2.include=*", + "management.endpoint.health.group.test1.additional-path=server:/check1", + "management.endpoint.health.group.test2.additional-path=management:/check2", "management.server.port=0") + .run((context) -> { + assertThat(getResponseStatus(context, "/check1")).isEqualTo(HttpStatus.OK); + assertThat(getResponseStatus(context, "/check2")).isEqualTo(HttpStatus.UNAUTHORIZED); + assertThat(getResponseStatus(context, "/actuator/health")).isEqualTo(HttpStatus.UNAUTHORIZED); + }); + } + private HttpStatus getResponseStatus(AssertableWebApplicationContext context, String path) throws IOException, jakarta.servlet.ServletException { FilterChainProxy filterChainProxy = context.getBean(FilterChainProxy.class); @@ -214,4 +252,19 @@ SecurityFilterChain testRemoteDevToolsSecurityFilterChain(HttpSecurity http) thr } + static class MockWebServerApplicationContext extends AnnotationConfigServletWebApplicationContext + implements WebServerApplicationContext { + + @Override + public WebServer getWebServer() { + return null; + } + + @Override + public String getServerNamespace() { + return "server"; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/AdditionalPathsMapper.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/AdditionalPathsMapper.java new file mode 100644 index 000000000000..0b390c8fb913 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/AdditionalPathsMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2024 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.actuate.endpoint.web; + +import java.util.List; + +import org.springframework.boot.actuate.endpoint.EndpointId; + +/** + * Strategy interface used to provide a mapping between an endpoint ID and any additional + * paths where it will be exposed. + * + * @author Phillip Webb + * @since 3.4.0 + */ +@FunctionalInterface +public interface AdditionalPathsMapper { + + /** + * Resolve the additional paths for the specified {@code endpointId} and web server + * namespace. + * @param endpointId the id of an endpoint + * @param webServerNamespace the web server namespace + * @return the additional paths of the endpoint or {@code null} if this mapper doesn't + * support the given endpoint ID. + */ + List getAdditionalPaths(EndpointId endpointId, WebServerNamespace webServerNamespace); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoint.java index 0a39892c9123..f75f9f1207cd 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -16,6 +16,9 @@ package org.springframework.boot.actuate.endpoint.web; +import java.util.Collections; +import java.util.List; + import org.springframework.boot.actuate.endpoint.ExposableEndpoint; /** @@ -30,11 +33,23 @@ public interface PathMappedEndpoint { /** - * Return the root path of the endpoint, relative to the context that exposes it. For - * example, a root path of {@code example} would be exposed under the URL - * "/{actuator-context}/example". + * Return the root path of the endpoint (relative to the context and base path) that + * exposes it. For example, a root path of {@code example} would be exposed under the + * URL "/{actuator-context}/example". * @return the root path for the endpoint + * @see PathMappedEndpoints#getBasePath */ String getRootPath(); + /** + * Return any additional paths (relative to the context) for the given + * {@link WebServerNamespace}. + * @param webServerNamespace the web server namespace + * @return a list of additional paths + * @since 3.4.0 + */ + default List getAdditionalPaths(WebServerNamespace webServerNamespace) { + return Collections.emptyList(); + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java index c8be88751b18..96995b2e0daf 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java @@ -20,12 +20,14 @@ import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.stream.Stream; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointsSupplier; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; /** * A collection of {@link PathMappedEndpoint path mapped endpoints}. @@ -101,7 +103,7 @@ public String getPath(EndpointId endpointId) { } /** - * Return the root paths for each mapped endpoint. + * Return the root paths for each mapped endpoint (excluding additional paths). * @return all root paths */ public Collection getAllRootPaths() { @@ -109,13 +111,36 @@ public Collection getAllRootPaths() { } /** - * Return the full paths for each mapped endpoint. + * Return the full paths for each mapped endpoint (excluding additional paths). * @return all root paths */ public Collection getAllPaths() { return stream().map(this::getPath).toList(); } + /** + * Return the additional paths for each mapped endpoint. + * @param webServerNamespace the web server namespace + * @param endpointId the endpoint ID + * @return all additional paths + * @since 3.4.0 + */ + public Collection getAdditionalPaths(WebServerNamespace webServerNamespace, EndpointId endpointId) { + return getAdditionalPaths(webServerNamespace, getEndpoint(endpointId)).toList(); + } + + private Stream getAdditionalPaths(WebServerNamespace webServerNamespace, PathMappedEndpoint endpoint) { + List additionalPaths = (endpoint != null) ? endpoint.getAdditionalPaths(webServerNamespace) : null; + if (CollectionUtils.isEmpty(additionalPaths)) { + return Stream.empty(); + } + return additionalPaths.stream().map(this::getAdditionalPath); + } + + private String getAdditionalPath(String path) { + return path.startsWith("/") ? path : "/" + path; + } + /** * Return the {@link PathMappedEndpoint} with the given ID or {@code null} if the * endpoint cannot be found. diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespace.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespace.java index 97b1ccee36d0..f0637492d01e 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespace.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespace.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -19,7 +19,8 @@ import org.springframework.util.StringUtils; /** - * Enumeration of server namespaces. + * A web server namespace used for disambiguation when multiple web servers are running in + * the same application (for example a management context running on a different port). * * @author Phillip Webb * @author Madhura Bhave @@ -43,17 +44,14 @@ private WebServerNamespace(String value) { this.value = value; } + /** + * Return the value of the namespace. + * @return the value + */ public String getValue() { return this.value; } - public static WebServerNamespace from(String value) { - if (StringUtils.hasText(value)) { - return new WebServerNamespace(value); - } - return SERVER; - } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -71,4 +69,22 @@ public int hashCode() { return this.value.hashCode(); } + @Override + public String toString() { + return this.value; + } + + /** + * Factory method to create a new {@link WebServerNamespace} from a value. If the + * value is empty or {@code null} then {@link #SERVER} is returned. + * @param value the namespace value or {@code null} + * @return the web server namespace + */ + public static WebServerNamespace from(String value) { + if (StringUtils.hasText(value)) { + return new WebServerNamespace(value); + } + return SERVER; + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebEndpoint.java index b62f4f6dda92..7543284f4645 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/DiscoveredWebEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 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. @@ -17,12 +17,16 @@ package org.springframework.boot.actuate.endpoint.web.annotation; import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredEndpoint; import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.WebOperation; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; /** * A discovered {@link ExposableWebEndpoint web endpoint}. @@ -33,10 +37,14 @@ class DiscoveredWebEndpoint extends AbstractDiscoveredEndpoint imp private final String rootPath; + private Collection additionalPathsMappers; + DiscoveredWebEndpoint(EndpointDiscoverer discoverer, Object endpointBean, EndpointId id, String rootPath, - boolean enabledByDefault, Collection operations) { + boolean enabledByDefault, Collection operations, + Collection additionalPathsMappers) { super(discoverer, endpointBean, id, enabledByDefault, operations); this.rootPath = rootPath; + this.additionalPathsMappers = additionalPathsMappers; } @Override @@ -44,4 +52,15 @@ public String getRootPath() { return this.rootPath; } + @Override + public List getAdditionalPaths(WebServerNamespace webServerNamespace) { + return this.additionalPathsMappers.stream() + .flatMap((mapper) -> getAdditionalPaths(webServerNamespace, mapper)) + .toList(); + } + + private Stream getAdditionalPaths(WebServerNamespace webServerNamespace, AdditionalPathsMapper mapper) { + return mapper.getAdditionalPaths(getEndpointId(), webServerNamespace).stream(); + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer.java index 7b6dc7f510e9..aa8f9c3b0112 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.springframework.aot.hint.MemberCategory; @@ -29,6 +30,7 @@ import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMapper; @@ -51,6 +53,8 @@ public class WebEndpointDiscoverer extends EndpointDiscoverer endpointPathMappers; + private final List additionalPathsMappers; + private final RequestPredicateFactory requestPredicateFactory; /** @@ -61,13 +65,37 @@ public class WebEndpointDiscoverer extends EndpointDiscoverer endpointPathMappers, Collection invokerAdvisors, Collection> filters) { + this(applicationContext, parameterValueMapper, endpointMediaTypes, endpointPathMappers, Collections.emptyList(), + invokerAdvisors, filters); + } + + /** + * Create a new {@link WebEndpointDiscoverer} instance. + * @param applicationContext the source application context + * @param parameterValueMapper the parameter value mapper + * @param endpointMediaTypes the endpoint media types + * @param endpointPathMappers the endpoint path mappers + * @param additionalPathsMappers the + * @param invokerAdvisors invoker advisors to apply + * @param filters filters to apply + * @since 3.4.0 + */ + public WebEndpointDiscoverer(ApplicationContext applicationContext, ParameterValueMapper parameterValueMapper, + EndpointMediaTypes endpointMediaTypes, List endpointPathMappers, + List additionalPathsMappers, Collection invokerAdvisors, + Collection> filters) { super(applicationContext, parameterValueMapper, invokerAdvisors, filters); - this.endpointPathMappers = endpointPathMappers; + this.endpointPathMappers = (endpointPathMappers != null) ? endpointPathMappers : Collections.emptyList(); + this.additionalPathsMappers = (additionalPathsMappers != null) ? additionalPathsMappers + : Collections.emptyList(); this.requestPredicateFactory = new RequestPredicateFactory(endpointMediaTypes); } @@ -75,7 +103,8 @@ public WebEndpointDiscoverer(ApplicationContext applicationContext, ParameterVal protected ExposableWebEndpoint createEndpoint(Object endpointBean, EndpointId id, boolean enabledByDefault, Collection operations) { String rootPath = PathMapper.getRootPath(this.endpointPathMappers, id); - return new DiscoveredWebEndpoint(this, endpointBean, id, rootPath, enabledByDefault, operations); + return new DiscoveredWebEndpoint(this, endpointBean, id, rootPath, enabledByDefault, operations, + this.additionalPathsMappers); } @Override diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpointsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpointsTests.java index 37e3596fdfb0..1d5b2b466c15 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpointsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpointsTests.java @@ -129,19 +129,36 @@ void getEndpointWhenMissingIdShouldReturnNull() { assertThat(mapped.getEndpoint(EndpointId.of("xx"))).isNull(); } + @Test + void getAdditionalPathsShouldReturnCanonicalAdditionalPaths() { + PathMappedEndpoints mapped = createTestMapped(null); + assertThat(mapped.getAdditionalPaths(WebServerNamespace.SERVER, EndpointId.of("e2"))).containsExactly("/a2", + "/A2"); + assertThat(mapped.getAdditionalPaths(WebServerNamespace.MANAGEMENT, EndpointId.of("e2"))).isEmpty(); + assertThat(mapped.getAdditionalPaths(WebServerNamespace.SERVER, EndpointId.of("e3"))).isEmpty(); + } + private PathMappedEndpoints createTestMapped(String basePath) { List> endpoints = new ArrayList<>(); endpoints.add(mockEndpoint(EndpointId.of("e1"))); - endpoints.add(mockEndpoint(EndpointId.of("e2"), "p2")); + endpoints.add(mockEndpoint(EndpointId.of("e2"), "p2", WebServerNamespace.SERVER, List.of("/a2", "A2"))); endpoints.add(mockEndpoint(EndpointId.of("e3"), "p3")); endpoints.add(mockEndpoint(EndpointId.of("e4"))); return new PathMappedEndpoints(basePath, () -> endpoints); } private TestPathMappedEndpoint mockEndpoint(EndpointId id, String rootPath) { + return mockEndpoint(id, rootPath, null, null); + } + + private TestPathMappedEndpoint mockEndpoint(EndpointId id, String rootPath, WebServerNamespace webServerNamespace, + List additionalPaths) { TestPathMappedEndpoint endpoint = mock(TestPathMappedEndpoint.class); given(endpoint.getEndpointId()).willReturn(id); given(endpoint.getRootPath()).willReturn(rootPath); + if (webServerNamespace != null && additionalPaths != null) { + given(endpoint.getAdditionalPaths(webServerNamespace)).willReturn(additionalPaths); + } return endpoint; } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespaceTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespaceTests.java index 91eaebb7f0e9..add2c43fc2e3 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespaceTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/WebServerNamespaceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -53,4 +53,9 @@ void namespaceWithDifferentValuesAreNotEqual() { assertThat(WebServerNamespace.from("value")).isNotEqualTo(WebServerNamespace.from("other")); } + @Test + void toStringReturnsString() { + assertThat(WebServerNamespace.from("value")).hasToString("value"); + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/BaseConfiguration.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/BaseConfiguration.java index 6645dc5b64a9..55ef50552b88 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/BaseConfiguration.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/BaseConfiguration.java @@ -68,7 +68,8 @@ WebEndpointDiscoverer webEndpointDiscoverer(EndpointMediaTypes endpointMediaType ParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper( DefaultConversionService.getSharedInstance()); return new WebEndpointDiscoverer(applicationContext, parameterMapper, endpointMediaTypes, - pathMappers.orderedStream().toList(), Collections.emptyList(), Collections.emptyList()); + pathMappers.orderedStream().toList(), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); } @Bean diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscovererTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscovererTests.java index 02cc46336e3a..def37498209a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscovererTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscovererTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -43,12 +43,14 @@ import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvoker; import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpoint; +import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMapper; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate; +import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer.WebEndpointDiscovererRuntimeHints; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -223,6 +225,23 @@ void getEndpointsWhenHasCustomPathShouldReturnCustomPath() { }); } + @Test + void getEndpointsWhenHasAdditionalPaths() { + AdditionalPathsMapper additionalPathsMapper = (id, webServerNamespace) -> { + if (!WebServerNamespace.SERVER.equals(webServerNamespace)) { + return Collections.emptyList(); + } + return List.of("/test"); + }; + load((id) -> null, EndpointId::toString, additionalPathsMapper, + AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> { + Map endpoints = mapEndpoints(discoverer.getEndpoints()); + ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("test")); + assertThat(endpoint.getAdditionalPaths(WebServerNamespace.SERVER)).containsExactly("/test"); + assertThat(endpoint.getAdditionalPaths(WebServerNamespace.MANAGEMENT)).isEmpty(); + }); + } + @Test void shouldRegisterHints() { RuntimeHints runtimeHints = new RuntimeHints(); @@ -230,7 +249,6 @@ void shouldRegisterHints() { assertThat(RuntimeHintsPredicates.reflection() .onType(WebEndpointFilter.class) .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(runtimeHints); - } private void load(Class configuration, Consumer consumer) { @@ -239,6 +257,12 @@ private void load(Class configuration, Consumer consum private void load(Function timeToLive, PathMapper endpointPathMapper, Class configuration, Consumer consumer) { + load(timeToLive, endpointPathMapper, null, configuration, consumer); + } + + private void load(Function timeToLive, PathMapper endpointPathMapper, + AdditionalPathsMapper additionalPathsMapper, Class configuration, + Consumer consumer) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configuration)) { ConversionServiceParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper( DefaultConversionService.getSharedInstance()); @@ -246,6 +270,7 @@ private void load(Function timeToLive, PathMapper endpointPath Collections.singletonList("application/json")); WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(context, parameterMapper, mediaTypes, Collections.singletonList(endpointPathMapper), + (additionalPathsMapper != null) ? Collections.singletonList(additionalPathsMapper) : null, Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)), Collections.emptyList()); consumer.accept(discoverer); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java index c0ac708f205e..ffc396f20e7e 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java @@ -243,7 +243,7 @@ private void customize(ResourceConfig config) { EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), - Collections.emptyList()); + Collections.emptyList(), Collections.emptyList()); Collection resources = new JerseyEndpointResourceFactory().createEndpointResources( new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, new EndpointLinksResolver(discoverer.getEndpoints()), true); @@ -288,8 +288,8 @@ HttpHandler httpHandler(ApplicationContext applicationContext) { WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping() { EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, - new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), - Collections.emptyList()); + new ConversionServiceParameterValueMapper(), endpointMediaTypes, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); return new WebFluxEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), true); @@ -317,8 +317,8 @@ TomcatServletWebServerFactory tomcat() { WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() { EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, - new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), - Collections.emptyList()); + new ConversionServiceParameterValueMapper(), endpointMediaTypes, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), true); From d137191494b81ddc3710f06e10c79d1e8c695796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 10:17:23 +0200 Subject: [PATCH 264/271] Next development version (v3.2.11-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dbc63ad70574..679d01153780 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.2.10-SNAPSHOT +version=3.2.11-SNAPSHOT spring.build-type=oss org.gradle.caching=true From 94e8c5db36a432a7e1340de9acc4006adea1639b Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Wed, 18 Sep 2024 23:58:36 +0900 Subject: [PATCH 265/271] Polish gh-39957 and gh-41444 See gh-42359 --- .../reference/pages/features/logging.adoc | 4 ++-- .../boot/logging/LoggingSystemProperty.java | 1 + .../EnclosedInSquareBracketsConverter.java | 4 ++-- .../EnclosedInSquareBracketsConverter.java | 2 +- .../logging/LoggingSystemPropertiesTests.java | 6 +++--- .../log4j2/Log4J2LoggingSystemTests.java | 18 ++++++++---------- .../logback/LogbackLoggingSystemTests.java | 8 +++----- .../src/main/resources/log4j2.xml | 2 +- 8 files changed, 21 insertions(+), 24 deletions(-) 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 0dba5146f276..412fc2b83c41 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 @@ -712,13 +712,13 @@ The following listing shows three sample profiles: If you want to refer to properties from your Spring `Environment` within your Log4j2 configuration you can use `spring:` prefixed https://logging.apache.org/log4j/2.x/manual/lookups.html[lookups]. Doing so can be useful if you want to access values from your `application.properties` file in your Log4j2 configuration. -The following example shows how to set a Log4j2 property named `applicationName` and `applicationGroup` that reads `spring.application.name` and `spring.application.group` from the Spring `Environment`: +The following example shows how to set Log4j2 properties named `applicationName` and `applicationGroup` that read `spring.application.name` and `spring.application.group` from the Spring `Environment`: [source,xml] ---- ${spring:spring.application.name} - ${spring:spring.application.property} + ${spring:spring.application.group} ---- diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperty.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperty.java index d651d5684523..c06f803351d7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperty.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperty.java @@ -32,6 +32,7 @@ public enum LoggingSystemProperty { /** * Logging system property for the application group that should be logged. + * @since 3.4.0 */ APPLICATION_GROUP("APPLICATION_GROUP", "spring.application.group", "logging.include-application-group"), diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/EnclosedInSquareBracketsConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/EnclosedInSquareBracketsConverter.java index 907a5d710b25..190e23d869f1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/EnclosedInSquareBracketsConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/EnclosedInSquareBracketsConverter.java @@ -29,8 +29,8 @@ import org.apache.logging.log4j.core.pattern.PatternParser; /** - * Log4j2 {@link LogEventPatternConverter} used help format optional values that should be - * shown enclosed in square brackets. + * Log4j2 {@link LogEventPatternConverter} used to help format optional values that should + * be shown enclosed in square brackets. * * @author Phillip Webb * @since 3.4.0 diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/EnclosedInSquareBracketsConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/EnclosedInSquareBracketsConverter.java index e90fa5c6f20a..b9474cc364d5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/EnclosedInSquareBracketsConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/EnclosedInSquareBracketsConverter.java @@ -22,7 +22,7 @@ import org.springframework.util.StringUtils; /** - * Logback {@link CompositeConverter} used help format optional values that should be + * Logback {@link CompositeConverter} used to help format optional values that should be * shown enclosed in square brackets. * * @author Phillip Webb diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemPropertiesTests.java index 7b3763799f22..72308c2f98f7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/LoggingSystemPropertiesTests.java @@ -164,19 +164,19 @@ void legacyLoggedApplicationNameWhenHasApplicationName() { } @Test - void loggedApplicationGroupWhenHasApplicationGroup() { + void applicationGroupWhenHasApplicationGroup() { new LoggingSystemProperties(new MockEnvironment().withProperty("spring.application.group", "test")).apply(null); assertThat(getSystemProperty(LoggingSystemProperty.APPLICATION_GROUP)).isEqualTo("test"); } @Test - void loggedApplicationGroupWhenHasNoApplicationGroup() { + void applicationGroupWhenHasNoApplicationGroup() { new LoggingSystemProperties(new MockEnvironment()).apply(null); assertThat(getSystemProperty(LoggingSystemProperty.APPLICATION_GROUP)).isNull(); } @Test - void loggedApplicationGroupWhenApplicationGroupLoggingDisabled() { + void applicationGroupWhenApplicationGroupLoggingDisabled() { new LoggingSystemProperties(new MockEnvironment().withProperty("spring.application.group", "test") .withProperty("logging.include-application-group", "false")).apply(null); assertThat(getSystemProperty(LoggingSystemProperty.APPLICATION_GROUP)).isNull(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index dd7446367260..fccfc2f8f157 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -586,7 +586,7 @@ void applicationNameLoggingToConsoleWhenHasApplicationNameWithParenthesis(Captur @Test void applicationNameLoggingToConsoleWhenDisabled(CapturedOutput output) { - this.environment.setProperty("spring.application.name", "application-name"); + this.environment.setProperty("spring.application.name", "myapp"); this.environment.setProperty("logging.include-application-name", "false"); this.loggingSystem.setStandardConfigLocations(false); this.loggingSystem.beforeInitialize(); @@ -625,7 +625,7 @@ void applicationNameLoggingToFileWhenHasApplicationNameWithParenthesis() { @Test void applicationNameLoggingToFileWhenDisabled() { - this.environment.setProperty("spring.application.name", "application-name"); + this.environment.setProperty("spring.application.name", "myapp"); this.environment.setProperty("logging.include-application-name", "false"); new LoggingSystemProperties(this.environment).apply(); File file = new File(tmpDir(), "log4j2-test.log"); @@ -661,15 +661,14 @@ void applicationGroupLoggingToConsoleWhenHasApplicationGroupWithParenthesis(Capt @Test void applicationGroupLoggingToConsoleWhenDisabled(CapturedOutput output) { - this.environment.setProperty("spring.application.group", "application-group"); + this.environment.setProperty("spring.application.group", "mygroup"); this.environment.setProperty("logging.include-application-group", "false"); this.loggingSystem.setStandardConfigLocations(false); this.loggingSystem.beforeInitialize(); this.loggingSystem.initialize(this.initializationContext, null, null); this.logger.info("Hello world"); - assertThat(getLineWithText(output, "Hello world")).doesNotContain("${sys:LOGGED_APPLICATION_GROUP}") - .doesNotContain("${sys:APPLICATION_GROUP}") - .doesNotContain("myapp"); + assertThat(getLineWithText(output, "Hello world")).doesNotContain("${sys:APPLICATION_GROUP}") + .doesNotContain("mygroup"); } @Test @@ -700,7 +699,7 @@ void applicationGroupLoggingToFileWhenHasApplicationGroupWithParenthesis() { @Test void applicationGroupLoggingToFileWhenDisabled() { - this.environment.setProperty("spring.application.group", "application-group"); + this.environment.setProperty("spring.application.group", "mygroup"); this.environment.setProperty("logging.include-application-group", "false"); new LoggingSystemProperties(this.environment).apply(); File file = new File(tmpDir(), "log4j2-test.log"); @@ -709,9 +708,8 @@ void applicationGroupLoggingToFileWhenDisabled() { this.loggingSystem.beforeInitialize(); this.loggingSystem.initialize(this.initializationContext, null, logFile); this.logger.info("Hello world"); - assertThat(getLineWithText(file, "Hello world")).doesNotContain("${sys:LOGGED_APPLICATION_GROUP}") - .doesNotContain("${sys:APPLICATION_GROUP}") - .doesNotContain("myapp"); + assertThat(getLineWithText(file, "Hello world")).doesNotContain("${sys:APPLICATION_GROUP}") + .doesNotContain("mygroup"); } @Test diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 642fc388ef80..239ae546bb8c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -20,7 +20,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; import java.util.List; @@ -571,8 +570,7 @@ void initializeShouldApplyLogbackSystemPropertiesToTheContext() { Stream.of(LoggingSystemProperty.values()) .map(LoggingSystemProperty::getEnvironmentVariableName) .forEach(expectedProperties::add); - expectedProperties - .removeAll(Arrays.asList("LOG_FILE", "LOG_PATH", "LOGGED_APPLICATION_NAME", "LOGGED_APPLICATION_GROUP")); + expectedProperties.removeAll(List.of("LOG_FILE", "LOG_PATH")); expectedProperties.add("org.jboss.logging.provider"); expectedProperties.add("LOG_CORRELATION_PATTERN"); expectedProperties.add("CONSOLE_LOG_STRUCTURED_FORMAT"); @@ -822,7 +820,7 @@ void applicationNameLoggingToFileWhenHasApplicationNameWithParenthesis() { } @Test - void applicationNameLoggingToFileWhenDisabled(CapturedOutput output) { + void applicationNameLoggingToFileWhenDisabled() { this.environment.setProperty("spring.application.name", "myapp"); this.environment.setProperty("logging.include-application-name", "false"); File file = new File(tmpDir(), "logback-test.log"); @@ -939,7 +937,7 @@ void applicationGroupLoggingToFileWhenHasApplicationGroupWithParenthesis() { } @Test - void applicationGroupLoggingToFileWhenDisabled(CapturedOutput output) { + void applicationGroupLoggingToFileWhenDisabled() { this.environment.setProperty("spring.application.group", "myGroup"); this.environment.setProperty("logging.include-application-group", "false"); File file = new File(tmpDir(), "logback-test.log"); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-log4j2/src/main/resources/log4j2.xml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-log4j2/src/main/resources/log4j2.xml index ba8b3efd9caa..3f36c38809b5 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-log4j2/src/main/resources/log4j2.xml +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator-log4j2/src/main/resources/log4j2.xml @@ -2,7 +2,7 @@ ???? - %clr{%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{${sys:LOGGED_APPLICATION_NAME:-}${sys:LOGGED_APPLICATION_GROUP:-}[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx + %clr{%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{${sys:APPLICATION_NAME:-}${sys:APPLICATION_GROUP:-}[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx From 814369e8b05621439a963f37d2c2b50f1bb3c329 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Sep 2024 10:58:53 +0100 Subject: [PATCH 266/271] Enable graceful shutdown by default Closes gh-37495 --- .../autoconfigure/web/ServerProperties.java | 2 +- ...ervletWebServerFactoryCustomizerTests.java | 4 +- .../pages/web/graceful-shutdown.adoc | 42 ++++++++++++------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 8647ddae3afb..0142e34a6916 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -109,7 +109,7 @@ public class ServerProperties { /** * Type of shutdown that the server will support. */ - private Shutdown shutdown = Shutdown.IMMEDIATE; + private Shutdown shutdown = Shutdown.GRACEFUL; @NestedConfigurationProperty private Ssl ssl; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java index 66e0ae477108..73e1348c3560 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java @@ -186,11 +186,11 @@ void sessionStoreDir() { @Test void whenShutdownPropertyIsSetThenShutdownIsCustomized() { Map map = new HashMap<>(); - map.put("server.shutdown", "graceful"); + map.put("server.shutdown", "immediate"); bindProperties(map); ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class); this.customizer.customize(factory); - then(factory).should().setShutdown(assertArg((shutdown) -> assertThat(shutdown).isEqualTo(Shutdown.GRACEFUL))); + then(factory).should().setShutdown(assertArg((shutdown) -> assertThat(shutdown).isEqualTo(Shutdown.IMMEDIATE))); } private void bindProperties(Map map) { diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/graceful-shutdown.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/graceful-shutdown.adoc index 8490b8babae5..02a02304e9ee 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/graceful-shutdown.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/graceful-shutdown.adoc @@ -1,37 +1,47 @@ [[web.graceful-shutdown]] = Graceful Shutdown -Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications. +Graceful shutdown is enabled by default with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications. It occurs as part of closing the application context and is performed in the earliest phase of stopping `SmartLifecycle` beans. This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted. +To configure the timeout period, configure the configprop:spring.lifecycle.timeout-per-shutdown-phase[] property, as shown in the following example: + +[configprops,yaml] +---- +spring: + lifecycle: + timeout-per-shutdown-phase: "20s" +---- + +NOTE: Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later. + +IMPORTANT: Shutdown in your IDE may be immediate rather than graceful if it does not send a proper `SIGTERM` signal. +See the documentation of your IDE for more details. + + + +[[web.graceful-shutdown.rejecting-requests-during-the-grace-period]] +== Rejecting Requests During the Grace Period + The exact way in which new requests are not permitted varies depending on the web server that is being used. Implementations may stop accepting requests at the network layer, or they may return a response with a specific HTTP status code or HTTP header. The use of persistent connections can also change the way that requests stop being accepted. -TIP: To learn about more the specific method used with your web server, see the `shutDownGracefully` API documentation for javadoc:org.springframework.boot.web.embedded.tomcat.TomcatWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.netty.NettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.jetty.JettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[] or javadoc:org.springframework.boot.web.embedded.undertow.UndertowWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[]. +TIP: To learn more about the specific method used with your web server, see the `shutDownGracefully` API documentation for javadoc:org.springframework.boot.web.embedded.tomcat.TomcatWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.netty.NettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[], javadoc:org.springframework.boot.web.embedded.jetty.JettyWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[] or javadoc:org.springframework.boot.web.embedded.undertow.UndertowWebServer#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[]. Jetty, Reactor Netty, and Tomcat will stop accepting new requests at the network layer. Undertow will accept new connections but respond immediately with a service unavailable (503) response. -NOTE: Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later. -To enable graceful shutdown, configure the configprop:server.shutdown[] property, as shown in the following example: -[configprops,yaml] ----- -server: - shutdown: "graceful" ----- +[[web.graceful-shutdown.disabling-graceful-shutdown]] +== Disabling Graceful Shutdown -To configure the timeout period, configure the configprop:spring.lifecycle.timeout-per-shutdown-phase[] property, as shown in the following example: +To disable graceful shutdown, configure the configprop:server.shutdown[] property, as shown in the following example: [configprops,yaml] ---- -spring: - lifecycle: - timeout-per-shutdown-phase: "20s" +server: + shutdown: "immediate" ---- - -IMPORTANT: Using graceful shutdown with your IDE may not work properly if it does not send a proper `SIGTERM` signal. -See the documentation of your IDE for more details. From 627c691616c77c42b22c728f1145c5d20716a842 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Sep 2024 11:22:08 +0100 Subject: [PATCH 267/271] Document the reason for deprecation of clean-on-validation-error See gh-42295 --- .../boot/autoconfigure/flyway/FlywayProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java index 370dc85e5b11..a78e654e7882 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java @@ -598,7 +598,7 @@ public void setCleanDisabled(boolean cleanDisabled) { } @Deprecated(since = "3.4.0", forRemoval = true) - @DeprecatedConfigurationProperty(since = "3.4.0") + @DeprecatedConfigurationProperty(since = "3.4.0", reason = "Deprecated in Flyway 10.18") public boolean isCleanOnValidationError() { return this.cleanOnValidationError; } From 6346d4fd6b0702ee84198899e21068f0cbfd6f02 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 19 Sep 2024 12:03:04 +0100 Subject: [PATCH 268/271] Accommodate absence of last execution A task's last execution is absent if it has not yet been executed. This commit updates the documentation test to accommodate this possibility. See gh-42351 --- .../ScheduledTasksEndpointDocumentationTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java index 5e8e7c833d1c..e63717bccd87 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java @@ -77,7 +77,8 @@ void scheduledTasks() { fieldWithPath("custom.[].trigger").description("Trigger for the task.")) .andWithPrefix("*.[].", fieldWithPath("lastExecution").description("Last execution of this task, if any.") - .optional()) + .optional() + .type(JsonFieldType.OBJECT)) .andWithPrefix("*.[].lastExecution.", lastExecution()))); } @@ -98,7 +99,8 @@ private FieldDescriptor nextExecutionWithPrefix(String prefix) { private FieldDescriptor[] lastExecution() { return new FieldDescriptor[] { - fieldWithPath("status").description("Status of the last execution (STARTED, SUCCESS, ERROR)."), + fieldWithPath("status").description("Status of the last execution (STARTED, SUCCESS, ERROR).") + .type(JsonFieldType.STRING), fieldWithPath("time").description("Time of the last execution.").type(JsonFieldType.STRING), fieldWithPath("exception.type").description("Exception type thrown by the task, if any.") .type(JsonFieldType.STRING) From ae898ed383f4a6a843a22a79cf70725719c7844c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 13:46:37 +0200 Subject: [PATCH 269/271] Next development version (v3.3.5-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5dd8b0065247..fee3f5fc4f87 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=3.3.4-SNAPSHOT +version=3.3.5-SNAPSHOT latestVersion=false spring.build-type=oss From 09b57eff766a04bc9286f89da3242eb710f6f72f Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Tue, 17 Sep 2024 18:42:54 +0300 Subject: [PATCH 270/271] Add option for configuring max messages per task See gh-42341 --- ...efaultJmsListenerContainerFactoryConfigurer.java | 1 + .../boot/autoconfigure/jms/JmsProperties.java | 13 +++++++++++++ .../jms/JmsAutoConfigurationTests.java | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java index 66ab3db1ee6c..be9d767d0f26 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java @@ -135,6 +135,7 @@ public void configure(DefaultJmsListenerContainerFactory factory, ConnectionFact map.from(listenerProperties::isAutoStartup).to(factory::setAutoStartup); map.from(listenerProperties::formatConcurrency).to(factory::setConcurrency); map.from(listenerProperties::getReceiveTimeout).as(Duration::toMillis).to(factory::setReceiveTimeout); + map.from(listenerProperties::getMaxMessagesPerTask).to(factory::setMaxMessagesPerTask); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java index 7d87ec9169cb..051c12ef6b81 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java @@ -186,6 +186,11 @@ public static class Listener { */ private Duration receiveTimeout = Duration.ofSeconds(1); + /** + * Specify the maximum number of messages to process in one task. + */ + private Integer maxMessagesPerTask; + private final Session session = new Session(); public boolean isAutoStartup() { @@ -250,6 +255,14 @@ public void setReceiveTimeout(Duration receiveTimeout) { this.receiveTimeout = receiveTimeout; } + public Integer getMaxMessagesPerTask() { + return this.maxMessagesPerTask; + } + + public void setMaxMessagesPerTask(Integer maxMessagesPerTask) { + this.maxMessagesPerTask = maxMessagesPerTask; + } + public Session getSession() { return this.session; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java index 906b9f390ffd..91d0589e7591 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java @@ -176,7 +176,8 @@ void testJmsListenerContainerFactoryWithCustomSettings() { "spring.jms.listener.session.acknowledgeMode=client", "spring.jms.listener.session.transacted=false", "spring.jms.listener.minConcurrency=2", "spring.jms.listener.receiveTimeout=2s", "spring.jms.listener.maxConcurrency=10", - "spring.jms.subscription-durable=true", "spring.jms.client-id=exampleId") + "spring.jms.subscription-durable=true", "spring.jms.client-id=exampleId", + "spring.jms.listener.max-messages-per-task=10") .run(this::testJmsListenerContainerFactoryWithCustomSettings); } @@ -188,6 +189,7 @@ private void testJmsListenerContainerFactoryWithCustomSettings(AssertableApplica assertThat(container.getConcurrentConsumers()).isEqualTo(2); assertThat(container.getMaxConcurrentConsumers()).isEqualTo(10); assertThat(container).hasFieldOrPropertyWithValue("receiveTimeout", 2000L); + assertThat(container).hasFieldOrPropertyWithValue("maxMessagesPerTask", 10); assertThat(container.isSubscriptionDurable()).isTrue(); assertThat(container.getClientId()).isEqualTo("exampleId"); } From e930a963aded6ba4374e615d27c1ef0f6e9eec68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 19 Sep 2024 14:19:01 +0200 Subject: [PATCH 271/271] Polish "Add option for configuring max messages per task" See gh-42341 --- .../springframework/boot/autoconfigure/jms/JmsProperties.java | 4 +++- .../boot/autoconfigure/jms/JmsAutoConfigurationTests.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java index 051c12ef6b81..aaf6c8a9ada6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java @@ -187,7 +187,9 @@ public static class Listener { private Duration receiveTimeout = Duration.ofSeconds(1); /** - * Specify the maximum number of messages to process in one task. + * Specify the maximum number of messages to process in one task. By default, + * unlimited unless a SchedulingTaskExecutor is configured on the listener (10 + * messages), as it indicates a preference for short-lived tasks. */ private Integer maxMessagesPerTask; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java index 91d0589e7591..7df4a2ad49c7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java @@ -177,7 +177,7 @@ void testJmsListenerContainerFactoryWithCustomSettings() { "spring.jms.listener.session.transacted=false", "spring.jms.listener.minConcurrency=2", "spring.jms.listener.receiveTimeout=2s", "spring.jms.listener.maxConcurrency=10", "spring.jms.subscription-durable=true", "spring.jms.client-id=exampleId", - "spring.jms.listener.max-messages-per-task=10") + "spring.jms.listener.max-messages-per-task=5") .run(this::testJmsListenerContainerFactoryWithCustomSettings); } @@ -189,7 +189,7 @@ private void testJmsListenerContainerFactoryWithCustomSettings(AssertableApplica assertThat(container.getConcurrentConsumers()).isEqualTo(2); assertThat(container.getMaxConcurrentConsumers()).isEqualTo(10); assertThat(container).hasFieldOrPropertyWithValue("receiveTimeout", 2000L); - assertThat(container).hasFieldOrPropertyWithValue("maxMessagesPerTask", 10); + assertThat(container).hasFieldOrPropertyWithValue("maxMessagesPerTask", 5); assertThat(container.isSubscriptionDurable()).isTrue(); assertThat(container.getClientId()).isEqualTo("exampleId"); }