diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 37205707ea20..e5956ae140b5 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -20,10 +20,10 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 - name: Set up Gradle - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 - name: Build env: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index c87c892efbf0..0a3f50c1f72a 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -10,4 +10,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 diff --git a/.gitignore b/.gitignore index a5256ecef4fa..2a812bc5079c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ target secrets.yml .gradletasknamecache .sts4-cache +.git-hooks/ diff --git a/.sdkmanrc b/.sdkmanrc index 6dbcaa9337a9..c217333df42a 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=17.0.9-librca +java=17.0.10-librca diff --git a/README.adoc b/README.adoc index b603722e4e25..7870511ad7a3 100755 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,4 @@ -= Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"] += Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"] :docs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference :github: https://github.com/spring-projects/spring-boot diff --git a/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java index 175518e7cf15..339e47fa60ef 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java index e3bce2d4f41a..0e7b5a9ffc6d 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ci/images/ci-image-jdk21/Dockerfile b/ci/images/ci-image-jdk21/Dockerfile index 5e7c13199978..8c942a87e959 100644 --- a/ci/images/ci-image-jdk21/Dockerfile +++ b/ci/images/ci-image-jdk21/Dockerfile @@ -1,5 +1,5 @@ ARG DOCKER_PROXY_CACHE= -FROM ${DOCKER_PROXY_CACHE}ubuntu:jammy-20240111 +FROM ${DOCKER_PROXY_CACHE}ubuntu:jammy-20240212 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 beb253a407ce..a8c2fb87c3e2 100644 --- a/ci/images/ci-image/Dockerfile +++ b/ci/images/ci-image/Dockerfile @@ -1,5 +1,5 @@ ARG DOCKER_PROXY_CACHE= -FROM ${DOCKER_PROXY_CACHE}ubuntu:jammy-20240111 +FROM ${DOCKER_PROXY_CACHE}ubuntu:jammy-20240212 ADD setup.sh /setup.sh ADD get-jdk-url.sh /get-jdk-url.sh diff --git a/ci/images/get-docker-url.sh b/ci/images/get-docker-url.sh index 965d738b72cb..cf11be941203 100755 --- a/ci/images/get-docker-url.sh +++ b/ci/images/get-docker-url.sh @@ -1,5 +1,5 @@ #!/bin/bash set -e -version="24.0.7" +version="25.0.3" echo "https://download.docker.com/linux/static/stable/x86_64/docker-$version.tgz"; diff --git a/ci/images/get-jdk-url.sh b/ci/images/get-jdk-url.sh index f026e7fea70e..cf0495c6e60f 100755 --- a/ci/images/get-jdk-url.sh +++ b/ci/images/get-jdk-url.sh @@ -3,7 +3,7 @@ set -e case "$1" in java17) - echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz" + echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10+13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz" ;; java21) echo "https://github.com/bell-sw/Liberica/releases/download/21.0.2+14/bellsoft-jdk21.0.2+14-linux-amd64.tar.gz" diff --git a/ci/scripts/detect-docker-updates.sh b/ci/scripts/detect-docker-updates.sh index 3d15719b56ec..d517dd8fa2eb 100755 --- a/ci/scripts/detect-docker-updates.sh +++ b/ci/scripts/detect-docker-updates.sh @@ -8,8 +8,8 @@ if [[ $latest_version =~ (beta|rc) ]]; then fi title_prefix="Upgrade CI to Docker" -milestone_number=$( curl -s https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPO}/milestones\?state\=open | jq -c --arg MILESTONE "$MILESTONE" '.[] | select(.title==$MILESTONE)' | jq -r '.number') -existing_upgrade_issues=$( curl -s https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPO}/issues\?labels\=type:%20task\&state\=open\&creator\=spring-builds\&milestone\=${milestone_number} | jq -c --arg TITLE_PREFIX "$title_prefix" '.[] | select(.title | startswith($TITLE_PREFIX))' ) +milestone_number=$( curl -u ${GITHUB_USERNAME}:${GITHUB_PASSWORD} -s https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPO}/milestones\?state\=open | jq -c --arg MILESTONE "$MILESTONE" '.[] | select(.title==$MILESTONE)' | jq -r '.number') +existing_upgrade_issues=$( curl -u ${GITHUB_USERNAME}:${GITHUB_PASSWORD} -s https://api.github.com/repos/${GITHUB_ORGANIZATION}/${GITHUB_REPO}/issues\?labels\=type:%20task\&state\=open\&creator\=spring-builds\&milestone\=${milestone_number} | jq -c --arg TITLE_PREFIX "$title_prefix" '.[] | select(.title | startswith($TITLE_PREFIX))' ) latest="https://download.docker.com/linux/static/stable/x86_64/docker-$latest_version.tgz" current=$( git-repo/ci/images/get-docker-url.sh ) diff --git a/gradle.properties b/gradle.properties index d6dd96dafc1a..65a5c1668c2f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,19 @@ -version=3.2.2-SNAPSHOT +version=3.2.3 org.gradle.caching=true org.gradle.parallel=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 assertjVersion=3.24.2 -commonsCodecVersion=1.16.0 +commonsCodecVersion=1.16.1 +commonsCompressVersion=1.21 hamcrestVersion=2.2 -jacksonVersion=2.15.3 -junitJupiterVersion=5.10.1 +jacksonVersion=2.15.4 +junitJupiterVersion=5.10.2 kotlinVersion=1.9.22 mavenVersion=3.9.4 nativeBuildToolsVersion=0.9.28 -springFrameworkVersion=6.1.3 -tomcatVersion=10.1.18 +springFrameworkVersion=6.1.4 +tomcatVersion=10.1.19 kotlin.stdlib.default.dependency=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index afba109285af..d64cd4917707 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b1624c473c42..3e593191a337 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68d65c8..1aa94a426907 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f135d..25da30dbdeee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration.java index 1541778479a9..959789adfd50 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration.java @@ -23,13 +23,16 @@ import org.aspectj.weaver.Advice; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAspectsAutoConfiguration.ObservationAnnotationsEnabledCondition; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; /** * {@link EnableAutoConfiguration Auto-configuration} for Micrometer-based metrics @@ -40,7 +43,7 @@ */ @AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class }) @ConditionalOnClass({ MeterRegistry.class, Advice.class }) -@ConditionalOnProperty(prefix = "micrometer.observations.annotations", name = "enabled", havingValue = "true") +@Conditional(ObservationAnnotationsEnabledCondition.class) @ConditionalOnBean(MeterRegistry.class) public class MetricsAspectsAutoConfiguration { @@ -59,4 +62,22 @@ TimedAspect timedAspect(MeterRegistry registry, return timedAspect; } + static final class ObservationAnnotationsEnabledCondition extends AnyNestedCondition { + + ObservationAnnotationsEnabledCondition() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @ConditionalOnProperty(prefix = "micrometer.observations.annotations", name = "enabled", havingValue = "true") + static class MicrometerObservationsEnabledCondition { + + } + + @ConditionalOnProperty(prefix = "management.observations.annotations", name = "enabled", havingValue = "true") + static class ManagementObservationsEnabledCondition { + + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java index a1c4a600fe91..e9da837d148e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java index 1ef058236f8a..b53699f2c1f4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfiguration.java @@ -32,11 +32,13 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -97,7 +99,7 @@ public PropagatingReceiverTracingObservationHandler propagatingReceiverTracin @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) - @ConditionalOnProperty(prefix = "micrometer.observations.annotations", name = "enabled", havingValue = "true") + @Conditional(ObservationAnnotationsEnabledCondition.class) static class SpanAspectConfiguration { @Bean @@ -124,4 +126,22 @@ SpanAspect spanAspect(MethodInvocationProcessor methodInvocationProcessor) { } + static final class ObservationAnnotationsEnabledCondition extends AnyNestedCondition { + + ObservationAnnotationsEnabledCondition() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } + + @ConditionalOnProperty(prefix = "micrometer.observations.annotations", name = "enabled", havingValue = "true") + static class MicrometerObservationsEnabledCondition { + + } + + @ConditionalOnProperty(prefix = "management.observations.annotations", name = "enabled", havingValue = "true") + static class ManagementObservationsEnabledCondition { + + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java index 6f598cc41b95..c38ef94d1253 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -224,7 +224,7 @@ List getEffectiveConsumedTypes() { /** * Supported propagation types. The declared order of the values matter. */ - enum PropagationType { + public enum PropagationType { /** * W3C propagation. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java index 0e1746e3e607..aa13066f1419 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,6 +81,7 @@ private void customizeSameAsParentContext(T factory) { customizers.add(BeanFactoryUtils.beanOfTypeIncludingAncestors(this.beanFactory, customizerClass)); } catch (NoSuchBeanDefinitionException ex) { + // Ignore } } invokeCustomizers(factory, customizers); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 90eef215d6cf..f5cac72f8ac0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -30,6 +30,14 @@ "description": "Whether to enable default metrics exporters.", "defaultValue": true }, + { + "name": "management.endpoint.configprops.show-values", + "defaultValue": "never" + }, + { + "name": "management.endpoint.env.show-values", + "defaultValue": "never" + }, { "name": "management.endpoint.health.probes.add-additional-paths", "type": "java.lang.Boolean", @@ -61,6 +69,10 @@ "description": "Whether to validate health group membership on startup. Validation fails if a group includes or excludes a health contributor that does not exist.", "defaultValue": true }, + { + "name": "management.endpoint.quartz.show-values", + "defaultValue": "never" + }, { "name": "management.endpoints.enabled-by-default", "type": "java.lang.Boolean", @@ -2061,6 +2073,12 @@ "level": "error" } }, + { + "name": "management.observations.annotations.enabled", + "type": "java.lang.Boolean", + "description": "Whether auto-configuration of Micrometer annotations is enabled.", + "defaultValue": false + }, { "name": "management.otlp.metrics.export.base-time-unit", "defaultValue": "milliseconds" @@ -2233,8 +2251,10 @@ { "name": "micrometer.observations.annotations.enabled", "type": "java.lang.Boolean", - "description": "Whether auto-configuration of Micrometer annotations is enabled.", - "defaultValue": false + "deprecation": { + "level": "error", + "replacement": "management.observations.annotations.enabled" + } } ], "hints": [ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java index 99d2b4452810..aaf3805f91d0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AuditEventsEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.util.Arrays; import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; @@ -56,7 +56,7 @@ class AuditEventsEndpointDocumentationTests extends MockMvcEndpointDocumentation void allAuditEvents() throws Exception { String queryTimestamp = "2017-11-07T09:37Z"; given(this.repository.find(any(), any(), any())) - .willReturn(Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap()))); + .willReturn(List.of(new AuditEvent("alice", "logout", Collections.emptyMap()))); this.mockMvc.perform(get("/actuator/auditevents").param("after", queryTimestamp)) .andExpect(status().isOk()) .andDo(document("auditevents/all", @@ -72,7 +72,7 @@ void filteredAuditEvents() throws Exception { OffsetDateTime now = OffsetDateTime.now(); String queryTimestamp = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(now); given(this.repository.find("alice", now.toInstant(), "logout")) - .willReturn(Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap()))); + .willReturn(List.of(new AuditEvent("alice", "logout", Collections.emptyMap()))); this.mockMvc .perform(get("/actuator/auditevents").param("principal", "alice") .param("after", queryTimestamp) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java index 282eb162f18b..4eb3fb8bcd4e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -50,7 +49,7 @@ class BeansEndpointDocumentationTests extends MockMvcEndpointDocumentationTests @Test void beans() throws Exception { - List beanFields = Arrays.asList(fieldWithPath("aliases").description("Names of any aliases."), + List beanFields = List.of(fieldWithPath("aliases").description("Names of any aliases."), fieldWithPath("scope").description("Scope of the bean."), fieldWithPath("type").description("Fully qualified type of the bean."), fieldWithPath("resource").description("Resource in which the bean was defined, if any.") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/CachesEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/CachesEndpointDocumentationTests.java index ba1fc9346e64..44cf427a2967 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/CachesEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/CachesEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -50,8 +49,7 @@ */ class CachesEndpointDocumentationTests extends MockMvcEndpointDocumentationTests { - private static final List levelFields = Arrays.asList( - fieldWithPath("name").description("Cache name."), + private static final List levelFields = List.of(fieldWithPath("name").description("Cache name."), fieldWithPath("cacheManager").description("Cache manager name."), fieldWithPath("target").description("Fully qualified name of the native cache.")); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java index 3e36dff8ad78..58586569d147 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,10 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -30,13 +27,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -51,26 +44,13 @@ */ class ConditionsReportEndpointDocumentationTests extends MockMvcEndpointDocumentationTests { - private MockMvc mockMvc; - - @Autowired - private WebApplicationContext applicationContext; - - @Override - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext) - .apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation).uris()) - .build(); - } - @Test void conditions() throws Exception { - List positiveMatchFields = Arrays.asList( + List positiveMatchFields = List.of( fieldWithPath("").description("Classes and methods with conditions that were matched."), fieldWithPath(".*.[].condition").description("Name of the condition."), fieldWithPath(".*.[].message").description("Details of why the condition was matched.")); - List negativeMatchFields = Arrays.asList( + List negativeMatchFields = List.of( fieldWithPath("").description("Classes and methods with conditions that were not matched."), fieldWithPath(".*.notMatched").description("Conditions that were matched."), fieldWithPath(".*.notMatched.[].condition").description("Name of the condition."), @@ -104,7 +84,7 @@ ConditionsReportEndpoint autoConfigurationReportEndpoint(ConfigurableApplication ConditionEvaluationReport conditionEvaluationReport = ConditionEvaluationReport .get(context.getBeanFactory()); conditionEvaluationReport - .recordEvaluationCandidates(Arrays.asList(PropertyPlaceholderAutoConfiguration.class.getName())); + .recordEvaluationCandidates(List.of(PropertyPlaceholderAutoConfiguration.class.getName())); return new ConditionsReportEndpoint(context); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java index 154f0f217a40..591b40e72318 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.List; import javax.sql.DataSource; @@ -61,7 +60,7 @@ void flyway() throws Exception { } private List migrationFieldDescriptors() { - return Arrays.asList(fieldWithPath("checksum").description("Checksum of the migration, if any.").optional(), + return List.of(fieldWithPath("checksum").description("Checksum of the migration, if any.").optional(), fieldWithPath("description").description("Description of the migration, if any.").optional(), fieldWithPath("executionTime").description("Execution time in milliseconds of an applied migration.") .optional(), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java index a001d6869910..cc6920b3bf36 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; import java.io.File; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -68,7 +67,7 @@ */ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests { - private static final List componentFields = Arrays.asList( + private static final List componentFields = List.of( fieldWithPath("status").description("Status of a specific part of the application"), subsectionWithPath("details").description("Details of the health of a specific part of the application.")); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HttpExchangesEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HttpExchangesEndpointDocumentationTests.java index 9f757f2af553..95a91984ce75 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HttpExchangesEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HttpExchangesEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,9 +22,9 @@ import java.time.Duration; import java.time.Instant; import java.time.ZoneId; -import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.List; import java.util.UUID; import org.junit.jupiter.api.Test; @@ -66,11 +66,11 @@ void httpExchanges() throws Exception { given(request.getUri()).willReturn(URI.create("https://api.example.com")); given(request.getMethod()).willReturn("GET"); given(request.getHeaders()) - .willReturn(Collections.singletonMap(HttpHeaders.ACCEPT, Arrays.asList("application/json"))); + .willReturn(Collections.singletonMap(HttpHeaders.ACCEPT, List.of("application/json"))); RecordableHttpResponse response = mock(RecordableHttpResponse.class); given(response.getStatus()).willReturn(200); given(response.getHeaders()) - .willReturn(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, Arrays.asList("application/json"))); + .willReturn(Collections.singletonMap(HttpHeaders.CONTENT_TYPE, List.of("application/json"))); Principal principal = mock(Principal.class); given(principal.getName()).willReturn("alice"); Instant instant = Instant.parse("2022-12-22T13:43:41.00Z"); @@ -78,7 +78,7 @@ void httpExchanges() throws Exception { Clock end = Clock.offset(start, Duration.ofMillis(23)); HttpExchange exchange = HttpExchange.start(start, request) .finish(end, response, () -> principal, () -> UUID.randomUUID().toString(), EnumSet.allOf(Include.class)); - given(this.repository.findAll()).willReturn(Arrays.asList(exchange)); + given(this.repository.findAll()).willReturn(List.of(exchange)); this.mockMvc.perform(get("/actuator/httpexchanges")) .andExpect(status().isOk()) .andDo(document("httpexchanges", responseFields( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java index 50329c8f86aa..cd12c81c2ca9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.List; import liquibase.changelog.ChangeSet.ExecType; @@ -59,7 +58,7 @@ void liquibase() throws Exception { } private List getChangeSetFieldDescriptors() { - return Arrays.asList(fieldWithPath("author").description("Author of the change set."), + return List.of(fieldWithPath("author").description("Author of the change set."), fieldWithPath("changeLog").description("Change log that contains the change set."), fieldWithPath("comments").description("Comments on the change set."), fieldWithPath("contexts").description("Contexts of the change set."), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java index 33f90fbe7563..acc1b10a2169 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LoggersEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; -import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; @@ -55,18 +54,14 @@ */ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTests { - private static final List levelFields = Arrays.asList( + private static final List levelFields = List.of( fieldWithPath("configuredLevel").description("Configured level of the logger, if any.").optional(), fieldWithPath("effectiveLevel").description("Effective level of the logger.")); - private static final List groupLevelFields; - - static { - groupLevelFields = Arrays - .asList(fieldWithPath("configuredLevel").description("Configured level of the logger group, if any.") - .type(JsonFieldType.STRING) - .optional(), fieldWithPath("members").description("Loggers that are part of this group")); - } + private static final List groupLevelFields = List + .of(fieldWithPath("configuredLevel").description("Configured level of the logger group, if any.") + .type(JsonFieldType.STRING) + .optional(), fieldWithPath("members").description("Loggers that are part of this group")); @MockBean private LoggingSystem loggingSystem; @@ -78,7 +73,7 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest void allLoggers() throws Exception { given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class)); given(this.loggingSystem.getLoggerConfigurations()) - .willReturn(Arrays.asList(new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO), + .willReturn(List.of(new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO), new LoggerConfiguration("com.example", LogLevel.DEBUG, LogLevel.DEBUG))); this.mockMvc.perform(get("/actuator/loggers")) .andExpect(status().isOk()) @@ -164,7 +159,7 @@ LoggersEndpoint endpoint(LoggingSystem loggingSystem, LoggerGroups groups) { } private Map> getLoggerGroups() { - return Collections.singletonMap("test", Arrays.asList("test.member1", "test.member2")); + return Collections.singletonMap("test", List.of("test.member1", "test.member2")); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java index 79c673e81282..ea189c393f31 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointReactiveDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -81,7 +80,7 @@ void webTestClient(RestDocumentationContextProvider restDocumentation) { @Test void mappings() { - List requestMappingConditions = Arrays.asList( + List requestMappingConditions = List.of( requestMappingConditionField("").description("Details of the request mapping conditions.").optional(), requestMappingConditionField(".consumes").description("Details of the consumes condition"), requestMappingConditionField(".consumes.[].mediaType").description("Consumed media type."), @@ -101,7 +100,7 @@ void mappings() { requestMappingConditionField(".produces").description("Details of the produces condition."), requestMappingConditionField(".produces.[].mediaType").description("Produced media type."), requestMappingConditionField(".produces.[].negated").description("Whether the media type is negated.")); - List handlerMethod = Arrays.asList( + List handlerMethod = List.of( fieldWithPath("*.[].details.handlerMethod").optional() .type(JsonFieldType.OBJECT) .description("Details of the method, if any, that will handle requests to this mapping."), @@ -111,13 +110,13 @@ void mappings() { .description("Name of the method."), fieldWithPath("*.[].details.handlerMethod.descriptor").type(JsonFieldType.STRING) .description("Descriptor of the method as specified in the Java Language Specification.")); - List handlerFunction = Arrays.asList( + List handlerFunction = List.of( fieldWithPath("*.[].details.handlerFunction").optional() .type(JsonFieldType.OBJECT) .description("Details of the function, if any, that will handle requests to this mapping."), fieldWithPath("*.[].details.handlerFunction.className").type(JsonFieldType.STRING) .description("Fully qualified name of the class of the function.")); - List dispatcherHandlerFields = new ArrayList<>(Arrays.asList( + List dispatcherHandlerFields = new ArrayList<>(List.of( fieldWithPath("*") .description("Dispatcher handler mappings, if any, keyed by dispatcher handler bean name."), fieldWithPath("*.[].details").optional() diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java index b6399c7c3d4d..b11040e97f84 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/MappingsEndpointServletDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -94,7 +93,7 @@ void mappings() { .optional() .type(JsonFieldType.OBJECT), parentIdField()); - List dispatcherServletFields = new ArrayList<>(Arrays.asList( + List dispatcherServletFields = new ArrayList<>(List.of( fieldWithPath("*") .description("Dispatcher servlet mappings, if any, keyed by dispatcher servlet bean name."), fieldWithPath("*.[].details").optional() @@ -102,7 +101,7 @@ void mappings() { .description("Additional implementation-specific details about the mapping. Optional."), fieldWithPath("*.[].handler").description("Handler for the mapping."), fieldWithPath("*.[].predicate").description("Predicate for the mapping."))); - List requestMappingConditions = Arrays.asList( + List requestMappingConditions = List.of( requestMappingConditionField("").description("Details of the request mapping conditions.").optional(), requestMappingConditionField(".consumes").description("Details of the consumes condition"), requestMappingConditionField(".consumes.[].mediaType").description("Consumed media type."), @@ -122,7 +121,7 @@ void mappings() { requestMappingConditionField(".produces").description("Details of the produces condition."), requestMappingConditionField(".produces.[].mediaType").description("Produced media type."), requestMappingConditionField(".produces.[].negated").description("Whether the media type is negated.")); - List handlerMethod = Arrays.asList( + List handlerMethod = List.of( fieldWithPath("*.[].details.handlerMethod").optional() .type(JsonFieldType.OBJECT) .description("Details of the method, if any, that will handle requests to this mapping."), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/QuartzEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/QuartzEndpointDocumentationTests.java index 63093af47b98..fd51daf5d113 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/QuartzEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/QuartzEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -130,10 +130,10 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests .withInterval(1, IntervalUnit.HOUR)) .build(); - private static final List triggerSummary = Arrays.asList(previousFireTime(""), nextFireTime(""), + private static final List triggerSummary = List.of(previousFireTime(""), nextFireTime(""), priority("")); - private static final List cronTriggerSummary = Arrays.asList( + private static final List cronTriggerSummary = List.of( fieldWithPath("expression").description("Cron expression to use."), fieldWithPath("timeZone").type(JsonFieldType.STRING) .optional() @@ -158,8 +158,8 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests fieldWithPath("timeZone").type(JsonFieldType.STRING) .description("Time zone within which time calculations will be performed, if any.")); - private static final List customTriggerSummary = Collections.singletonList( - fieldWithPath("trigger").description("A toString representation of the custom trigger instance.")); + private static final List customTriggerSummary = List + .of(fieldWithPath("trigger").description("A toString representation of the custom trigger instance.")); private static final FieldDescriptor[] commonCronDetails = new FieldDescriptor[] { fieldWithPath("group").description("Name of the group."), @@ -280,7 +280,7 @@ void quartzJob() throws Exception { setPreviousNextFireTime(secondTrigger, "2020-12-04T03:00:00Z", "2020-12-04T12:00:00Z"); mockTriggers(firstTrigger, secondTrigger); given(this.scheduler.getTriggersOfJob(jobOne.getKey())) - .willAnswer((invocation) -> Arrays.asList(firstTrigger, secondTrigger)); + .willAnswer((invocation) -> List.of(firstTrigger, secondTrigger)); this.mockMvc.perform(get("/actuator/quartz/jobs/samples/jobOne")) .andExpect(status().isOk()) .andDo(document("quartz/job-details", responseFields( @@ -398,7 +398,7 @@ void quartzTriggerCustom() throws Exception { .andWithPrefix("custom.", customTriggerSummary))); } - private T setupTriggerDetails(TriggerBuilder builder, TriggerState state) + private void setupTriggerDetails(TriggerBuilder builder, TriggerState state) throws SchedulerException { T trigger = builder.withIdentity("example", "samples") .withDescription("Example trigger") @@ -409,7 +409,6 @@ private T setupTriggerDetails(TriggerBuilder builder, Tri setPreviousNextFireTime(trigger, "2020-12-04T03:00:00Z", "2020-12-07T03:00:00Z"); given(this.scheduler.getTriggerState(trigger.getKey())).willReturn(state); mockTriggers(trigger); - return trigger; } private static FieldDescriptor startTime(String prefix) { @@ -481,7 +480,7 @@ private void mockTriggers(Trigger... triggers) throws SchedulerException { } } - private T setPreviousNextFireTime(T trigger, String previousFireTime, String nextFireTime) { + private void setPreviousNextFireTime(T trigger, String previousFireTime, String nextFireTime) { OperableTrigger operableTrigger = (OperableTrigger) trigger; if (previousFireTime != null) { operableTrigger.setPreviousFireTime(fromUtc(previousFireTime)); @@ -489,7 +488,6 @@ private T setPreviousNextFireTime(T trigger, String previous if (nextFireTime != null) { operableTrigger.setNextFireTime(fromUtc(nextFireTime)); } - return trigger; } private static Date fromUtc(String utcTime) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java index efcc258699f1..cd757aa6cd51 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ScheduledTasksEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskHolder; -import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; @@ -73,8 +72,7 @@ void scheduledTasks() throws Exception { initialDelayWithPrefix("fixedRate.[]."), fieldWithPath("custom").description("Tasks with custom triggers, if any."), targetFieldWithPrefix("custom.[]."), - fieldWithPath("custom.[].trigger").description("Trigger for the task.")))) - .andDo(MockMvcResultHandlers.print()); + fieldWithPath("custom.[].trigger").description("Trigger for the task.")))); } private FieldDescriptor targetFieldWithPrefix(String prefix) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java index 0d43d8fa122d..c92c522005bf 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; import java.time.Instant; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -65,7 +64,7 @@ class SessionsEndpointDocumentationTests extends MockMvcEndpointDocumentationTes private static final Session sessionThree = createSession(Instant.now().minusSeconds(60 * 60 * 2), Instant.now().minusSeconds(12)); - private static final List sessionFields = Arrays.asList( + private static final List sessionFields = List.of( fieldWithPath("id").description("ID of the session."), fieldWithPath("attributeNames").description("Names of the attributes stored in the session."), fieldWithPath("creationTime").description("Timestamp of when the session was created."), diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java index 033312870bff..36e5d3fdbf69 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ShutdownEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.core.env.Environment; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -51,7 +50,7 @@ void shutdown() throws Exception { static class TestConfiguration { @Bean - ShutdownEndpoint endpoint(Environment environment) { + ShutdownEndpoint endpoint() { ShutdownEndpoint endpoint = new ShutdownEndpoint(); endpoint.setApplicationContext(new AnnotationConfigApplicationContext()); return endpoint; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfigurationTests.java index d2ecaf6f06d2..5371146e0f42 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfigurationTests.java @@ -41,7 +41,7 @@ class MetricsAspectsAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple()) - .withPropertyValues("micrometer.observations.annotations.enabled=true") + .withPropertyValues("management.observations.annotations.enabled=true") .withConfiguration(AutoConfigurations.of(MetricsAspectsAutoConfiguration.class)); @Test @@ -54,6 +54,17 @@ void shouldNotConfigureAspectsByDefault() { }); } + @Test + void shouldConfigureAspectsWithLegacyProperty() { + new ApplicationContextRunner().with(MetricsRun.simple()) + .withConfiguration(AutoConfigurations.of(MetricsAspectsAutoConfiguration.class)) + .withPropertyValues("micrometer.observations.annotations.enabled=true") + .run((context) -> { + assertThat(context).hasSingleBean(CountedAspect.class); + assertThat(context).hasSingleBean(TimedAspect.class); + }); + } + @Test void shouldConfigureAspects() { this.contextRunner.run((context) -> { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java index f671d494159e..5fb5e7d6af8f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/MicrometerTracingAutoConfigurationTests.java @@ -52,13 +52,12 @@ class MicrometerTracingAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withPropertyValues("micrometer.observations.annotations.enabled=true") + .withPropertyValues("management.observations.annotations.enabled=true") .withConfiguration(AutoConfigurations.of(MicrometerTracingAutoConfiguration.class)); @Test void shouldSupplyBeans() { this.contextRunner.withUserConfiguration(TracerConfiguration.class, PropagatorConfiguration.class) - .withPropertyValues("micrometer.observations.annotations.enabled=true") .run((context) -> { assertThat(context).hasSingleBean(DefaultTracingObservationHandler.class); assertThat(context).hasSingleBean(PropagatingReceiverTracingObservationHandler.class); @@ -133,7 +132,7 @@ void shouldNotSupplyBeansIfTracerIsMissing() { @Test void shouldNotSupplyAspectBeansIfPropertyIsDisabled() { this.contextRunner.withUserConfiguration(TracerConfiguration.class, PropagatorConfiguration.class) - .withPropertyValues("micrometer.observations.annotations.enabled=false") + .withPropertyValues("management.observations.annotations.enabled=false") .run((context) -> { assertThat(context).doesNotHaveBean(DefaultNewSpanParser.class); assertThat(context).doesNotHaveBean(ImperativeMethodInvocationProcessor.class); @@ -141,6 +140,18 @@ void shouldNotSupplyAspectBeansIfPropertyIsDisabled() { }); } + @Test + void shouldSupplyAspectBeansIfLegacyPropertyIsEnabled() { + new ApplicationContextRunner().withPropertyValues("micrometer.observations.annotations.enabled=true") + .withConfiguration(AutoConfigurations.of(MicrometerTracingAutoConfiguration.class)) + .withUserConfiguration(TracerConfiguration.class, PropagatorConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(DefaultNewSpanParser.class); + assertThat(context).hasSingleBean(ImperativeMethodInvocationProcessor.class); + assertThat(context).hasSingleBean(SpanAspect.class); + }); + } + @Test void shouldNotSupplyBeansIfAspectjIsMissing() { this.contextRunner.withUserConfiguration(TracerConfiguration.class) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java index 6e24168e6258..8cd51645de92 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -110,6 +110,7 @@ private void cleanExpiredCachedResponses(long accessTime) { this.cachedResponses.entrySet().removeIf((entry) -> entry.getValue().isStale(accessTime, this.timeToLive)); } catch (Exception ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 3621a5b94a52..3cb0cf56e0a6 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -83,6 +83,7 @@ * @author Madhura Bhave * @author Phillip Webb * @author Brian Clozel + * @author Scott Frederick * @since 2.0.0 */ @ImportRuntimeHints(AbstractWebFluxEndpointHandlerMappingRuntimeHints.class) @@ -260,6 +261,26 @@ public Object invoke(InvocationContext context) { } + protected static final class ExceptionCapturingInvoker implements OperationInvoker { + + private final OperationInvoker invoker; + + public ExceptionCapturingInvoker(OperationInvoker invoker) { + this.invoker = invoker; + } + + @Override + public Object invoke(InvocationContext context) { + try { + return this.invoker.invoke(context); + } + catch (Exception ex) { + return Mono.error(ex); + } + } + + } + /** * Reactive handler providing actuator links at the root endpoint. */ @@ -303,9 +324,9 @@ private ReactiveWebOperationAdapter(WebOperation operation) { private OperationInvoker getInvoker(WebOperation operation) { OperationInvoker invoker = operation::invoke; if (operation.isBlocking()) { - invoker = new ElasticSchedulerInvoker(invoker); + return new ElasticSchedulerInvoker(invoker); } - return invoker; + return new ExceptionCapturingInvoker(invoker); } private Supplier> getSecurityContextSupplier() { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java index 23f370bac339..cc2286270fd3 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/annotation/AbstractWebEndpointIntegrationTests.java @@ -226,6 +226,31 @@ void readOperationWithSingleQueryParameters() { .isEqualTo("1 2")); } + @Test + void readOperationWithQueryParametersMissing() { + load(QueryEndpointConfiguration.class, + (client) -> client.get().uri("/query").exchange().expectStatus().isBadRequest()); + } + + @Test + void reactiveReadOperationWithSingleQueryParameters() { + load(ReactiveQueryEndpointConfiguration.class, + (client) -> client.get() + .uri("/query?param=test") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("query") + .isEqualTo("test")); + } + + @Test + void reactiveReadOperationWithQueryParametersMissing() { + load(ReactiveQueryEndpointConfiguration.class, + (client) -> client.get().uri("/query").exchange().expectStatus().isBadRequest()); + } + @Test void readOperationWithSingleQueryParametersAndMultipleValues() { load(QueryEndpointConfiguration.class, @@ -732,6 +757,17 @@ QueryWithListEndpoint queryEndpoint() { } + @Configuration(proxyBeanMethods = false) + @Import(BaseConfiguration.class) + static class ReactiveQueryEndpointConfiguration { + + @Bean + ReactiveQueryEndpoint reactiveQueryEndpoint() { + return new ReactiveQueryEndpoint(); + } + + } + @Configuration(proxyBeanMethods = false) @Import(BaseConfiguration.class) static class VoidWriteResponseEndpointConfiguration { @@ -974,6 +1010,16 @@ Map queryWithParameterList(String one, List two) { } + @Endpoint(id = "query") + static class ReactiveQueryEndpoint { + + @ReadOperation + Mono> query(String param) { + return Mono.just(Collections.singletonMap("query", param)); + } + + } + @Endpoint(id = "voidwrite") static class VoidWriteResponseEndpoint { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java index 167ad6a0bc5e..347b8b881e9a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,6 +79,7 @@ void healthWhenIndicatorIsSlow(CapturedOutput output) { Thread.sleep(100); } catch (InterruptedException ex) { + // Ignore } return this.up; }; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java index d26f3d0d6b91..bdbd781ed1d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/SharedMetadataReaderFactoryContextInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import org.springframework.aot.AotDetector; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; @@ -38,11 +37,13 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -124,6 +125,7 @@ private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry reg registry.getBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } catch (NoSuchBeanDefinitionException ex) { + // Ignore } } @@ -186,14 +188,14 @@ private void configureConfigurationClassPostProcessor(ConfigurationClassPostProc * {@link FactoryBean} to create the shared {@link MetadataReaderFactory}. */ static class SharedMetadataReaderFactoryBean - implements FactoryBean, BeanClassLoaderAware, + implements FactoryBean, ResourceLoaderAware, ApplicationListener { private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory; @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(classLoader); + public void setResourceLoader(ResourceLoader resourceLoader) { + this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(resourceLoader); } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java index fc4fba61f0ba..53f561a30a6e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheCondition.java index 884bf693d651..34159a584b5b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,7 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM } } catch (BindException ex) { + // Ignore } return ConditionOutcome.noMatch(message.because("unknown cache type")); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 60f0f90cdd54..b90ac437e933 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -500,6 +500,7 @@ private Set> resolveWhenPossible(Set classNames) { resolved.add(resolve(className, this.classLoader)); } catch (ClassNotFoundException | NoClassDefFoundError ex) { + // Ignore } } return resolved; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java index 38cad56487c8..3894d95c345a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import org.neo4j.driver.Driver; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -30,23 +29,18 @@ import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration; import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration; -import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; import org.springframework.data.neo4j.aot.Neo4jManagedTypes; import org.springframework.data.neo4j.core.DatabaseSelectionProvider; -import org.springframework.data.neo4j.core.Neo4jClient; -import org.springframework.data.neo4j.core.Neo4jOperations; -import org.springframework.data.neo4j.core.Neo4jTemplate; import org.springframework.data.neo4j.core.convert.Neo4jConversions; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.RelationshipProperties; import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; -import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionManager; /** * {@link EnableAutoConfiguration Auto-configuration} for Spring Data Neo4j. @@ -64,6 +58,7 @@ @ConditionalOnClass({ Driver.class, Neo4jTransactionManager.class, PlatformTransactionManager.class }) @EnableConfigurationProperties(Neo4jDataProperties.class) @ConditionalOnBean(Driver.class) +@Import({ Neo4jTransactionManagerConfiguration.class, Neo4jTransactionalComponentsConfiguration.class }) public class Neo4jDataAutoConfiguration { @Bean @@ -96,25 +91,4 @@ public DatabaseSelectionProvider databaseSelectionProvider(Neo4jDataProperties p : DatabaseSelectionProvider.getDefaultSelectionProvider(); } - @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME) - @ConditionalOnMissingBean - public Neo4jClient neo4jClient(Driver driver, DatabaseSelectionProvider databaseNameProvider) { - return Neo4jClient.create(driver, databaseNameProvider); - } - - @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_TEMPLATE_BEAN_NAME) - @ConditionalOnMissingBean(Neo4jOperations.class) - public Neo4jTemplate neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext) { - return new Neo4jTemplate(neo4jClient, neo4jMappingContext); - } - - @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME) - @ConditionalOnMissingBean(TransactionManager.class) - public Neo4jTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider, - ObjectProvider optionalCustomizers) { - Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider); - optionalCustomizers.ifAvailable((customizer) -> customizer.customize((TransactionManager) transactionManager)); - return transactionManager; - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jReactiveDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jReactiveDataAutoConfiguration.java index 94fa7836d314..b3c4752c0faf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jReactiveDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jReactiveDataAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.springframework.data.neo4j.core.ReactiveNeo4jOperations; import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager; import org.springframework.data.neo4j.repository.config.ReactiveNeo4jRepositoryConfigurationExtension; import org.springframework.transaction.ReactiveTransactionManager; @@ -68,4 +69,11 @@ public ReactiveNeo4jTemplate reactiveNeo4jTemplate(ReactiveNeo4jClient neo4jClie return new ReactiveNeo4jTemplate(neo4jClient, neo4jMappingContext); } + @Bean(ReactiveNeo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME) + @ConditionalOnMissingBean(ReactiveTransactionManager.class) + ReactiveNeo4jTransactionManager rectiveNeo4jTransactionManager(Driver driver, + ReactiveDatabaseSelectionProvider databaseNameProvider) { + return new ReactiveNeo4jTransactionManager(driver, databaseNameProvider); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java index 05f2c7729e14..b4ebda404436 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,13 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.ConditionalOnRepositoryType; import org.springframework.boot.autoconfigure.data.RepositoryType; import org.springframework.context.annotation.Import; +import org.springframework.data.neo4j.core.Neo4jTemplate; import org.springframework.data.neo4j.repository.Neo4jRepository; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; @@ -52,6 +54,7 @@ @AutoConfiguration(after = Neo4jDataAutoConfiguration.class) @ConditionalOnClass({ Driver.class, Neo4jRepository.class }) @ConditionalOnMissingBean({ Neo4jRepositoryFactoryBean.class, Neo4jRepositoryConfigurationExtension.class }) +@ConditionalOnBean(Neo4jTemplate.class) @ConditionalOnRepositoryType(store = "neo4j", type = RepositoryType.IMPERATIVE) @Import(Neo4jRepositoriesRegistrar.class) public class Neo4jRepositoriesAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionManagerConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionManagerConfiguration.java new file mode 100644 index 000000000000..53fb41d9954c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionManagerConfiguration.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.data.neo4j; + +import org.neo4j.driver.Driver; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; +import org.springframework.transaction.TransactionManager; + +/** + * Configuration for a Neo4j-backed {@link TransactionManager}. + * + * @author Andy Wilkinson + */ +@Configuration(proxyBeanMethods = false) +class Neo4jTransactionManagerConfiguration { + + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME) + @ConditionalOnMissingBean(TransactionManager.class) + Neo4jTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider, + ObjectProvider optionalCustomizers) { + Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider); + optionalCustomizers.ifAvailable((customizer) -> customizer.customize((TransactionManager) transactionManager)); + return transactionManager; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionalComponentsConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionalComponentsConfiguration.java new file mode 100644 index 000000000000..d225e3cdbef3 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jTransactionalComponentsConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.data.neo4j; + +import org.neo4j.driver.Driver; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Neo4j components that require a {@link PlatformTransactionManager}. + * + * @author Andy Wilkinson + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(PlatformTransactionManager.class) +class Neo4jTransactionalComponentsConfiguration { + + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME) + @ConditionalOnMissingBean + Neo4jClient neo4jClient(Driver driver, DatabaseSelectionProvider databaseNameProvider) { + return Neo4jClient.create(driver, databaseNameProvider); + } + + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_TEMPLATE_BEAN_NAME) + @ConditionalOnMissingBean(Neo4jOperations.class) + Neo4jTemplate neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext) { + return new Neo4jTemplate(neo4jClient, neo4jMappingContext); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java index af590733e2b6..12d0dd3d1835 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,8 +112,8 @@ public RouterFunction graphQlRouterFunction(GraphQlHttpHandler h String path = properties.getPath(); logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path)); RouterFunctions.Builder builder = RouterFunctions.route(); - builder = builder.GET(path, this::onlyAllowPost); builder = builder.POST(path, SUPPORTS_MEDIATYPES, httpHandler::handleRequest); + builder = builder.GET(path, this::onlyAllowPost); if (properties.getGraphiql().isEnabled()) { GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath()); builder = builder.GET(properties.getGraphiql().getPath(), graphQlHandler::handleRequest); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java index 4c82ba3b5126..e9ecabc0c1c9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,9 +112,9 @@ public RouterFunction graphQlRouterFunction(GraphQlHttpHandler h String path = properties.getPath(); logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path)); RouterFunctions.Builder builder = RouterFunctions.route(); - builder = builder.GET(path, this::onlyAllowPost); builder = builder.POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON) .and(RequestPredicates.accept(SUPPORTED_MEDIA_TYPES)), httpHandler::handleRequest); + builder = builder.GET(path, this::onlyAllowPost); if (properties.getGraphiql().isEnabled()) { GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath()); builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::handleRequest); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java index 4fd3f99778cc..94ed69c74517 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,17 +78,22 @@ public int getOrder() { public void customize(GsonBuilder builder) { GsonProperties properties = this.properties; PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(properties::getGenerateNonExecutableJson).toCall(builder::generateNonExecutableJson); + map.from(properties::getGenerateNonExecutableJson).whenTrue().toCall(builder::generateNonExecutableJson); map.from(properties::getExcludeFieldsWithoutExposeAnnotation) + .whenTrue() .toCall(builder::excludeFieldsWithoutExposeAnnotation); map.from(properties::getSerializeNulls).whenTrue().toCall(builder::serializeNulls); - map.from(properties::getEnableComplexMapKeySerialization).toCall(builder::enableComplexMapKeySerialization); - map.from(properties::getDisableInnerClassSerialization).toCall(builder::disableInnerClassSerialization); + map.from(properties::getEnableComplexMapKeySerialization) + .whenTrue() + .toCall(builder::enableComplexMapKeySerialization); + map.from(properties::getDisableInnerClassSerialization) + .whenTrue() + .toCall(builder::disableInnerClassSerialization); map.from(properties::getLongSerializationPolicy).to(builder::setLongSerializationPolicy); map.from(properties::getFieldNamingPolicy).to(builder::setFieldNamingPolicy); - map.from(properties::getPrettyPrinting).toCall(builder::setPrettyPrinting); - map.from(properties::getLenient).toCall(builder::setLenient); - map.from(properties::getDisableHtmlEscaping).toCall(builder::disableHtmlEscaping); + map.from(properties::getPrettyPrinting).whenTrue().toCall(builder::setPrettyPrinting); + map.from(properties::getLenient).whenTrue().toCall(builder::setLenient); + map.from(properties::getDisableHtmlEscaping).whenTrue().toCall(builder::disableHtmlEscaping); map.from(properties::getDateFormat).to(builder::setDateFormat); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java index bfd53a5dacf9..1e7504195d56 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java index c60565b5918f..4a0483f58caf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,6 +65,7 @@ * @author Soby Chacko * @author Alexander Preuß * @author Phillip Webb + * @author Jonas Geiregat * @since 3.2.0 */ @AutoConfiguration @@ -131,7 +132,7 @@ PulsarTemplate pulsarTemplate(PulsarProducerFactory pulsarProducerFactory, @Bean @ConditionalOnMissingBean(PulsarConsumerFactory.class) - DefaultPulsarConsumerFactory pulsarConsumerFactory(PulsarClient pulsarClient, + DefaultPulsarConsumerFactory pulsarConsumerFactory(PulsarClient pulsarClient, ObjectProvider> customizersProvider) { List> customizers = new ArrayList<>(); customizers.add(this.propertiesMapper::customizeConsumerBuilder); @@ -150,7 +151,7 @@ private void applyConsumerBuilderCustomizers(List> @Bean @ConditionalOnMissingBean(name = "pulsarListenerContainerFactory") - ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( + ConcurrentPulsarListenerContainerFactory pulsarListenerContainerFactory( PulsarConsumerFactory pulsarConsumerFactory, SchemaResolver schemaResolver, TopicResolver topicResolver, Environment environment) { PulsarContainerProperties containerProperties = new PulsarContainerProperties(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java index ed9411512eb0..ef66f77f67d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarPropertiesMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.pulsar.client.admin.PulsarAdminBuilder; import org.apache.pulsar.client.api.ClientBuilder; @@ -30,7 +31,6 @@ import org.apache.pulsar.client.api.ProducerBuilder; import org.apache.pulsar.client.api.PulsarClientException.UnsupportedAuthenticationException; import org.apache.pulsar.client.api.ReaderBuilder; -import org.apache.pulsar.common.util.ObjectMapperFactory; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.pulsar.listener.PulsarContainerProperties; @@ -87,7 +87,10 @@ private void customizeAuthentication(AuthenticationConsumer authentication, private String getAuthenticationParamsJson(Map params) { Map sortedParams = new TreeMap<>(params); try { - return ObjectMapperFactory.create().writeValueAsString(sortedParams); + return sortedParams.entrySet() + .stream() + .map((e) -> "\"%s\":\"%s\"".formatted(e.getKey(), e.getValue())) + .collect(Collectors.joining(",", "{", "}")); } catch (Exception ex) { throw new IllegalStateException("Could not convert auth parameters to encoded string", ex); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java index d5d861aad7c6..fc9640ba10b4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -164,6 +164,7 @@ private abstract static class AbstractOptions { lookup.put(getCanonicalName(field.getName()), option); } catch (IllegalAccessException ex) { + // Ignore } } }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java index e9ebef362499..cab4dd72b436 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; +import org.springframework.boot.autoconfigure.thread.Threading; import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.boot.autoconfigure.validation.ValidatorAdapter; import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain; @@ -55,6 +56,7 @@ import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.format.FormatterRegistry; import org.springframework.format.support.FormattingConversionService; @@ -149,6 +151,8 @@ public static class WebFluxConfig implements WebFluxConfigurer { private static final Log logger = LogFactory.getLog(WebFluxConfig.class); + private final Environment environment; + private final Resources resourceProperties; private final WebFluxProperties webFluxProperties; @@ -163,11 +167,12 @@ public static class WebFluxConfig implements WebFluxConfigurer { private final ObjectProvider viewResolvers; - public WebFluxConfig(WebProperties webProperties, WebFluxProperties webFluxProperties, + public WebFluxConfig(Environment environment, WebProperties webProperties, WebFluxProperties webFluxProperties, ListableBeanFactory beanFactory, ObjectProvider resolvers, ObjectProvider codecCustomizers, ObjectProvider resourceHandlerRegistrationCustomizer, ObjectProvider viewResolvers) { + this.environment = environment; this.resourceProperties = webProperties.getResources(); this.webFluxProperties = webFluxProperties; this.beanFactory = beanFactory; @@ -189,7 +194,8 @@ public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { @Override public void configureBlockingExecution(BlockingExecutionConfigurer configurer) { - if (this.beanFactory.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) { + if (Threading.VIRTUAL.isActive(this.environment) && this.beanFactory + .containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) { Object taskExecutor = this.beanFactory .getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); if (taskExecutor instanceof AsyncTaskExecutor asyncTaskExecutor) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java index a6df6386d7e3..f5fff60f90a6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -495,6 +495,7 @@ private Resource getIndexHtmlResource(Resource location) { } } catch (Exception ex) { + // Ignore } return null; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/DefaultErrorViewResolver.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/DefaultErrorViewResolver.java index e10df19a51b2..23474a1dc5b7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/DefaultErrorViewResolver.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/DefaultErrorViewResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,6 +126,7 @@ private ModelAndView resolveResource(String viewName, Map model) } } catch (Exception ex) { + // Ignore } } return null; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java index 3c2c63d781a0..ae845a1f6bbf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -578,6 +578,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw jobRegistry.register(getJobFactory()); } catch (DuplicateJobException ex) { + // Ignore } } return bean; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/security/GraphQlWebMvcSecurityAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/security/GraphQlWebMvcSecurityAutoConfigurationTests.java index a7deb9a3afdb..45ea3fdcc818 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/security/GraphQlWebMvcSecurityAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/security/GraphQlWebMvcSecurityAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,14 +46,12 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -84,8 +82,7 @@ void contributesSecurityComponents() { void anonymousUserShouldBeUnauthorized() { testWith((mockMvc) -> { String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author }}"; - MvcResult result = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn(); - mockMvc.perform(asyncDispatch(result)) + mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("data.bookById.name").doesNotExist()) @@ -97,10 +94,7 @@ void anonymousUserShouldBeUnauthorized() { void authenticatedUserShouldGetData() { testWith((mockMvc) -> { String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author }}"; - MvcResult result = mockMvc - .perform(post("/graphql").content("{\"query\": \"" + query + "\"}").with(user("rob"))) - .andReturn(); - mockMvc.perform(asyncDispatch(result)) + mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}").with(user("rob"))) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("data.bookById.name").value("GraphQL for beginners")) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java index 6df0fdff742a..cd082e39ac6b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.function.RouterFunction; @@ -52,7 +51,6 @@ import org.springframework.web.socket.server.support.WebSocketHandlerMapping; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -88,8 +86,7 @@ void shouldContributeDefaultBeans() { void simpleQueryShouldWork() { testWith((mockMvc) -> { String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }"; - MvcResult result = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn(); - mockMvc.perform(asyncDispatch(result)) + mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_GRAPHQL_RESPONSE)) .andExpect(jsonPath("data.bookById.name").value("GraphQL for beginners")); @@ -120,8 +117,7 @@ void shouldRejectQueryWithInvalidJson() { void shouldConfigureWebInterceptors() { testWith((mockMvc) -> { String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }"; - MvcResult result = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn(); - mockMvc.perform(asyncDispatch(result)) + mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")) .andExpect(status().isOk()) .andExpect(header().string("X-Custom-Header", "42")); }); @@ -152,12 +148,10 @@ void shouldSupportCors() { testWith((mockMvc) -> { String query = "{" + " bookById(id: \\\"book-1\\\"){ " + " id" + " name" + " pageCount" + " author" + " }" + "}"; - MvcResult result = mockMvc + mockMvc .perform(post("/graphql").header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST") .header(HttpHeaders.ORIGIN, "https://example.com") .content("{\"query\": \"" + query + "\"}")) - .andReturn(); - mockMvc.perform(asyncDispatch(result)) .andExpect(status().isOk()) .andExpect(header().stringValues(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "https://example.com")) .andExpect(header().stringValues(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java index 9084e4a4940d..179280d8d180 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/gson/GsonAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ void gsonRegistration() { } @Test - void generateNonExecutableJson() { + void generateNonExecutableJsonTrue() { this.contextRunner.withPropertyValues("spring.gson.generate-non-executable-json:true").run((context) -> { Gson gson = context.getBean(Gson.class); assertThat(gson.toJson(new DataObject())).isNotEqualTo("{\"data\":1}"); @@ -68,7 +68,15 @@ void generateNonExecutableJson() { } @Test - void excludeFieldsWithoutExposeAnnotation() { + void generateNonExecutableJsonFalse() { + this.contextRunner.withPropertyValues("spring.gson.generate-non-executable-json:false").run((context) -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{\"data\":1}"); + }); + } + + @Test + void excludeFieldsWithoutExposeAnnotationTrue() { this.contextRunner.withPropertyValues("spring.gson.exclude-fields-without-expose-annotation:true") .run((context) -> { Gson gson = context.getBean(Gson.class); @@ -76,6 +84,15 @@ void excludeFieldsWithoutExposeAnnotation() { }); } + @Test + void excludeFieldsWithoutExposeAnnotationFalse() { + this.contextRunner.withPropertyValues("spring.gson.exclude-fields-without-expose-annotation:false") + .run((context) -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{\"data\":1}"); + }); + } + @Test void serializeNullsTrue() { this.contextRunner.withPropertyValues("spring.gson.serialize-nulls:true").run((context) -> { @@ -93,7 +110,7 @@ void serializeNullsFalse() { } @Test - void enableComplexMapKeySerialization() { + void enableComplexMapKeySerializationTrue() { this.contextRunner.withPropertyValues("spring.gson.enable-complex-map-key-serialization:true") .run((context) -> { Gson gson = context.getBean(Gson.class); @@ -103,6 +120,17 @@ void enableComplexMapKeySerialization() { }); } + @Test + void enableComplexMapKeySerializationFalse() { + this.contextRunner.withPropertyValues("spring.gson.enable-complex-map-key-serialization:false") + .run((context) -> { + Gson gson = context.getBean(Gson.class); + Map original = new LinkedHashMap<>(); + original.put(new DataObject(), "a"); + assertThat(gson.toJson(original)).contains(DataObject.class.getName()).doesNotContain("\"data\":"); + }); + } + @Test void notDisableInnerClassSerialization() { this.contextRunner.run((context) -> { @@ -113,7 +141,7 @@ void notDisableInnerClassSerialization() { } @Test - void disableInnerClassSerialization() { + void disableInnerClassSerializationTrue() { this.contextRunner.withPropertyValues("spring.gson.disable-inner-class-serialization:true").run((context) -> { Gson gson = context.getBean(Gson.class); WrapperObject wrapperObject = new WrapperObject(); @@ -121,6 +149,15 @@ void disableInnerClassSerialization() { }); } + @Test + void disableInnerClassSerializationFalse() { + this.contextRunner.withPropertyValues("spring.gson.disable-inner-class-serialization:false").run((context) -> { + Gson gson = context.getBean(Gson.class); + WrapperObject wrapperObject = new WrapperObject(); + assertThat(gson.toJson(wrapperObject.new NestedObject())).isEqualTo("{\"data\":\"nested\"}"); + }); + } + @Test void withLongSerializationPolicy() { this.contextRunner.withPropertyValues("spring.gson.long-serialization-policy:" + LongSerializationPolicy.STRING) @@ -156,13 +193,21 @@ void customGsonBuilder() { } @Test - void withPrettyPrinting() { + void withPrettyPrintingTrue() { this.contextRunner.withPropertyValues("spring.gson.pretty-printing:true").run((context) -> { Gson gson = context.getBean(Gson.class); assertThat(gson.toJson(new DataObject())).isEqualTo("{\n \"data\": 1\n}"); }); } + @Test + void withPrettyPrintingFalse() { + this.contextRunner.withPropertyValues("spring.gson.pretty-printing:false").run((context) -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.toJson(new DataObject())).isEqualTo("{\"data\":1}"); + }); + } + @Test void withoutLenient() { this.contextRunner.run((context) -> { @@ -172,7 +217,7 @@ void withoutLenient() { } @Test - void withLenient() { + void withLenientTrue() { this.contextRunner.withPropertyValues("spring.gson.lenient:true").run((context) -> { Gson gson = context.getBean(Gson.class); assertThat(gson).hasFieldOrPropertyWithValue("lenient", true); @@ -180,7 +225,15 @@ void withLenient() { } @Test - void withHtmlEscaping() { + void withLenientFalse() { + this.contextRunner.withPropertyValues("spring.gson.lenient:false").run((context) -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson).hasFieldOrPropertyWithValue("lenient", false); + }); + } + + @Test + void withoutDisableHtmlEscaping() { this.contextRunner.run((context) -> { Gson gson = context.getBean(Gson.class); assertThat(gson.htmlSafe()).isTrue(); @@ -188,12 +241,19 @@ void withHtmlEscaping() { } @Test - void withoutHtmlEscaping() { + void withDisableHtmlEscapingTrue() { this.contextRunner.withPropertyValues("spring.gson.disable-html-escaping:true").run((context) -> { Gson gson = context.getBean(Gson.class); assertThat(gson.htmlSafe()).isFalse(); }); + } + @Test + void withDisableHtmlEscapingFalse() { + this.contextRunner.withPropertyValues("spring.gson.disable-html-escaping:false").run((context) -> { + Gson gson = context.getBean(Gson.class); + assertThat(gson.htmlSafe()).isTrue(); + }); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java index 7e56f4129ead..dd8cd2428a98 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/pulsar/PulsarAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.TestConfiguration; @@ -373,6 +374,15 @@ void whenHasUserDefinedCustomizersAppliesInCorrectOrder() { }); } + @Test + void injectsExpectedBeanWithExplicitGenericType() { + this.contextRunner.withBean(ExplicitGenericTypeConfig.class) + .run((context) -> assertThat(context).getBean(ExplicitGenericTypeConfig.class) + .hasFieldOrPropertyWithValue("consumerFactory", context.getBean(PulsarConsumerFactory.class)) + .hasFieldOrPropertyWithValue("containerFactory", + context.getBean(ConcurrentPulsarListenerContainerFactory.class))); + } + @TestConfiguration(proxyBeanMethods = false) static class ConsumerBuilderCustomizersConfig { @@ -390,6 +400,20 @@ ConsumerBuilderCustomizer customizerBar() { } + static class ExplicitGenericTypeConfig { + + @Autowired + PulsarConsumerFactory consumerFactory; + + @Autowired + ConcurrentPulsarListenerContainerFactory containerFactory; + + static class TestType { + + } + + } + } @Nested diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProviderTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProviderTests.java index a16517fe971d..542e22b1681d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProviderTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketWebSocketNettyRouteProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,7 +74,14 @@ void webEndpointsShouldWork() { .block(Duration.ofSeconds(3)); assertThat(rsocketResponse.getName()).isEqualTo("rsocket"); WebTestClient client = createWebTestClient(serverContext.getWebServer()); - client.get().uri("/protocol").exchange().expectStatus().isOk().expectBody().jsonPath("name", "http"); + client.get() + .uri("/protocol") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("name") + .isEqualTo("http"); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java index b3f557101d68..eabb3a4655c6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,6 +106,7 @@ void filterIsRegisteredWithAsyncErrorAndRequestDispatcherTypes() { delegatingFilterProxy.doFilter(null, null, null); } catch (Exception ex) { + // Ignore } assertThat(delegatingFilterProxy).extracting("delegate") .isSameAs(context.getBean(SessionRepositoryFilter.class)); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java index cf40c58a52c9..53731f72fcfb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,8 @@ import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; +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.ValueSource; @@ -687,8 +689,20 @@ void problemDetailsExceptionHandlerIsOrderedAt0() { } @Test - void asyncTaskExecutorWithApplicationTaskExecutor() { + void asyncTaskExecutorWithPlatformThreadsAndApplicationTaskExecutor() { this.contextRunner.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) + .run((context) -> { + assertThat(context).hasSingleBean(AsyncTaskExecutor.class); + assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") + .isNull(); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndApplicationTaskExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).hasSingleBean(AsyncTaskExecutor.class); assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") @@ -697,8 +711,10 @@ void asyncTaskExecutorWithApplicationTaskExecutor() { } @Test - void asyncTaskExecutorWithNonMatchApplicationTaskExecutorBean() { - this.contextRunner.withUserConfiguration(CustomApplicationTaskExecutorConfig.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndNonMatchApplicationTaskExecutorBean() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomApplicationTaskExecutorConfig.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).doesNotHaveBean(AsyncTaskExecutor.class); @@ -708,8 +724,10 @@ void asyncTaskExecutorWithNonMatchApplicationTaskExecutorBean() { } @Test - void asyncTaskExecutorWithWebFluxConfigurerCanOverrideExecutor() { - this.contextRunner.withUserConfiguration(CustomAsyncTaskExecutorConfigurer.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndWebFluxConfigurerCanOverrideExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomAsyncTaskExecutorConfigurer.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> assertThat(context.getBean(RequestMappingHandlerAdapter.class)) .extracting("scheduler.executor") @@ -717,13 +735,15 @@ void asyncTaskExecutorWithWebFluxConfigurerCanOverrideExecutor() { } @Test - void asyncTaskExecutorWithCustomNonApplicationTaskExecutor() { - this.contextRunner.withUserConfiguration(CustomAsyncTaskExecutorConfig.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndCustomNonApplicationTaskExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomAsyncTaskExecutorConfig.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).hasSingleBean(AsyncTaskExecutor.class); assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") - .isNotSameAs(context.getBean("customTaskExecutor")); + .isNull(); }); } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 63c2c287ab98..e5f445ef646c 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -123,7 +123,7 @@ bom { ] } } - library("Byte Buddy", "1.14.11") { + library("Byte Buddy", "1.14.12") { group("net.bytebuddy") { modules = [ "byte-buddy", @@ -247,7 +247,7 @@ bom { ] } } - library("Dropwizard Metrics", "4.2.23") { + library("Dropwizard Metrics", "4.2.25") { group("io.dropwizard.metrics") { imports = [ "metrics-bom" @@ -339,7 +339,7 @@ bom { ] } } - library("Groovy", "4.0.17") { + library("Groovy", "4.0.18") { group("org.apache.groovy") { imports = [ "groovy-bom" @@ -377,7 +377,7 @@ bom { ] } } - library("Hibernate", "6.4.1.Final") { + library("Hibernate", "6.4.4.Final") { group("org.hibernate.orm") { modules = [ "hibernate-agroal", @@ -465,7 +465,7 @@ bom { ] } } - library("Infinispan", "14.0.21.Final") { + library("Infinispan", "14.0.24.Final") { group("org.infinispan") { imports = [ "infinispan-bom" @@ -606,7 +606,7 @@ bom { ] } } - library("Janino", "3.1.11") { + library("Janino", "3.1.12") { group("org.codehaus.janino") { modules = [ "commons-compiler", @@ -671,18 +671,14 @@ bom { ] } } - library("Jetty Reactive HTTPClient", "4.0.1") { - prohibit { - versionRange "[4.0.2]" - because "it causes problems in Spring Framework (https://github.com/spring-projects/spring-framework/issues/31931#issue-2061468092)" - } + library("Jetty Reactive HTTPClient", "4.0.3") { group("org.eclipse.jetty") { modules = [ "jetty-reactive-httpclient" ] } } - library("Jetty", "12.0.5") { + library("Jetty", "12.0.6") { group("org.eclipse.jetty.ee10") { imports = [ "jetty-ee10-bom" @@ -701,7 +697,7 @@ bom { ] } } - library("jOOQ", "3.18.9") { + library("jOOQ", "3.18.11") { group("org.jooq") { modules = [ "jooq", @@ -714,7 +710,7 @@ bom { ] } } - library("Json Path", "2.8.0") { + library("Json Path", "2.9.0") { group("com.jayway.jsonpath") { modules = [ "json-path", @@ -818,7 +814,7 @@ bom { ] } } - library("Kotlin Serialization", "1.6.2") { + library("Kotlin Serialization", "1.6.3") { group("org.jetbrains.kotlinx") { imports = [ "kotlinx-serialization-bom" @@ -866,7 +862,7 @@ bom { ] } } - library("MariaDB", "3.3.2") { + library("MariaDB", "3.3.3") { group("org.mariadb.jdbc") { modules = [ "mariadb-java-client" @@ -971,7 +967,7 @@ bom { ] } } - library("Maven Shade Plugin", "3.5.1") { + library("Maven Shade Plugin", "3.5.2") { group("org.apache.maven.plugins") { plugins = [ "maven-shade-plugin" @@ -999,7 +995,7 @@ bom { ] } } - library("Micrometer", "1.12.2") { + library("Micrometer", "1.12.3") { considerSnapshots() group("io.micrometer") { modules = [ @@ -1012,7 +1008,7 @@ bom { ] } } - library("Micrometer Tracing", "1.2.2") { + library("Micrometer Tracing", "1.2.3") { considerSnapshots() calendarName = "Tracing" group("io.micrometer") { @@ -1074,7 +1070,7 @@ bom { ] } } - library("Neo4j Java Driver", "5.15.0") { + library("Neo4j Java Driver", "5.17.0") { alignWithVersion { from "org.springframework.data:spring-data-neo4j" managedBy "Spring Data Bom" @@ -1085,7 +1081,7 @@ bom { ] } } - library("Netty", "4.1.105.Final") { + library("Netty", "4.1.107.Final") { group("io.netty") { imports = [ "netty-bom" @@ -1127,7 +1123,7 @@ bom { ] } } - library("Postgresql", "42.6.0") { + library("Postgresql", "42.6.1") { group("org.postgresql") { modules = [ "postgresql" @@ -1220,7 +1216,7 @@ bom { ] } } - library("Pulsar Reactive", "0.5.2") { + library("Pulsar Reactive", "0.5.3") { group("org.apache.pulsar") { modules = [ "pulsar-client-reactive-adapter", @@ -1331,7 +1327,7 @@ bom { ] } } - library("Reactor Bom", "2023.0.2") { + library("Reactor Bom", "2023.0.3") { considerSnapshots() calendarName = "Reactor" group("io.projectreactor") { @@ -1475,7 +1471,7 @@ bom { ] } } - library("SLF4J", "2.0.11") { + library("SLF4J", "2.0.12") { group("org.slf4j") { modules = [ "jcl-over-slf4j", @@ -1499,7 +1495,7 @@ bom { ] } } - library("Spring AMQP", "3.1.1") { + library("Spring AMQP", "3.1.2") { considerSnapshots() group("org.springframework.amqp") { imports = [ @@ -1507,7 +1503,7 @@ bom { ] } } - library("Spring Authorization Server", "1.2.1") { + library("Spring Authorization Server", "1.2.2") { considerSnapshots() group("org.springframework.security") { modules = [ @@ -1515,7 +1511,7 @@ bom { ] } } - library("Spring Batch", "5.1.0") { + library("Spring Batch", "5.1.1") { considerSnapshots() group("org.springframework.batch") { imports = [ @@ -1523,7 +1519,7 @@ bom { ] } } - library("Spring Data Bom", "2023.1.2") { + library("Spring Data Bom", "2023.1.3") { considerSnapshots() calendarName = "Spring Data Release" group("org.springframework.data") { @@ -1541,7 +1537,7 @@ bom { ] } } - library("Spring GraphQL", "1.2.4") { + library("Spring GraphQL", "1.2.5") { considerSnapshots() group("org.springframework.graphql") { modules = [ @@ -1558,7 +1554,7 @@ bom { ] } } - library("Spring Integration", "6.2.1") { + library("Spring Integration", "6.2.2") { considerSnapshots() group("org.springframework.integration") { imports = [ @@ -1566,7 +1562,7 @@ bom { ] } } - library("Spring Kafka", "3.1.1") { + library("Spring Kafka", "3.1.2") { considerSnapshots() group("org.springframework.kafka") { modules = [ @@ -1575,7 +1571,7 @@ bom { ] } } - library("Spring LDAP", "3.2.1") { + library("Spring LDAP", "3.2.2") { considerSnapshots() group("org.springframework.ldap") { modules = [ @@ -1586,7 +1582,7 @@ bom { ] } } - library("Spring Pulsar", "1.0.2") { + library("Spring Pulsar", "1.0.3") { considerSnapshots() group("org.springframework.pulsar") { imports = [ @@ -1610,7 +1606,7 @@ bom { ] } } - library("Spring Security", "6.2.1") { + library("Spring Security", "6.2.2") { considerSnapshots() group("org.springframework.security") { imports = [ @@ -1645,7 +1641,7 @@ bom { ] } } - library("Testcontainers", "1.19.3") { + library("Testcontainers", "1.19.5") { group("org.testcontainers") { imports = [ "testcontainers-bom" @@ -1705,7 +1701,7 @@ bom { ] } } - library("Undertow", "2.3.10.Final") { + library("Undertow", "2.3.12.Final") { group("io.undertow") { modules = [ "undertow-core", diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/Regex.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/Regex.java index c417ae0d1d47..b6ae27942d44 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/Regex.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/Regex.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ final class Regex implements CharSequence { private static final Regex PATH_COMPONENT; static { Regex segment = Regex.of("[a-z0-9]+"); - Regex separator = Regex.group("[._]|__|[-]*"); + Regex separator = Regex.group("[._-]{1,2}"); Regex separatedSegment = Regex.group(separator, segment).oneOrMoreTimes(); PATH_COMPONENT = Regex.of(segment, Regex.group(separatedSegment).zeroOrOnce()); } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java index ee00bd9d1b03..1cfd2b60acc4 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,6 +72,7 @@ private void check(RunningService service, int port, Socket socket) throws IOExc } } catch (SocketTimeoutException ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/activemq/ActiveMQDockerComposeConnectionDetailsFactory.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/activemq/ActiveMQDockerComposeConnectionDetailsFactory.java index ac3809d8da21..30597c45a8dd 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/activemq/ActiveMQDockerComposeConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/service/connection/activemq/ActiveMQDockerComposeConnectionDetailsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.boot.docker.compose.service.connection.activemq; -import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails; import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionDetails; import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; @@ -43,7 +42,7 @@ protected ActiveMQConnectionDetails getDockerComposeConnectionDetails(DockerComp } /** - * {@link RabbitConnectionDetails} backed by a {@code rabbitmq} + * {@link ActiveMQConnectionDetails} backed by a {@code activemq} * {@link RunningService}. */ static class ActiveMQDockerComposeConnectionDetails extends DockerComposeConnectionDetails diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/ImageReferenceTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/ImageReferenceTests.java index 797b82e09d08..e9eb9045819f 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/ImageReferenceTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/ImageReferenceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.springframework.boot.docker.compose.core; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.Timeout.ThreadMode; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -39,6 +41,16 @@ void ofSimpleName() { assertThat(reference).hasToString("docker.io/library/ubuntu"); } + @Test + void ofSimpleNameWithSingleCharacterSuffix() { + ImageReference reference = ImageReference.of("ubuntu-a"); + assertThat(reference.getDomain()).isEqualTo("docker.io"); + assertThat(reference.getName()).isEqualTo("library/ubuntu-a"); + assertThat(reference.getTag()).isNull(); + assertThat(reference.getDigest()).isNull(); + assertThat(reference).hasToString("docker.io/library/ubuntu-a"); + } + @Test void ofLibrarySlashName() { ImageReference reference = ImageReference.of("library/ubuntu"); @@ -149,13 +161,21 @@ void ofCustomDomainAndPortWithTag() { } @Test - void ofWhenHasIllegalCharacter() { + void ofWhenHasIllegalCharacterThrowsException() { assertThatIllegalArgumentException() .isThrownBy(() -> ImageReference .of("registry.example.com/example/example-app:1.6.0-dev.2.uncommitted+wip.foo.c75795d")) .withMessageContaining("Unable to parse image reference"); } + @Test + @Timeout(value = 1, threadMode = ThreadMode.SEPARATE_THREAD) + void ofWhenImageNameIsVeryLongAndHasIllegalCharacterThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> ImageReference + .of("docker.io/library/this-image-has-a-long-name-with-an-invalid-tag-which-is-at-danger-of-catastrophic-backtracking:1.0.0+1234")) + .withMessageContaining("Unable to parse image reference"); + } + @Test void equalsAndHashCode() { ImageReference r1 = ImageReference.of("ubuntu:bionic"); 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 4787fd22214a..0d5a036e94ed 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 @@ -301,7 +301,7 @@ By default, any authenticated user is authorized. For JMX endpoints, all users are always authorized. The following example allows all users with the `admin` role to view values from the `/env` endpoint in their original form. -Unuthorized users, or users without the `admin` role, will see only sanitized values. +Unauthorized users, or users without the `admin` role, will see only sanitized values. [source,yaml,indent=0,subs="verbatim",configprops,configblocks] ---- diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc index 0fe6739f3c44..184f8c80eb92 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc @@ -1067,7 +1067,7 @@ Metrics for Jetty's `Connector` instances are bound by using Micrometer's `Jetty [[actuator.metrics.supported.timed-annotation]] ==== @Timed Annotation Support -To enable scanning of `@Timed` annotations, you will need to set the configprop:micrometer.observations.annotations.enabled[] property to `true`. +To enable scanning of `@Timed` annotations, you will need to set the configprop:management.observations.annotations.enabled[] property to `true`. Please refer to the {micrometer-concepts-docs}#_the_timed_annotation[Micrometer documentation]. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc index e0b02262c7b6..11b641484d33 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/observability.adoc @@ -80,7 +80,8 @@ Spring Boot's actuator module includes basic support for https://opentelemetry.i It provides a bean of type `OpenTelemetry`, and if there are beans of type `SdkTracerProvider`, `ContextPropagators`, `SdkLoggerProvider` or `SdkMeterProvider` in the application context, they automatically get registered. Additionally, it provides a `Resource` bean. -The attributes of the `Resource` can be configured via the configprop:management.opentelemetry.resource-attributes[] configuration property. +The attributes of the auto-configured `Resource` can be configured via the configprop:management.opentelemetry.resource-attributes[] configuration property. +If you have defined your own `Resource` bean, this will no longer be the case. NOTE: Spring Boot does not provide auto-configuration for OpenTelemetry metrics or logging. OpenTelemetry tracing is only auto-configured when used together with <>. @@ -91,5 +92,5 @@ The next sections will provide more details about logging, metrics and traces. [[actuator.observability.annotations]] === Micrometer Observation Annotations support -To enable scanning of metrics and tracing annotations like `@Timed`, `@Counted`, `@MeterTag` and `@NewSpan` annotations, you will need to set the configprop:micrometer.observations.annotations.enabled[] property to `true`. +To enable scanning of metrics and tracing annotations like `@Timed`, `@Counted`, `@MeterTag` and `@NewSpan` annotations, you will need to set the configprop:management.observations.annotations.enabled[] property to `true`. This feature is supported Micrometer directly, please refer to the {micrometer-concepts-docs}#_the_timed_annotation[Micrometer] and {micrometer-tracing-docs}/api.html#_aspect_oriented_programming[Micrometer Tracing] reference docs. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/task-execution-and-scheduling.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/task-execution-and-scheduling.adoc index 547be8751408..f238c9b96934 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/task-execution-and-scheduling.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/task-execution-and-scheduling.adoc @@ -37,9 +37,11 @@ This changes the thread pool to use a bounded queue so that when the queue is fu Shrinking of the pool is more aggressive as threads are reclaimed when they are idle for 10 seconds (rather than 60 seconds by default). A scheduler can also be auto-configured if it needs to be associated with scheduled task execution (using `@EnableScheduling` for instance). -When virtual threads are enabled (using Java 21+ and configprop:spring.threads.virtual.enabled[] set to `true`) this will be a `SimpleAsyncTaskScheduler` that uses virtual threads. -Otherwise, it will be a `ThreadPoolTaskScheduler` with sensible defaults. +If virtual threads are enabled (using Java 21+ and configprop:spring.threads.virtual.enabled[] set to `true`) this will be a `SimpleAsyncTaskScheduler` that uses virtual threads. +This `SimpleAsyncTaskScheduler` will ignore any pooling related properties. + +If virtual threads are not enabled, it will be a `ThreadPoolTaskScheduler` with sensible defaults. The `ThreadPoolTaskScheduler` uses one thread by default and its settings can be fine-tuned using the `spring.task.scheduling` namespace, as shown in the following example: [source,yaml,indent=0,subs="verbatim",configprops,configblocks] 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 24bcf0d1ee44..7e24391e1375 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 @@ -41,7 +41,7 @@ The `spring-boot-starter-test` "`Starter`" (in the `test` `scope`) contains the * https://site.mockito.org/[Mockito]: A Java mocking framework. * https://github.com/skyscreamer/JSONassert[JSONassert]: An assertion library for JSON. * https://github.com/jayway/JsonPath[JsonPath]: XPath for JSON. -* https://https://github.com/awaitility/awaitility[Awaitility]: A library for testing asynchronous systems. +* https://github.com/awaitility/awaitility[Awaitility]: A library for testing asynchronous systems. We generally find these common libraries to be useful when writing tests. If these libraries do not suit your needs, you can add additional test dependencies of your own. 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 a8502afc7f19..0b9f33e7edbc 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 @@ -194,6 +194,8 @@ Alternatively, the SSL trust material can be configured in an < } } catch (AbandonedRunException ex) { + // Ignore } catch (Exception ex) { if (this.failedContexts.size() == 1) { diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssert.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssert.java index 317593cb682b..02fa5109d8bf 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssert.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssert.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -203,6 +203,7 @@ public ApplicationContextAssert doesNotHaveBean(String name) { getApplicationContext(), name, bean)); } catch (NoSuchBeanDefinitionException ex) { + // Ignore } return this; } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java index 449ba819ad30..ae9066273f0e 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -316,6 +316,7 @@ private void closeQuietly(Closeable closeable) { closeable.close(); } catch (IOException ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java index bd00e51232ee..797abc24de5f 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1095,6 +1095,7 @@ void assertDoesNotHavePath() { failWithMessage("Expecting no JSON path \"%s\"", this.expression); } catch (PathNotFoundException ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java index 45712061c92c..be4561d45a53 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java @@ -43,6 +43,7 @@ * * @author Phillip Webb * @author Andy Wilkinson + * @author Moritz Halbritter * @since 1.4.2 * @see ResetMocksTestExecutionListener */ @@ -57,6 +58,7 @@ public final int getOrder() { @Override public void prepareTestInstance(TestContext testContext) throws Exception { + closeMocks(testContext); initMocks(testContext); injectFields(testContext); } @@ -65,6 +67,7 @@ public void prepareTestInstance(TestContext testContext) throws Exception { public void beforeTestMethod(TestContext testContext) throws Exception { if (Boolean.TRUE.equals( testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) { + closeMocks(testContext); initMocks(testContext); reinjectFields(testContext); } @@ -72,10 +75,12 @@ public void beforeTestMethod(TestContext testContext) throws Exception { @Override public void afterTestMethod(TestContext testContext) throws Exception { - Object mocks = testContext.getAttribute(MOCKS_ATTRIBUTE_NAME); - if (mocks instanceof AutoCloseable closeable) { - closeable.close(); - } + closeMocks(testContext); + } + + @Override + public void afterTestClass(TestContext testContext) throws Exception { + closeMocks(testContext); } private void initMocks(TestContext testContext) { @@ -84,6 +89,13 @@ private void initMocks(TestContext testContext) { } } + private void closeMocks(TestContext testContext) throws Exception { + Object mocks = testContext.getAttribute(MOCKS_ATTRIBUTE_NAME); + if (mocks instanceof AutoCloseable closeable) { + closeable.close(); + } + } + private boolean hasMockitoAnnotations(TestContext testContext) { MockitoAnnotationCollection collector = new MockitoAnnotationCollection(); ReflectionUtils.doWithFields(testContext.getTestClass(), collector); @@ -126,7 +138,7 @@ private static final class MockitoAnnotationCollection implements FieldCallback private final Set annotations = new LinkedHashSet<>(); @Override - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + public void doWith(Field field) throws IllegalArgumentException { for (Annotation annotation : field.getDeclaredAnnotations()) { if (annotation.annotationType().getName().startsWith("org.mockito")) { this.annotations.add(annotation); diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java index 901b999bbf37..5f44bac53b47 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java @@ -171,8 +171,8 @@ public void setUriTemplateHandler(UriTemplateHandler handler) { } /** - * Returns the root URI applied by a {@link RootUriTemplateHandler} or {@code ""} if - * the root URI is not available. + * Returns the root URI applied by {@link RestTemplateBuilder#rootUri(String)} or + * {@code ""} if the root URI has not been applied. * @return the root URI */ public String getRootUri() { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerIntegrationTests.java new file mode 100644 index 000000000000..1357629a616d --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerIntegrationTests.java @@ -0,0 +1,499 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.mock.mockito; + +import java.util.List; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.ClassOrderer; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +/** + * Integration tests for {@link MockitoTestExecutionListener}. + * + * @author Moritz Halbritter + */ +@ExtendWith(SpringExtension.class) +class MockitoTestExecutionListenerIntegrationTests { + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class MockedStaticTests { + + private static final UUID uuid = UUID.randomUUID(); + + @Mock + private MockedStatic mockedStatic; + + @Test + @Order(1) + @Disabled + void shouldReturnConstantValueDisabled() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + @Test + @Order(2) + void shouldNotFailBecauseOfMockedStaticNotBeingClosed() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD) + class MockedStaticTestsDirtiesContext { + + private static final UUID uuid = UUID.randomUUID(); + + @Mock + private MockedStatic mockedStatic; + + @Test + @Order(1) + @Disabled + void shouldReturnConstantValueDisabled() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + @Test + @Order(2) + void shouldNotFailBecauseOfMockedStaticNotBeingClosed() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + @Test + @Order(3) + void shouldNotFailBecauseOfMockedStaticNotBeingClosedWhenMocksAreReinjected() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestClassOrder(ClassOrderer.OrderAnnotation.class) + class MockedStaticTestsIfClassContainsOnlyDisabledTests { + + @Nested + @Order(1) + class TestClass1 { + + private static final UUID uuid = UUID.randomUUID(); + + @Mock + private MockedStatic mockedStatic; + + @Test + @Order(1) + @Disabled + void disabledTest() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + } + + } + + @Nested + @Order(2) + class TestClass2 { + + private static final UUID uuid = UUID.randomUUID(); + + @Mock + private MockedStatic mockedStatic; + + @Test + @Order(1) + void shouldNotFailBecauseMockedStaticHasNotBeenClosed() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestClassOrder(ClassOrderer.OrderAnnotation.class) + class MockedStaticTestsIfClassContainsNoTests { + + @Nested + @Order(1) + class TestClass1 { + + @Mock + private MockedStatic mockedStatic; + + } + + @Nested + @Order(2) + class TestClass2 { + + private static final UUID uuid = UUID.randomUUID(); + + @Mock + private MockedStatic mockedStatic; + + @Test + @Order(1) + void shouldNotFailBecauseMockedStaticHasNotBeenClosed() { + this.mockedStatic.when(UUID::randomUUID).thenReturn(uuid); + UUID result = UUID.randomUUID(); + assertThat(result).isEqualTo(uuid); + } + + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class ConfigureMockInBeforeEach { + + @Mock + private List mock; + + @BeforeEach + void setUp() { + given(this.mock.size()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.size()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.size()).willReturn(2); + assertThat(this.mock.size()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotBeAffectedByOtherTests() { + assertThat(this.mock.size()).isEqualTo(1); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestInstance(Lifecycle.PER_CLASS) + class ConfigureMockInBeforeAll { + + @Mock + private List mock; + + @BeforeAll + void setUp() { + given(this.mock.size()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.size()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.size()).willReturn(2); + assertThat(this.mock.size()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotBeAffectedByOtherTest() { + assertThat(this.mock.size()).isEqualTo(2); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetAfterInBeforeEach { + + @MockBean(reset = MockReset.AFTER) + private MyBean mock; + + @BeforeEach + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.call()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotBeAffectedByOtherTests() { + assertThat(this.mock.call()).isEqualTo(1); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetBeforeInBeforeEach { + + @MockBean(reset = MockReset.BEFORE) + private MyBean mock; + + @BeforeEach + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.call()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotBeAffectedByOtherTests() { + assertThat(this.mock.call()).isEqualTo(1); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetNoneInBeforeEach { + + @MockBean(reset = MockReset.NONE) + private MyBean mock; + + @BeforeEach + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.call()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotBeAffectedByOtherTests() { + assertThat(this.mock.call()).isEqualTo(1); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestInstance(Lifecycle.PER_CLASS) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetAfterInBeforeAll { + + @MockBean(reset = MockReset.AFTER) + private MyBean mock; + + @BeforeAll + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.call()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldResetMockAfterReconfiguration() { + assertThat(this.mock.call()).isEqualTo(0); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestInstance(Lifecycle.PER_CLASS) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetBeforeInBeforeAll { + + @MockBean(reset = MockReset.BEFORE) + private MyBean mock; + + @BeforeAll + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldResetMockBeforeThisMethod() { + assertThat(this.mock.call()).isEqualTo(0); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldResetMockAfterReconfiguration() { + assertThat(this.mock.call()).isEqualTo(0); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @TestInstance(Lifecycle.PER_CLASS) + @Import(MyBeanConfiguration.class) + class ConfigureMockBeanWithResetNoneInBeforeAll { + + @MockBean(reset = MockReset.NONE) + private MyBean mock; + + @BeforeAll + void setUp() { + given(this.mock.call()).willReturn(1); + } + + @Test + @Order(1) + void shouldUseSetUpConfiguration() { + assertThat(this.mock.call()).isEqualTo(1); + } + + @Test + @Order(2) + void shouldBeAbleToReconfigureMock() { + given(this.mock.call()).willReturn(2); + assertThat(this.mock.call()).isEqualTo(2); + } + + @Test + @Order(3) + void shouldNotResetMock() { + assertThat(this.mock.call()).isEqualTo(2); + } + + } + + interface MyBean { + + int call(); + + } + + private static final class DefaultMyBean implements MyBean { + + @Override + public int call() { + return -1; + } + + } + + @TestConfiguration(proxyBeanMethods = false) + private static final class MyBeanConfiguration { + + @Bean + MyBean myBean() { + return new DefaultMyBean(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleBeanFactoryPostProcessor.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleBeanFactoryPostProcessor.java index 516eb45026fd..cb193482b588 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleBeanFactoryPostProcessor.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/lifecycle/TestcontainersLifecycleBeanFactoryPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) } } catch (NoSuchBeanDefinitionException ex) { + // Ignore } } } diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySource.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySource.java index 1994303645ef..d2df1e65b101 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySource.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySource.java @@ -115,7 +115,7 @@ private static DynamicPropertyRegistry attach(Environment environment, Applicati if (eventPublisher != null) { propertySource.addEventPublisher(eventPublisher); } - else if (registry != null) { + else if (registry != null && !registry.containsBeanDefinition(EventPublisherRegistrar.NAME)) { registry.registerBeanDefinition(EventPublisherRegistrar.NAME, new RootBeanDefinition( EventPublisherRegistrar.class, () -> new EventPublisherRegistrar(environment))); } @@ -129,7 +129,7 @@ static TestcontainersPropertySource getOrAdd(ConfigurableEnvironment environment return getOrAdd(environment); } Assert.state(propertySource instanceof TestcontainersPropertySource, - "Incorrect DynamicValuesPropertySource type registered"); + "Incorrect TestcontainersPropertySource type registered"); return ((TestcontainersPropertySource) propertySource); } @@ -138,7 +138,7 @@ static TestcontainersPropertySource getOrAdd(ConfigurableEnvironment environment * to the {@link TestcontainersPropertySource}. This class is a * {@link BeanFactoryPostProcessor} so that it is initialized as early as possible. */ - private static class EventPublisherRegistrar implements BeanFactoryPostProcessor, ApplicationEventPublisherAware { + static class EventPublisherRegistrar implements BeanFactoryPostProcessor, ApplicationEventPublisherAware { static final String NAME = EventPublisherRegistrar.class.getName(); diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java index 2a41db031fdc..d2edbd9db575 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,6 +93,7 @@ public final D getConnectionDetails(ContainerConnectionSource source) { } } catch (NoClassDefFoundError ex) { + // Ignore } return null; } diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceTests.java index 4dce61ffc942..e11488b349c3 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/properties/TestcontainersPropertySourceTests.java @@ -23,6 +23,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.testcontainers.properties.TestcontainersPropertySource.EventPublisherRegistrar; import org.springframework.context.ApplicationEvent; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.env.EnumerablePropertySource; @@ -42,6 +44,13 @@ class TestcontainersPropertySourceTests { private MockEnvironment environment = new MockEnvironment(); + private GenericApplicationContext context = new GenericApplicationContext(); + + TestcontainersPropertySourceTests() { + ((DefaultListableBeanFactory) this.context.getBeanFactory()).setAllowBeanDefinitionOverriding(false); + this.context.setEnvironment(this.environment); + } + @Test void getPropertyWhenHasValueSupplierReturnsSuppliedValue() { DynamicPropertyRegistry registry = TestcontainersPropertySource.attach(this.environment); @@ -90,14 +99,14 @@ void getSourceReturnsImmutableSource() { } @Test - void attachWhenNotAttachedAttaches() { + void attachToEnvironmentWhenNotAttachedAttaches() { TestcontainersPropertySource.attach(this.environment); PropertySource propertySource = this.environment.getPropertySources().get(TestcontainersPropertySource.NAME); assertThat(propertySource).isNotNull(); } @Test - void attachWhenAlreadyAttachedReturnsExisting() { + void attachToEnvironmentWhenAlreadyAttachedReturnsExisting() { DynamicPropertyRegistry r1 = TestcontainersPropertySource.attach(this.environment); PropertySource p1 = this.environment.getPropertySources().get(TestcontainersPropertySource.NAME); DynamicPropertyRegistry r2 = TestcontainersPropertySource.attach(this.environment); @@ -106,6 +115,24 @@ void attachWhenAlreadyAttachedReturnsExisting() { assertThat(p1).isSameAs(p2); } + @Test + void attachToEnvironmentAndContextWhenNotAttachedAttaches() { + TestcontainersPropertySource.attach(this.environment, this.context); + PropertySource propertySource = this.environment.getPropertySources().get(TestcontainersPropertySource.NAME); + assertThat(propertySource).isNotNull(); + assertThat(this.context.containsBean(EventPublisherRegistrar.NAME)); + } + + @Test + void attachToEnvironmentAndContextWhenAlreadyAttachedReturnsExisting() { + DynamicPropertyRegistry r1 = TestcontainersPropertySource.attach(this.environment, this.context); + PropertySource p1 = this.environment.getPropertySources().get(TestcontainersPropertySource.NAME); + DynamicPropertyRegistry r2 = TestcontainersPropertySource.attach(this.environment, this.context); + PropertySource p2 = this.environment.getPropertySources().get(TestcontainersPropertySource.NAME); + assertThat(r1).isSameAs(r2); + assertThat(p1).isSameAs(p2); + } + @Test void getPropertyPublishesEvent() { try (GenericApplicationContext applicationContext = new GenericApplicationContext()) { diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java index 299244128601..b3167fb8035c 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/build.gradle index 8f5db820dfba..f3886f7b8471 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/build.gradle @@ -19,6 +19,17 @@ configurations.all { if (dependency.requested.group.equals("org.springframework")) { dependency.useVersion("6.0.10") } + // We manage the version of commons-compress here rather than + // in spring-boot-parent to minimize conflicts with Testcontainers + if (dependency.requested.group.equals("org.apache.commons") + && dependency.requested.name.equals("commons-compress")) { + dependency.useVersion("$commonsCompressVersion") + } + // Downgrade Testcontainers for compatibility with the managed + // version of Commons Compress. + if (dependency.requested.group.equals("org.testcontainers")) { + dependency.useVersion("1.19.3") + } } } } @@ -27,7 +38,7 @@ dependencies { api("com.fasterxml.jackson.core:jackson-databind") api("com.fasterxml.jackson.module:jackson-module-parameter-names") api("net.java.dev.jna:jna-platform") - api("org.apache.commons:commons-compress:1.19") + api("org.apache.commons:commons-compress:$commonsCompressVersion") api("org.apache.httpcomponents.client5:httpclient5") api("org.springframework:spring-core") api("org.tomlj:tomlj:1.0.0") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java index 5a76b8ab0cc2..31c84cefc10e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java index 735c76619f6a..b485552efcd8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/TarGzipBuildpack.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java index 284a3ea1b12a..06bddbdf0839 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java @@ -18,6 +18,7 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,9 +31,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -293,29 +292,20 @@ public void exportLayerFiles(ImageReference reference, IOBiConsumer layerFiles = new HashMap<>(); - try (TarArchiveInputStream tar = new TarArchiveInputStream(response.getContent())) { + Path exportFile = copyToTemp(response.getContent()); + ImageArchiveManifest manifest = getManifest(reference, exportFile); + try (TarArchiveInputStream tar = new TarArchiveInputStream(new FileInputStream(exportFile.toFile()))) { TarArchiveEntry entry = tar.getNextTarEntry(); while (entry != null) { - if (entry.getName().equals("manifest.json")) { - manifest = readManifest(tar); - } - if (entry.getName().endsWith(".tar")) { - layerFiles.put(entry.getName(), copyToTemp(tar)); + if (manifestContainsLayerEntry(manifest, entry.getName())) { + Path layerFile = copyToTemp(tar); + exports.accept(entry.getName(), layerFile); + Files.delete(layerFile); } entry = tar.getNextTarEntry(); } } - Assert.notNull(manifest, "Manifest not found in image " + reference); - for (Map.Entry entry : layerFiles.entrySet()) { - String name = entry.getKey(); - Path path = entry.getValue(); - if (manifestContainsLayerEntry(manifest, name)) { - exports.accept(name, path); - } - Files.delete(path); - } + Files.delete(exportFile); } /** @@ -355,13 +345,26 @@ public void tag(ImageReference sourceReference, ImageReference targetReference) http().post(uri).close(); } + private ImageArchiveManifest getManifest(ImageReference reference, Path exportFile) throws IOException { + try (TarArchiveInputStream tar = new TarArchiveInputStream(new FileInputStream(exportFile.toFile()))) { + TarArchiveEntry entry = tar.getNextTarEntry(); + while (entry != null) { + if (entry.getName().equals("manifest.json")) { + return readManifest(tar); + } + entry = tar.getNextTarEntry(); + } + } + throw new IllegalArgumentException("Manifest not found in image " + reference); + } + private ImageArchiveManifest readManifest(TarArchiveInputStream tar) throws IOException { String manifestContent = new BufferedReader(new InputStreamReader(tar, StandardCharsets.UTF_8)).lines() .collect(Collectors.joining()); return ImageArchiveManifest.of(new ByteArrayInputStream(manifestContent.getBytes(StandardCharsets.UTF_8))); } - private Path copyToTemp(TarArchiveInputStream in) throws IOException { + private Path copyToTemp(InputStream in) throws IOException { Path path = Files.createTempFile("create-builder-scratch-", null); try (OutputStream out = Files.newOutputStream(path)) { StreamUtils.copy(in, out); 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 e2b5259a11e7..480392a032a5 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-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -203,6 +203,7 @@ private PrivateKey parse(byte[] bytes) { return keyFactory.generatePrivate(keySpec); } catch (InvalidKeySpecException | NoSuchAlgorithmException ex) { + // Ignore } } return null; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java index c843097d100f..648565ee69ba 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Regex.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ final class Regex implements CharSequence { private static final Regex PATH_COMPONENT; static { Regex segment = Regex.of("[a-z0-9]+"); - Regex separator = Regex.group("[._]|__|[-]*"); + Regex separator = Regex.group("[._-]{1,2}"); Regex separatedSegment = Regex.group(separator, segment).oneOrMoreTimes(); PATH_COMPONENT = Regex.of(segment, Regex.group(separatedSegment).zeroOrOnce()); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpackTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpackTests.java index bb9c95b61b24..d33757f19060 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpackTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/DirectoryBuildpackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java index 2ad43f3ecc10..f42d6ee60b60 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -189,6 +190,7 @@ private Object withMockLayers(InvocationOnMock invocation) { tarOut.finish(); } consumer.accept("test", tarFile.toPath()); + Files.delete(tarFile.toPath()); } catch (IOException ex) { fail("Error writing mock layers", ex); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java index 2bb460b85cd1..814914861b2d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -382,6 +382,7 @@ void exportLayersWithSymlinksExportsLayerTars() throws Exception { } @Test + @SuppressWarnings("removal") void exportLayerFilesDeletesTempFiles() throws Exception { ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base"); URI exportUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/get"); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/TotalProgressListenerTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/TotalProgressListenerTests.java index 1dd2db0e3dcf..b61faba2673a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/TotalProgressListenerTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/TotalProgressListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,6 +78,7 @@ public void onUpdate(TestImageUpdateEvent event) { Thread.sleep(10); } catch (InterruptedException ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java index 318bf7985c54..20717b8a5d7c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java index d33abc7f38b8..9ec351e503db 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageReferenceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ import java.io.File; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.Timeout.ThreadMode; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -43,6 +45,16 @@ void ofSimpleName() { assertThat(reference).hasToString("docker.io/library/ubuntu"); } + @Test + void ofSimpleNameWithSingleCharacterSuffix() { + ImageReference reference = ImageReference.of("ubuntu-a"); + assertThat(reference.getDomain()).isEqualTo("docker.io"); + assertThat(reference.getName()).isEqualTo("library/ubuntu-a"); + assertThat(reference.getTag()).isNull(); + assertThat(reference.getDigest()).isNull(); + assertThat(reference).hasToString("docker.io/library/ubuntu-a"); + } + @Test void ofLibrarySlashName() { ImageReference reference = ImageReference.of("library/ubuntu"); @@ -173,7 +185,7 @@ void ofImageNameTagAndDigest() { } @Test - void ofWhenHasIllegalCharacter() { + void ofWhenHasIllegalCharacterThrowsException() { assertThatIllegalArgumentException() .isThrownBy(() -> ImageReference .of("registry.example.com/example/example-app:1.6.0-dev.2.uncommitted+wip.foo.c75795d")) @@ -188,6 +200,14 @@ void ofWhenContainsUpperCaseThrowsException() { .withMessageContaining("Unable to parse image reference"); } + @Test + @Timeout(value = 1, threadMode = ThreadMode.SEPARATE_THREAD) + void ofWhenIsVeryLongAndHasIllegalCharacter() { + assertThatIllegalArgumentException().isThrownBy(() -> ImageReference + .of("docker.io/library/this-image-has-a-long-name-with-an-invalid-tag-which-is-at-danger-of-catastrophic-backtracking:1.0.0+1234")) + .withMessageContaining("Unable to parse image reference"); + } + @Test void forJarFile() { assertForJarFile("spring-boot.2.0.0.BUILD-SNAPSHOT.jar", "library/spring-boot", "2.0.0.BUILD-SNAPSHOT"); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/LayerTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/LayerTests.java index db72d542c7c6..b07861d7fb55 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/LayerTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/LayerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarArchiveTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarArchiveTests.java index 0f5be72a0f75..615dc03a0fa6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarArchiveTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarArchiveTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarLayoutWriterTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarLayoutWriterTests.java index 847abc77fa81..1bb1d87fdbd2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarLayoutWriterTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/TarLayoutWriterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchiveTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchiveTests.java index cd11ef39298f..a1c887510d26 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchiveTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/io/ZipFileTarArchiveTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSON.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSON.java index 058f5674e4db..97404944fced 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSON.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSON.java @@ -51,7 +51,8 @@ static Double toDouble(Object value) { try { return Double.valueOf((String) value); } - catch (NumberFormatException ignored) { + catch (NumberFormatException ex) { + // Ignore } } return null; @@ -68,7 +69,8 @@ static Integer toInteger(Object value) { try { return (int) Double.parseDouble((String) value); } - catch (NumberFormatException ignored) { + catch (NumberFormatException ex) { + // Ignore } } return null; @@ -85,7 +87,8 @@ static Long toLong(Object value) { try { return (long) Double.parseDouble((String) value); } - catch (NumberFormatException ignored) { + catch (NumberFormatException ex) { + // Ignore } } return null; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONObject.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONObject.java index a4cdb2025748..acf177a7508e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONObject.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONObject.java @@ -827,7 +827,8 @@ else if (o.getClass().isArray()) { return o.toString(); } } - catch (Exception ignored) { + catch (Exception ex) { + // Ignore } return null; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONTokener.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONTokener.java index 3e390ec97fd6..682ca94ae7ea 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONTokener.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/json-shade/java/org/springframework/boot/configurationprocessor/json/JSONTokener.java @@ -320,7 +320,8 @@ else if (number.startsWith("0") && number.length() > 1) { try { return Double.valueOf(literal); } - catch (NumberFormatException ignored) { + catch (NumberFormatException ex) { + // Ignore } /* ... finally give up. We have an unquoted string */ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle index 35cd303513d7..36a2c3b8f6e1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/build.gradle @@ -28,6 +28,17 @@ configurations { if (dependency.requested.group.equals("org.springframework")) { dependency.useVersion("6.0.10") } + // We manage the version of commons-compress here rather than + // in spring-boot-parent to minimize conflicts with Testcontainers + if (dependency.requested.group.equals("org.apache.commons") + && dependency.requested.name.equals("commons-compress")) { + dependency.useVersion("$commonsCompressVersion") + } + // Downgrade Testcontainers for compatibility with the managed + // version of Commons Compress. + if (dependency.requested.group.equals("org.testcontainers")) { + dependency.useVersion("1.19.3") + } } } } @@ -39,7 +50,7 @@ dependencies { implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform")) implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-tools")) implementation("io.spring.gradle:dependency-management-plugin") - implementation("org.apache.commons:commons-compress") + implementation("org.apache.commons:commons-compress:$commonsCompressVersion") implementation("org.springframework:spring-core") optional("org.graalvm.buildtools:native-gradle-plugin") 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 b85ccfc86356..5e949f1157de 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -123,8 +123,8 @@ public void apply(Project project) { private void verifyGradleVersion() { GradleVersion currentVersion = GradleVersion.current(); - if (currentVersion.compareTo(GradleVersion.version("7.4")) < 0) { - throw new GradleException("Spring Boot plugin requires Gradle 7.x (7.4 or later). " + if (currentVersion.compareTo(GradleVersion.version("7.5")) < 0) { + throw new GradleException("Spring Boot plugin requires Gradle 7.x (7.5 or later). " + "The current version is " + currentVersion); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java index f35546ca578f..780944145dc2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootZipCopyAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -204,6 +204,7 @@ private void closeQuietly(OutputStream outputStream) { outputStream.close(); } catch (IOException ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java index d30629bb32de..6d2daf320a1a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java index 0776f94cc5d2..8128321c3872 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,9 +40,9 @@ class SpringBootPluginIntegrationTests { @Test @DisabledForJreRange(min = JRE.JAVA_20) void failFastWithVersionOfGradle7LowerThanRequired() { - BuildResult result = this.gradleBuild.gradleVersion("7.3.3").buildAndFail(); + BuildResult result = this.gradleBuild.gradleVersion("7.4.1").buildAndFail(); assertThat(result.getOutput()) - .contains("Spring Boot plugin requires Gradle 7.x (7.4 or later). The current version is Gradle 7.3.3"); + .contains("Spring Boot plugin requires Gradle 7.x (7.5 or later). The current version is Gradle 7.4.1"); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle index d3df269b1b2c..c71fca96a3b8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-test-support/build.gradle @@ -17,7 +17,7 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-daemon-client:$kotlinVersion") - implementation("org.apache.commons:commons-compress") + implementation("org.apache.commons:commons-compress:$commonsCompressVersion") implementation("org.assertj:assertj-core") } 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 0a4049a69505..8aa1fc3cf9d8 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,9 +35,9 @@ private GradleVersions() { @SuppressWarnings("UnstableApiUsage") public static List allCompatible() { if (isJavaVersion(JavaVersion.VERSION_20)) { - return Arrays.asList("8.1.1", "8.5"); + return Arrays.asList("8.1.1", "8.6"); } - return Arrays.asList("7.5.1", GradleVersion.current().getVersion(), "8.0.2", "8.5"); + return Arrays.asList("7.5.1", GradleVersion.current().getVersion(), "8.0.2", "8.6"); } public static String minimumCompatible() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java index 1ec5a39214bf..c2d2a815dcb3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-jarmode-layertools/src/test/java/org/springframework/boot/jarmode/layertools/ExtractCommandTests.java @@ -21,6 +21,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.Runtime.Version; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.attribute.BasicFileAttributeView; @@ -30,6 +31,7 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.Iterator; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -38,6 +40,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; import org.mockito.Mock; @@ -59,11 +63,13 @@ @ExtendWith(MockitoExtension.class) class ExtractCommandTests { - private static final FileTime CREATION_TIME = FileTime.from(Instant.now().minus(3, ChronoUnit.DAYS)); + private static final Instant NOW = Instant.now(); - private static final FileTime LAST_MODIFIED_TIME = FileTime.from(Instant.now().minus(2, ChronoUnit.DAYS)); + private static final FileTime CREATION_TIME = FileTime.from(NOW.minus(3, ChronoUnit.DAYS)); - private static final FileTime LAST_ACCESS_TIME = FileTime.from(Instant.now().minus(1, ChronoUnit.DAYS)); + private static final FileTime LAST_MODIFIED_TIME = FileTime.from(NOW.minus(2, ChronoUnit.DAYS)); + + private static final FileTime LAST_ACCESS_TIME = FileTime.from(NOW.minus(1, ChronoUnit.DAYS)); @TempDir File temp; @@ -107,10 +113,11 @@ private void timeAttributes(File file) { .readAttributes(); assertThat(basicAttributes.lastModifiedTime().to(TimeUnit.SECONDS)) .isEqualTo(LAST_MODIFIED_TIME.to(TimeUnit.SECONDS)); - assertThat(basicAttributes.creationTime().to(TimeUnit.SECONDS)).satisfiesAnyOf( - (creationTime) -> assertThat(creationTime).isEqualTo(CREATION_TIME.to(TimeUnit.SECONDS)), - // On macOS (at least) the creation time is the last modified time - (creationTime) -> assertThat(creationTime).isEqualTo(LAST_MODIFIED_TIME.to(TimeUnit.SECONDS))); + FileTime expectedCreationTime = expectedCreationTime(); + if (expectedCreationTime != null) { + assertThat(basicAttributes.creationTime().to(TimeUnit.SECONDS)) + .isEqualTo(expectedCreationTime.to(TimeUnit.SECONDS)); + } assertThat(basicAttributes.lastAccessTime().to(TimeUnit.SECONDS)) .isEqualTo(LAST_ACCESS_TIME.to(TimeUnit.SECONDS)); } @@ -119,6 +126,22 @@ private void timeAttributes(File file) { } } + private FileTime expectedCreationTime() { + // macOS uses last modified time until Java 20 where it uses creation time. + // https://github.com/openjdk/jdk21u-dev/commit/6397d564a5dab07f81bf4c69b116ebfabb2446ba + if (OS.MAC.isCurrentOs()) { + return (EnumSet.range(JRE.JAVA_17, JRE.JAVA_19).contains(JRE.currentVersion())) ? LAST_MODIFIED_TIME + : CREATION_TIME; + } + if (OS.LINUX.isCurrentOs()) { + // Linux uses the modified time until Java 21.0.2 where a bug means that it + // uses the birth time which it has not set, preventing us from verifying it. + // https://github.com/openjdk/jdk21u-dev/commit/4cf572e3b99b675418e456e7815fb6fd79245e30 + return (Runtime.version().compareTo(Version.parse("21.0.2")) >= 0) ? null : LAST_MODIFIED_TIME; + } + return CREATION_TIME; + } + @Test void runWhenHasDestinationOptionExtractsLayers() { given(this.context.getArchiveFile()).willReturn(this.jarFile); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java index 7e3e2fa22392..15f4608a8d1f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java @@ -127,6 +127,7 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE return result; } catch (ClassNotFoundException ex) { + // Ignore } } if (this.exploded) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/archive/JarFileArchive.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/archive/JarFileArchive.java index 91e7bc53a486..049debfc7246 100755 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/archive/JarFileArchive.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/archive/JarFileArchive.java @@ -151,6 +151,7 @@ private Path createUnpackDirectory(Path parent) { return unpackDirectory; } catch (IOException ex) { + // Ignore } } throw new IllegalStateException("Failed to create unpack directory in directory '" + parent + "'"); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/Handler.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/Handler.java index 932dea654867..6909c3c5faca 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/Handler.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/Handler.java @@ -140,6 +140,7 @@ private URLConnection openFallbackTomcatConnection(URL url) { return connection; } catch (IOException ex) { + // Ignore } } return null; @@ -154,6 +155,7 @@ private boolean isTomcatWarUrl(String file) { } } catch (Exception ex) { + // Ignore } } return false; @@ -174,6 +176,7 @@ private URLConnection openFallbackContextConnection(URL url) { } } catch (Exception ex) { + // Ignore } return null; } @@ -425,6 +428,7 @@ static void captureJarContextUrl() { } } catch (Exception ex) { + // Ignore } } finally { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/JarFile.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/JarFile.java index 6e548048dbf0..cc834e14dac6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/JarFile.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-classic/src/main/java/org/springframework/boot/loader/jar/JarFile.java @@ -144,6 +144,7 @@ private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccess super.close(); } catch (IOException ioex) { + // Ignore } throw ex; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/build.gradle index f7968f659d51..7c2053406537 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/build.gradle @@ -29,13 +29,19 @@ configurations { if (dependency.requested.group.equals("org.springframework")) { dependency.useVersion("6.0.10") } + // We manage the version of commons-compress here rather than + // in spring-boot-parent to minimize conflicts with Testcontainers + if (dependency.requested.group.equals("org.apache.commons") + && dependency.requested.name.equals("commons-compress")) { + dependency.useVersion("$commonsCompressVersion") + } } } } } dependencies { - api("org.apache.commons:commons-compress") + api("org.apache.commons:commons-compress:$commonsCompressVersion") api("org.springframework:spring-core") compileOnly("ch.qos.logback:logback-classic") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFile.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFile.java index 676eb046b1e1..6168202950ea 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFile.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,8 +101,10 @@ public class NestedJarFile extends JarFile { * Creates a new {@link NestedJarFile} instance to read from the specific * {@code File}. * @param file the jar file to be opened for reading - * @param nestedEntryName the nested entry name to open or {@code null} + * @param nestedEntryName the nested entry name to open * @throws IOException on I/O error + * @throws IllegalArgumentException if {@code nestedEntryName} is {@code null} or + * empty */ public NestedJarFile(File file, String nestedEntryName) throws IOException { this(file, nestedEntryName, null, true, Cleaner.instance); @@ -112,9 +114,11 @@ public NestedJarFile(File file, String nestedEntryName) throws IOException { * Creates a new {@link NestedJarFile} instance to read from the specific * {@code File}. * @param file the jar file to be opened for reading - * @param nestedEntryName the nested entry name to open or {@code null} + * @param nestedEntryName the nested entry name to open * @param version the release version to use when opening a multi-release jar * @throws IOException on I/O error + * @throws IllegalArgumentException if {@code nestedEntryName} is {@code null} or + * empty */ public NestedJarFile(File file, String nestedEntryName, Runtime.Version version) throws IOException { this(file, nestedEntryName, version, true, Cleaner.instance); @@ -124,11 +128,13 @@ public NestedJarFile(File file, String nestedEntryName, Runtime.Version version) * Creates a new {@link NestedJarFile} instance to read from the specific * {@code File}. * @param file the jar file to be opened for reading - * @param nestedEntryName the nested entry name to open or {@code null} + * @param nestedEntryName the nested entry name to open * @param version the release version to use when opening a multi-release jar * @param onlyNestedJars if only nested jars should be opened * @param cleaner the cleaner used to release resources * @throws IOException on I/O error + * @throws IllegalArgumentException if {@code nestedEntryName} is {@code null} or + * empty */ NestedJarFile(File file, String nestedEntryName, Runtime.Version version, boolean onlyNestedJars, Cleaner cleaner) throws IOException { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFileResources.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFileResources.java index e1fb4d8d0930..e4e66ee2ac8a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFileResources.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/NestedJarFileResources.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java index 0409fe6e3d99..7daac7ee950f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Map.Entry; +import org.springframework.boot.loader.net.util.UrlDecoder; import org.springframework.boot.loader.ref.Cleaner; /** @@ -76,7 +77,7 @@ class NestedUrlConnection extends URLConnection { private NestedLocation parseNestedLocation(URL url) throws MalformedURLException { try { - return NestedLocation.parse(url.getPath()); + return NestedLocation.parse(UrlDecoder.decode(url.getPath())); } catch (IllegalArgumentException ex) { throw new MalformedURLException(ex.getMessage()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/nio/file/NestedFileSystem.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/nio/file/NestedFileSystem.java index 1b4e94871132..9b770cbd2122 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/nio/file/NestedFileSystem.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/nio/file/NestedFileSystem.java @@ -138,6 +138,7 @@ private void closeZipFileSystem(FileSystem zipFileSystem) { zipFileSystem.close(); } catch (Exception ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java index 673620f155bd..b69b74fb1d0c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java index 4e7298ffb4b9..b5afa6311ec2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/NestedJarFileTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/NestedJarFileTests.java index 782dd1369ac8..40a59c7baae3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/NestedJarFileTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/NestedJarFileTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java index b194610486e2..606d00c53edc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java @@ -121,13 +121,7 @@ void getPermissionReturnsFilePermission() throws Exception { @Test void getInputStreamReturnsContentOfNestedJar() throws Exception { NestedUrlConnection connection = new NestedUrlConnection(this.url); - try (InputStream actual = connection.getInputStream()) { - try (ZipContent zipContent = ZipContent.open(this.jarFile.toPath())) { - try (InputStream expected = zipContent.getEntry("nested.jar").openContent().asInputStream()) { - assertThat(actual).hasSameContentAs(expected); - } - } - } + assertHasSameContentAsNestedJar(connection); } @Test @@ -163,6 +157,29 @@ void getLastModifiedHeaderReturnsFileModifiedTime() throws IOException { } } + @Test + void createDecodesUrlPath() throws Exception { + File withSpace = new File(this.temp, "te st"); + withSpace.mkdirs(); + this.jarFile = new File(withSpace, "test.jar"); + TestJar.create(this.jarFile); + this.url = new URL("nested:" + this.jarFile.toURI().getRawPath() + "/!nested.jar"); + assertThat(this.url.toString()).contains("%20"); + NestedUrlConnection connection = new NestedUrlConnection(this.url); + assertHasSameContentAsNestedJar(connection); + assertThat(connection.getLastModified()).isEqualTo(this.jarFile.lastModified()); + } + + private void assertHasSameContentAsNestedJar(NestedUrlConnection connection) throws IOException { + try (InputStream actual = connection.getInputStream()) { + try (ZipContent zipContent = ZipContent.open(this.jarFile.toPath())) { + try (InputStream expected = zipContent.getEntry("nested.jar").openContent().asInputStream()) { + assertThat(actual).hasSameContentAs(expected); + } + } + } + } + private long withoutNanos(long epochMilli) { return Instant.ofEpochMilli(epochMilli).with(ChronoField.NANO_OF_SECOND, 0).toEpochMilli(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipContentTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipContentTests.java index c2d8ff02e55a..21cdbe4a780c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipContentTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/zip/ZipContentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,6 +88,7 @@ void tearDown() throws Exception { this.zipContent.close(); } catch (IllegalStateException ex) { + // Ignore } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/running.adoc b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/running.adoc index a1f877c626b2..9e678319d8b0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/running.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/running.adoc @@ -79,11 +79,11 @@ The following configuration suspend the process until a debugger has joined on p include::../maven/running/debug-pom.xml[tags=debug] ---- -These arguments can be specified on the command line as well, make sure to wrap that properly, that is: +These arguments can be specified on the command line as well: [indent=0] ---- - $ mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" + $ mvn spring-boot:run -Dspring-boot.run.jvmArguments=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005 ---- diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/maven/running/debug-pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/maven/running/debug-pom.xml index 08db158eb8bf..b81d99214ffe 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/maven/running/debug-pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/maven/running/debug-pom.xml @@ -8,7 +8,7 @@ spring-boot-maven-plugin - -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java index b89459cdf340..d96b959fe64c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/JarIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -349,6 +349,7 @@ void repackagedJarContainsTheLayersIndexByDefault(MavenBuild mavenBuild) { .anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-2")); } catch (IOException ex) { + // Ignore } }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java index e7cebf3c576b..56a266b5bfd6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/WarIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -167,6 +167,7 @@ void repackagedWarContainsTheLayersIndexByDefault(MavenBuild mavenBuild) { .anyMatch((dependency) -> dependency.startsWith("WEB-INF/lib-provided/")); } catch (IOException ex) { + // Ignore } }); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunArgumentsTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunArgumentsTests.java index 714d9b01c14c..388d5bfba423 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunArgumentsTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/RunArgumentsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,11 +63,10 @@ void parseEmpty() { } @Test - void parseDebugFlags() { - String[] args = parseArgs("-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"); - assertThat(args).hasSize(2); - assertThat(args[0]).isEqualTo("-Xdebug"); - assertThat(args[1]).isEqualTo("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"); + void parseDebugFlag() { + String[] args = parseArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"); + assertThat(args).hasSize(1); + assertThat(args[0]).isEqualTo("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"); } @Test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathClassLoader.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathClassLoader.java index 8f3bbd14e0ac..5ddbe97ab585 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathClassLoader.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -186,6 +186,7 @@ private static boolean isShortenedIntelliJJar(URL url) { return createdBy != null && createdBy.contains("IntelliJ"); } catch (Exception ex) { + // Ignore } } return false; @@ -330,6 +331,7 @@ private boolean isExcluded(URL url) { } } catch (URISyntaxException ex) { + // Ignore } } return false; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationBannerPrinter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationBannerPrinter.java index 4e744703e744..e104dedc2fc0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationBannerPrinter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationBannerPrinter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import org.apache.commons.logging.Log; @@ -95,9 +96,11 @@ private Banner getTextBanner(Environment environment) { private String createStringFromBanner(Banner banner, Environment environment, Class mainApplicationClass) throws UnsupportedEncodingException { + String charset = environment.getProperty("spring.banner.charset", StandardCharsets.UTF_8.name()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - banner.printBanner(environment, mainApplicationClass, new PrintStream(baos)); - String charset = environment.getProperty("spring.banner.charset", "UTF-8"); + try (PrintStream printStream = new PrintStream(baos, false, charset)) { + banner.printBanner(environment, mainApplicationClass, printStream); + } return baos.toString(charset); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java index 1553ead56d57..50b61eb1eb92 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -151,6 +151,7 @@ AnsiElement getElement(String postfix) { return this.factory.apply(Integer.parseInt(postfix)); } catch (IllegalArgumentException ex) { + // Ignore } } return null; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBean.java index 3fa38d84391b..bea1f693c052 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBean.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -168,6 +168,7 @@ private static Map getAll(ConfigurableAppli } } catch (Exception ex) { + // Ignore } } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index 1329c7a043ac..57e6234ee757 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,9 @@ protected Object bindAggregate(ConfigurationPropertyName name, Bindable targe ConfigurationProperty property = source.getConfigurationProperty(name); if (property != null && !hasDescendants) { getContext().setConfigurationProperty(property); - return getContext().getConverter().convert(property.getValue(), target); + Object result = property.getValue(); + result = getContext().getPlaceholdersResolver().resolvePlaceholders(result); + return getContext().getConverter().convert(result, target); } source = source.filter(name::isAncestorOf); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java index f5c6a488c692..14c8ef67277d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -110,6 +110,7 @@ private void collectUnbound(ConfigurationPropertyName name, Set isUnbound(name, candidate)).getConfigurationProperty(unboundName)); } catch (Exception ex) { + // Ignore } } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java index 26224addd924..b92e2b8c7509 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -212,6 +212,7 @@ private T getBoundField(Map boundFields, Strin } } catch (Exception ex) { + // Ignore } return null; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java index f11fc89dc7d8..9cb2d77b7364 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySourcesPropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,6 +49,7 @@ public boolean containsProperty(String key) { return attached.findConfigurationProperty(name) != null; } catch (Exception ex) { + // Ignore } } } @@ -91,6 +92,7 @@ private Object findPropertyValue(String key) { return (configurationProperty != null) ? configurationProperty.getValue() : null; } catch (Exception ex) { + // Ignore } } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java index 05ead2bddaee..e679b541d1a8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/DefaultPropertyMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,6 +75,7 @@ private ConfigurationPropertyName tryMap(String propertySourceName) { } } catch (Exception ex) { + // Ignore } return ConfigurationPropertyName.EMPTY; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java index 8c683db01d3d..df9f76994fc2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,6 +89,7 @@ public ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName } } catch (Exception ex) { + // Ignore } } return null; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzer.java index a9eae1eb2268..5b779d96897a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java index 1283dbbf0df2..6e69343c7d81 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/TextResourceOrigin.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,6 +121,7 @@ private String getResourceDescription(ClassPathResource resource) { } } catch (IOException ex) { + // Ignore } return resource.getDescription(); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationHome.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationHome.java index cd05e9c023db..5d0b3b6f26c2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationHome.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationHome.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +82,7 @@ private Class getStartClass(Enumeration manifestResources) { } } catch (Exception ex) { + // Ignore } } return null; @@ -98,6 +99,7 @@ private File findSource(Class sourceClass) { } } catch (Exception ex) { + // Ignore } return null; } @@ -112,6 +114,7 @@ private boolean isUnitTest() { } } catch (Exception ex) { + // Ignore } return false; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java index a10a9ce10022..49d8ac59f60b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -150,10 +150,8 @@ public RestTemplateBuilder detectRequestFactory(boolean detectRequestFactory) { /** * Set a root URL that should be applied to each request that starts with {@code '/'}. - * Since this works by adding a {@link UriTemplateHandler} to the - * {@link RestTemplate}, the root URL will only apply when {@code String} variants of - * the {@link RestTemplate} methods are used for specifying the request URL. See - * {@link RootUriTemplateHandler} for details. + * The root URL will only apply when {@code String} variants of the + * {@link RestTemplate} methods are used for specifying the request URL. * @param rootUri the root URI or {@code null} * @return a new builder instance */ @@ -639,7 +637,7 @@ public T configure(T restTemplate) { restTemplate.setErrorHandler(this.errorHandler); } if (this.rootUri != null) { - RootUriTemplateHandler.addTo(restTemplate, this.rootUri); + RootUriBuilderFactory.applyTo(restTemplate, this.rootUri); } restTemplate.getInterceptors().addAll(this.interceptors); if (!CollectionUtils.isEmpty(this.customizers)) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriBuilderFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriBuilderFactory.java new file mode 100644 index 000000000000..da2945a7f475 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriBuilderFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.client; + +import org.springframework.util.Assert; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriBuilderFactory; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriTemplateHandler; + +/** + * {@link UriBuilderFactory} to set the root for URI that starts with {@code '/'}. + * + * @author Scott Frederick + * @since 3.2.3 + */ +public class RootUriBuilderFactory extends RootUriTemplateHandler implements UriBuilderFactory { + + @SuppressWarnings("removal") + RootUriBuilderFactory(String rootUri) { + super(rootUri); + } + + @SuppressWarnings("removal") + RootUriBuilderFactory(String rootUri, UriTemplateHandler delegate) { + super(rootUri, delegate); + } + + @Override + public UriBuilder uriString(String uriTemplate) { + return UriComponentsBuilder.fromUriString(apply(uriTemplate)); + } + + @Override + public UriBuilder builder() { + return UriComponentsBuilder.newInstance(); + } + + /** + * Apply a {@link RootUriBuilderFactory} instance to the given {@link RestTemplate}. + * @param restTemplate the {@link RestTemplate} to add the builder factory to + * @param rootUri the root URI + */ + static void applyTo(RestTemplate restTemplate, String rootUri) { + Assert.notNull(restTemplate, "RestTemplate must not be null"); + RootUriBuilderFactory handler = new RootUriBuilderFactory(rootUri, restTemplate.getUriTemplateHandler()); + restTemplate.setUriTemplateHandler(handler); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriTemplateHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriTemplateHandler.java index e52c59b95fe6..4a007a6fcaa7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriTemplateHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RootUriTemplateHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ * {@link UriTemplateHandler} to set the root for URI that starts with {@code '/'}. * * @author Phillip Webb + * @author Scott Frederick * @since 1.4.0 */ public class RootUriTemplateHandler implements UriTemplateHandler { @@ -47,7 +48,9 @@ protected RootUriTemplateHandler(UriTemplateHandler handler) { /** * Create a new {@link RootUriTemplateHandler} instance. * @param rootUri the root URI to be used to prefix relative URLs + * @deprecated since 3.2.3 for removal in 3.4.0, with no replacement */ + @Deprecated(since = "3.2.3", forRemoval = true) public RootUriTemplateHandler(String rootUri) { this(rootUri, new DefaultUriBuilderFactory()); } @@ -55,8 +58,10 @@ public RootUriTemplateHandler(String rootUri) { /** * Create a new {@link RootUriTemplateHandler} instance. * @param rootUri the root URI to be used to prefix relative URLs - * @param handler the delegate handler + * @param handler the handler handler + * @deprecated since 3.2.3 for removal in 3.4.0, with no replacement */ + @Deprecated(since = "3.2.3", forRemoval = true) public RootUriTemplateHandler(String rootUri, UriTemplateHandler handler) { Assert.notNull(rootUri, "RootUri must not be null"); Assert.notNull(handler, "Handler must not be null"); @@ -74,7 +79,7 @@ public URI expand(String uriTemplate, Object... uriVariables) { return this.handler.expand(apply(uriTemplate), uriVariables); } - private String apply(String uriTemplate) { + String apply(String uriTemplate) { if (StringUtils.startsWithIgnoreCase(uriTemplate, "/")) { return getRootUri() + uriTemplate; } @@ -91,7 +96,9 @@ public String getRootUri() { * @param wrapper the wrapper to apply to the delegate URI template handler * @return the new handler * @since 2.3.10 + * @deprecated since 3.2.3 for removal in 3.4.0, with no replacement */ + @Deprecated(since = "3.2.3", forRemoval = true) public RootUriTemplateHandler withHandlerWrapper(Function wrapper) { return new RootUriTemplateHandler(getRootUri(), wrapper.apply(this.handler)); } @@ -101,7 +108,9 @@ public RootUriTemplateHandler withHandlerWrapper(Function 0) { sleep(100); } - System.out.println(this.activeRequests.get()); this.shuttingDown = false; long activeRequests = this.activeRequests.get(); if (activeRequests == 0) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java index 6644d775fc4e..f7c34851b61a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java index a9e6e6c2c953..6be46694fa88 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/GracefulShutdown.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/GracefulShutdown.java index 3215a0de8609..2accad1852a9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/GracefulShutdown.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/GracefulShutdown.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; import org.apache.catalina.Container; import org.apache.catalina.Service; @@ -51,32 +52,44 @@ final class GracefulShutdown { void shutDownGracefully(GracefulShutdownCallback callback) { logger.info("Commencing graceful shutdown. Waiting for active requests to complete"); - new Thread(() -> doShutdown(callback), "tomcat-shutdown").start(); + CountDownLatch shutdownUnderway = new CountDownLatch(1); + new Thread(() -> doShutdown(callback, shutdownUnderway), "tomcat-shutdown").start(); + try { + shutdownUnderway.await(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } } - private void doShutdown(GracefulShutdownCallback callback) { - List connectors = getConnectors(); - connectors.forEach(this::close); + private void doShutdown(GracefulShutdownCallback callback, CountDownLatch shutdownUnderway) { try { - for (Container host : this.tomcat.getEngine().findChildren()) { - for (Container context : host.findChildren()) { - while (!this.aborted && isActive(context)) { - Thread.sleep(50); - } - if (this.aborted) { - logger.info("Graceful shutdown aborted with one or more requests still active"); - callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE); - return; + List connectors = getConnectors(); + connectors.forEach(this::close); + shutdownUnderway.countDown(); + try { + for (Container host : this.tomcat.getEngine().findChildren()) { + for (Container context : host.findChildren()) { + while (!this.aborted && isActive(context)) { + Thread.sleep(50); + } + if (this.aborted) { + logger.info("Graceful shutdown aborted with one or more requests still active"); + callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE); + return; + } } } } - + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + logger.info("Graceful shutdown complete"); + callback.shutdownComplete(GracefulShutdownResult.IDLE); } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); + finally { + shutdownUnderway.countDown(); } - logger.info("Graceful shutdown complete"); - callback.shutdownComplete(GracefulShutdownResult.IDLE); } private List getConnectors() { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java index bce6152a7e68..20a34d020eb3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -155,6 +155,7 @@ private void closeSilently(Closeable closeable) { closeable.close(); } catch (Exception ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java index a4a4d813c462..d1a615718cc4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/support/ErrorPageFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -304,6 +304,7 @@ private static void addClassIfPresent(Collection> collection, String cl collection.add(ClassUtils.forName(className, null)); } catch (Throwable ex) { + // Ignore } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationBannerPrinterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationBannerPrinterTests.java index 30a112e705db..50250c8c0c00 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationBannerPrinterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/SpringApplicationBannerPrinterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,20 @@ package org.springframework.boot; +import org.apache.commons.logging.Log; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; /** * Tests for {@link SpringApplicationBannerPrinter}. @@ -38,4 +46,17 @@ void shouldRegisterRuntimeHints() { assertThat(RuntimeHintsPredicates.resource().forResource("banner.txt")).accepts(runtimeHints); } + @Test + void shouldUseUtf8() { + ResourceLoader resourceLoader = new GenericApplicationContext(); + Resource resource = resourceLoader.getResource("classpath:/banner-utf8.txt"); + SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(resourceLoader, + new ResourceBanner(resource)); + Log log = mock(Log.class); + printer.print(new MockEnvironment(), SpringApplicationBannerPrinterTests.class, log); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + then(log).should().info(captor.capture()); + assertThat(captor.getValue()).isEqualToIgnoringNewLines("\uD83D\uDE0D Spring Boot! \uD83D\uDE0D"); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/metrics/buffering/BufferingApplicationStartupTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/metrics/buffering/BufferingApplicationStartupTests.java index efc50373185c..d146f4750714 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/metrics/buffering/BufferingApplicationStartupTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/metrics/buffering/BufferingApplicationStartupTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -139,6 +139,7 @@ void multiThreadedAccessShouldWork() throws InterruptedException { Thread.sleep(1); } catch (InterruptedException ex) { + // Ignore } step.end(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/BindConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/BindConverterTests.java index 6768f00cff1b..827839d543a4 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/BindConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/BindConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -208,6 +208,7 @@ void convertWhenUsingTypeConverterConversionServiceFromMultipleThreads() { thread.join(); } catch (InterruptedException ex) { + // Ignore } } assertThat(results).isNotEmpty().doesNotContainNull(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java index cb03bcebc451..1a4306298f67 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -610,6 +610,20 @@ void bindToMapWithWildcardShouldConvertToTheRightType() { .containsExactly("127.0.0.1", "127.0.0.2"); } + @Test + void bindToMapWithPlaceholdersShouldResolve() { + DefaultConversionService conversionService = new DefaultConversionService(); + conversionService.addConverter(new MapConverter()); + StandardEnvironment environment = new StandardEnvironment(); + Binder binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment), + conversionService, null, null); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "bar=bc"); + this.sources.add(new MockConfigurationPropertySource("foo", "a${bar},${bar}d")); + Map map = binder.bind("foo", STRING_STRING_MAP).get(); + assertThat(map).containsKey("abc"); + assertThat(map).containsKey("bcd"); + } + private Bindable> getMapBindable(Class keyGeneric, ResolvableType valueType) { ResolvableType keyType = ResolvableType.forClass(keyGeneric); return Bindable.of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType)); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzerTests.java index 87eb14b67d2d..07d8bff36942 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/PatternParseFailureAnalyzerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcherTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcherTests.java index cb028295eed0..e8aae88112c2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcherTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcherTests.java @@ -133,6 +133,7 @@ private void join(Thread thread) { thread.join(1000); } catch (InterruptedException ex) { + // Ignore } } @@ -205,6 +206,7 @@ protected void initialized(Supplier context) { Thread.sleep(200); } catch (InterruptedException ex) { + // Ignore } this.initialized.set(true); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RestTemplateBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RestTemplateBuilderTests.java index c4d1e8ea4095..fc617087fafc 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RestTemplateBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RestTemplateBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,7 +126,7 @@ void rootUriShouldApplyAfterUriTemplateHandler() { .build(); UriTemplateHandler handler = template.getUriTemplateHandler(); handler.expand("/hello"); - assertThat(handler).isInstanceOf(RootUriTemplateHandler.class); + assertThat(handler).isInstanceOf(RootUriBuilderFactory.class); then(uriTemplateHandler).should().expand("https://example.com/hello"); } @@ -439,7 +439,7 @@ void customizerShouldBeAppliedAtTheEnd() { .customizers((restTemplate) -> { assertThat(restTemplate.getInterceptors()).hasSize(1); assertThat(restTemplate.getMessageConverters()).contains(this.messageConverter); - assertThat(restTemplate.getUriTemplateHandler()).isInstanceOf(RootUriTemplateHandler.class); + assertThat(restTemplate.getUriTemplateHandler()).isInstanceOf(RootUriBuilderFactory.class); assertThat(restTemplate.getErrorHandler()).isEqualTo(errorHandler); ClientHttpRequestFactory actualRequestFactory = restTemplate.getRequestFactory(); assertThat(actualRequestFactory).isInstanceOf(InterceptingClientHttpRequestFactory.class); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriBuilderFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriBuilderFactoryTests.java new file mode 100644 index 000000000000..1a77773cd742 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriBuilderFactoryTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.client; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.jupiter.api.Test; + +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriBuilderFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RootUriBuilderFactory}. + * + * @author Scott Frederick + */ +class RootUriBuilderFactoryTests { + + @Test + void uriStringPrefixesRoot() throws URISyntaxException { + UriBuilderFactory builderFactory = new RootUriBuilderFactory("https://example.com"); + UriBuilder builder = builderFactory.uriString("/hello"); + assertThat(builder.build()).isEqualTo(new URI("https://example.com/hello")); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriTemplateHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriTemplateHandlerTests.java index 2e8140008920..56288dc3d71f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriTemplateHandlerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/client/RootUriTemplateHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ * @author Phillip Webb */ @ExtendWith(MockitoExtension.class) +@SuppressWarnings("removal") class RootUriTemplateHandlerTests { private URI uri; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index b93516705e87..a3bb489a5ec6 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -1275,7 +1275,7 @@ void whenARequestIsActiveThenStopWillComplete() throws InterruptedException { blockingServlet.admitOne(); } catch (RuntimeException ex) { - + // Ignore } } @@ -1331,7 +1331,7 @@ void whenARequestIsActiveAfterGracefulShutdownEndsThenStopWillComplete() throws blockingServlet.admitOne(); } catch (RuntimeException ex) { - + // Ignore } } diff --git a/spring-boot-project/spring-boot/src/test/resources/banner-utf8.txt b/spring-boot-project/spring-boot/src/test/resources/banner-utf8.txt new file mode 100644 index 000000000000..f3077be2fc5e --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/banner-utf8.txt @@ -0,0 +1 @@ +😍 Spring Boot! 😍 diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java index 39b879011cb9..ce326c1702ba 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java index d9af5bd4bf44..dd5954a73e52 100644 --- a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +73,7 @@ void configureGradleBuild() { this.gradleBuild.scriptPropertyFrom(new File("../../gradle.properties"), "nativeBuildToolsVersion"); this.gradleBuild.expectDeprecationMessages("BPL_SPRING_CLOUD_BINDINGS_ENABLED.*true.*Deprecated"); this.gradleBuild.expectDeprecationMessages("BOM table is deprecated"); + this.gradleBuild.expectDeprecationMessages("Command \"packages\" is deprecated, use `syft scan` instead"); this.gradleBuild.gradleVersion(GradleVersions.maximumCompatible()); } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle index 7b39a164bf39..db4e6b461290 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/build.gradle @@ -7,7 +7,7 @@ plugins { description = "Spring Boot Launch Script Integration Tests" -def jdkVersion = "17.0.8.1+1" +def jdkVersion = "17.0.10+13" def jdkArch = "aarch64".equalsIgnoreCase(System.getProperty("os.arch")) ? "aarch64" : "amd64" configurations { diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/intTest/resources/conf/Ubuntu/jammy-20240111/Dockerfile b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/intTest/resources/conf/Ubuntu/jammy-20240111/Dockerfile index 0f7782c811f0..39c971efaf85 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/intTest/resources/conf/Ubuntu/jammy-20240111/Dockerfile +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/intTest/resources/conf/Ubuntu/jammy-20240111/Dockerfile @@ -1,10 +1,10 @@ -FROM ubuntu:jammy-20240111 as prepare +FROM ubuntu:jammy-20240212 as prepare COPY downloads/* /opt/download/ RUN mkdir -p /opt/jdk && \ cd /opt/jdk && \ tar xzf /opt/download/* --strip-components=1 -FROM ubuntu:jammy-20240111 +FROM ubuntu:jammy-20240212 RUN apt-get update && apt-get install -y software-properties-common curl COPY --from=prepare /opt/jdk /opt/jdk ENV JAVA_HOME /opt/jdk diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/resources/conf/oracle-jdk-17/Dockerfile b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/resources/conf/oracle-jdk-17/Dockerfile index f27488f00f85..f16f5d10d08d 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/resources/conf/oracle-jdk-17/Dockerfile +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-loader-tests/src/intTest/resources/conf/oracle-jdk-17/Dockerfile @@ -1,10 +1,10 @@ -FROM ubuntu:jammy-20240111 as prepare +FROM ubuntu:jammy-20240212 as prepare COPY downloads/* /opt/download/ RUN mkdir -p /opt/jdk && \ cd /opt/jdk && \ tar xzf /opt/download/* --strip-components=1 -FROM ubuntu:jammy-20240111 +FROM ubuntu:jammy-20240212 COPY --from=prepare /opt/jdk /opt/jdk ENV JAVA_HOME /opt/jdk ENV PATH $JAVA_HOME/bin:$PATH diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/intTest/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/intTest/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java index b60b41e220e7..fabd0e4a56b8 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/intTest/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/intTest/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,6 +132,7 @@ public void run() { StreamUtils.copy(this.input, this.output); } catch (IOException ex) { + // Ignore } } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/SampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/SampleActuatorApplicationTests.java index 8ddd0cefe697..ca12587bf87b 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/SampleActuatorApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/SampleActuatorApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,6 +177,18 @@ void testLegacyHyphen() { assertThat(entity.getBody()).contains(entry("legacy", "legacy")); } + @Test + @SuppressWarnings("unchecked") + void testInfo() { + ResponseEntity> entity = asMapEntity( + this.restTemplate.withBasicAuth("user", "password").getForEntity("/actuator/info", Map.class)); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).containsKey("build"); + Map body = entity.getBody(); + Map example = (Map) body.get("example"); + assertThat(example).containsEntry("someKey", "someValue"); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) static ResponseEntity> asMapEntity(ResponseEntity entity) { return (ResponseEntity) entity;