From 33afbcc0925c86dd5b492a1fc1e968eba954e711 Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 22 Sep 2022 09:10:06 +0000 Subject: [PATCH 01/84] Next development version (v2.6.13-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 837b553c6a62..faa7df26f6d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=2.6.12-SNAPSHOT +version=2.6.13-SNAPSHOT org.gradle.caching=true org.gradle.parallel=true From f1d7e21752c166bcfbe8c24b5b3625575b60f10f Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 22 Sep 2022 11:07:36 +0000 Subject: [PATCH 02/84] Next development version (v2.7.5-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6b6069422c6c..5d1ca241dc40 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=2.7.4-SNAPSHOT +version=2.7.5-SNAPSHOT org.gradle.caching=true org.gradle.parallel=true From cfac7f55a4aa1e55c26ce764259862169a931d2c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 26 Sep 2022 13:13:16 +0100 Subject: [PATCH 03/84] Correct annotations on BootBuildImage's file-based inputs Closes gh-32495 --- .../boot/gradle/tasks/bundling/BootBuildImage.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java index 22acd8b05fdb..6cd092ac73c4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java @@ -32,8 +32,11 @@ import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; import org.gradle.work.DisableCachingByDefault; @@ -121,7 +124,8 @@ public BootBuildImage() { * Returns the property for the archive file from which the image will be built. * @return the archive file property */ - @Input + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) public RegularFileProperty getArchiveFile() { return this.archiveFile; } @@ -132,7 +136,8 @@ public RegularFileProperty getArchiveFile() { * @deprecated since 2.5.0 for removal in 2.7.0 in favor of {@link #getArchiveFile()} */ @Deprecated - @Input + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) public RegularFileProperty getJar() { return this.archiveFile; } From 81598b51f9be687026beeab9bfe06808aa25999a Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Mon, 26 Sep 2022 14:18:17 -0500 Subject: [PATCH 04/84] Use task outputs in distZip Paketo system tests Using the outputs from `distZip` and `bootDistZip` as input to the `bootBuildImage` task instead of indirectly using the output file path prevents Gradle warnings about implicit task dependencies. Fixes gh-32506 --- .../image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle | 2 +- .../image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle index b363aae0f848..0c76d495d71b 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle @@ -37,5 +37,5 @@ application { } bootBuildImage { - archiveFile = new File("${buildDir}/distributions/${project.name}-boot.zip") + archiveFile = bootDistZip.archiveFile } \ No newline at end of file diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle index 261af47bf740..797ccb88293f 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle @@ -37,5 +37,5 @@ application { } bootBuildImage { - archiveFile = new File("${buildDir}/distributions/${project.name}.zip") + archiveFile = distZip.archiveFile } \ No newline at end of file From cee353fba6e50008b464209835141e8cd69aca9c Mon Sep 17 00:00:00 2001 From: dreis2211 Date: Mon, 26 Sep 2022 19:57:19 +0200 Subject: [PATCH 05/84] Avoid using JUnit 4 assertions See gh-32504 --- .../client/TestRestTemplateExtensionsTests.kt | 10 ++++---- .../boot/SpringApplicationExtensionsTests.kt | 23 ++++++++----------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt index 854831660c07..6501d3afa814 100644 --- a/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt +++ b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * 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. @@ -18,7 +18,7 @@ package org.springframework.boot.test.web.client import io.mockk.mockk import io.mockk.verify -import org.junit.Assert +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.core.ParameterizedTypeReference import org.springframework.http.HttpEntity @@ -251,9 +251,9 @@ class TestRestTemplateExtensionsTests { .apply { addAll(method.parameterTypes.filter { it != kClass.java }) } val f = extensions.getDeclaredMethod(method.name, *parameters.toTypedArray()).kotlinFunction!! - Assert.assertEquals(1, f.typeParameters.size) - Assert.assertEquals(listOf(Any::class.createType()), - f.typeParameters[0].upperBounds) + assertThat(f.typeParameters.size).isEqualTo(1) + assertThat(listOf(Any::class.createType())) + .isEqualTo(f.typeParameters[0].upperBounds) } } } diff --git a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt index e35d774b5ba3..caff58f3d2b3 100644 --- a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt +++ b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-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. @@ -15,10 +15,7 @@ */ package org.springframework.boot -import org.junit.Assert.assertArrayEquals -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.beans.factory.getBean @@ -37,7 +34,7 @@ class SpringApplicationExtensionsTests { @Test fun `Kotlin runApplication() top level function`() { val context = runApplication() - assertNotNull(context) + assertThat(context).isNotNull } @Test @@ -46,16 +43,16 @@ class SpringApplicationExtensionsTests { val context = runApplication { setEnvironment(environment) } - assertNotNull(context) - assertEquals(environment, context.environment) + assertThat(context).isNotNull + assertThat(environment).isEqualTo(context.environment) } @Test fun `Kotlin runApplication(arg1, arg2) top level function`() { val context = runApplication("--debug", "spring", "boot") val args = context.getBean() - assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) - assertTrue(args.containsOption("debug")) + assertThat(args.nonOptionArgs.toTypedArray()).containsExactly("spring", "boot") + assertThat(args.containsOption("debug")).isTrue } @Test @@ -65,9 +62,9 @@ class SpringApplicationExtensionsTests { setEnvironment(environment) } val args = context.getBean() - assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) - assertTrue(args.containsOption("debug")) - assertEquals(environment, context.environment) + assertThat(args.nonOptionArgs.toTypedArray()).containsExactly("spring", "boot") + assertThat(args.containsOption("debug")).isTrue + assertThat(environment).isEqualTo(context.environment) } @Configuration(proxyBeanMethods = false) From 0457536d721a62b4d64ce100b2f6492581f83eb0 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 27 Sep 2022 08:24:03 +0200 Subject: [PATCH 06/84] Polish "Avoid using JUnit 4 assertions" See gh-32504 --- .../springframework/boot/SpringApplicationExtensionsTests.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt index caff58f3d2b3..d1da352d9e75 100644 --- a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt +++ b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt @@ -52,7 +52,7 @@ class SpringApplicationExtensionsTests { val context = runApplication("--debug", "spring", "boot") val args = context.getBean() assertThat(args.nonOptionArgs.toTypedArray()).containsExactly("spring", "boot") - assertThat(args.containsOption("debug")).isTrue + assertThat(args.containsOption("debug")).isEqualTo(true) } @Test @@ -63,7 +63,7 @@ class SpringApplicationExtensionsTests { } val args = context.getBean() assertThat(args.nonOptionArgs.toTypedArray()).containsExactly("spring", "boot") - assertThat(args.containsOption("debug")).isTrue + assertThat(args.containsOption("debug")).isEqualTo(true) assertThat(environment).isEqualTo(context.environment) } From 975affc49790d80a3e8f151398ada7afdc010d96 Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Mon, 26 Sep 2022 23:53:43 +0900 Subject: [PATCH 07/84] Enable LoaderIntegrationTests with Java 19 See gh-32501 --- .../springframework/boot/loader/LoaderIntegrationTests.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/java/org/springframework/boot/loader/LoaderIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/java/org/springframework/boot/loader/LoaderIntegrationTests.java index 5f8fd546e2fd..e51af0d8e74d 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/java/org/springframework/boot/loader/LoaderIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/java/org/springframework/boot/loader/LoaderIntegrationTests.java @@ -23,8 +23,6 @@ import java.util.function.Supplier; import java.util.stream.Stream; -import org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.GenericContainer; @@ -46,7 +44,6 @@ * @author Phillip Webb */ @DisabledIfDockerUnavailable -@EnabledForJreRange(max = JRE.JAVA_18) class LoaderIntegrationTests { private final ToStringConsumer output = new ToStringConsumer(); @@ -81,7 +78,7 @@ static Stream javaRuntimes() { javaRuntimes.add(JavaRuntime.openJdk(JavaVersion.EIGHT)); javaRuntimes.add(JavaRuntime.openJdk(JavaVersion.ELEVEN)); javaRuntimes.add(JavaRuntime.openJdk(JavaVersion.SEVENTEEN)); - javaRuntimes.add(JavaRuntime.openJdk(JavaVersion.EIGHTEEN)); + javaRuntimes.add(JavaRuntime.openJdk(JavaVersion.NINETEEN)); javaRuntimes.add(JavaRuntime.oracleJdk17()); return javaRuntimes.stream().filter(JavaRuntime::isCompatible); } From f51c4c3df0bfd9cec77f048e959672fe6d82d5bb Mon Sep 17 00:00:00 2001 From: Mico Piira <--global> Date: Mon, 26 Sep 2022 21:19:10 +0300 Subject: [PATCH 08/84] Use non-blocking API in CouchbaseReactiveHealthIndicator See gh-32505 --- .../CouchbaseReactiveHealthIndicator.java | 7 ++++--- .../CouchbaseReactiveHealthIndicatorTests.java | 14 ++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java index b368f9306e1b..363ddc6c9e14 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java @@ -46,9 +46,10 @@ public CouchbaseReactiveHealthIndicator(Cluster cluster) { @Override protected Mono doHealthCheck(Health.Builder builder) { - DiagnosticsResult diagnostics = this.cluster.diagnostics(); - new CouchbaseHealth(diagnostics).applyTo(builder); - return Mono.just(builder.build()); + return this.cluster.reactive().diagnostics().map(diagnostics -> { + new CouchbaseHealth(diagnostics).applyTo(builder); + return builder.build(); + }); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java index adc00a875134..0106c562150a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java @@ -28,10 +28,12 @@ import com.couchbase.client.core.endpoint.EndpointState; import com.couchbase.client.core.service.ServiceType; import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.ReactiveCluster; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; +import reactor.core.publisher.Mono; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -52,13 +54,15 @@ void couchbaseClusterIsUp() { Collections.singletonList(new EndpointDiagnostics(ServiceType.KV, EndpointState.CONNECTED, "127.0.0.1", "127.0.0.1", Optional.empty(), Optional.of(1234L), Optional.of("endpoint-1")))); DiagnosticsResult diagnostics = new DiagnosticsResult(endpoints, "test-sdk", "test-id"); - given(cluster.diagnostics()).willReturn(diagnostics); + ReactiveCluster reactiveCluster = mock(ReactiveCluster.class); + given(reactiveCluster.diagnostics()).willReturn(Mono.just(diagnostics)); + given(cluster.reactive()).willReturn(reactiveCluster); Health health = healthIndicator.health().block(Duration.ofSeconds(30)); assertThat(health.getStatus()).isEqualTo(Status.UP); assertThat(health.getDetails()).containsEntry("sdk", "test-sdk"); assertThat(health.getDetails()).containsKey("endpoints"); assertThat((List>) health.getDetails().get("endpoints")).hasSize(1); - then(cluster).should().diagnostics(); + then(reactiveCluster).should().diagnostics(); } @Test @@ -73,13 +77,15 @@ void couchbaseClusterIsDown() { new EndpointDiagnostics(ServiceType.KV, EndpointState.CONNECTING, "127.0.0.1", "127.0.0.1", Optional.empty(), Optional.of(1234L), Optional.of("endpoint-2")))); DiagnosticsResult diagnostics = new DiagnosticsResult(endpoints, "test-sdk", "test-id"); - given(cluster.diagnostics()).willReturn(diagnostics); + ReactiveCluster reactiveCluster = mock(ReactiveCluster.class); + given(reactiveCluster.diagnostics()).willReturn(Mono.just(diagnostics)); + given(cluster.reactive()).willReturn(reactiveCluster); Health health = healthIndicator.health().block(Duration.ofSeconds(30)); assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat(health.getDetails()).containsEntry("sdk", "test-sdk"); assertThat(health.getDetails()).containsKey("endpoints"); assertThat((List>) health.getDetails().get("endpoints")).hasSize(2); - then(cluster).should().diagnostics(); + then(reactiveCluster).should().diagnostics(); } } From 8f598f876bf9ab82ff3de98961ea3a0da2b13e66 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 27 Sep 2022 08:34:22 +0200 Subject: [PATCH 09/84] Polish "Use non-blocking API in CouchbaseReactiveHealthIndicator" See gh-32505 --- .../actuate/couchbase/CouchbaseReactiveHealthIndicator.java | 3 +-- .../couchbase/CouchbaseReactiveHealthIndicatorTests.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java index 363ddc6c9e14..da470f16a0ed 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.couchbase; -import com.couchbase.client.core.diagnostics.DiagnosticsResult; import com.couchbase.client.java.Cluster; import reactor.core.publisher.Mono; @@ -46,7 +45,7 @@ public CouchbaseReactiveHealthIndicator(Cluster cluster) { @Override protected Mono doHealthCheck(Health.Builder builder) { - return this.cluster.reactive().diagnostics().map(diagnostics -> { + return this.cluster.reactive().diagnostics().map((diagnostics) -> { new CouchbaseHealth(diagnostics).applyTo(builder); return builder.build(); }); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java index 0106c562150a..5cf047147225 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicatorTests.java @@ -30,10 +30,10 @@ import com.couchbase.client.java.Cluster; import com.couchbase.client.java.ReactiveCluster; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; -import reactor.core.publisher.Mono; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; From 5db6cc1cba1ebf2ed9460db600df97693f87b740 Mon Sep 17 00:00:00 2001 From: heqiang <531364804@qq.com> Date: Thu, 22 Sep 2022 22:48:29 +0800 Subject: [PATCH 10/84] Remove redundant @ExtendWith(SpringExtension.class) for sample See gh-32476 --- .../testing/springbootapplications/jmx/MyJmxTests.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.java index ede8846b3798..606b179760ba 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/jmx/MyJmxTests.java @@ -19,16 +19,13 @@ import javax.management.MBeanServer; 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.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; -@ExtendWith(SpringExtension.class) @SpringBootTest(properties = "spring.jmx.enabled=true") @DirtiesContext class MyJmxTests { From be48f37a83fe048418e2aed6776b58d68f99c98d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 29 Sep 2022 09:56:27 +0100 Subject: [PATCH 11/84] Fix LogbackMetrics auto-config test with Logback and Log4j2 on cp Closes gh-32533 --- ...icsAutoConfigurationWithLog4j2AndLogbackTests.java} | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/{MetricsAutoConfigurationWithLog4j2AndLogbackTests.java => LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java} (80%) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationWithLog4j2AndLogbackTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java similarity index 80% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationWithLog4j2AndLogbackTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java index 4466d8278c41..632a2f26a972 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationWithLog4j2AndLogbackTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java @@ -19,6 +19,7 @@ import io.micrometer.core.instrument.binder.logging.LogbackMetrics; import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; @@ -27,17 +28,18 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link MetricsAutoConfiguration} when both Log4j2 and Logback are on the - * classpath. + * Tests for {@link LogbackMetricsAutoConfiguration} when both Log4j2 and Logback are on + * the classpath. * * @author Andy Wilkinson */ @ClassPathExclusions("log4j-to-slf4j-*.jar") @ClassPathOverrides({ "org.apache.logging.log4j:log4j-core:2.9.0", "org.apache.logging.log4j:log4j-slf4j-impl:2.9.0" }) -class MetricsAutoConfigurationWithLog4j2AndLogbackTests { +class LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class, + SimpleMetricsExportAutoConfiguration.class, LogbackMetricsAutoConfiguration.class)); @Test void doesNotConfigureLogbackMetrics() { From 84a25c7dcf47460c00d9c2fd9c47f01de782e664 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 29 Sep 2022 11:25:21 +0100 Subject: [PATCH 12/84] Configure Log4j2 classpath overrides consistently Closes gh-32537 --- ...4jLoggerContextAutoConfigurationTests.java | 6 +-- ...onfigurationWithLog4j2AndLogbackTests.java | 6 +-- .../ConfigureClasspathToPreferLog4j2.java | 42 +++++++++++++++++++ .../testsupport/logging/package-info.java | 20 +++++++++ .../log4j2/Log4J2LoggingSystemTests.java | 4 +- 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/ConfigureClasspathToPreferLog4j2.java create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/package-info.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/Log4J2MetricsWithLog4jLoggerContextAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/Log4J2MetricsWithLog4jLoggerContextAutoConfigurationTests.java index a38764b8117f..b89d1b06b88a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/Log4J2MetricsWithLog4jLoggerContextAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/Log4J2MetricsWithLog4jLoggerContextAutoConfigurationTests.java @@ -23,8 +23,7 @@ import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.boot.testsupport.classpath.ClassPathOverrides; +import org.springframework.boot.testsupport.logging.ConfigureClasspathToPreferLog4j2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -35,8 +34,7 @@ * * @author Andy Wilkinson */ -@ClassPathExclusions("log4j-to-slf4j-*.jar") -@ClassPathOverrides("org.apache.logging.log4j:log4j-core:2.11.1") +@ConfigureClasspathToPreferLog4j2 class Log4J2MetricsWithLog4jLoggerContextAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple()) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java index 632a2f26a972..a764a32afa46 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests.java @@ -22,8 +22,7 @@ import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.boot.testsupport.classpath.ClassPathOverrides; +import org.springframework.boot.testsupport.logging.ConfigureClasspathToPreferLog4j2; import static org.assertj.core.api.Assertions.assertThat; @@ -33,8 +32,7 @@ * * @author Andy Wilkinson */ -@ClassPathExclusions("log4j-to-slf4j-*.jar") -@ClassPathOverrides({ "org.apache.logging.log4j:log4j-core:2.9.0", "org.apache.logging.log4j:log4j-slf4j-impl:2.9.0" }) +@ConfigureClasspathToPreferLog4j2 class LogbackMetricsAutoConfigurationWithLog4j2AndLogbackTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/ConfigureClasspathToPreferLog4j2.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/ConfigureClasspathToPreferLog4j2.java new file mode 100644 index 000000000000..28ea4a389fa9 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/ConfigureClasspathToPreferLog4j2.java @@ -0,0 +1,42 @@ +/* + * 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.testsupport.logging; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; +import org.springframework.boot.testsupport.classpath.ClassPathOverrides; + +/** + * Configures the classpath to prefer Log4j2. Other logging systems may still be on the + * classpath, but Log4j2 will be positioned before them. + * + * @author Andy Wilkinson + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@ClassPathExclusions("log4j-to-slf4j-*.jar") +@ClassPathOverrides({ "org.apache.logging.log4j:log4j-core:2.17.2", + "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" }) +public @interface ConfigureClasspathToPreferLog4j2 { + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/package-info.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/package-info.java new file mode 100644 index 000000000000..dd5169297fde --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/logging/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Internal support classes used in Spring Boot tests related to logging. + */ +package org.springframework.boot.testsupport.logging; 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 1a040a6bbdf0..c23aa8e59d09 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 @@ -50,7 +50,7 @@ import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystemProperties; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.boot.testsupport.classpath.ClassPathOverrides; +import org.springframework.boot.testsupport.logging.ConfigureClasspathToPreferLog4j2; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.mock.env.MockEnvironment; @@ -76,7 +76,7 @@ */ @ExtendWith(OutputCaptureExtension.class) @ClassPathExclusions("logback-*.jar") -@ClassPathOverrides("org.apache.logging.log4j:log4j-slf4j-impl:2.17.2") +@ConfigureClasspathToPreferLog4j2 class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { private final TestLog4J2LoggingSystem loggingSystem = new TestLog4J2LoggingSystem(); From 8a93abfaaa2d3a4127ad2701dc93749726e75c94 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 29 Sep 2022 12:52:02 +0100 Subject: [PATCH 13/84] Improve diagnostics when run does not produce expected logging --- .../boot/build/docs/ApplicationRunner.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/docs/ApplicationRunner.java b/buildSrc/src/main/java/org/springframework/boot/build/docs/ApplicationRunner.java index b66a60edfbea..ba2f59dddecf 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/docs/ApplicationRunner.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/docs/ApplicationRunner.java @@ -21,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -133,8 +134,10 @@ void runApplication() throws IOException { private void awaitLogging(Process process) { long end = System.currentTimeMillis() + 30000; String expectedLogging = this.expectedLogging.get(); + List outputLines = Collections.emptyList(); while (System.currentTimeMillis() < end) { - for (String line : outputLines()) { + outputLines = outputLines(); + for (String line : outputLines) { if (line.contains(expectedLogging)) { return; } @@ -143,7 +146,10 @@ private void awaitLogging(Process process) { throw new IllegalStateException("Process exited before '" + expectedLogging + "' was logged"); } } - throw new IllegalStateException("'" + expectedLogging + "' was not logged within 30 seconds"); + StringBuilder message = new StringBuilder( + "After 30 seconds '" + expectedLogging + "' had not be logged in the following output:\n\n"); + outputLines.forEach((line) -> message.append(line).append("\n")); + throw new IllegalStateException(message.toString()); } private List outputLines() { From efc431bdc4c1f91ba3802a25454229a7f4f68f7b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 30 Sep 2022 17:59:28 -0700 Subject: [PATCH 14/84] Support empty @DefaultValue annotations on aggregates and optional Update `ValueObjectBinder` to allow an empty `@DefaultValue` to be used on map, collection, arrays and optional types. Closes gh-32559 --- .../properties/bind/ValueObjectBinder.java | 32 ++++++++--- .../ConfigurationPropertiesTests.java | 56 +++++++++++++++++++ .../bind/ValueObjectBinderTests.java | 54 +++++++++++------- 3 files changed, 115 insertions(+), 27 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java index ab446865b8a6..e6736155bc2f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -17,6 +17,7 @@ package org.springframework.boot.context.properties.bind; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; @@ -25,6 +26,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import kotlin.reflect.KFunction; import kotlin.reflect.KParameter; @@ -32,6 +34,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; +import org.springframework.core.CollectionFactory; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; @@ -101,7 +104,7 @@ private T getDefaultValue(Binder.Context context, ConstructorParameter param if (annotation instanceof DefaultValue) { String[] defaultValue = ((DefaultValue) annotation).value(); if (defaultValue.length == 0) { - return getNewInstanceIfPossible(context, type); + return getNewDefaultValueInstanceIfPossible(context, type); } return convertDefaultValue(context.getConverter(), defaultValue, type, annotations); } @@ -124,7 +127,7 @@ private T convertDefaultValue(BindConverter converter, String[] defaultValue } @SuppressWarnings("unchecked") - private T getNewInstanceIfPossible(Binder.Context context, ResolvableType type) { + private T getNewDefaultValueInstanceIfPossible(Binder.Context context, ResolvableType type) { Class resolved = (Class) type.resolve(); Assert.state(resolved == null || isEmptyDefaultValueAllowed(resolved), () -> "Parameter of type " + type + " must have a non-empty default value."); @@ -132,14 +135,27 @@ private T getNewInstanceIfPossible(Binder.Context context, ResolvableType ty if (instance != null) { return instance; } - return (resolved != null) ? BeanUtils.instantiateClass(resolved) : null; + if (resolved != null) { + if (Optional.class == resolved) { + return (T) Optional.empty(); + } + if (Collection.class.isAssignableFrom(resolved)) { + return (T) CollectionFactory.createCollection(resolved, 0); + } + if (Map.class.isAssignableFrom(resolved)) { + return (T) CollectionFactory.createMap(resolved, 0); + } + if (resolved.isArray()) { + return (T) Array.newInstance(resolved.getComponentType(), 0); + } + return BeanUtils.instantiateClass(resolved); + } + return null; } private boolean isEmptyDefaultValueAllowed(Class type) { - if (type.isPrimitive() || type.isEnum() || isAggregate(type) || type.getName().startsWith("java.lang")) { - return false; - } - return true; + return (Optional.class == type || isAggregate(type)) + || !(type.isPrimitive() || type.isEnum() || type.getName().startsWith("java.lang")); } private boolean isAggregate(Class type) { 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 eda312947acf..b23119dc0d5c 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 @@ -29,6 +29,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; @@ -853,6 +854,17 @@ void loadWhenBindingToConstructorParametersWithDefaultValuesShouldBind() { assertThat(bean.getBar()).isEqualTo(0); } + @Test + void loadWhenBindingToConstructorParametersWithEmptyDefaultValueShouldBind() { + load(ConstructorParameterEmptyDefaultValueConfiguration.class); + ConstructorParameterEmptyDefaultValueProperties bean = this.context + .getBean(ConstructorParameterEmptyDefaultValueProperties.class); + assertThat(bean.getSet()).isEmpty(); + assertThat(bean.getMap()).isEmpty(); + assertThat(bean.getArray()).isEmpty(); + assertThat(bean.getOptional()).isEmpty(); + } + @Test void loadWhenBindingToConstructorParametersWithDefaultDataUnitShouldBind() { load(ConstructorParameterWithUnitConfiguration.class); @@ -2145,6 +2157,45 @@ int getBar() { } + @ConstructorBinding + @ConfigurationProperties(prefix = "test") + static class ConstructorParameterEmptyDefaultValueProperties { + + private final Set set; + + private final Map map; + + private final int[] array; + + private final Optional optional; + + ConstructorParameterEmptyDefaultValueProperties(@DefaultValue Set set, + @DefaultValue Map map, @DefaultValue int[] array, + @DefaultValue Optional optional) { + this.set = set; + this.map = map; + this.array = array; + this.optional = optional; + } + + Set getSet() { + return this.set; + } + + Map getMap() { + return this.map; + } + + int[] getArray() { + return this.array; + } + + Optional getOptional() { + return this.optional; + } + + } + @ConstructorBinding @ConfigurationProperties(prefix = "test") static class ConstructorParameterWithUnitProperties { @@ -2225,6 +2276,11 @@ static class ConstructorParameterConfiguration { } + @EnableConfigurationProperties(ConstructorParameterEmptyDefaultValueProperties.class) + static class ConstructorParameterEmptyDefaultValueConfiguration { + + } + @EnableConfigurationProperties(ConstructorParameterWithUnitProperties.class) static class ConstructorParameterWithUnitConfiguration { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java index 73711f854634..529e2aeeb7d7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; @@ -293,29 +294,31 @@ void bindWhenJavaLangParameterWithEmptyDefaultValueShouldThrowException() { } @Test - void bindWhenCollectionParameterWithEmptyDefaultValueShouldThrowException() { - assertThatExceptionOfType(BindException.class) - .isThrownBy(() -> this.binder.bindOrCreate("foo", - Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForCollectionTypes.class))) - .withStackTraceContaining( - "Parameter of type java.util.List must have a non-empty default value."); + void bindWhenCollectionParameterWithEmptyDefaultValueShouldReturnEmptyInstance() { + NestedConstructorBeanWithEmptyDefaultValueForCollectionTypes bound = this.binder.bindOrCreate("foo", + Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForCollectionTypes.class)); + assertThat(bound.getListValue()).isEmpty(); } @Test - void bindWhenMapParametersWithEmptyDefaultValueShouldThrowException() { - assertThatExceptionOfType(BindException.class) - .isThrownBy(() -> this.binder.bindOrCreate("foo", - Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForMapTypes.class))) - .withStackTraceContaining( - "Parameter of type java.util.Map must have a non-empty default value."); + void bindWhenMapParametersWithEmptyDefaultValueShouldReturnEmptyInstance() { + NestedConstructorBeanWithEmptyDefaultValueForMapTypes bound = this.binder.bindOrCreate("foo", + Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForMapTypes.class)); + assertThat(bound.getMapValue()).isEmpty(); } @Test - void bindWhenArrayParameterWithEmptyDefaultValueShouldThrowException() { - assertThatExceptionOfType(BindException.class) - .isThrownBy(() -> this.binder.bindOrCreate("foo", - Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForArrayTypes.class))) - .withStackTraceContaining("Parameter of type java.lang.String[] must have a non-empty default value."); + void bindWhenArrayParameterWithEmptyDefaultValueShouldReturnEmptyInstance() { + NestedConstructorBeanWithEmptyDefaultValueForArrayTypes bound = this.binder.bindOrCreate("foo", + Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForArrayTypes.class)); + assertThat(bound.getArrayValue()).isEmpty(); + } + + @Test + void bindWhenOptionalParameterWithEmptyDefaultValueShouldReturnEmptyInstance() { + NestedConstructorBeanWithEmptyDefaultValueForOptionalTypes bound = this.binder.bindOrCreate("foo", + Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForOptionalTypes.class)); + assertThat(bound.getOptionalValue()).isEmpty(); } @Test @@ -753,8 +756,7 @@ static class NestedConstructorBeanWithEmptyDefaultValueForArrayTypes { private final String[] arrayValue; - NestedConstructorBeanWithEmptyDefaultValueForArrayTypes(@DefaultValue String[] arrayValue, - @DefaultValue Integer intValue) { + NestedConstructorBeanWithEmptyDefaultValueForArrayTypes(@DefaultValue String[] arrayValue) { this.arrayValue = arrayValue; } @@ -764,6 +766,20 @@ String[] getArrayValue() { } + static class NestedConstructorBeanWithEmptyDefaultValueForOptionalTypes { + + private final Optional optionalValue; + + NestedConstructorBeanWithEmptyDefaultValueForOptionalTypes(@DefaultValue Optional optionalValue) { + this.optionalValue = optionalValue; + } + + Optional getOptionalValue() { + return this.optionalValue; + } + + } + static class NestedConstructorBeanWithEmptyDefaultValueForEnumTypes { private Foo foo; From 75f4d9e3fdad11055243b6ec30e0760076a69b7a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 30 Sep 2022 21:16:48 -0700 Subject: [PATCH 15/84] Fix empty @DefaultValue annotation test failures on Java 17 See gh-32559 --- .../context/properties/bind/ValueObjectBinder.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java index e6736155bc2f..c6378d4bd8af 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java @@ -131,10 +131,6 @@ private T getNewDefaultValueInstanceIfPossible(Binder.Context context, Resol Class resolved = (Class) type.resolve(); Assert.state(resolved == null || isEmptyDefaultValueAllowed(resolved), () -> "Parameter of type " + type + " must have a non-empty default value."); - T instance = create(Bindable.of(type), context); - if (instance != null) { - return instance; - } if (resolved != null) { if (Optional.class == resolved) { return (T) Optional.empty(); @@ -148,9 +144,12 @@ private T getNewDefaultValueInstanceIfPossible(Binder.Context context, Resol if (resolved.isArray()) { return (T) Array.newInstance(resolved.getComponentType(), 0); } - return BeanUtils.instantiateClass(resolved); } - return null; + T instance = create(Bindable.of(type), context); + if (instance != null) { + return instance; + } + return (resolved != null) ? BeanUtils.instantiateClass(resolved) : null; } private boolean isEmptyDefaultValueAllowed(Class type) { From e917bd0ed00567b39878decd3fbfd6b35245a3eb Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 3 Oct 2022 16:55:44 -0700 Subject: [PATCH 16/84] Only build OutputCapture strings when necessary Update `OutputCapture` so that expensive build operations are only performed when necessary. This update is especially important for Kotlin users calling `"Expected String" in output` since this results in a call to Kotlin's `CharSequence.contains` which calls the `length()` and `charAt()` methods many times. Closes gh-32033 --- .../boot/test/system/OutputCapture.java | 53 +++++++++++--- .../boot/test/system/OutputCaptureTests.java | 72 +++++++++++++++---- 2 files changed, 100 insertions(+), 25 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java index f35a610df8b9..37804a042dbb 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/system/OutputCapture.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; @@ -48,6 +49,12 @@ class OutputCapture implements CapturedOutput { private AnsiOutputState ansiOutputState; + private final AtomicReference out = new AtomicReference<>(null); + + private final AtomicReference err = new AtomicReference<>(null); + + private final AtomicReference all = new AtomicReference<>(null); + /** * Push a new system capture session onto the stack. */ @@ -55,13 +62,15 @@ final void push() { if (this.systemCaptures.isEmpty()) { this.ansiOutputState = AnsiOutputState.saveAndDisable(); } - this.systemCaptures.addLast(new SystemCapture()); + clearExisting(); + this.systemCaptures.addLast(new SystemCapture(this::clearExisting)); } /** * Pop the last system capture session from the stack. */ final void pop() { + clearExisting(); this.systemCaptures.removeLast().release(); if (this.systemCaptures.isEmpty() && this.ansiOutputState != null) { this.ansiOutputState.restore(); @@ -97,7 +106,7 @@ public String toString() { */ @Override public String getAll() { - return get((type) -> true); + return get(this.all, (type) -> true); } /** @@ -106,7 +115,7 @@ public String getAll() { */ @Override public String getOut() { - return get(Type.OUT::equals); + return get(this.out, Type.OUT::equals); } /** @@ -115,19 +124,35 @@ public String getOut() { */ @Override public String getErr() { - return get(Type.ERR::equals); + return get(this.err, Type.ERR::equals); } /** * Resets the current capture session, clearing its captured output. */ void reset() { + clearExisting(); this.systemCaptures.peek().reset(); } - private String get(Predicate filter) { + void clearExisting() { + this.out.set(null); + this.err.set(null); + this.all.set(null); + } + + private String get(AtomicReference existing, Predicate filter) { Assert.state(!this.systemCaptures.isEmpty(), "No system captures found. Please check your output capture registration."); + String result = existing.get(); + if (result == null) { + result = build(filter); + existing.compareAndSet(null, result); + } + return result; + } + + String build(Predicate filter) { StringBuilder builder = new StringBuilder(); for (SystemCapture systemCapture : this.systemCaptures) { systemCapture.append(builder, filter); @@ -141,6 +166,8 @@ private String get(Predicate filter) { */ private static class SystemCapture { + private final Runnable onCapture; + private final Object monitor = new Object(); private final PrintStreamCapture out; @@ -149,7 +176,8 @@ private static class SystemCapture { private final List capturedStrings = new ArrayList<>(); - SystemCapture() { + SystemCapture(Runnable onCapture) { + this.onCapture = onCapture; this.out = new PrintStreamCapture(System.out, this::captureOut); this.err = new PrintStreamCapture(System.err, this::captureErr); System.setOut(this.out); @@ -162,14 +190,17 @@ void release() { } private void captureOut(String string) { - synchronized (this.monitor) { - this.capturedStrings.add(new CapturedString(Type.OUT, string)); - } + capture(new CapturedString(Type.OUT, string)); } private void captureErr(String string) { + capture(new CapturedString(Type.ERR, string)); + } + + private void capture(CapturedString e) { synchronized (this.monitor) { - this.capturedStrings.add(new CapturedString(Type.ERR, string)); + this.onCapture.run(); + this.capturedStrings.add(e); } } @@ -276,7 +307,7 @@ public String toString() { /** * Types of content that can be captured. */ - private enum Type { + enum Type { OUT, ERR diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java index a18b398541a3..c6c3ac341082 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/system/OutputCaptureTests.java @@ -19,6 +19,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.NoSuchElementException; +import java.util.function.Predicate; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -42,7 +43,7 @@ class OutputCaptureTests { private TestPrintStream systemErr; - private OutputCapture output = new OutputCapture(); + private TestOutputCapture output = new TestOutputCapture(); @BeforeEach void replaceSystemStreams() { @@ -129,38 +130,69 @@ void subSequenceReturnsCapturedSubSequence() { @Test void getAllReturnsAllCapturedOutput() { - this.output.push(); - System.out.print("A"); - System.err.print("B"); - System.out.print("C"); + pushAndPrint(); assertThat(this.output.getAll()).isEqualTo("ABC"); } @Test void toStringReturnsAllCapturedOutput() { - this.output.push(); - System.out.print("A"); - System.err.print("B"); - System.out.print("C"); + pushAndPrint(); assertThat(this.output.toString()).isEqualTo("ABC"); } @Test void getErrReturnsOnlyCapturedErrOutput() { - this.output.push(); - System.out.print("A"); - System.err.print("B"); - System.out.print("C"); + pushAndPrint(); assertThat(this.output.getErr()).isEqualTo("B"); } @Test void getOutReturnsOnlyCapturedOutOutput() { + pushAndPrint(); + assertThat(this.output.getOut()).isEqualTo("AC"); + } + + @Test + void getAllUsesCache() { + pushAndPrint(); + for (int i = 0; i < 10; i++) { + assertThat(this.output.getAll()).isEqualTo("ABC"); + } + assertThat(this.output.buildCount).isEqualTo(1); + System.out.print("X"); + assertThat(this.output.getAll()).isEqualTo("ABCX"); + assertThat(this.output.buildCount).isEqualTo(2); + } + + @Test + void getOutUsesCache() { + pushAndPrint(); + for (int i = 0; i < 10; i++) { + assertThat(this.output.getOut()).isEqualTo("AC"); + } + assertThat(this.output.buildCount).isEqualTo(1); + System.out.print("X"); + assertThat(this.output.getOut()).isEqualTo("ACX"); + assertThat(this.output.buildCount).isEqualTo(2); + } + + @Test + void getErrUsesCache() { + pushAndPrint(); + for (int i = 0; i < 10; i++) { + assertThat(this.output.getErr()).isEqualTo("B"); + } + assertThat(this.output.buildCount).isEqualTo(1); + System.err.print("X"); + assertThat(this.output.getErr()).isEqualTo("BX"); + assertThat(this.output.buildCount).isEqualTo(2); + } + + private void pushAndPrint() { this.output.push(); System.out.print("A"); System.err.print("B"); System.out.print("C"); - assertThat(this.output.getOut()).isEqualTo("AC"); } static class TestPrintStream extends PrintStream { @@ -176,4 +208,16 @@ public String toString() { } + static class TestOutputCapture extends OutputCapture { + + int buildCount; + + @Override + String build(Predicate filter) { + this.buildCount++; + return super.build(filter); + } + + } + } From c4a0dad6fea6f12380042b478547ac7bd5a5f6ec Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 6 Oct 2022 11:10:45 +0100 Subject: [PATCH 17/84] Upgrade to Spring Java Format 0.0.35 Closes gh-32609 --- buildSrc/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties index 9c37269cc2e2..73f4b49e00d5 100644 --- a/buildSrc/gradle.properties +++ b/buildSrc/gradle.properties @@ -1 +1 @@ -javaFormatVersion=0.0.34 +javaFormatVersion=0.0.35 From 8d2af7ea93e5bc0c4f132b70706b14421b5778e0 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:04:08 +0200 Subject: [PATCH 18/84] Start building against Reactor 2020.0.24 snapshots See gh-32587 --- 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 4aaad164c2e8..739d5949ee87 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1460,7 +1460,7 @@ bom { ] } } - library("Reactor Bom", "2020.0.23") { + library("Reactor Bom", "2020.0.24-SNAPSHOT") { group("io.projectreactor") { imports = [ "reactor-bom" From 556543043e0a2439658bd81afaa0d514e1d1733f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:04:34 +0200 Subject: [PATCH 19/84] Start building against Micrometer 1.8.11 snapshots See gh-32586 --- 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 739d5949ee87..9afade8970fd 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1253,7 +1253,7 @@ bom { ] } } - library("Micrometer", "1.8.10") { + library("Micrometer", "1.8.11-SNAPSHOT") { group("io.micrometer") { modules = [ "micrometer-registry-stackdriver" { From 9b002e1c053e51e298d8b166be78c4bba8ddfdab Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:05:57 +0200 Subject: [PATCH 20/84] Start building against Spring Retry 1.3.4 snapshots See gh-32593 --- 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 9afade8970fd..d23ad37d8c46 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1760,7 +1760,7 @@ bom { ] } } - library("Spring Retry", "1.3.3") { + library("Spring Retry", "1.3.4-SNAPSHOT") { group("org.springframework.retry") { modules = [ "spring-retry" From 2b2f23ef9ccb7b2c8700d6771eaea65b9e546714 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:05:00 +0200 Subject: [PATCH 21/84] Start building against Spring Data 2021.1.8 snapshots See gh-32588 --- 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 d23ad37d8c46..1f556d6e6f71 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1701,7 +1701,7 @@ bom { ] } } - library("Spring Data Bom", "2021.1.7") { + library("Spring Data Bom", "2021.1.8-SNAPSHOT") { group("org.springframework.data") { imports = [ "spring-data-bom" From 525b724adc9cd8d07c98cad771056791e9bb996a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:05:27 +0200 Subject: [PATCH 22/84] Start building against Spring Kafka 2.8.10 snapshots See gh-32589 --- 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 1f556d6e6f71..52d6e0a375c3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1729,7 +1729,7 @@ bom { ] } } - library("Spring Kafka", "2.8.9") { + library("Spring Kafka", "2.8.10-SNAPSHOT") { group("org.springframework.kafka") { modules = [ "spring-kafka", From 86176e8f4bad62deb764d30d8d29578d824c9f68 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:06:22 +0200 Subject: [PATCH 23/84] Start building against Spring Security 5.6.8 snapshots See gh-32590 --- 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 52d6e0a375c3..df548783f0ae 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1767,7 +1767,7 @@ bom { ] } } - library("Spring Security", "5.6.7") { + library("Spring Security", "5.6.8-SNAPSHOT") { group("org.springframework.security") { imports = [ "spring-security-bom" From 593f2ca7feb0c2cd15a9d2dfe842e5c9138d3e1e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:45:47 +0200 Subject: [PATCH 24/84] Start building against Reactor 2020.0.24 snapshots See gh-32592 --- 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 6ef51645e4de..c6feea436970 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1435,7 +1435,7 @@ bom { ] } } - library("Reactor Bom", "2020.0.23") { + library("Reactor Bom", "2020.0.24-SNAPSHOT") { group("io.projectreactor") { imports = [ "reactor-bom" From edd907eb9d2c6f9b506bb568ad4fb63f70d8ae2d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:46:22 +0200 Subject: [PATCH 25/84] Start building against Micrometer 1.9.5 snapshots See gh-32591 --- 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 c6feea436970..1815f981661f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1284,7 +1284,7 @@ bom { ] } } - library("Micrometer", "1.9.4") { + library("Micrometer", "1.9.5-SNAPSHOT") { group("io.micrometer") { modules = [ "micrometer-registry-stackdriver" { From 1db0e998a69829d9b810f4476aa0accd0c0c2d9a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:46:49 +0200 Subject: [PATCH 26/84] Start building against Spring Retry 1.3.4 snapshots See gh-32594 --- 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 1815f981661f..37f8a5b17e9b 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1761,7 +1761,7 @@ bom { ] } } - library("Spring Retry", "1.3.3") { + library("Spring Retry", "1.3.4-SNAPSHOT") { group("org.springframework.retry") { modules = [ "spring-retry" From 4803f289feeab3451e9f58adf1219684a85d8e05 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:47:12 +0200 Subject: [PATCH 27/84] Start building against Spring Data 2021.2.4 snapshots See gh-32595 --- 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 37f8a5b17e9b..1ad33596e482 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1674,7 +1674,7 @@ bom { ] } } - library("Spring Data Bom", "2021.2.3") { + library("Spring Data Bom", "2021.2.4-SNAPSHOT") { prohibit("[2022.0.0-M1,)") { because "it uses Spring Framework 6" } From 1de704b8649f5ad90bffdd76d2aaa8d0319d06e4 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:47:40 +0200 Subject: [PATCH 28/84] Start building against Spring Kafka 2.9.2 snapshots See gh-32596 --- 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 1ad33596e482..02f5181bd2e6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1722,7 +1722,7 @@ bom { ] } } - library("Spring Kafka", "2.8.9") { + library("Spring Kafka", "2.8.10-SNAPSHOT") { prohibit("[3.0.0-M1,)") { because "it uses Spring Framework 6" } From 627725b10ffcca0554bab382aa29584bb7d51c35 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 7 Oct 2022 09:48:08 +0200 Subject: [PATCH 29/84] Start building against Spring Security 5.7.4 snapshots See gh-32597 --- 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 02f5181bd2e6..c012f72cdb42 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1768,7 +1768,7 @@ bom { ] } } - library("Spring Security", "5.7.3") { + library("Spring Security", "5.7.4-SNAPSHOT") { prohibit("[6.0.0-M1,)") { because "it uses Spring Framework 6" } From 1efbfdb3d34ec49a45871598b6abf3ffff0b0498 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 10 Oct 2022 09:33:16 +0200 Subject: [PATCH 30/84] Upgrade to Ehcache3 3.9.10 Closes gh-32648 --- 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 df548783f0ae..150b3201f6f9 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -272,7 +272,7 @@ bom { ] } } - library("Ehcache3", "3.9.9") { + library("Ehcache3", "3.9.10") { group("org.ehcache") { modules = [ "ehcache", From 19c3078623af9cab0e01b9e1b7e7859288f1af6e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 10 Oct 2022 09:33:19 +0200 Subject: [PATCH 31/84] Upgrade to Hibernate 5.6.12.Final Closes gh-32649 --- 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 150b3201f6f9..c39266e1817e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -426,7 +426,7 @@ bom { ] } } - library("Hibernate", "5.6.11.Final") { + library("Hibernate", "5.6.12.Final") { prohibit("[6.0.0.Alpha2,)") { because "it uses the jakarta.* namespace" } From e5cbc9cfe0d7b3cc04dac1a3d7b675aeb751d58d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 10 Oct 2022 09:33:21 +0200 Subject: [PATCH 32/84] Upgrade to Tomcat 9.0.68 Closes gh-32650 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index faa7df26f6d0..1e1a277202ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ org.gradle.parallel=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 kotlinVersion=1.6.21 -tomcatVersion=9.0.65 +tomcatVersion=9.0.68 kotlin.stdlib.default.dependency=false From ac9b869cfd71c38ba9420f5042ed5892ec71cc83 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 10 Oct 2022 09:33:24 +0200 Subject: [PATCH 33/84] Upgrade to Undertow 2.2.20.Final Closes gh-32651 --- 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 c39266e1817e..19f2fb09869f 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1872,7 +1872,7 @@ bom { ] } } - library("Undertow", "2.2.19.Final") { + library("Undertow", "2.2.20.Final") { group("io.undertow") { modules = [ "undertow-core", From f49d1704f219938aa99acc09b261cb4e31276d2b Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:13:04 +0200 Subject: [PATCH 34/84] Upgrade to Micrometer 1.8.11 Closes gh-32586 --- 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 19f2fb09869f..69d37f8b7a7c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1253,7 +1253,7 @@ bom { ] } } - library("Micrometer", "1.8.11-SNAPSHOT") { + library("Micrometer", "1.8.11") { group("io.micrometer") { modules = [ "micrometer-registry-stackdriver" { From 7e0de51cd24eaf14edfaceceafc4b1aa4e45c9be Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:24 +0200 Subject: [PATCH 35/84] Upgrade to Ehcache3 3.10.2 Closes gh-32663 --- 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 c012f72cdb42..34d7c0439598 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -290,7 +290,7 @@ bom { ] } } - library("Ehcache3", "3.10.1") { + library("Ehcache3", "3.10.2") { group("org.ehcache") { modules = [ "ehcache", From 483d62fa407d9819c7678c9e89ee16f7473e8c24 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:27 +0200 Subject: [PATCH 36/84] Upgrade to Embedded Mongo 3.4.11 Closes gh-32664 --- 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 34d7c0439598..b26a3787df05 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -330,7 +330,7 @@ bom { ] } } - library("Embedded Mongo", "3.4.9") { + library("Embedded Mongo", "3.4.11") { group("de.flapdoodle.embed") { modules = [ "de.flapdoodle.embed.mongo" From 858a48d979cee484a7c1041b96ec6b8bffb593b7 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:29 +0200 Subject: [PATCH 37/84] Upgrade to Hibernate 5.6.12.Final Closes gh-32665 --- 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 b26a3787df05..d1385d092a0a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -457,7 +457,7 @@ bom { ] } } - library("Hibernate", "5.6.11.Final") { + library("Hibernate", "5.6.12.Final") { prohibit("[6.0.0.Alpha2,)") { because "it uses the jakarta.* namespace" } From 1207d55f34c9f51066daba4fd9e6c11b148127ff Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:31 +0200 Subject: [PATCH 38/84] Upgrade to Lettuce 6.1.10.RELEASE Closes gh-32666 --- 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 d1385d092a0a..7477eef58586 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1110,7 +1110,7 @@ bom { ] } } - library("Lettuce", "6.1.9.RELEASE") { + library("Lettuce", "6.1.10.RELEASE") { group("io.lettuce") { modules = [ "lettuce-core" From fabdc5a7b6728f0c448d8c430dde8b4c30fd3045 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:34 +0200 Subject: [PATCH 39/84] Upgrade to Tomcat 9.0.68 Closes gh-32667 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5d1ca241dc40..ba80446ebd59 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ org.gradle.parallel=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 kotlinVersion=1.6.21 -tomcatVersion=9.0.65 +tomcatVersion=9.0.68 kotlin.stdlib.default.dependency=false From 8a231e52b3b589c2ee46dfee8f493780b9c5408c Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:36 +0200 Subject: [PATCH 40/84] Upgrade to Undertow 2.2.20.Final Closes gh-32668 --- 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 7477eef58586..5a6c358dffaa 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1885,7 +1885,7 @@ bom { ] } } - library("Undertow", "2.2.19.Final") { + library("Undertow", "2.2.20.Final") { group("io.undertow") { modules = [ "undertow-core", From 5b6a5873a5165d836607d60e2f9e3935bd84f36a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 06:15:51 +0200 Subject: [PATCH 41/84] Upgrade to Micrometer 1.9.5 Closes gh-32591 --- 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 5a6c358dffaa..69b2ead233a3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1284,7 +1284,7 @@ bom { ] } } - library("Micrometer", "1.9.5-SNAPSHOT") { + library("Micrometer", "1.9.5") { group("io.micrometer") { modules = [ "micrometer-registry-stackdriver" { From 907396021f179b3186408fbabe1ff17f5f1e011f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2022 11:06:30 +0200 Subject: [PATCH 42/84] Upgrade to Lettuce 6.1.10.RELEASE Closes gh-32669 --- 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 69d37f8b7a7c..ab1d85efc594 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1079,7 +1079,7 @@ bom { ] } } - library("Lettuce", "6.1.9.RELEASE") { + library("Lettuce", "6.1.10.RELEASE") { group("io.lettuce") { modules = [ "lettuce-core" From 215eb9635581bd3469f36f140b63bdf0b9ab4eca Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 11 Oct 2022 19:14:04 +0100 Subject: [PATCH 43/84] Improve error handling in SpringBootPlugin Closes gh-32677 --- .../boot/gradle/plugin/SpringBootPlugin.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java index 576fd0869247..54d12d2173ff 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java @@ -130,12 +130,15 @@ private void registerPluginActions(Project project, Configuration bootArchives) private void withPluginClassOfAction(PluginApplicationAction action, Consumer>> consumer) { + Class> pluginClass; try { - consumer.accept(action.getPluginClass()); + pluginClass = action.getPluginClass(); } catch (Throwable ex) { - // Plugin class unavailable. Continue. + // Plugin class unavailable. + return; } + consumer.accept(pluginClass); } } From e0c79ce598f62f4a4404124a5abf1bde04b49057 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 11 Oct 2022 22:40:18 -0700 Subject: [PATCH 44/84] Support PEM formatted elliptic-curve TLS keys Update `PrivateKeyParser` to support loading PEM formatted elliptic-curve keys. We need to do a similar trick to the PKCS1 keys and convert them to a `PKCS8EncodedKeySpec`. Fixes gh-32646 --- .../platform/docker/ssl/PrivateKeyParser.java | 233 +++++++++++++----- .../docker/ssl/KeyStoreFactoryTests.java | 6 +- .../platform/docker/ssl/PemFileWriter.java | 9 +- .../docker/ssl/PrivateKeyParserTests.java | 46 +++- .../docker/ssl/SslContextFactoryTests.java | 4 +- 5 files changed, 225 insertions(+), 73 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParser.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParser.java index 63e870b28df2..002791a88de9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParser.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -24,8 +24,11 @@ import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -43,21 +46,68 @@ final class PrivateKeyParser { private static final String PKCS1_FOOTER = "-+END\\s+RSA\\s+PRIVATE\\s+KEY[^-]*-+"; + private static final String PKCS8_HEADER = "-+BEGIN\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + private static final String PKCS8_FOOTER = "-+END\\s+PRIVATE\\s+KEY[^-]*-+"; - private static final String PKCS8_HEADER = "-+BEGIN\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + private static final String EC_HEADER = "-+BEGIN\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + + private static final String EC_FOOTER = "-+END\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+"; private static final String BASE64_TEXT = "([a-z0-9+/=\\r\\n]+)"; - private static final Pattern PKCS1_PATTERN = Pattern.compile(PKCS1_HEADER + BASE64_TEXT + PKCS1_FOOTER, - Pattern.CASE_INSENSITIVE); + private static final List PEM_PARSERS; + static { + List parsers = new ArrayList<>(); + parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, "RSA", PrivateKeyParser::createKeySpecForPkcs1)); + parsers.add(new PemParser(EC_HEADER, EC_FOOTER, "EC", PrivateKeyParser::createKeySpecForEc)); + parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, "RSA", PKCS8EncodedKeySpec::new)); + PEM_PARSERS = Collections.unmodifiableList(parsers); + } + + /** + * ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.1}. + */ + private static final int[] RSA_ALGORITHM = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; - private static final Pattern PKCS8_KEY_PATTERN = Pattern.compile(PKCS8_HEADER + BASE64_TEXT + PKCS8_FOOTER, - Pattern.CASE_INSENSITIVE); + /** + * ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}. + */ + private static final int[] EC_ALGORITHM = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.132.0.34}. + */ + private static final int[] EC_PARAMETERS = { 0x2b, 0x81, 0x04, 0x00, 0x22 }; private PrivateKeyParser() { } + private static PKCS8EncodedKeySpec createKeySpecForPkcs1(byte[] bytes) { + return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null); + } + + private static PKCS8EncodedKeySpec createKeySpecForEc(byte[] bytes) { + return createKeySpecForAlgorithm(bytes, EC_ALGORITHM, EC_PARAMETERS); + } + + private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[] algorithm, int[] parameters) { + try { + DerEncoder encoder = new DerEncoder(); + encoder.integer(0x00); // Version 0 + DerEncoder algorithmIdentifier = new DerEncoder(); + algorithmIdentifier.objectIdentifier(algorithm); + algorithmIdentifier.objectIdentifier(parameters); + byte[] byteArray = algorithmIdentifier.toByteArray(); + encoder.sequence(byteArray); + encoder.octetString(bytes); + return new PKCS8EncodedKeySpec(encoder.toSequence()); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + /** * Load a private key from the specified file paths. * @param path the path to the private key file @@ -66,80 +116,137 @@ private PrivateKeyParser() { static PrivateKey parse(Path path) { try { String text = readText(path); - Matcher matcher = PKCS1_PATTERN.matcher(text); - if (matcher.find()) { - return parsePkcs1(decodeBase64(matcher.group(1))); + for (PemParser pemParser : PEM_PARSERS) { + PrivateKey privateKey = pemParser.parse(text); + if (privateKey != null) { + return privateKey; + } } - matcher = PKCS8_KEY_PATTERN.matcher(text); - if (matcher.find()) { - return parsePkcs8(decodeBase64(matcher.group(1))); - } - throw new IllegalStateException("Unrecognized private key format in " + path); + throw new IllegalStateException("Unrecognized private key format"); } - catch (GeneralSecurityException | IOException ex) { + catch (Exception ex) { throw new IllegalStateException("Error loading private key file " + path, ex); } } - private static PrivateKey parsePkcs1(byte[] privateKeyBytes) throws GeneralSecurityException { - byte[] pkcs8Bytes = convertPkcs1ToPkcs8(privateKeyBytes); - return parsePkcs8(pkcs8Bytes); + private static String readText(Path path) throws IOException { + byte[] bytes = Files.readAllBytes(path); + return new String(bytes, StandardCharsets.UTF_8); } - private static byte[] convertPkcs1ToPkcs8(byte[] pkcs1) { - try { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - int pkcs1Length = pkcs1.length; - int totalLength = pkcs1Length + 22; - // Sequence + total length - result.write(bytes(0x30, 0x82)); - result.write((totalLength >> 8) & 0xff); - result.write(totalLength & 0xff); - // Integer (0) - result.write(bytes(0x02, 0x01, 0x00)); - // Sequence: 1.2.840.113549.1.1.1, NULL - result.write( - bytes(0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00)); - // Octet string + length - result.write(bytes(0x04, 0x82)); - result.write((pkcs1Length >> 8) & 0xff); - result.write(pkcs1Length & 0xff); - // PKCS1 - result.write(pkcs1); - return result.toByteArray(); + /** + * Parser for a specific PEM format. + */ + private static class PemParser { + + private final Pattern pattern; + + private final String algorithm; + + private final Function keySpecFactory; + + PemParser(String header, String footer, String algorithm, + Function keySpecFactory) { + this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE); + this.algorithm = algorithm; + this.keySpecFactory = keySpecFactory; } - catch (IOException ex) { - throw new IllegalStateException(ex); + + PrivateKey parse(String text) { + Matcher matcher = this.pattern.matcher(text); + return (!matcher.find()) ? null : parse(decodeBase64(matcher.group(1))); } - } - private static byte[] bytes(int... elements) { - byte[] result = new byte[elements.length]; - for (int i = 0; i < elements.length; i++) { - result[i] = (byte) elements[i]; + private static byte[] decodeBase64(String content) { + byte[] contentBytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes(); + return Base64Utils.decode(contentBytes); } - return result; + + private PrivateKey parse(byte[] bytes) { + try { + PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes); + KeyFactory keyFactory = KeyFactory.getInstance(this.algorithm); + return keyFactory.generatePrivate(keySpec); + } + catch (GeneralSecurityException ex) { + throw new IllegalArgumentException("Unexpected key format", ex); + } + } + } - private static PrivateKey parsePkcs8(byte[] privateKeyBytes) throws GeneralSecurityException { - try { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePrivate(keySpec); + /** + * Simple ASN.1 DER encoder. + */ + static class DerEncoder { + + private final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + void objectIdentifier(int... encodedObjectIdentifier) throws IOException { + int code = (encodedObjectIdentifier != null) ? 0x06 : 0x05; + codeLengthBytes(code, bytes(encodedObjectIdentifier)); } - catch (InvalidKeySpecException ex) { - throw new IllegalArgumentException("Unexpected key format", ex); + + void integer(int... encodedInteger) throws IOException { + codeLengthBytes(0x02, bytes(encodedInteger)); } - } - private static String readText(Path path) throws IOException { - byte[] bytes = Files.readAllBytes(path); - return new String(bytes, StandardCharsets.UTF_8); - } + void octetString(byte[] bytes) throws IOException { + codeLengthBytes(0x04, bytes); + } + + void sequence(int... elements) throws IOException { + sequence(bytes(elements)); + } + + void sequence(byte[] bytes) throws IOException { + codeLengthBytes(0x30, bytes); + } + + void codeLengthBytes(int code, byte[] bytes) throws IOException { + this.stream.write(code); + int length = (bytes != null) ? bytes.length : 0; + if (length <= 127) { + this.stream.write(length & 0xFF); + } + else { + ByteArrayOutputStream lengthStream = new ByteArrayOutputStream(); + while (length != 0) { + lengthStream.write(length & 0xFF); + length = length >> 8; + } + byte[] lengthBytes = lengthStream.toByteArray(); + this.stream.write(0x80 | lengthBytes.length); + for (int i = lengthBytes.length - 1; i >= 0; i--) { + this.stream.write(lengthBytes[i]); + } + } + if (bytes != null) { + this.stream.write(bytes); + } + } + + private static byte[] bytes(int... elements) { + if (elements == null) { + return null; + } + byte[] result = new byte[elements.length]; + for (int i = 0; i < elements.length; i++) { + result[i] = (byte) elements[i]; + } + return result; + } + + byte[] toSequence() throws IOException { + DerEncoder sequenceEncoder = new DerEncoder(); + sequenceEncoder.sequence(toByteArray()); + return sequenceEncoder.toByteArray(); + } + + byte[] toByteArray() { + return this.stream.toByteArray(); + } - private static byte[] decodeBase64(String content) { - byte[] contentBytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes(); - return Base64Utils.decode(contentBytes); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactoryTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactoryTests.java index 083f5d9b0431..b294b8d4c8a9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactoryTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/KeyStoreFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -64,10 +64,10 @@ void createKeyStoreWithCertChain() } @Test - void createKeyStoreWithCertChainAndPrivateKey() + void createKeyStoreWithCertChainAndRsaPrivateKey() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { Path certPath = this.fileWriter.writeFile("cert.pem", PemFileWriter.CA_CERTIFICATE, PemFileWriter.CERTIFICATE); - Path keyPath = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_KEY); + Path keyPath = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_RSA_KEY); KeyStore keyStore = KeyStoreFactory.create(certPath, keyPath, "test-alias"); assertThat(keyStore.containsAlias("test-alias")).isTrue(); assertThat(keyStore.getCertificate("test-alias")).isNotNull(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PemFileWriter.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PemFileWriter.java index 39f3c19ccc91..05888b14401f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PemFileWriter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PemFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -81,7 +81,7 @@ public class PemFileWriter { + "a/OsPdOw0j+NqFDBd3mSMhSVgfvXdK6j9WaxY1VGXyaidLARgvn63wfzgr857sQW\n" + "c8eSxbwEQxwlMvVxW6Os4VhCfUQr8VrBrvPa2zs+6IlK+Ug=\n" + "-----END CERTIFICATE-----\n"; - public static final String PRIVATE_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN RSA PRIVATE KEY-----\n" + public static final String PRIVATE_RSA_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQDO5HdnIxePcJtfjkO0ORhPenF7nljT6/zcHou9bX+huAZhNAfr\n" + "LlxxL5y6R04/aXV/2cwSCvIyGf8fEZ0nbivysvs5tUDGFFmbVggIAlvFs4AevQp/\n" + "4UBvCkl90vW6jg7kALz+eBkCQ+/uJFbjQRoxW+A2g+J7zJj7QC0mX/GQmQIDAQAB\n" @@ -96,6 +96,11 @@ public class PemFileWriter { + "8bbHWILSIhFJHg0V7skCQDa8/YkRWF/3pwIZNWQr4ce4OzvYsFMkRvGRdX8B2a0p\n" + "wSKcVTdEdO2DhBlYddN0zG0rjq4vDMtdmldEl4BdldQ=\n" + "-----END RSA PRIVATE KEY-----\n"; + public static final String PRIVATE_EC_KEY = EXAMPLE_SECRET_QUALIFIER + "-----BEGIN EC PRIVATE KEY-----\n" + + "MHcCAQEEIIwZkO8Zjbggzi8wwrk5rzSPzUX31gqTRhBYw4AL6w44oAoGCCqGSM49\n" + + "AwEHoUQDQgAE8y28khug747bA68M90IAMCPHAYyen+RsN6i84LORpNDUhv00QZWd\n" + + "hOhjWFCQjnewR98Y8pEb1fnORll4LhHPlQ==\n" + "-----END EC PRIVATE KEY-----"; + private final Path tempDir; public PemFileWriter() throws IOException { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParserTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParserTests.java index 10896e86f77a..f55f79aaf3e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParserTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/PrivateKeyParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -26,8 +26,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.boot.buildpack.platform.docker.ssl.PrivateKeyParser.DerEncoder; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -35,6 +38,7 @@ * Tests for {@link PrivateKeyParser}. * * @author Scott Frederick + * @author Phillip Webb */ class PrivateKeyParserTests { @@ -60,8 +64,18 @@ void parsePkcs8KeyFile() throws IOException { } @Test - void parsePkcs1KeyFile() throws IOException { - Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_KEY); + void parsePkcs1RsaKeyFile() throws IOException { + Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_RSA_KEY); + PrivateKey privateKey = PrivateKeyParser.parse(path); + assertThat(privateKey).isNotNull(); + // keys in PKCS#1 format are converted to PKCS#8 for parsing + assertThat(privateKey.getFormat()).isEqualTo("PKCS#8"); + Files.delete(path); + } + + @Test + void parsePkcs1EcKeyFile() throws IOException { + Path path = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_EC_KEY); PrivateKey privateKey = PrivateKeyParser.parse(path); assertThat(privateKey).isNotNull(); // keys in PKCS#1 format are converted to PKCS#8 for parsing @@ -84,4 +98,30 @@ void parseWithInvalidPathWillThrowException() throws URISyntaxException { .withMessageContaining(path.toString()); } + @Nested + class DerEncoderTests { + + @Test + void codeLengthBytesShort() throws Exception { + DerEncoder encoder = new DerEncoder(); + encoder.codeLengthBytes(0, new byte[127]); + assertThat(encoder.toByteArray()).startsWith(0x0, 0x7F); + } + + @Test + void codeLengthBytesMedium() throws Exception { + DerEncoder encoder = new DerEncoder(); + encoder.codeLengthBytes(0, new byte[130]); + assertThat(encoder.toByteArray()).startsWith(0x0, 0x81, 0x82); + } + + @Test + void codeLengthBytesLong() throws Exception { + DerEncoder encoder = new DerEncoder(); + encoder.codeLengthBytes(0, new byte[258]); + assertThat(encoder.toByteArray()).startsWith(0x0, 0x82, 0x01, 0x02); + } + + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/SslContextFactoryTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/SslContextFactoryTests.java index 0f370d5a8e53..964529b4de31 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/SslContextFactoryTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/ssl/SslContextFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -48,7 +48,7 @@ void tearDown() throws IOException { @Test void createKeyStoreWithCertChain() throws IOException { this.fileWriter.writeFile("cert.pem", PemFileWriter.CERTIFICATE); - this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_KEY); + this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_RSA_KEY); this.fileWriter.writeFile("ca.pem", PemFileWriter.CA_CERTIFICATE); SSLContext sslContext = new SslContextFactory().forDirectory(this.fileWriter.getTempDir().toString()); assertThat(sslContext).isNotNull(); From ee60ed6e78271d224e1c1c07a756d83c7ad71cf6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 12 Oct 2022 17:08:40 +0100 Subject: [PATCH 45/84] Upgrade to Gradle Enterprise Gradle Plugin 3.11.2 Closes gh-32687 --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 198166cfde2d..3be636fea636 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,7 +22,7 @@ pluginManagement { } plugins { - id "com.gradle.enterprise" version "3.11.1" + id "com.gradle.enterprise" version "3.11.2" id "io.spring.ge.conventions" version "0.0.11" } From 6c1103cde0badba9d2a3e175aa78cc9512687426 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Wed, 12 Oct 2022 11:09:22 -0500 Subject: [PATCH 46/84] Support PEM formatted elliptic-curve TLS keys Apply the changes from e0c79ce598f62f4a4404124a5abf1bde04b49057 to the `PrivateKeyParser` used for web server SSL configuration. See gh-32646 --- .../boot/web/server/PrivateKeyParser.java | 233 +++++++++++++----- .../web/server/PrivateKeyParserTests.java | 9 + .../src/test/resources/test-ec-key.pem | 5 + 3 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/test/resources/test-ec-key.pem diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/PrivateKeyParser.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/PrivateKeyParser.java index 924653d12528..93df27cb1c6c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/PrivateKeyParser.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/PrivateKeyParser.java @@ -24,8 +24,11 @@ import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -45,21 +48,68 @@ final class PrivateKeyParser { private static final String PKCS1_FOOTER = "-+END\\s+RSA\\s+PRIVATE\\s+KEY[^-]*-+"; + private static final String PKCS8_HEADER = "-+BEGIN\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + private static final String PKCS8_FOOTER = "-+END\\s+PRIVATE\\s+KEY[^-]*-+"; - private static final String PKCS8_HEADER = "-+BEGIN\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + private static final String EC_HEADER = "-+BEGIN\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+"; + + private static final String EC_FOOTER = "-+END\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+"; private static final String BASE64_TEXT = "([a-z0-9+/=\\r\\n]+)"; - private static final Pattern PKCS1_PATTERN = Pattern.compile(PKCS1_HEADER + BASE64_TEXT + PKCS1_FOOTER, - Pattern.CASE_INSENSITIVE); + private static final List PEM_PARSERS; + static { + List parsers = new ArrayList<>(); + parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, "RSA", PrivateKeyParser::createKeySpecForPkcs1)); + parsers.add(new PemParser(EC_HEADER, EC_FOOTER, "EC", PrivateKeyParser::createKeySpecForEc)); + parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, "RSA", PKCS8EncodedKeySpec::new)); + PEM_PARSERS = Collections.unmodifiableList(parsers); + } + + /** + * ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.1}. + */ + private static final int[] RSA_ALGORITHM = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; - private static final Pattern PKCS8_KEY_PATTERN = Pattern.compile(PKCS8_HEADER + BASE64_TEXT + PKCS8_FOOTER, - Pattern.CASE_INSENSITIVE); + /** + * ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}. + */ + private static final int[] EC_ALGORITHM = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 }; + + /** + * ASN.1 encoded object identifier {@literal 1.3.132.0.34}. + */ + private static final int[] EC_PARAMETERS = { 0x2b, 0x81, 0x04, 0x00, 0x22 }; private PrivateKeyParser() { } + private static PKCS8EncodedKeySpec createKeySpecForPkcs1(byte[] bytes) { + return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null); + } + + private static PKCS8EncodedKeySpec createKeySpecForEc(byte[] bytes) { + return createKeySpecForAlgorithm(bytes, EC_ALGORITHM, EC_PARAMETERS); + } + + private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[] algorithm, int[] parameters) { + try { + DerEncoder encoder = new DerEncoder(); + encoder.integer(0x00); // Version 0 + DerEncoder algorithmIdentifier = new DerEncoder(); + algorithmIdentifier.objectIdentifier(algorithm); + algorithmIdentifier.objectIdentifier(parameters); + byte[] byteArray = algorithmIdentifier.toByteArray(); + encoder.sequence(byteArray); + encoder.octetString(bytes); + return new PKCS8EncodedKeySpec(encoder.toSequence()); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + /** * Load a private key from the specified resource. * @param resource the private key to parse @@ -68,82 +118,139 @@ private PrivateKeyParser() { static PrivateKey parse(String resource) { try { String text = readText(resource); - Matcher matcher = PKCS1_PATTERN.matcher(text); - if (matcher.find()) { - return parsePkcs1(decodeBase64(matcher.group(1))); + for (PemParser pemParser : PEM_PARSERS) { + PrivateKey privateKey = pemParser.parse(text); + if (privateKey != null) { + return privateKey; + } } - matcher = PKCS8_KEY_PATTERN.matcher(text); - if (matcher.find()) { - return parsePkcs8(decodeBase64(matcher.group(1))); - } - throw new IllegalStateException("Unrecognized private key format in " + resource); + throw new IllegalStateException("Unrecognized private key format"); } - catch (GeneralSecurityException | IOException ex) { + catch (Exception ex) { throw new IllegalStateException("Error loading private key file " + resource, ex); } } - private static PrivateKey parsePkcs1(byte[] privateKeyBytes) throws GeneralSecurityException { - byte[] pkcs8Bytes = convertPkcs1ToPkcs8(privateKeyBytes); - return parsePkcs8(pkcs8Bytes); + private static String readText(String resource) throws IOException { + URL url = ResourceUtils.getURL(resource); + try (Reader reader = new InputStreamReader(url.openStream())) { + return FileCopyUtils.copyToString(reader); + } } - private static byte[] convertPkcs1ToPkcs8(byte[] pkcs1) { - try { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - int pkcs1Length = pkcs1.length; - int totalLength = pkcs1Length + 22; - // Sequence + total length - result.write(bytes(0x30, 0x82)); - result.write((totalLength >> 8) & 0xff); - result.write(totalLength & 0xff); - // Integer (0) - result.write(bytes(0x02, 0x01, 0x00)); - // Sequence: 1.2.840.113549.1.1.1, NULL - result.write( - bytes(0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00)); - // Octet string + length - result.write(bytes(0x04, 0x82)); - result.write((pkcs1Length >> 8) & 0xff); - result.write(pkcs1Length & 0xff); - // PKCS1 - result.write(pkcs1); - return result.toByteArray(); + /** + * Parser for a specific PEM format. + */ + private static class PemParser { + + private final Pattern pattern; + + private final String algorithm; + + private final Function keySpecFactory; + + PemParser(String header, String footer, String algorithm, + Function keySpecFactory) { + this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE); + this.algorithm = algorithm; + this.keySpecFactory = keySpecFactory; } - catch (IOException ex) { - throw new IllegalStateException(ex); + + PrivateKey parse(String text) { + Matcher matcher = this.pattern.matcher(text); + return (!matcher.find()) ? null : parse(decodeBase64(matcher.group(1))); + } + + private static byte[] decodeBase64(String content) { + byte[] contentBytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes(); + return Base64Utils.decode(contentBytes); } - } - private static byte[] bytes(int... elements) { - byte[] result = new byte[elements.length]; - for (int i = 0; i < elements.length; i++) { - result[i] = (byte) elements[i]; + private PrivateKey parse(byte[] bytes) { + try { + PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes); + KeyFactory keyFactory = KeyFactory.getInstance(this.algorithm); + return keyFactory.generatePrivate(keySpec); + } + catch (GeneralSecurityException ex) { + throw new IllegalArgumentException("Unexpected key format", ex); + } } - return result; + } - private static PrivateKey parsePkcs8(byte[] privateKeyBytes) throws GeneralSecurityException { - try { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePrivate(keySpec); + /** + * Simple ASN.1 DER encoder. + */ + static class DerEncoder { + + private final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + void objectIdentifier(int... encodedObjectIdentifier) throws IOException { + int code = (encodedObjectIdentifier != null) ? 0x06 : 0x05; + codeLengthBytes(code, bytes(encodedObjectIdentifier)); } - catch (InvalidKeySpecException ex) { - throw new IllegalArgumentException("Unexpected key format", ex); + + void integer(int... encodedInteger) throws IOException { + codeLengthBytes(0x02, bytes(encodedInteger)); } - } - private static String readText(String resource) throws IOException { - URL url = ResourceUtils.getURL(resource); - try (Reader reader = new InputStreamReader(url.openStream())) { - return FileCopyUtils.copyToString(reader); + void octetString(byte[] bytes) throws IOException { + codeLengthBytes(0x04, bytes); + } + + void sequence(int... elements) throws IOException { + sequence(bytes(elements)); + } + + void sequence(byte[] bytes) throws IOException { + codeLengthBytes(0x30, bytes); + } + + void codeLengthBytes(int code, byte[] bytes) throws IOException { + this.stream.write(code); + int length = (bytes != null) ? bytes.length : 0; + if (length <= 127) { + this.stream.write(length & 0xFF); + } + else { + ByteArrayOutputStream lengthStream = new ByteArrayOutputStream(); + while (length != 0) { + lengthStream.write(length & 0xFF); + length = length >> 8; + } + byte[] lengthBytes = lengthStream.toByteArray(); + this.stream.write(0x80 | lengthBytes.length); + for (int i = lengthBytes.length - 1; i >= 0; i--) { + this.stream.write(lengthBytes[i]); + } + } + if (bytes != null) { + this.stream.write(bytes); + } + } + + private static byte[] bytes(int... elements) { + if (elements == null) { + return null; + } + byte[] result = new byte[elements.length]; + for (int i = 0; i < elements.length; i++) { + result[i] = (byte) elements[i]; + } + return result; + } + + byte[] toSequence() throws IOException { + DerEncoder sequenceEncoder = new DerEncoder(); + sequenceEncoder.sequence(toByteArray()); + return sequenceEncoder.toByteArray(); + } + + byte[] toByteArray() { + return this.stream.toByteArray(); } - } - private static byte[] decodeBase64(String content) { - byte[] contentBytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes(); - return Base64Utils.decode(contentBytes); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/PrivateKeyParserTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/PrivateKeyParserTests.java index 0a55581ea915..31a54748034b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/PrivateKeyParserTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/PrivateKeyParserTests.java @@ -35,6 +35,15 @@ void parsePkcs8KeyFile() { PrivateKey privateKey = PrivateKeyParser.parse("classpath:test-key.pem"); assertThat(privateKey).isNotNull(); assertThat(privateKey.getFormat()).isEqualTo("PKCS#8"); + assertThat(privateKey.getAlgorithm()).isEqualTo("RSA"); + } + + @Test + void parsePkcs8KeyFileWithEcdsa() { + PrivateKey privateKey = PrivateKeyParser.parse("classpath:test-ec-key.pem"); + assertThat(privateKey).isNotNull(); + assertThat(privateKey.getFormat()).isEqualTo("PKCS#8"); + assertThat(privateKey.getAlgorithm()).isEqualTo("EC"); } @Test diff --git a/spring-boot-project/spring-boot/src/test/resources/test-ec-key.pem b/spring-boot-project/spring-boot/src/test/resources/test-ec-key.pem new file mode 100644 index 000000000000..b3a1ce0bd8ea --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/test-ec-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBEZhSR+d8kwL5L/K0f/eNBm4RfzyyA1jfg+dV1/8WvqoAoGCCqGSM49 +AwEHoUQDQgAEBbfdBTSUWuui7O2R+W9mDPjAHjgdBJsjrjnvkjnq8f/k4U/OqvjK +qnHEZwYgdaF2WqYdqBYMns0n+tSMgBoonQ== +-----END EC PRIVATE KEY----- From ba9e481310dcc0cbfb4eecb5675265d78b81651a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 13 Oct 2022 11:26:22 +0200 Subject: [PATCH 47/84] Upgrade Ubuntu version in CI images Closes gh-32690 --- ci/images/ci-image-jdk11/Dockerfile | 2 +- ci/images/ci-image-jdk17/Dockerfile | 2 +- ci/images/ci-image-jdk19/Dockerfile | 2 +- ci/images/ci-image/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/images/ci-image-jdk11/Dockerfile b/ci/images/ci-image-jdk11/Dockerfile index bb69eb25978b..8b3ca4c1a106 100644 --- a/ci/images/ci-image-jdk11/Dockerfile +++ b/ci/images/ci-image-jdk11/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image-jdk17/Dockerfile b/ci/images/ci-image-jdk17/Dockerfile index 291bea02381f..934ed8e8e024 100644 --- a/ci/images/ci-image-jdk17/Dockerfile +++ b/ci/images/ci-image-jdk17/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image-jdk19/Dockerfile b/ci/images/ci-image-jdk19/Dockerfile index 937aff67bdf0..8700d8b697e5 100644 --- a/ci/images/ci-image-jdk19/Dockerfile +++ b/ci/images/ci-image-jdk19/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220801 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image/Dockerfile b/ci/images/ci-image/Dockerfile index 85c9b078d221..bbf95f24dc56 100644 --- a/ci/images/ci-image/Dockerfile +++ b/ci/images/ci-image/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh From b79cbcb31de46623ffe4acf66357d9b29b00d996 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 13 Oct 2022 11:27:40 +0200 Subject: [PATCH 48/84] Upgrade Ubuntu version in CI images Closes gh-32692 --- ci/images/ci-image-jdk11/Dockerfile | 2 +- ci/images/ci-image-jdk17/Dockerfile | 2 +- ci/images/ci-image-jdk19/Dockerfile | 2 +- ci/images/ci-image/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/images/ci-image-jdk11/Dockerfile b/ci/images/ci-image-jdk11/Dockerfile index bb69eb25978b..8b3ca4c1a106 100644 --- a/ci/images/ci-image-jdk11/Dockerfile +++ b/ci/images/ci-image-jdk11/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image-jdk17/Dockerfile b/ci/images/ci-image-jdk17/Dockerfile index 291bea02381f..934ed8e8e024 100644 --- a/ci/images/ci-image-jdk17/Dockerfile +++ b/ci/images/ci-image-jdk17/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image-jdk19/Dockerfile b/ci/images/ci-image-jdk19/Dockerfile index 937aff67bdf0..8700d8b697e5 100644 --- a/ci/images/ci-image-jdk19/Dockerfile +++ b/ci/images/ci-image-jdk19/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220801 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/ci-image/Dockerfile b/ci/images/ci-image/Dockerfile index 85c9b078d221..bbf95f24dc56 100644 --- a/ci/images/ci-image/Dockerfile +++ b/ci/images/ci-image/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:focal-20220826 +FROM ubuntu:focal-20220922 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh From 78bf7b640c29630a554d844176c49739a8ef9a1e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 12:37:39 +0100 Subject: [PATCH 49/84] Start building against Spring Data 2021.1.9 snapshots See gh-32588 --- 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 ab1d85efc594..78b978c6935e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1701,7 +1701,7 @@ bom { ] } } - library("Spring Data Bom", "2021.1.8-SNAPSHOT") { + library("Spring Data Bom", "2021.1.9-SNAPSHOT") { group("org.springframework.data") { imports = [ "spring-data-bom" From c9b929bd89056b0c224a00b8950d7e522b26f170 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 12:39:12 +0100 Subject: [PATCH 50/84] Starting building against Spring Data 2021.2.5 snapshots See gh-32595 --- 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 69b2ead233a3..73d8c47fe1ea 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1674,7 +1674,7 @@ bom { ] } } - library("Spring Data Bom", "2021.2.4-SNAPSHOT") { + library("Spring Data Bom", "2021.2.5-SNAPSHOT") { prohibit("[2022.0.0-M1,)") { because "it uses Spring Framework 6" } From 3244a4a59bdb470a36e8bdda10bdd1b4f858add7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 14:30:51 +0100 Subject: [PATCH 51/84] Upgrade to Netty 4.1.84.Final Closes gh-32695 --- 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 78b978c6935e..581927ce2539 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1322,7 +1322,7 @@ bom { ] } } - library("Netty", "4.1.82.Final") { + library("Netty", "4.1.84.Final") { group("io.netty") { imports = [ "netty-bom" From 5329eac5e4b8cfd69396cc5781b658b7a64b38f6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 14:31:47 +0100 Subject: [PATCH 52/84] Upgrade to Reactor 2020.0.24 Closes gh-32587 --- 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 581927ce2539..42b6253498d3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1460,7 +1460,7 @@ bom { ] } } - library("Reactor Bom", "2020.0.24-SNAPSHOT") { + library("Reactor Bom", "2020.0.24") { group("io.projectreactor") { imports = [ "reactor-bom" From eaf0523656388eeceb8767cb179f8f36c5700c7f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 14:32:37 +0100 Subject: [PATCH 53/84] Upgrade to Spring Data 2021.1.9 Closes gh-32588 --- 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 42b6253498d3..42752d304bdf 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1701,7 +1701,7 @@ bom { ] } } - library("Spring Data Bom", "2021.1.9-SNAPSHOT") { + library("Spring Data Bom", "2021.1.9") { group("org.springframework.data") { imports = [ "spring-data-bom" From 567146954d2f68536d0720d1b714d8c5506d1f1e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:41:36 +0100 Subject: [PATCH 54/84] Upgrade to Reactor 2020.0.24 Closes gh-32592 --- 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 73d8c47fe1ea..b6f17f374473 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1435,7 +1435,7 @@ bom { ] } } - library("Reactor Bom", "2020.0.24-SNAPSHOT") { + library("Reactor Bom", "2020.0.24") { group("io.projectreactor") { imports = [ "reactor-bom" From 4474af6265d2923f5dad1ff2ad10cd0ec0e10c33 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:42:16 +0100 Subject: [PATCH 55/84] Upgrade to Spring Data 2021.2.5 Closes gh-32595 --- 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 b6f17f374473..23b7a0c739fa 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1674,7 +1674,7 @@ bom { ] } } - library("Spring Data Bom", "2021.2.5-SNAPSHOT") { + library("Spring Data Bom", "2021.2.5") { prohibit("[2022.0.0-M1,)") { because "it uses Spring Framework 6" } From 7470342ddad3b78e1da5ae6f93c3f22d90add906 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:44:49 +0100 Subject: [PATCH 56/84] Upgrade to Byte Buddy 1.12.18 Closes gh-32697 --- 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 23b7a0c739fa..53a9dc42f2d3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -152,7 +152,7 @@ bom { ] } } - library("Byte Buddy", "1.12.17") { + library("Byte Buddy", "1.12.18") { group("net.bytebuddy") { modules = [ "byte-buddy", From b9148ede2f451a7d2e9ad06831e8c1390ff560da Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:44:51 +0100 Subject: [PATCH 57/84] Upgrade to Hazelcast 5.1.4 Closes gh-32698 --- 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 53a9dc42f2d3..036928a9ab6c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -441,7 +441,7 @@ bom { ] } } - library("Hazelcast", "5.1.3") { + library("Hazelcast", "5.1.4") { group("com.hazelcast") { modules = [ "hazelcast", From 09d7bede29ac8ad24c8711b6bb3ad2d91ad0eef6 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:44:54 +0100 Subject: [PATCH 58/84] Upgrade to Jetty Reactive HTTPClient 1.1.13 Closes gh-32699 --- 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 036928a9ab6c..ebaa744cf9a7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -933,7 +933,7 @@ bom { ] } } - library("Jetty Reactive HTTPClient", "1.1.12") { + library("Jetty Reactive HTTPClient", "1.1.13") { prohibit("[2,)") { because "it uses the jakarta.* namespace" } From 654c3353d539ed832a92158cb71fb6ce5fd5d41b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 15:44:57 +0100 Subject: [PATCH 59/84] Upgrade to Netty 4.1.84.Final Closes gh-32700 --- 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 ebaa744cf9a7..b2fc09d10f51 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1344,7 +1344,7 @@ bom { ] } } - library("Netty", "4.1.82.Final") { + library("Netty", "4.1.84.Final") { group("io.netty") { imports = [ "netty-bom" From 0411ebeeaf33637cb9c9ba0d4cd1821535d522c9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 20:10:07 +0100 Subject: [PATCH 60/84] Upgrade to Jackson Bom 2.13.4.20221013 Closes gh-32722 --- 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 42752d304bdf..a5411c597235 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -553,7 +553,7 @@ bom { ] } } - library("Jackson Bom", "2.13.4") { + library("Jackson Bom", "2.13.4.20221013") { group("com.fasterxml.jackson") { imports = [ "jackson-bom" From 936cb41c6e8cd8929eda4f444a1d023167dec14b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 20:10:11 +0100 Subject: [PATCH 61/84] Upgrade to Jetty Reactive HTTPClient 1.1.13 Closes gh-32723 --- 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 a5411c597235..3f664248dd94 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -902,7 +902,7 @@ bom { ] } } - library("Jetty Reactive HTTPClient", "1.1.12") { + library("Jetty Reactive HTTPClient", "1.1.13") { prohibit("[2,)") { because "it uses the jakarta.* namespace" } From 9fd3d78bae028b0391f7a023b62caadf498e91a9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 13 Oct 2022 21:18:15 +0100 Subject: [PATCH 62/84] Upgrade to Jackson Bom 2.13.4.20221013 Closes gh-32724 --- 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 b2fc09d10f51..0dd268b1d943 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -584,7 +584,7 @@ bom { ] } } - library("Jackson Bom", "2.13.4") { + library("Jackson Bom", "2.13.4.20221013") { group("com.fasterxml.jackson") { imports = [ "jackson-bom" From c858c1e99886996266f1f95d5ec51899fa02e10a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 14 Oct 2022 10:23:47 +0200 Subject: [PATCH 63/84] Upgrade to Spring Retry 1.3.4 Closes gh-32593 --- 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 3f664248dd94..f650a5bc85d7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1760,7 +1760,7 @@ bom { ] } } - library("Spring Retry", "1.3.4-SNAPSHOT") { + library("Spring Retry", "1.3.4") { group("org.springframework.retry") { modules = [ "spring-retry" From 040c78062e35a3609e6108cc1b6036399e4d2405 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 14 Oct 2022 10:28:30 +0200 Subject: [PATCH 64/84] Upgrade to Spring Retry 1.3.4 Closes gh-32594 --- 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 0dd268b1d943..2f6ee94febb3 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1761,7 +1761,7 @@ bom { ] } } - library("Spring Retry", "1.3.4-SNAPSHOT") { + library("Spring Retry", "1.3.4") { group("org.springframework.retry") { modules = [ "spring-retry" From 6641a98913902bd9ec08b3c6b729189cafc7c827 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 15 Oct 2022 14:16:03 -0700 Subject: [PATCH 65/84] Improve the phrasing around property source ordering --- .../src/docs/asciidoc/features/external-config.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc index a75e31d3b78d..13a964897bf9 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc @@ -6,7 +6,8 @@ You can use a variety of external configuration sources, include Java properties Property values can be injected directly into your beans by using the `@Value` annotation, accessed through Spring's `Environment` abstraction, or be <> through `@ConfigurationProperties`. Spring Boot uses a very particular `PropertySource` order that is designed to allow sensible overriding of values. -Properties are considered in the following order (with values from lower items overriding earlier ones): +Later property sources can override the values defined in earlier ones. +Sources are considered in the following order: . Default properties (specified by setting `SpringApplication.setDefaultProperties`). . {spring-framework-api}/context/annotation/PropertySource.html[`@PropertySource`] annotations on your `@Configuration` classes. From dade5ec3aabe431e71fbc70f491320468a6e2f55 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 17:11:18 +0100 Subject: [PATCH 66/84] Test against Gradle 6.9.3 Closes gh-32757 --- .../boot/testsupport/gradle/testkit/GradleVersions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java index 4a8eeb4cacf3..faf2640a3ce3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java @@ -42,7 +42,7 @@ public static List allCompatible() { if (isJava16()) { return Arrays.asList("7.0.2", "7.1", "7.2", "7.3.3", "7.4.2", GradleVersion.current().getVersion()); } - return Arrays.asList("6.8.3", "6.9.2", "7.0.2", "7.1.1", "7.2", "7.3.3", "7.4.2", + return Arrays.asList("6.8.3", "6.9.3", "7.0.2", "7.1.1", "7.2", "7.3.3", "7.4.2", GradleVersion.current().getVersion()); } From a16e3e668850128dceed601d6c9238d641ec51ec Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 19:42:27 +0100 Subject: [PATCH 67/84] Upgrade to Spring Kafka 2.8.10 Closes gh-32589 --- 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 f650a5bc85d7..1301986317ee 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1729,7 +1729,7 @@ bom { ] } } - library("Spring Kafka", "2.8.10-SNAPSHOT") { + library("Spring Kafka", "2.8.10") { group("org.springframework.kafka") { modules = [ "spring-kafka", From bdb4f243008921e10095e3d7f69ee3eb136c746c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 19:44:24 +0100 Subject: [PATCH 68/84] Upgrade to Spring Kafka 2.8.10 Closes gh-32596 --- 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 2f6ee94febb3..08c67ddf2a41 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1722,7 +1722,7 @@ bom { ] } } - library("Spring Kafka", "2.8.10-SNAPSHOT") { + library("Spring Kafka", "2.8.10") { prohibit("[3.0.0-M1,)") { because "it uses Spring Framework 6" } From a92388cbeeca0d3c16121c0f13420603d971b698 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 20:43:40 +0100 Subject: [PATCH 69/84] Upgrade to Spring Security 5.6.8 Closes gh-32590 --- 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 1301986317ee..27add70c22f6 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1767,7 +1767,7 @@ bom { ] } } - library("Spring Security", "5.6.8-SNAPSHOT") { + library("Spring Security", "5.6.8") { group("org.springframework.security") { imports = [ "spring-security-bom" From 99f0a5e01d96c49fdc21a319e69777240ee7ae2f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 17 Oct 2022 20:45:40 +0100 Subject: [PATCH 70/84] Upgrade to Spring Security 5.7.4 Closes gh-32597 --- 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 08c67ddf2a41..73dd973739f1 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1768,7 +1768,7 @@ bom { ] } } - library("Spring Security", "5.7.4-SNAPSHOT") { + library("Spring Security", "5.7.4") { prohibit("[6.0.0-M1,)") { because "it uses Spring Framework 6" } From c498ad0320f25ecc067c95a430a049511a35e25b Mon Sep 17 00:00:00 2001 From: yuanhao18 Date: Tue, 18 Oct 2022 21:59:37 +0800 Subject: [PATCH 71/84] Use more specific bean method return types in KafkaAutoConfiguration Update `KafkaAutoConfiguration` so that more specific bean types are returned. See gh-32770 --- .../autoconfigure/kafka/KafkaAutoConfiguration.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java index 6a6e0183293f..485ff1e9b9d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java @@ -74,14 +74,14 @@ public KafkaAutoConfiguration(KafkaProperties properties) { } @Bean - @ConditionalOnMissingBean(ProducerListener.class) - public ProducerListener kafkaProducerListener() { + @ConditionalOnMissingBean(LoggingProducerListener.class) + public LoggingProducerListener kafkaProducerListener() { return new LoggingProducerListener<>(); } @Bean - @ConditionalOnMissingBean(ConsumerFactory.class) - public ConsumerFactory kafkaConsumerFactory( + @ConditionalOnMissingBean(DefaultKafkaConsumerFactory.class) + public DefaultKafkaConsumerFactory kafkaConsumerFactory( ObjectProvider customizers) { DefaultKafkaConsumerFactory factory = new DefaultKafkaConsumerFactory<>( this.properties.buildConsumerProperties()); @@ -90,8 +90,8 @@ public ProducerListener kafkaProducerListener() { } @Bean - @ConditionalOnMissingBean(ProducerFactory.class) - public ProducerFactory kafkaProducerFactory( + @ConditionalOnMissingBean(DefaultKafkaProducerFactory.class) + public DefaultKafkaProducerFactory kafkaProducerFactory( ObjectProvider customizers) { DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory<>( this.properties.buildProducerProperties()); From b02c702ad18c37947733e9a2a8732a58feaa6cdb Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 18 Oct 2022 15:35:09 -0700 Subject: [PATCH 72/84] Polish 'Use more specific bean method return types in KafkaAutoConfiguration' See gh-32770 --- .../boot/autoconfigure/kafka/KafkaAutoConfiguration.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java index 485ff1e9b9d1..d9efe18bdeb6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -74,13 +74,13 @@ public KafkaAutoConfiguration(KafkaProperties properties) { } @Bean - @ConditionalOnMissingBean(LoggingProducerListener.class) + @ConditionalOnMissingBean(ProducerListener.class) public LoggingProducerListener kafkaProducerListener() { return new LoggingProducerListener<>(); } @Bean - @ConditionalOnMissingBean(DefaultKafkaConsumerFactory.class) + @ConditionalOnMissingBean(ConsumerFactory.class) public DefaultKafkaConsumerFactory kafkaConsumerFactory( ObjectProvider customizers) { DefaultKafkaConsumerFactory factory = new DefaultKafkaConsumerFactory<>( @@ -90,7 +90,7 @@ public LoggingProducerListener kafkaProducerListener() { } @Bean - @ConditionalOnMissingBean(DefaultKafkaProducerFactory.class) + @ConditionalOnMissingBean(ProducerFactory.class) public DefaultKafkaProducerFactory kafkaProducerFactory( ObjectProvider customizers) { DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory<>( From 3b25132e387829e115e18e202b15fac4903a4abd Mon Sep 17 00:00:00 2001 From: Guirong Hu Date: Sun, 9 Oct 2022 16:31:57 +0800 Subject: [PATCH 73/84] Add DataSize serializer for configprops endpoint See gh-32645 --- ...ConfigurationPropertiesReportEndpoint.java | 17 ++++++++- ...gurationPropertiesReportEndpointTests.java | 35 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java index f1545096eb56..240136500777 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java @@ -41,6 +41,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.BeanSerializerFactory; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; @@ -48,6 +49,7 @@ import com.fasterxml.jackson.databind.ser.SerializerFactory; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -80,6 +82,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; +import org.springframework.util.unit.DataSize; /** * {@link Endpoint @Endpoint} to expose application properties from @@ -188,12 +191,12 @@ protected void configureJsonMapper(JsonMapper.Builder builder) { builder.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); builder.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); builder.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false); - JsonMapper.builder(); builder.configure(MapperFeature.USE_STD_BEAN_NAMING, true); builder.serializationInclusion(Include.NON_NULL); applyConfigurationPropertiesFilter(builder); applySerializationModifier(builder); builder.addModule(new JavaTimeModule()); + builder.addModule(new ConfigurationPropertiesModule()); } private void applyConfigurationPropertiesFilter(JsonMapper.Builder builder) { @@ -474,6 +477,18 @@ public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider } + /** + * {@link SimpleModule} for configure the serializer. + */ + private static final class ConfigurationPropertiesModule extends SimpleModule { + + private ConfigurationPropertiesModule() { + super(); + addSerializer(DataSize.class, ToStringSerializer.instance); + } + + } + /** * {@link BeanSerializerModifier} to return only relevant configuration properties. */ diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java index b98c72b311fb..95053638d6c3 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java @@ -46,6 +46,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.mock.env.MockPropertySource; +import org.springframework.util.unit.DataSize; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -168,6 +169,19 @@ void descriptorWithMixedBooleanProperty() { (properties) -> assertThat(properties.get("mixedBoolean")).isEqualTo(true))); } + @Test + void descriptorWithDataSizeProperty() { + String configSize = "1MB"; + String stringifySize = DataSize.parse(configSize).toString(); + this.contextRunner.withUserConfiguration(DataSizePropertiesConfiguration.class) + .withPropertyValues(String.format("data.size=%s", configSize)).run(assertProperties("data", + (properties) -> assertThat(properties.get("size")).isEqualTo(stringifySize), (inputs) -> { + Map size = (Map) inputs.get("size"); + assertThat(size.get("value")).isEqualTo(configSize); + assertThat(size.get("origin")).isEqualTo("\"data.size\" from property source \"test\""); + })); + } + @Test void sanitizeWithDefaultSettings() { this.contextRunner.withUserConfiguration(TestPropertiesConfiguration.class) @@ -690,6 +704,27 @@ public void setMixedBoolean(Boolean mixedBoolean) { } + @Configuration(proxyBeanMethods = false) + @EnableConfigurationProperties(DataSizeProperties.class) + static class DataSizePropertiesConfiguration { + + } + + @ConfigurationProperties("data") + public static class DataSizeProperties { + + private DataSize size; + + public DataSize getSize() { + return this.size; + } + + public void setSize(DataSize size) { + this.size = size; + } + + } + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(Gh4415Properties.class) static class Gh4415PropertiesConfiguration { From de111dba5c195a6bb113964f2c1cecd036367441 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 18 Oct 2022 15:46:56 -0700 Subject: [PATCH 74/84] Polish 'Add DataSize serializer for configprops endpoint' See gh-32645 --- .../properties/ConfigurationPropertiesReportEndpoint.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java index 240136500777..00df14059d48 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java @@ -483,7 +483,6 @@ public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider private static final class ConfigurationPropertiesModule extends SimpleModule { private ConfigurationPropertiesModule() { - super(); addSerializer(DataSize.class, ToStringSerializer.instance); } From 33e379c4a2a8f50f4d2490943537d0d1b6f831f6 Mon Sep 17 00:00:00 2001 From: Guirong Hu Date: Tue, 27 Sep 2022 19:02:36 +0800 Subject: [PATCH 75/84] Use exclamation character for the document separator prefix See gh-32521 --- .../asciidoc/features/external-config.adoc | 4 +- .../env/OriginTrackedPropertiesLoader.java | 42 +++++++---------- .../OriginTrackedPropertiesLoaderTests.java | 47 +++++++++++++++++-- .../existing-non-multi-document.properties | 16 +++++++ 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc index 13a964897bf9..fb147302fd73 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc @@ -498,7 +498,7 @@ For example, the following file has two logical documents: on-cloud-platform: "kubernetes" ---- -For `application.properties` files a special `#---` comment is used to mark the document splits: +For `application.properties` files a special `#---` or `!---` comment is used to mark the document splits: [source,properties,indent=0,subs="verbatim"] ---- @@ -509,7 +509,7 @@ For `application.properties` files a special `#---` comment is used to mark the ---- NOTE: Property file separators must not have any leading whitespace and must have exactly three hyphen characters. -The lines immediately before and after the separator must not be comments. +The lines immediately before and after the separator must not be same comment prefix. TIP: Multi-document property files are often used in conjunction with activation properties such as `spring.config.activate.on-profile`. See the <> for details. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java index adf07a191b2f..4d2cb2c84b83 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -78,7 +78,8 @@ List load(boolean expandLists) throws IOException { StringBuilder buffer = new StringBuilder(); try (CharacterReader reader = new CharacterReader(this.resource)) { while (reader.read()) { - if (reader.isPoundCharacter()) { + if (reader.isCommentPrefixCharacter()) { + char commentPrefixCharacter = reader.getCharacter(); if (isNewDocument(reader)) { if (!document.isEmpty()) { documents.add(document); @@ -89,12 +90,12 @@ List load(boolean expandLists) throws IOException { if (document.isEmpty() && !documents.isEmpty()) { document = documents.remove(documents.size() - 1); } - reader.setLastLineComment(true); + reader.setLastLineCommentPrefix(commentPrefixCharacter); reader.skipComment(); } } else { - reader.setLastLineComment(false); + reader.setLastLineCommentPrefix(-1); loadKeyAndValue(expandLists, document, reader, buffer); } } @@ -161,10 +162,10 @@ private OriginTrackedValue loadValue(StringBuilder buffer, CharacterReader reade } private boolean isNewDocument(CharacterReader reader) throws IOException { - if (reader.isLastLineComment()) { + if (reader.isSameLastLineCommentPrefix()) { return false; } - boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter(); + boolean result = reader.getLocation().getColumn() == 0; result = result && readAndExpect(reader, reader::isHyphenCharacter); result = result && readAndExpect(reader, reader::isHyphenCharacter); result = result && readAndExpect(reader, reader::isHyphenCharacter); @@ -196,7 +197,7 @@ private static class CharacterReader implements Closeable { private int character; - private boolean lastLineComment; + private int lastLineCommentPrefix; CharacterReader(Resource resource) throws IOException { this.reader = new LineNumberReader( @@ -209,20 +210,11 @@ public void close() throws IOException { } boolean read() throws IOException { - return read(false); - } - - boolean read(boolean wrappedLine) throws IOException { this.escaped = false; this.character = this.reader.read(); this.columnNumber++; if (this.columnNumber == 0) { skipWhitespace(); - if (!wrappedLine) { - if (this.character == '!') { - skipComment(); - } - } } if (this.character == '\\') { this.escaped = true; @@ -241,12 +233,8 @@ private void skipWhitespace() throws IOException { } } - private void setLastLineComment(boolean lastLineComment) { - this.lastLineComment = lastLineComment; - } - - private boolean isLastLineComment() { - return this.lastLineComment; + private void setLastLineCommentPrefix(int lastLineCommentPrefix) { + this.lastLineCommentPrefix = lastLineCommentPrefix; } private void skipComment() throws IOException { @@ -264,7 +252,7 @@ private void readEscaped() throws IOException { } else if (this.character == '\n') { this.columnNumber = -1; - read(true); + read(); } else if (this.character == 'u') { readUnicode(); @@ -318,8 +306,12 @@ Location getLocation() { return new Location(this.reader.getLineNumber(), this.columnNumber); } - boolean isPoundCharacter() { - return this.character == '#'; + boolean isSameLastLineCommentPrefix() { + return this.lastLineCommentPrefix == this.character; + } + + boolean isCommentPrefixCharacter() { + return this.character == '#' || this.character == '!'; } boolean isHyphenCharacter() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java index 13d1af165ce4..5a591c4de28f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * 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. @@ -183,33 +183,70 @@ void getImmediateMultiline() { } @Test - void loadWhenMultiDocumentWithoutWhitespaceLoadsMultiDoc() throws IOException { + void loadWhenMultiDocumentWithPoundPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException { String content = "a=a\n#---\nb=b"; List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); assertThat(loaded).hasSize(2); } @Test - void loadWhenMultiDocumentWithLeadingWhitespaceLoadsSingleDoc() throws IOException { + void loadWhenMultiDocumentWithExclamationPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException { + String content = "a=a\n!---\nb=b"; + List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(2); + } + + @Test + void loadWhenMultiDocumentWithPoundPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException { String content = "a=a\n \t#---\nb=b"; List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); assertThat(loaded).hasSize(1); } @Test - void loadWhenMultiDocumentWithTrailingWhitespaceLoadsMultiDoc() throws IOException { + void loadWhenMultiDocumentWithExclamationPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException { + String content = "a=a\n \t!---\nb=b"; + List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(1); + } + + @Test + void loadWhenMultiDocumentWithPoundPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException { String content = "a=a\n#--- \t \nb=b"; List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); assertThat(loaded).hasSize(2); } @Test - void loadWhenMultiDocumentWithTrailingCharsLoadsSingleDoc() throws IOException { + void loadWhenMultiDocumentWithExclamationPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException { + String content = "a=a\n!--- \t \nb=b"; + List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(2); + } + + @Test + void loadWhenMultiDocumentWithPoundPrefixAndTrailingCharsLoadsSingleDoc() throws IOException { String content = "a=a\n#--- \tcomment\nb=b"; List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); assertThat(loaded).hasSize(1); } + @Test + void loadWhenMultiDocumentWithExclamationPrefixAndTrailingCharsLoadsSingleDoc() throws IOException { + String content = "a=a\n!--- \tcomment\nb=b"; + List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(1); + } + + @Test + void loadWhenMultiDocumentSeparatorPrefixDifferentFromCommentPrefixLoadsMultiDoc() throws IOException { + String[] contents = new String[] { "a=a\n# comment\n!---\nb=b", "a=a\n! comment\n#---\nb=b" }; + for (String content : contents) { + List loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(2); + } + } + @Test void getPropertyWithWhitespaceAfterKey() { OriginTrackedValue value = getFromFirst("bar"); diff --git a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties index b5e003745cb6..faab368e3723 100644 --- a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties +++ b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties @@ -14,3 +14,19 @@ boot=bar #--- bar=ok + +!--- +! Test +!--- + +ok=well + +!--- +! Test + +well=hello + +! Test +!--- + +hello=world From 2c7114fa17f2631259e9408be97bf543a29c1526 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 18 Oct 2022 17:06:36 -0700 Subject: [PATCH 76/84] Polish 'Use exclamation character for the document separator prefix' See gh-32521 --- .../boot/env/OriginTrackedPropertiesLoader.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java index 4d2cb2c84b83..4d6706920790 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java @@ -42,6 +42,7 @@ * @author Madhura Bhave * @author Phillip Webb * @author Thiago Hirata + * @author Guirong Hu */ class OriginTrackedPropertiesLoader { @@ -90,12 +91,12 @@ List load(boolean expandLists) throws IOException { if (document.isEmpty() && !documents.isEmpty()) { document = documents.remove(documents.size() - 1); } - reader.setLastLineCommentPrefix(commentPrefixCharacter); + reader.setLastLineCommentPrefixCharacter(commentPrefixCharacter); reader.skipComment(); } } else { - reader.setLastLineCommentPrefix(-1); + reader.setLastLineCommentPrefixCharacter(-1); loadKeyAndValue(expandLists, document, reader, buffer); } } @@ -197,7 +198,7 @@ private static class CharacterReader implements Closeable { private int character; - private int lastLineCommentPrefix; + private int lastLineCommentPrefixCharacter; CharacterReader(Resource resource) throws IOException { this.reader = new LineNumberReader( @@ -233,8 +234,8 @@ private void skipWhitespace() throws IOException { } } - private void setLastLineCommentPrefix(int lastLineCommentPrefix) { - this.lastLineCommentPrefix = lastLineCommentPrefix; + private void setLastLineCommentPrefixCharacter(int lastLineCommentPrefixCharacter) { + this.lastLineCommentPrefixCharacter = lastLineCommentPrefixCharacter; } private void skipComment() throws IOException { @@ -307,7 +308,7 @@ Location getLocation() { } boolean isSameLastLineCommentPrefix() { - return this.lastLineCommentPrefix == this.character; + return this.lastLineCommentPrefixCharacter == this.character; } boolean isCommentPrefixCharacter() { From 4ed072a06bdaaa0b745b23a6db816ec7af22999a Mon Sep 17 00:00:00 2001 From: Henning Poettker Date: Mon, 17 Oct 2022 01:09:31 +0200 Subject: [PATCH 77/84] Upgrade MySQL Connector/J and use new Maven identifiers See gh-32747 --- .../spring-boot-autoconfigure/build.gradle | 2 +- .../spring-boot-dependencies/build.gradle | 9 ++++++++- spring-boot-project/spring-boot/build.gradle | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index e5e6c67817be..9df315d1aee5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -224,13 +224,13 @@ dependencies { testImplementation("com.github.h-thurow:simple-jndi") testImplementation("com.ibm.db2:jcc") testImplementation("com.jayway.jsonpath:json-path") + testImplementation("com.mysql:mysql-connector-j") testImplementation("com.squareup.okhttp3:mockwebserver") testImplementation("com.sun.xml.messaging.saaj:saaj-impl") testImplementation("io.projectreactor:reactor-test") testImplementation("io.r2dbc:r2dbc-h2") testImplementation("jakarta.json:jakarta.json-api") testImplementation("jakarta.xml.ws:jakarta.xml.ws-api") - testImplementation("mysql:mysql-connector-java") testImplementation("org.apache.johnzon:johnzon-jsonb") testImplementation("org.apache.logging.log4j:log4j-to-slf4j") testImplementation("org.apache.tomcat.embed:tomcat-embed-jasper") diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 27add70c22f6..0e607eae1211 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1299,7 +1299,14 @@ bom { ] } } - library("MySQL", "8.0.30") { + library("MySQL", "8.0.31") { + group("com.mysql") { + modules = [ + "mysql-connector-j" { + exclude group: "com.google.protobuf", module: "protobuf-java" + } + ] + } group("mysql") { modules = [ "mysql-connector-java" { diff --git a/spring-boot-project/spring-boot/build.gradle b/spring-boot-project/spring-boot/build.gradle index 136dda6bafc5..4adf86dbcc34 100644 --- a/spring-boot-project/spring-boot/build.gradle +++ b/spring-boot-project/spring-boot/build.gradle @@ -113,6 +113,7 @@ dependencies { testImplementation("com.ibm.db2:jcc") testImplementation("com.jayway.jsonpath:json-path") testImplementation("com.microsoft.sqlserver:mssql-jdbc") + testImplementation("com.mysql:mysql-connector-j") testImplementation("com.squareup.okhttp3:okhttp") testImplementation("com.sun.xml.messaging.saaj:saaj-impl") testImplementation("io.projectreactor:reactor-test") @@ -120,7 +121,6 @@ dependencies { testImplementation("jakarta.inject:jakarta.inject-api") testImplementation("jakarta.persistence:jakarta.persistence-api") testImplementation("jakarta.xml.ws:jakarta.xml.ws-api") - testImplementation("mysql:mysql-connector-java") testImplementation("net.sourceforge.jtds:jtds") testImplementation("org.apache.derby:derby") testImplementation("org.awaitility:awaitility") From 02760ffee41543e96e3dcbe2a2e1a9d660830bc4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Oct 2022 10:44:03 +0100 Subject: [PATCH 78/84] Upgrade to Dependency Management Plugin 1.0.15.RELEASE Closes gh-32784 --- 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 0e607eae1211..919665638cd9 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -237,7 +237,7 @@ bom { ] } } - library("Dependency Management Plugin", "1.0.14.RELEASE") { + library("Dependency Management Plugin", "1.0.15.RELEASE") { group("io.spring.gradle") { modules = [ "dependency-management-plugin" From 65bdcdb2a5be6c0d8b6643c02c8dbd288ea57745 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Oct 2022 10:44:06 +0100 Subject: [PATCH 79/84] Upgrade to Glassfish JAXB 2.3.7 Closes gh-32785 --- 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 919665638cd9..8670e9c78779 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -356,7 +356,7 @@ bom { ] } } - library("Glassfish JAXB", "2.3.6") { + library("Glassfish JAXB", "2.3.7") { prohibit("[3.0.0-M1,)") { because "it uses the jakarta.* namespace" } From ba02a6474385939d9a03c44c5fd4f990f7969776 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Oct 2022 11:44:48 +0100 Subject: [PATCH 80/84] Upgrade to Dependency Management Plugin 1.0.15.RELEASE Closes gh-32786 --- 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 598fae94a0b3..a4d0bcabe91e 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -255,7 +255,7 @@ bom { ] } } - library("Dependency Management Plugin", "1.0.14.RELEASE") { + library("Dependency Management Plugin", "1.0.15.RELEASE") { group("io.spring.gradle") { modules = [ "dependency-management-plugin" From 3aa835845ea2d6bfe845cc34b5e4a9bdec464e8d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Oct 2022 11:44:51 +0100 Subject: [PATCH 81/84] Upgrade to Glassfish JAXB 2.3.7 Closes gh-32787 --- 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 a4d0bcabe91e..e7be6a37acd7 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -377,7 +377,7 @@ bom { ] } } - library("Glassfish JAXB", "2.3.6") { + library("Glassfish JAXB", "2.3.7") { prohibit("[3.0.0-M1,)") { because "it uses the jakarta.* namespace" } From 6f787c4b79e40c53f5a883f60a2a425b7480ac24 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Oct 2022 11:44:54 +0100 Subject: [PATCH 82/84] Upgrade to Infinispan 13.0.12.Final Closes gh-32788 --- 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 e7be6a37acd7..b564378b030a 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -570,7 +570,7 @@ bom { ] } } - library("Infinispan", "13.0.11.Final") { + library("Infinispan", "13.0.12.Final") { group("org.infinispan") { imports = [ "infinispan-bom" From c416c17fe85507e82ea172d43b54086a1a8d02a4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 19 Oct 2022 21:53:21 -0700 Subject: [PATCH 83/84] Polish documentation title case --- .../src/docs/asciidoc/actuator/endpoints.adoc | 2 +- .../src/docs/asciidoc/actuator/monitoring.adoc | 2 +- .../docs/asciidoc/cli/groovy-beans-dsl.adoc | 2 +- .../src/docs/asciidoc/cli/maven-setting.adoc | 2 +- .../src/docs/asciidoc/cli/using-the-cli.adoc | 4 ++-- .../container-images/efficient-images.adoc | 4 ++-- .../src/docs/asciidoc/data/nosql.adoc | 6 +++--- .../src/docs/asciidoc/documentation/using.adoc | 2 +- .../asciidoc/executable-jar/jarfile-class.adoc | 2 +- .../asciidoc/features/external-config.adoc | 18 +++++++++--------- .../src/docs/asciidoc/features/kotlin.adoc | 2 +- .../src/docs/asciidoc/features/testing.adoc | 10 +++++----- .../src/docs/asciidoc/howto/batch.adoc | 2 +- .../src/docs/asciidoc/howto/build.adoc | 2 +- .../src/docs/asciidoc/howto/webserver.adoc | 8 ++++---- .../src/docs/asciidoc/io/jta.adoc | 2 +- .../src/docs/asciidoc/messaging/amqp.adoc | 2 +- .../src/docs/asciidoc/upgrading/from-1x.adoc | 2 +- .../docs/asciidoc/upgrading/to-feature.adoc | 2 +- .../src/docs/asciidoc/using/devtools.adoc | 2 +- .../using/running-your-application.adoc | 2 +- .../src/docs/asciidoc/web/servlet.adoc | 6 +++--- .../src/docs/asciidoc/web/spring-security.adoc | 2 +- 23 files changed, 44 insertions(+), 44 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc index df3c45099c08..48d806012608 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc @@ -1034,7 +1034,7 @@ This would make `liveness` available at `/livez` and `readiness` at `readyz` on [[actuator.endpoints.kubernetes-probes.external-state]] -==== Checking External State with Kubernetes Probes +==== Checking External State With Kubernetes Probes Actuator configures the "`liveness`" and "`readiness`" probes as Health Groups. This means that all the <> are available for them. You can, for example, configure additional Health Indicators: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/monitoring.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/monitoring.adoc index 877f39541df1..499ec66a1351 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/monitoring.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/monitoring.adoc @@ -1,5 +1,5 @@ [[actuator.monitoring]] -== Monitoring and Management over HTTP +== Monitoring and Management Over HTTP If you are developing a web application, Spring Boot Actuator auto-configures all enabled endpoints to be exposed over HTTP. The default convention is to use the `id` of the endpoint with a prefix of `/actuator` as the URL path. For example, `health` is exposed as `/actuator/health`. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/groovy-beans-dsl.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/groovy-beans-dsl.adoc index b523e3d30b1c..b5f1b9509511 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/groovy-beans-dsl.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/groovy-beans-dsl.adoc @@ -1,5 +1,5 @@ [[cli.groovy-beans-dsl]] -== Developing Applications with the Groovy Beans DSL +== Developing Applications With the Groovy Beans DSL Spring Framework 4.0 has native support for a `beans{}` "`DSL`" (borrowed from https://grails.org/[Grails]), and you can embed bean definitions in your Groovy application scripts by using the same format. This is sometimes a good way to include external features like middleware declarations, as shown in the following example: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/maven-setting.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/maven-setting.adoc index 6be011286e47..6b99e94d85bb 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/maven-setting.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/maven-setting.adoc @@ -1,5 +1,5 @@ [[cli.maven-setting]] -== Configuring the CLI with settings.xml +== Configuring the CLI With settings.xml The Spring Boot CLI uses Maven Resolver, Maven's dependency resolution engine, to resolve dependencies. The CLI makes use of the Maven configuration found in `~/.m2/settings.xml` to configure Maven Resolver. The following configuration settings are honored by the CLI: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/using-the-cli.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/using-the-cli.adoc index 066472a4767f..20485959c4a5 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/using-the-cli.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/cli/using-the-cli.adoc @@ -50,7 +50,7 @@ The `version` command provides a quick way to check which version of Spring Boot [[cli.using-the-cli.run]] -=== Running Applications with the CLI +=== Running Applications With the CLI You can compile and run Groovy source code by using the `run` command. The Spring Boot CLI is completely self-contained, so you do not need any external Groovy installation. @@ -197,7 +197,7 @@ However, to ensure consistent ordering of the dependency management, you can use [[cli.using-the-cli.multiple-source-files]] -=== Applications with Multiple Source Files +=== Applications With Multiple Source Files You can use "`shell globbing`" with all commands that accept file input. Doing so lets you use multiple files from a single directory, as shown in the following example: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/container-images/efficient-images.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/container-images/efficient-images.adoc index 744089d3fb3a..b6cc601575f4 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/container-images/efficient-images.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/container-images/efficient-images.adoc @@ -1,5 +1,5 @@ [[container-images.efficient-images]] -== Efficient container images +== Efficient Container Images It is easily possible to package a Spring Boot fat jar as a docker image. However, there are various downsides to copying and running the fat jar as is in the docker image. There’s always a certain amount of overhead when running a fat jar without unpacking it, and in a containerized environment this can be noticeable. @@ -9,7 +9,7 @@ If you put jar files in the layer before your application classes, Docker often [[container-images.efficient-images.unpacking]] -=== Unpacking the fat jar +=== Unpacking the Executable JAR If you are running your application from a container, you can use an executable jar, but it is also often an advantage to explode it and run it in a different way. Certain PaaS implementations may also choose to unpack archives before they run. For example, Cloud Foundry operates this way. 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 b72bc6d1073a..c91f98b8d94f 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 @@ -275,7 +275,7 @@ Spring Boot provides a dedicated "`Starter`", `spring-boot-starter-data-elastics [[data.nosql.elasticsearch.connecting-using-rest]] -==== Connecting to Elasticsearch using REST clients +==== Connecting to Elasticsearch Using REST clients Elasticsearch ships https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html[two different REST clients] that you can use to query a cluster: the "Low Level" client and the "High Level" client. Spring Boot provides support for the "High Level" client, which ships with `org.elasticsearch.client:elasticsearch-rest-high-level-client`. Additionally, Spring Boot provides support for a reactive client, based on Spring Framework's `WebClient`, that ships with `org.springframework.data:spring-data-elasticsearch`. @@ -293,7 +293,7 @@ You can use `spring.elasticsearch.*` properties to further tune how the clients ---- [[data.nosql.elasticsearch.connecting-using-rest.restclient]] -===== Connecting to Elasticsearch using RestHighLevelClient +===== Connecting to Elasticsearch Using RestHighLevelClient If you have `elasticsearch-rest-high-level-client` on the classpath, Spring Boot will auto-configure and register a `RestHighLevelClient` bean. In addition to the properties described previously, to fine-tune the `RestHighLevelClient`, you can register an arbitrary number of beans that implement `RestClientBuilderCustomizer` for more advanced customizations. To take full control over its registration, define a `RestClientBuilder` bean. @@ -316,7 +316,7 @@ You can further tune how `Sniffer` is configured, as shown in the following exam [[data.nosql.elasticsearch.connecting-using-rest.webclient]] -===== Connecting to Elasticsearch using ReactiveElasticsearchClient +===== Connecting to Elasticsearch Using ReactiveElasticsearchClient {spring-data-elasticsearch}[Spring Data Elasticsearch] ships `ReactiveElasticsearchClient` for querying Elasticsearch instances in a reactive fashion. It is built on top of WebFlux's `WebClient`, so both `spring-boot-starter-elasticsearch` and `spring-boot-starter-webflux` dependencies are useful to enable this support. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/documentation/using.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/documentation/using.adoc index 122466f852d3..c5a30914f843 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/documentation/using.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/documentation/using.adoc @@ -1,5 +1,5 @@ [[documentation.using]] -== Developing with Spring Boot +== Developing With Spring Boot Ready to actually start using Spring Boot? <>: * *Build systems:* <> | <> | <> | <> diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/executable-jar/jarfile-class.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/executable-jar/jarfile-class.adoc index 65e3d3de9b5e..da7c616fb314 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/executable-jar/jarfile-class.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/executable-jar/jarfile-class.adoc @@ -26,7 +26,7 @@ We do not need to unpack the archive, and we do not need to read all entry data [[appendix.executable-jar.jarfile-class.compatibility]] -=== Compatibility with the Standard Java "`JarFile`" +=== Compatibility With the Standard Java "`JarFile`" Spring Boot Loader strives to remain compatible with existing code and libraries. `org.springframework.boot.loader.jar.JarFile` extends from `java.util.jar.JarFile` and should work as a drop-in replacement. The `getURL()` method returns a `URL` that opens a connection compatible with `java.net.JarURLConnection` and can be used with Java's `URLClassLoader`. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc index fb147302fd73..c1d7ea7bbd18 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc @@ -101,7 +101,7 @@ This means that the JSON cannot override properties from lower order property so [[features.external-config.files]] -=== External Application Properties [[features.external-config.files]] +=== External Application Properties Spring Boot will automatically find and load `application.properties` and `application.yaml` files from the following locations when your application starts: . From the classpath @@ -474,7 +474,7 @@ See the _<` will return a Map with the [[features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables]] -===== Binding from Environment Variables +===== Binding From Environment Variables Most operating systems impose strict rules around the names that can be used for environment variables. For example, Linux shell variables can contain only letters (`a` to `z` or `A` to `Z`), numbers (`0` to `9`) or the underscore character (`_`). By convention, Unix shell variables will also have their names in UPPERCASE. @@ -1117,7 +1117,7 @@ Doing so gives a transparent upgrade path while supporting a much richer format. [[features.external-config.typesafe-configuration-properties.conversion.periods]] -===== Converting periods +===== Converting Periods In addition to durations, Spring Boot can also work with `java.time.Period` type. The following formats can be used in application properties: 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 92662bccbe14..f432f3f1cd22 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 @@ -1,5 +1,5 @@ [[features.kotlin]] -== Kotlin support +== Kotlin Support https://kotlinlang.org[Kotlin] is a statically-typed language targeting the JVM (and other platforms) which allows writing concise and elegant code while providing {kotlin-docs}java-interop.html[interoperability] with existing libraries written in Java. Spring Boot provides Kotlin support by leveraging the support in other Spring projects such as Spring Framework, Spring Data, and Reactor. 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 8024b33e06ec..b4e398094de8 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 @@ -171,7 +171,7 @@ include::{docs-java}/features/testing/springbootapplications/usingapplicationarg [[features.testing.spring-boot-applications.with-mock-environment]] -==== Testing with a mock environment +==== Testing With a Mock Environment By default, `@SpringBootTest` does not start the server but instead sets up a mock environment for testing web endpoints. With Spring MVC, we can query our web endpoints using {spring-framework-docs}/testing.html#spring-mvc-test-framework[`MockMvc`] or `WebTestClient`, as shown in the following example: @@ -203,7 +203,7 @@ If you need to test these lower-level concerns, you can start a fully running se [[features.testing.spring-boot-applications.with-running-server]] -==== Testing with a running server +==== Testing With a Running Server If you need to start a full running server, we recommend that you use random ports. If you use `@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`, an available port is picked at random each time your test runs. @@ -710,7 +710,7 @@ It can also be used to configure the host, scheme, and port that appears in any [[features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-mock-mvc]] -===== Auto-configured Spring REST Docs Tests with Mock MVC +===== Auto-configured Spring REST Docs Tests With Mock MVC `@AutoConfigureRestDocs` customizes the `MockMvc` bean to use Spring REST Docs when testing servlet-based web applications. You can inject it by using `@Autowired` and use it in your tests as you normally would when using Mock MVC and Spring REST Docs, as shown in the following example: @@ -738,7 +738,7 @@ include::{docs-java}/features/testing/springbootapplications/autoconfiguredsprin [[features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-web-test-client]] -===== Auto-configured Spring REST Docs Tests with WebTestClient +===== Auto-configured Spring REST Docs Tests With WebTestClient `@AutoConfigureRestDocs` can also be used with `WebTestClient` when testing reactive web applications. You can inject it by using `@Autowired` and use it in your tests as you normally would when using `@WebFluxTest` and Spring REST Docs, as shown in the following example: @@ -765,7 +765,7 @@ include::{docs-java}/features/testing/springbootapplications/autoconfiguredsprin [[features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-rest-assured]] -===== Auto-configured Spring REST Docs Tests with REST Assured +===== Auto-configured Spring REST Docs Tests With REST Assured `@AutoConfigureRestDocs` makes a `RequestSpecification` bean, preconfigured to use Spring REST Docs, available to your tests. You can inject it by using `@Autowired` and use it in your tests as you normally would when using REST Assured and Spring REST Docs, as shown in the following example: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/batch.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/batch.adoc index 3b492be4626a..0f732801532a 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/batch.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/batch.adoc @@ -30,7 +30,7 @@ See {spring-boot-autoconfigure-module-code}/batch/BatchAutoConfiguration.java[Ba [[howto.batch.running-from-the-command-line]] -=== Running from the Command Line +=== Running From the Command Line Spring Boot converts any command line argument starting with `--` to a property to add to the `Environment`, see <>. This should not be used to pass arguments to batch jobs. To specify batch arguments on the command line, use the regular format (that is without `--`), as shown in the following example: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/build.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/build.adoc index 8f6eac81229f..278823f356da 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/build.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/build.adoc @@ -257,7 +257,7 @@ See {spring-boot-maven-plugin-docs}#run-example-debug[this example] for more det [[howto.build.build-an-executable-archive-with-ant-without-using-spring-boot-antlib]] -=== Build an Executable Archive from Ant without Using spring-boot-antlib +=== Build an Executable Archive From Ant without Using spring-boot-antlib To build with Ant, you need to grab dependencies, compile, and then create a jar or war archive. To make it executable, you can either use the `spring-boot-antlib` module or you can follow these instructions: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/webserver.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/webserver.adoc index f588b520e6b0..143413ccd0e1 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/webserver.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/webserver.adoc @@ -219,7 +219,7 @@ The details of the `h2` support depend on the chosen web server and the applicat [[howto.webserver.configure-http2.tomcat]] -==== HTTP/2 with Tomcat +==== HTTP/2 With Tomcat Spring Boot ships by default with Tomcat 9.0.x which supports `h2c` out of the box and `h2` out of the box when using JDK 9 or later. Alternatively, `h2` can be used on JDK 8 if the `libtcnative` library and its dependencies are installed on the host operating system. @@ -239,7 +239,7 @@ This error is not fatal, and the application still starts with HTTP/1.1 SSL supp [[howto.webserver.configure-http2.jetty]] -==== HTTP/2 with Jetty +==== HTTP/2 With Jetty For HTTP/2 support, Jetty requires the additional `org.eclipse.jetty.http2:http2-server` dependency. To use `h2c` no other dependencies are required. To use `h2`, you also need to choose one of the following dependencies, depending on your deployment: @@ -251,7 +251,7 @@ To use `h2`, you also need to choose one of the following dependencies, dependin [[howto.webserver.configure-http2.netty]] -==== HTTP/2 with Reactor Netty +==== HTTP/2 With Reactor Netty The `spring-boot-webflux-starter` is using by default Reactor Netty as a server. Reactor Netty supports `h2c` using JDK 8 or later with no additional dependencies. Reactor Netty supports `h2` using the JDK support with JDK 9 or later. @@ -264,7 +264,7 @@ Developers can choose to import only the required dependencies using a classifie [[howto.webserver.configure-http2.undertow]] -==== HTTP/2 with Undertow +==== HTTP/2 With Undertow As of Undertow 1.4.0+, both `h2` and `h2c` are supported on JDK 8 without any additional dependencies. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/jta.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/jta.adoc index 29cdc2779d74..e6dd6763cdbc 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/jta.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/jta.adoc @@ -1,5 +1,5 @@ [[io.jta]] -== Distributed Transactions with JTA +== Distributed Transactions With JTA Spring Boot supports distributed JTA transactions across multiple XA resources by using an https://www.atomikos.com/[Atomikos] embedded transaction manager. JTA transactions are also supported when deploying to a suitable Java EE Application Server. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/messaging/amqp.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/messaging/amqp.adoc index da0a4d5efd32..1936095e79e1 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/messaging/amqp.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/messaging/amqp.adoc @@ -7,7 +7,7 @@ Spring Boot offers several conveniences for working with AMQP through RabbitMQ, [[messaging.amqp.rabbitmq]] -=== RabbitMQ support +=== RabbitMQ Support https://www.rabbitmq.com/[RabbitMQ] is a lightweight, reliable, scalable, and portable message broker based on the AMQP protocol. Spring uses `RabbitMQ` to communicate through the AMQP protocol. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/from-1x.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/from-1x.adoc index 10b440cac895..845cad71208c 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/from-1x.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/from-1x.adoc @@ -1,5 +1,5 @@ [[upgrading.from-1x]] -== Upgrading from 1.x +== Upgrading From 1.x If you are upgrading from the `1.x` release of Spring Boot, check the {github-wiki}/Spring-Boot-2.0-Migration-Guide["`migration guide`" on the project wiki] that provides detailed upgrade instructions. Check also the {github-wiki}["`release notes`"] for a list of "`new and noteworthy`" features for each release. \ No newline at end of file diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/to-feature.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/to-feature.adoc index 0f89c4f3323d..c901c572e1d1 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/to-feature.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/upgrading/to-feature.adoc @@ -1,5 +1,5 @@ [[upgrading.to-feature]] -== Upgrading to a new feature release +== Upgrading to a New Feature Release When upgrading to a new feature release, some properties may have been renamed or removed. Spring Boot provides a way to analyze your application's environment and print diagnostics at startup, but also temporarily migrate properties at runtime for you. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc index 9665caab4567..ce35945d6fb7 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc @@ -130,7 +130,7 @@ These work by rewriting classes as they are loaded to make them more amenable to [[using.devtools.restart.logging-condition-delta]] -==== Logging changes in condition evaluation +==== Logging Changes in Condition Evaluation By default, each time your application restarts, a report showing the condition evaluation delta is logged. The report shows the changes to your application's auto-configuration as you make changes such as adding or removing beans and setting configuration properties. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/running-your-application.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/running-your-application.adoc index 16419169b889..639d1dd637c9 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/running-your-application.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/running-your-application.adoc @@ -10,7 +10,7 @@ If you choose to package your application as a war file, see your server and IDE [[using.running-your-application.from-an-ide]] -=== Running from an IDE +=== Running From an IDE You can run a Spring Boot application from your IDE as a Java application. However, you first need to import your project. Import steps vary depending on your IDE and build system. 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 d149e4742022..a1798d1a3870 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 @@ -433,7 +433,7 @@ The `ErrorController` then picks up any unhandled exceptions. [[web.servlet.spring-mvc.error-handling.error-pages-without-spring-mvc]] -===== Mapping Error Pages outside of Spring MVC +===== Mapping Error Pages Outside of Spring MVC For applications that do not use Spring MVC, you can use the `ErrorPageRegistrar` interface to directly register `ErrorPages`. This abstraction works directly with the underlying embedded servlet container and works even if you do not have a Spring MVC `DispatcherServlet`. @@ -454,7 +454,7 @@ Note that the default `FilterRegistrationBean` does not include the `ERROR` disp [[web.servlet.spring-mvc.error-handling.in-a-war-deployment]] -===== Error handling in a war deployment +===== Error Handling in a WAR Deployment When deployed to a servlet container, Spring Boot uses its error page filter to forward a request with an error status to the appropriate error page. This is necessary as the servlet specification does not provide an API for registering error pages. Depending on the container that you are deploying your war file to and the technologies that your application uses, some additional configuration may be required. @@ -534,7 +534,7 @@ By default, the embedded server listens for HTTP requests on port `8080`. [[web.servlet.embedded-container.servlets-filters-listeners]] -==== Servlets, Filters, and listeners +==== Servlets, Filters, and Listeners When using an embedded servlet container, you can register servlets, filters, and all the listeners (such as `HttpSessionListener`) from the servlet spec, either by using Spring beans or by scanning for servlet components. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-security.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-security.adoc index 83af9974caf4..7baf4ef4ae82 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-security.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-security.adoc @@ -153,7 +153,7 @@ For production environments, consider using a `JdbcOAuth2AuthorizedClientService [[web.security.oauth2.client.common-providers]] -===== OAuth2 client registration for common providers +===== OAuth2 Client Registration for Common Providers For common OAuth2 and OpenID providers, including Google, Github, Facebook, and Okta, we provide a set of provider defaults (`google`, `github`, `facebook`, and `okta`, respectively). If you do not need to customize these providers, you can set the `provider` attribute to the one for which you need to infer defaults. From a0e0cd7ffb28ab16acb27b65db605a7a99ebd554 Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 20 Oct 2022 12:28:41 +0000 Subject: [PATCH 84/84] Release v2.7.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ba80446ebd59..29e00274cb7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=2.7.5-SNAPSHOT +version=2.7.5 org.gradle.caching=true org.gradle.parallel=true