From aaf626a53725f5cc6f63424f1095ef5e02b86108 Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 11 Nov 2021 07:42:44 +0000 Subject: [PATCH 01/78] Next development version (v5.3.14-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b46dbd487055..bc834bbefe0d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.3.13-SNAPSHOT +version=5.3.14-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true From 29572600dcb442ffdc7f95dced84e1c8d137bbc1 Mon Sep 17 00:00:00 2001 From: d4ksn Date: Tue, 16 Nov 2021 15:01:28 +0100 Subject: [PATCH 02/78] Ensure that references > MAX_REFERENCE_SIZE are not processed This commit ensures that only HTML references of length < MAX_REFERENCE_SIZE are considered as potential references. This check is possible because reference longer than 10 digits are out of bounds for Integers. Closes gh-1249 --- .../web/util/HtmlCharacterEntityDecoder.java | 4 ++-- .../org/springframework/web/util/HtmlUtilsTests.java | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java b/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java index 2277edfff3ad..2d6db518b20a 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java +++ b/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author 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,7 +72,7 @@ private void findNextPotentialReference(int startPosition) { boolean isPotentialReference = (this.nextPotentialReferencePosition != -1 && this.nextSemicolonPosition != -1 && - this.nextPotentialReferencePosition - this.nextSemicolonPosition < MAX_REFERENCE_SIZE); + this.nextSemicolonPosition - this.nextPotentialReferencePosition < MAX_REFERENCE_SIZE); if (isPotentialReference) { break; diff --git a/spring-web/src/test/java/org/springframework/web/util/HtmlUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/HtmlUtilsTests.java index 68d31898f54f..232a436e503c 100644 --- a/spring-web/src/test/java/org/springframework/web/util/HtmlUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/HtmlUtilsTests.java @@ -28,7 +28,7 @@ public class HtmlUtilsTests { @Test - public void testHtmlEscape() { + void testHtmlEscape() { String unescaped = "\"This is a quote'"; String escaped = HtmlUtils.htmlEscape(unescaped); assertThat(escaped).isEqualTo(""This is a quote'"); @@ -39,14 +39,14 @@ public void testHtmlEscape() { } @Test - public void testHtmlUnescape() { + void testHtmlUnescape() { String escaped = ""This is a quote'"; String unescaped = HtmlUtils.htmlUnescape(escaped); assertThat(unescaped).isEqualTo("\"This is a quote'"); } @Test - public void testEncodeIntoHtmlCharacterSet() { + void testEncodeIntoHtmlCharacterSet() { assertThat(HtmlUtils.htmlEscape("")).as("An empty string should be converted to an empty string").isEqualTo(""); assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters."); @@ -62,7 +62,7 @@ public void testEncodeIntoHtmlCharacterSet() { // SPR-9293 @Test - public void testEncodeIntoHtmlCharacterSetFromUtf8() { + void testEncodeIntoHtmlCharacterSetFromUtf8() { String utf8 = ("UTF-8"); assertThat(HtmlUtils.htmlEscape("", utf8)).as("An empty string should be converted to an empty string").isEqualTo(""); assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters."); @@ -74,7 +74,7 @@ public void testEncodeIntoHtmlCharacterSetFromUtf8() { } @Test - public void testDecodeFromHtmlCharacterSet() { + void testDecodeFromHtmlCharacterSet() { assertThat(HtmlUtils.htmlUnescape("")).as("An empty string should be converted to an empty string").isEqualTo(""); assertThat(HtmlUtils.htmlUnescape("This is a sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("This is a sentence containing no special characters."); From 5fbdd6dcfe7504327fe805971760f1b229d0cc62 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 17 Nov 2021 16:52:17 +0100 Subject: [PATCH 03/78] Throw exception using capturing patterns in AntPathMatcher Closes gh-27688 --- .../org/springframework/util/AntPathMatcher.java | 4 ++++ .../PathMatchingUrlHandlerMappingTests.java | 16 +++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index d304f08d2c00..63eea1e9eab8 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -733,6 +733,10 @@ else if (this.pattern != null) { } for (int i = 1; i <= matcher.groupCount(); i++) { String name = this.variableNames.get(i - 1); + if (name.startsWith("*")) { + throw new IllegalArgumentException("Capturing patterns (" + name + ") are not " + + "supported by the AntPathMatcher. Use the PathPatternParser instead."); + } String value = matcher.group(i); uriTemplateVariables.put(name, value); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java index 5e4e148c8511..4c988f1a7fd0 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java @@ -155,13 +155,15 @@ void actualPathMatching(SimpleUrlHandlerMapping mapping, WebApplicationContext w chain = getHandler(mapping, wac, request); assertThat(chain.getHandler()).isSameAs(defaultBean); - request = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla"); - chain = getHandler(mapping, wac, request); - assertThat(chain.getHandler()).isSameAs(bean); - - request = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp"); - chain = getHandler(mapping, wac, request); - assertThat(chain.getHandler()).isSameAs(bean); + if (mapping.getPatternParser() != null) { + request = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla"); + chain = getHandler(mapping, wac, request); + assertThat(chain.getHandler()).isSameAs(bean); + + request = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp"); + chain = getHandler(mapping, wac, request); + assertThat(chain.getHandler()).isSameAs(bean); + } request = new MockHttpServletRequest("GET", "/administrator/testing/longer2/notmatching/notmatching"); chain = getHandler(mapping, wac, request); From c37d6c30a0ef03aebf536cdf555dbf0e55563ef8 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 18 Nov 2021 19:23:28 +0100 Subject: [PATCH 04/78] Fix nullability declarations in MergedContextConfiguration --- .../test/context/MergedContextConfiguration.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java index 2ae169bd2959..318d9d0f8c2c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java @@ -112,8 +112,8 @@ public class MergedContextConfiguration implements Serializable { * @param activeProfiles the merged active bean definition profiles * @param contextLoader the resolved {@code ContextLoader} */ - public MergedContextConfiguration(Class testClass, String[] locations, Class[] classes, - String[] activeProfiles, ContextLoader contextLoader) { + public MergedContextConfiguration(Class testClass, @Nullable String[] locations, @Nullable Class[] classes, + @Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader) { this(testClass, locations, classes, null, activeProfiles, contextLoader); } @@ -128,9 +128,9 @@ public MergedContextConfiguration(Class testClass, String[] locations, Class< * @param activeProfiles the merged active bean definition profiles * @param contextLoader the resolved {@code ContextLoader} */ - public MergedContextConfiguration(Class testClass, String[] locations, Class[] classes, + public MergedContextConfiguration(Class testClass, @Nullable String[] locations, @Nullable Class[] classes, @Nullable Set>> contextInitializerClasses, - String[] activeProfiles, ContextLoader contextLoader) { + @Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader) { this(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader, null, null); } @@ -149,9 +149,9 @@ public MergedContextConfiguration(Class testClass, String[] locations, Class< * @param parent the parent configuration or {@code null} if there is no parent * @since 3.2.2 */ - public MergedContextConfiguration(Class testClass, String[] locations, Class[] classes, + public MergedContextConfiguration(Class testClass, @Nullable String[] locations, @Nullable Class[] classes, @Nullable Set>> contextInitializerClasses, - String[] activeProfiles, ContextLoader contextLoader, + @Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, @Nullable MergedContextConfiguration parent) { @@ -197,7 +197,7 @@ public MergedContextConfiguration(MergedContextConfiguration mergedConfig) { public MergedContextConfiguration(Class testClass, @Nullable String[] locations, @Nullable Class[] classes, @Nullable Set>> contextInitializerClasses, @Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations, - @Nullable String[] propertySourceProperties, ContextLoader contextLoader, + @Nullable String[] propertySourceProperties, @Nullable ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, @Nullable MergedContextConfiguration parent) { @@ -235,7 +235,7 @@ public MergedContextConfiguration(Class testClass, @Nullable String[] locatio @Nullable Set>> contextInitializerClasses, @Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations, @Nullable String[] propertySourceProperties, @Nullable Set contextCustomizers, - ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, + @Nullable ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, @Nullable MergedContextConfiguration parent) { this.testClass = testClass; From 722ab25f27ed096e04e0aedc93602d2e104c1de4 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 19 Nov 2021 12:37:00 +0100 Subject: [PATCH 05/78] Support empty file names in UriUtils::extractFileExtension Closes gh-27639 --- .../src/main/java/org/springframework/web/util/UriUtils.java | 2 +- .../test/java/org/springframework/web/util/UriUtilsTests.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java index a67be402e567..d6653c24f148 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java @@ -407,7 +407,7 @@ public static String extractFileExtension(String path) { int paramIndex = path.indexOf(';', begin); end = (paramIndex != -1 && paramIndex < end ? paramIndex : end); int extIndex = path.lastIndexOf('.', end); - if (extIndex != -1 && extIndex > begin) { + if (extIndex != -1 && extIndex >= begin) { return path.substring(extIndex + 1, end); } return null; diff --git a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java index 019107a535c4..f9dae3a38362 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java @@ -133,6 +133,7 @@ public void extractFileExtension() { assertThat(UriUtils.extractFileExtension("/products;q=11/view.html?param=/path/a.do")).isEqualTo("html"); assertThat(UriUtils.extractFileExtension("/products;q=11/view.html;r=22?param=/path/a.do")).isEqualTo("html"); assertThat(UriUtils.extractFileExtension("/products;q=11/view.html;r=22;s=33?param=/path/a.do")).isEqualTo("html"); + assertThat(UriUtils.extractFileExtension("/products/.html")).isEqualTo("html"); } } From dfd5374f024d844e8f9d5a2082692c2ec2b5a3a9 Mon Sep 17 00:00:00 2001 From: Jerome Prinet Date: Fri, 19 Nov 2021 14:36:27 +0100 Subject: [PATCH 06/78] Upgrade Gradle enterprise plugin to 3.7.2 Closes gh-27704 --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 811aa851d64e..0f97d1fcaa87 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { } plugins { - id "com.gradle.enterprise" version "3.6.1" + id "com.gradle.enterprise" version "3.7.2" id "io.spring.ge.conventions" version "0.0.7" } From b4b3c2ead681520509cc0b03a598e2ec566b78e8 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Fri, 19 Nov 2021 08:58:58 +0800 Subject: [PATCH 07/78] Fix typo See gh-27699 --- src/docs/asciidoc/data-access.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs/asciidoc/data-access.adoc b/src/docs/asciidoc/data-access.adoc index edf4ccbe5636..55d6be79646d 100644 --- a/src/docs/asciidoc/data-access.adoc +++ b/src/docs/asciidoc/data-access.adoc @@ -6788,20 +6788,20 @@ You can take control over result mapping by supplying a `Function` that called for each `Row` so it can can return arbitrary values (singular values, collections and maps, and objects). -The following example extracts the `id` column and emits its value: +The following example extracts the `name` column and emits its value: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- Flux names = client.sql("SELECT name FROM person") - .map(row -> row.get("id", String.class)) + .map(row -> row.get("name", String.class)) .all(); ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] .Kotlin ---- val names = client.sql("SELECT name FROM person") - .map{ row: Row -> row.get("id", String.class) } + .map{ row: Row -> row.get("name", String.class) } .flow() ---- From 767299c6dd607c7d540310fb9db9832ebaff659c Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Thu, 2 Apr 2015 13:52:49 +0200 Subject: [PATCH 08/78] Document @Bean definitions via default methods See gh-767 Co-authored-by: Stephane Nicoll --- src/docs/asciidoc/core/core-beans.adoc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/docs/asciidoc/core/core-beans.adoc b/src/docs/asciidoc/core/core-beans.adoc index 8d701befcbcc..22cdf42ce530 100644 --- a/src/docs/asciidoc/core/core-beans.adoc +++ b/src/docs/asciidoc/core/core-beans.adoc @@ -7953,6 +7953,26 @@ following text image shows: transferService -> com.acme.TransferServiceImpl ---- +You can also use default methods to define beans. This allows composition of bean +configurations by implementing interfaces with bean definitions on default methods. + +[source,java,indent=0,subs="verbatim,quotes",role="primary"] +.Java +---- + public interface BaseConfig { + + @Bean + default TransferServiceImpl transferService() { + return new TransferServiceImpl(); + } + } + + @Configuration + public class AppConfig implements BaseConfig { + + } +---- + You can also declare your `@Bean` method with an interface (or base class) return type, as the following example shows: From ca4f338d75dbdda3db1c4068c2718ebd8b04f9f3 Mon Sep 17 00:00:00 2001 From: awgtek Date: Fri, 11 Mar 2016 10:35:31 -0600 Subject: [PATCH 09/78] Refine StoredProcedure#declareParameter Javadoc See gh-1000 --- .../java/org/springframework/jdbc/object/StoredProcedure.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java index d6a24e5d8b07..03677ae44561 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java @@ -87,6 +87,8 @@ protected boolean allowsUnusedParameters() { * they appear in the database's stored procedure parameter list. *

Names are purely used to help mapping. * @param param the parameter object + * @throws InvalidDataAccessApiUsageException if the parameter has no name; or if the operation is + * already compiled, and hence cannot be configured further */ @Override public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException { From 544f35766119b2541963dae796365db2f7527fda Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 22 Nov 2021 13:20:16 +0100 Subject: [PATCH 10/78] Polish "Refine StoredProcedure#declareParameter Javadoc" See gh-1000 --- .../org/springframework/jdbc/object/StoredProcedure.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java index 03677ae44561..f740eb4b792f 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/StoredProcedure.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,8 +87,8 @@ protected boolean allowsUnusedParameters() { * they appear in the database's stored procedure parameter list. *

Names are purely used to help mapping. * @param param the parameter object - * @throws InvalidDataAccessApiUsageException if the parameter has no name; or if the operation is - * already compiled, and hence cannot be configured further + * @throws InvalidDataAccessApiUsageException if the parameter has no name, or if the + * operation is already compiled, and hence cannot be configured further */ @Override public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException { From 1e0bdf95d051912c327e9781673d9690941a8d4b Mon Sep 17 00:00:00 2001 From: julianladisch Date: Mon, 4 Apr 2016 10:30:22 +0200 Subject: [PATCH 11/78] Fix simple data format in appendix See gh-1025 --- src/docs/asciidoc/core/core-appendix.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/core/core-appendix.adoc b/src/docs/asciidoc/core/core-appendix.adoc index 53ef8e333583..095f767518a0 100644 --- a/src/docs/asciidoc/core/core-appendix.adoc +++ b/src/docs/asciidoc/core/core-appendix.adoc @@ -749,7 +749,7 @@ essentially the same as the following XML snippet: [source,xml,indent=0,subs="verbatim,quotes"] ---- - + ---- From bcb9f159ca602fb3437123ee3f3fffdaf3572251 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sun, 19 Feb 2017 02:24:50 +0900 Subject: [PATCH 12/78] Add tests for AbstractRoutingDataSource See gh-1330 --- .../AbstractRoutingDataSourceTests.java | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java new file mode 100644 index 000000000000..a39729161c9b --- /dev/null +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java @@ -0,0 +1,185 @@ +/* + * Copyright 2002-2017 the original author 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 + * + * http://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.jdbc.datasource.lookup; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javax.sql.DataSource; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.core.IsSame.sameInstance; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link AbstractRoutingDataSource}. + * @author Kazuki Shimizu + */ +public class AbstractRoutingDataSourceTests { + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void setTargetDataSources() { + final ThreadLocal lookupKey = new ThreadLocal<>(); + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return lookupKey.get(); + } + }; + DataSource ds1 = new StubDataSource(); + DataSource ds2 = new StubDataSource(); + + MapDataSourceLookup dataSourceLookup = new MapDataSourceLookup(); + dataSourceLookup.addDataSource("dataSource2", ds2); + routingDataSource.setDataSourceLookup(dataSourceLookup); + + Map targetDataSources = new HashMap<>(); + targetDataSources.put("ds1", ds1); + targetDataSources.put("ds2", "dataSource2"); + routingDataSource.setTargetDataSources(targetDataSources); + + routingDataSource.afterPropertiesSet(); + + lookupKey.set("ds1"); + assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds1)); + + lookupKey.set("ds2"); + assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds2)); + } + + @Test + public void targetDataSourcesIsNull() { + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return null; + } + }; + + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Property 'targetDataSources' is required"); + + routingDataSource.afterPropertiesSet(); + } + + @Test + public void dataSourceIsUnSupportedType() { + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return null; + } + }; + Map targetDataSources = new HashMap<>(); + targetDataSources.put("ds1", 1); + routingDataSource.setTargetDataSources(targetDataSources); + + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Illegal data source value - only [javax.sql.DataSource] and String supported: 1"); + + routingDataSource.afterPropertiesSet(); + } + + + + @Test + public void setDefaultTargetDataSource() { + final ThreadLocal lookupKey = new ThreadLocal<>(); + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return lookupKey.get(); + } + }; + DataSource ds = new StubDataSource(); + + routingDataSource.setTargetDataSources(new HashMap<>()); + routingDataSource.setDefaultTargetDataSource(ds); + + routingDataSource.afterPropertiesSet(); + + lookupKey.set("foo"); + assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds)); + } + + @Test + public void setDefaultTargetDataSourceFallbackIsFalse() { + final ThreadLocal lookupKey = new ThreadLocal<>(); + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return lookupKey.get(); + } + }; + DataSource ds = new StubDataSource(); + + routingDataSource.setTargetDataSources(new HashMap<>()); + routingDataSource.setDefaultTargetDataSource(ds); + routingDataSource.setLenientFallback(false); + + routingDataSource.afterPropertiesSet(); + + exception.expect(IllegalStateException.class); + exception.expectMessage("Cannot determine target DataSource for lookup key [foo]"); + + lookupKey.set("foo"); + routingDataSource.determineTargetDataSource(); + } + + @Test + public void setDefaultTargetDataSourceLookupKeyIsNullWhenFallbackIsFalse() { + final ThreadLocal lookupKey = new ThreadLocal<>(); + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return lookupKey.get(); + } + }; + DataSource ds = new StubDataSource(); + + routingDataSource.setTargetDataSources(new HashMap<>()); + routingDataSource.setDefaultTargetDataSource(ds); + routingDataSource.setLenientFallback(false); + + routingDataSource.afterPropertiesSet(); + + lookupKey.set(null); + assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds)); + } + + @Test + public void notInitialized() { + AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { + @Override + protected Object determineCurrentLookupKey() { + return null; + } + }; + + exception.expect(IllegalArgumentException.class); + exception.expectMessage("DataSource router not initialized"); + + routingDataSource.determineTargetDataSource(); + } + +} \ No newline at end of file From 23babe27bbae32325a076259672035decfccf9ca Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 22 Nov 2021 13:43:00 +0100 Subject: [PATCH 13/78] Polish "Add tests for AbstractRoutingDataSource" See gh-1330 --- .../AbstractRoutingDataSourceTests.java | 82 +++++++------------ 1 file changed, 29 insertions(+), 53 deletions(-) diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java index a39729161c9b..7fb3445c4a79 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author 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,29 +16,28 @@ package org.springframework.jdbc.datasource.lookup; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; -import static org.hamcrest.core.IsSame.sameInstance; -import static org.junit.Assert.assertThat; +import javax.sql.DataSource; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + /** * Tests for {@link AbstractRoutingDataSource}. + * * @author Kazuki Shimizu */ -public class AbstractRoutingDataSourceTests { - - @Rule - public final ExpectedException exception = ExpectedException.none(); +class AbstractRoutingDataSourceTests { @Test - public void setTargetDataSources() { + void setTargetDataSources() { final ThreadLocal lookupKey = new ThreadLocal<>(); AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override @@ -59,31 +58,26 @@ protected Object determineCurrentLookupKey() { routingDataSource.setTargetDataSources(targetDataSources); routingDataSource.afterPropertiesSet(); - lookupKey.set("ds1"); - assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds1)); - + assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds1); lookupKey.set("ds2"); - assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds2)); + assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds2); } @Test - public void targetDataSourcesIsNull() { + void targetDataSourcesIsNull() { AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override protected Object determineCurrentLookupKey() { return null; } }; - - exception.expect(IllegalArgumentException.class); - exception.expectMessage("Property 'targetDataSources' is required"); - - routingDataSource.afterPropertiesSet(); + assertThatIllegalArgumentException().isThrownBy(routingDataSource::afterPropertiesSet) + .withMessage("Property 'targetDataSources' is required"); } @Test - public void dataSourceIsUnSupportedType() { + void dataSourceIsUnSupportedType() { AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override protected Object determineCurrentLookupKey() { @@ -93,17 +87,13 @@ protected Object determineCurrentLookupKey() { Map targetDataSources = new HashMap<>(); targetDataSources.put("ds1", 1); routingDataSource.setTargetDataSources(targetDataSources); - - exception.expect(IllegalArgumentException.class); - exception.expectMessage("Illegal data source value - only [javax.sql.DataSource] and String supported: 1"); - - routingDataSource.afterPropertiesSet(); + assertThatIllegalArgumentException().isThrownBy(routingDataSource::afterPropertiesSet) + .withMessage("Illegal data source value - only [javax.sql.DataSource] and String supported: 1"); } - @Test - public void setDefaultTargetDataSource() { + void setDefaultTargetDataSource() { final ThreadLocal lookupKey = new ThreadLocal<>(); AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override @@ -112,18 +102,15 @@ protected Object determineCurrentLookupKey() { } }; DataSource ds = new StubDataSource(); - routingDataSource.setTargetDataSources(new HashMap<>()); routingDataSource.setDefaultTargetDataSource(ds); - routingDataSource.afterPropertiesSet(); - lookupKey.set("foo"); - assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds)); + assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds); } @Test - public void setDefaultTargetDataSourceFallbackIsFalse() { + void setDefaultTargetDataSourceFallbackIsFalse() { final ThreadLocal lookupKey = new ThreadLocal<>(); AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override @@ -132,22 +119,17 @@ protected Object determineCurrentLookupKey() { } }; DataSource ds = new StubDataSource(); - routingDataSource.setTargetDataSources(new HashMap<>()); routingDataSource.setDefaultTargetDataSource(ds); routingDataSource.setLenientFallback(false); - routingDataSource.afterPropertiesSet(); - - exception.expect(IllegalStateException.class); - exception.expectMessage("Cannot determine target DataSource for lookup key [foo]"); - lookupKey.set("foo"); - routingDataSource.determineTargetDataSource(); + assertThatIllegalStateException().isThrownBy(routingDataSource::determineTargetDataSource) + .withMessage("Cannot determine target DataSource for lookup key [foo]"); } @Test - public void setDefaultTargetDataSourceLookupKeyIsNullWhenFallbackIsFalse() { + void setDefaultTargetDataSourceLookupKeyIsNullWhenFallbackIsFalse() { final ThreadLocal lookupKey = new ThreadLocal<>(); AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() { @Override @@ -156,15 +138,12 @@ protected Object determineCurrentLookupKey() { } }; DataSource ds = new StubDataSource(); - routingDataSource.setTargetDataSources(new HashMap<>()); routingDataSource.setDefaultTargetDataSource(ds); routingDataSource.setLenientFallback(false); - routingDataSource.afterPropertiesSet(); - lookupKey.set(null); - assertThat(routingDataSource.determineTargetDataSource(), sameInstance(ds)); + assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds); } @Test @@ -175,11 +154,8 @@ protected Object determineCurrentLookupKey() { return null; } }; - - exception.expect(IllegalArgumentException.class); - exception.expectMessage("DataSource router not initialized"); - - routingDataSource.determineTargetDataSource(); + assertThatIllegalArgumentException().isThrownBy(routingDataSource::determineTargetDataSource) + .withMessage("DataSource router not initialized"); } } \ No newline at end of file From b67e97d38879d653f3746032e2c243cf58a27f92 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 22 Nov 2021 16:11:43 +0100 Subject: [PATCH 14/78] Polish --- .../datasource/lookup/AbstractRoutingDataSourceTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java index 7fb3445c4a79..8dc23a3f9651 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSourceTests.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -158,4 +158,4 @@ protected Object determineCurrentLookupKey() { .withMessage("DataSource router not initialized"); } -} \ No newline at end of file +} From e8eeb99ac894dde7896d080c0bcb18335e49ee78 Mon Sep 17 00:00:00 2001 From: qxo Date: Wed, 29 Nov 2017 17:44:05 +0800 Subject: [PATCH 15/78] Repect StaxDriver in XStreamMarshaller::marshalXmlStreamWriter This commit makes sure that namespaces configured via the streamDriver property are respected. --- .../springframework/oxm/xstream/XStreamMarshaller.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index ccb10d27c172..9ab9c9a5be30 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -696,7 +696,13 @@ protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) t @Override protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { try { - doMarshal(graph, new StaxWriter(new QNameMap(), streamWriter, this.nameCoder), null); + final StaxWriter writer; + if( streamDriver instanceof StaxDriver){ + writer = ((StaxDriver)streamDriver).createStaxWriter(streamWriter); + }else{ + writer = new StaxWriter( new QNameMap(),streamWriter, this.nameCoder); + } + doMarshal(graph,writer, null); } catch (XMLStreamException ex) { throw convertXStreamException(ex, true); From 2271b6078e65c6cee55c069ebb15f04fe04f0845 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 23 Nov 2021 10:39:38 +0100 Subject: [PATCH 16/78] Polishing contribution See gh-1607 --- .../oxm/xstream/XStreamMarshaller.java | 14 ++++++++------ .../oxm/xstream/XStreamMarshallerTests.java | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index 9ab9c9a5be30..3a22e4b6e8fe 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -56,6 +56,7 @@ import com.thoughtworks.xstream.io.xml.DomWriter; import com.thoughtworks.xstream.io.xml.QNameMap; import com.thoughtworks.xstream.io.xml.SaxWriter; +import com.thoughtworks.xstream.io.xml.StaxDriver; import com.thoughtworks.xstream.io.xml.StaxReader; import com.thoughtworks.xstream.io.xml.StaxWriter; import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder; @@ -696,13 +697,14 @@ protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) t @Override protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { try { - final StaxWriter writer; - if( streamDriver instanceof StaxDriver){ - writer = ((StaxDriver)streamDriver).createStaxWriter(streamWriter); - }else{ - writer = new StaxWriter( new QNameMap(),streamWriter, this.nameCoder); + StaxWriter writer; + if (this.streamDriver instanceof StaxDriver) { + writer = ((StaxDriver) this.streamDriver).createStaxWriter(streamWriter); } - doMarshal(graph,writer, null); + else { + writer = new StaxWriter(new QNameMap(), streamWriter, this.nameCoder); + } + doMarshal(graph, writer, null); } catch (XMLStreamException ex) { throw convertXStreamException(ex, true); diff --git a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java index 304d3fcc771b..44435fff9720 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java @@ -43,6 +43,8 @@ import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; import com.thoughtworks.xstream.io.json.JsonWriter; +import com.thoughtworks.xstream.io.xml.QNameMap; +import com.thoughtworks.xstream.io.xml.StaxDriver; import com.thoughtworks.xstream.security.AnyTypePermission; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -176,6 +178,23 @@ void marshalStaxResultXMLStreamWriter() throws Exception { assertThat(XmlContent.from(writer)).isSimilarTo(EXPECTED_STRING); } + @Test + void marshalStaxResultXMLStreamWriterDefaultNamespace() throws Exception { + QNameMap map = new QNameMap(); + map.setDefaultNamespace("https://example.com"); + map.setDefaultPrefix("spr"); + StaxDriver driver = new StaxDriver(map); + marshaller.setStreamDriver(driver); + + XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + StringWriter writer = new StringWriter(); + XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(writer); + Result result = StaxUtils.createStaxResult(streamWriter); + marshaller.marshal(flight, result); + assertThat(XmlContent.from(writer)).isSimilarTo( + "42"); + } + @Test void marshalStaxResultXMLEventWriter() throws Exception { XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); From 2c53e9e308dc39c5988c014a12d32917f0b5528d Mon Sep 17 00:00:00 2001 From: Federico Donnarumma Date: Fri, 25 Nov 2016 17:33:49 -0300 Subject: [PATCH 17/78] Expose prestartAllCoreThreads on ExecutorService See gh-1246 --- .../ThreadPoolExecutorFactoryBean.java | 21 +++++- .../ThreadPoolExecutorFactoryBeanTests.java | 64 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index e880489f80af..26dcec6370b7 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -73,12 +73,14 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport private int keepAliveSeconds = 60; - private boolean allowCoreThreadTimeOut = false; - private int queueCapacity = Integer.MAX_VALUE; + private boolean allowCoreThreadTimeOut = false; + private boolean exposeUnconfigurableExecutor = false; + private boolean prestartAllCoreThreads = false; + @Nullable private ExecutorService exposedExecutor; @@ -130,6 +132,17 @@ public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; } + /** + * Specify whether this FactoryBean should prestart all threads + * for the created executor. + *

Default is "false". + * Switch this flag to "true" to prestart the threads allocated for the current executor + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Specify whether this FactoryBean should expose an unconfigurable * decorator for the created executor. @@ -154,6 +167,10 @@ protected ExecutorService initializeExecutor( executor.allowCoreThreadTimeOut(true); } + if (this.prestartAllCoreThreads) { + executor.prestartAllCoreThreads(); + } + // Wrap executor with an unconfigurable decorator. this.exposedExecutor = (this.exposeUnconfigurableExecutor ? Executors.unconfigurableExecutorService(executor) : executor); diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index abba6cf16849..c5731e3139f6 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -16,17 +16,25 @@ package org.springframework.scheduling.concurrent; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; /** * @author Juergen Hoeller @@ -44,6 +52,21 @@ void defaultExecutor() throws Exception { context.close(); } + @Test + public void executorWithPreStartedThreads() throws Exception { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithPreStartedThreads.class); + ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); + + verify(executor).prestartAllCoreThreads(); + } + + @Test + public void executorWithNoPreStartedThreads() throws Exception { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithNoPreStartedThreads.class); + ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); + + verify(executor, never()).prestartAllCoreThreads(); + } @Configuration static class ExecutorConfig { @@ -55,4 +78,45 @@ ThreadPoolExecutorFactoryBean executor() { } + @Configuration + public static class ExecutorConfigWithPreStartedThreads { + + @Bean + public ThreadPoolExecutorFactoryBean executorChildFactory() { + ThreadPoolExecutorFactoryBeanMockingChild threadPoolExecutorFactoryBeanMockingChild = new ThreadPoolExecutorFactoryBeanMockingChild(); + threadPoolExecutorFactoryBeanMockingChild.setPrestartAllCoreThreads(true); + return threadPoolExecutorFactoryBeanMockingChild; + } + + @Bean + public ExecutorService childExecutor() { + return executorChildFactory().getObject(); + } + } + + @Configuration + public static class ExecutorConfigWithNoPreStartedThreads { + + @Bean + public ThreadPoolExecutorFactoryBean executorChildFactory() { + return new ThreadPoolExecutorFactoryBeanMockingChild(); + } + + @Bean + public ExecutorService childExecutor() { + return executorChildFactory().getObject(); + } + } + + private static class ThreadPoolExecutorFactoryBeanMockingChild extends ThreadPoolExecutorFactoryBean { + @Override + protected ThreadPoolExecutor createExecutor( + int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue queue, + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { + + return mock(ThreadPoolExecutor.class); + } + } + + } From 19a8b94b21a3cd8740b06b9910734cd136b226ad Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 23 Nov 2021 17:15:18 +0100 Subject: [PATCH 18/78] Polish "Expose prestartAllCoreThreads on ExecutorService" See gh-1246 --- .../ThreadPoolExecutorFactoryBean.java | 32 +++++----- .../concurrent/ThreadPoolTaskExecutor.java | 17 ++++- .../ThreadPoolExecutorFactoryBeanTests.java | 64 ++++++------------- 3 files changed, 52 insertions(+), 61 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index 26dcec6370b7..d95fef71f22a 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author 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,14 +73,14 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport private int keepAliveSeconds = 60; - private int queueCapacity = Integer.MAX_VALUE; - private boolean allowCoreThreadTimeOut = false; - private boolean exposeUnconfigurableExecutor = false; - private boolean prestartAllCoreThreads = false; + private int queueCapacity = Integer.MAX_VALUE; + + private boolean exposeUnconfigurableExecutor = false; + @Nullable private ExecutorService exposedExecutor; @@ -120,6 +120,16 @@ public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; } + /** + * Specify whether to start all core threads, causing them to idly wait for work. + *

Default is "false". + * @since 5.3.14 + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Set the capacity for the ThreadPoolExecutor's BlockingQueue. * Default is {@code Integer.MAX_VALUE}. @@ -132,17 +142,6 @@ public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; } - /** - * Specify whether this FactoryBean should prestart all threads - * for the created executor. - *

Default is "false". - * Switch this flag to "true" to prestart the threads allocated for the current executor - * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads - */ - public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { - this.prestartAllCoreThreads = prestartAllCoreThreads; - } - /** * Specify whether this FactoryBean should expose an unconfigurable * decorator for the created executor. @@ -166,7 +165,6 @@ protected ExecutorService initializeExecutor( if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } - if (this.prestartAllCoreThreads) { executor.prestartAllCoreThreads(); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java index 486e0c7cddb6..928814524c67 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,6 +94,8 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport private boolean allowCoreThreadTimeOut = false; + private boolean prestartAllCoreThreads = false; + @Nullable private TaskDecorator taskDecorator; @@ -197,6 +199,16 @@ public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; } + /** + * Specify whether to start all core threads, causing them to idly wait for work. + *

Default is "false". + * @since 5.3.14 + * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads + */ + public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) { + this.prestartAllCoreThreads = prestartAllCoreThreads; + } + /** * Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable} * about to be executed. @@ -256,6 +268,9 @@ public void execute(Runnable command) { if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } + if (this.prestartAllCoreThreads) { + executor.prestartAllCoreThreads(); + } this.threadPoolExecutor = executor; return executor; diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index c5731e3139f6..d350341a8c46 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,11 +25,11 @@ import org.junit.jupiter.api.Test; -import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -37,6 +37,8 @@ import static org.mockito.Mockito.verify; /** + * Tests for {@link ThreadPoolExecutorFactoryBean}. + * * @author Juergen Hoeller */ class ThreadPoolExecutorFactoryBeanTests { @@ -53,19 +55,25 @@ void defaultExecutor() throws Exception { } @Test - public void executorWithPreStartedThreads() throws Exception { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithPreStartedThreads.class); - ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); - - verify(executor).prestartAllCoreThreads(); + void executorWithDefaultSettingsDoesNotPrestartAllCoreThreads() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, TestThreadPoolExecutorFactoryBean::new); + context.refresh(); + ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class); + verify(threadPoolExecutor, never()).prestartAllCoreThreads(); } @Test - public void executorWithNoPreStartedThreads() throws Exception { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfigWithNoPreStartedThreads.class); - ThreadPoolExecutor executor = context.getBean("childExecutor", ThreadPoolExecutor.class); - - verify(executor, never()).prestartAllCoreThreads(); + void executorWithPrestartAllCoreThreads() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, () -> { + TestThreadPoolExecutorFactoryBean factoryBean = new TestThreadPoolExecutorFactoryBean(); + factoryBean.setPrestartAllCoreThreads(true); + return factoryBean; + }); + context.refresh(); + ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class); + verify(threadPoolExecutor).prestartAllCoreThreads(); } @Configuration @@ -78,37 +86,8 @@ ThreadPoolExecutorFactoryBean executor() { } - @Configuration - public static class ExecutorConfigWithPreStartedThreads { - - @Bean - public ThreadPoolExecutorFactoryBean executorChildFactory() { - ThreadPoolExecutorFactoryBeanMockingChild threadPoolExecutorFactoryBeanMockingChild = new ThreadPoolExecutorFactoryBeanMockingChild(); - threadPoolExecutorFactoryBeanMockingChild.setPrestartAllCoreThreads(true); - return threadPoolExecutorFactoryBeanMockingChild; - } - - @Bean - public ExecutorService childExecutor() { - return executorChildFactory().getObject(); - } - } + private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean { - @Configuration - public static class ExecutorConfigWithNoPreStartedThreads { - - @Bean - public ThreadPoolExecutorFactoryBean executorChildFactory() { - return new ThreadPoolExecutorFactoryBeanMockingChild(); - } - - @Bean - public ExecutorService childExecutor() { - return executorChildFactory().getObject(); - } - } - - private static class ThreadPoolExecutorFactoryBeanMockingChild extends ThreadPoolExecutorFactoryBean { @Override protected ThreadPoolExecutor createExecutor( int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue queue, @@ -118,5 +97,4 @@ protected ThreadPoolExecutor createExecutor( } } - } From 830cc3450fac3332be54865c53f448091f72748b Mon Sep 17 00:00:00 2001 From: Marco Krikke Date: Fri, 21 Nov 2014 17:32:42 +0100 Subject: [PATCH 19/78] Improved DataBinder Javadoc for xxx*yyy pattern matching The default documentation does not mention xxx*yyy pattern matching, which is, however, supported by PatternMatchUtils. Such a pattern can be useful for matching nested properties in all elements of a collection, e.g. property[*].nestedProperty. See gh-699 --- .../validation/DataBinder.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 1818b94b48f2..9ae262ee070e 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -418,10 +418,11 @@ public boolean isIgnoreInvalidFields() { } /** - * Register fields that should be allowed for binding. Default is all - * fields. Restrict this for example to avoid unwanted modifications - * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching + * Register fields that should be allowed for binding. Default is all fields. + * Restrict this for example to avoid unwanted modifications by malicious users + * when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary + * number of pattern parts), as well as direct equality. More sophisticated matching * can be implemented by overriding the {@code isAllowed} method. *

Alternatively, specify a list of disallowed fields. * @param allowedFields array of field names @@ -445,7 +446,8 @@ public String[] getAllowedFields() { * Register fields that should not be allowed for binding. Default is none. * Mark fields as disallowed for example to avoid unwanted modifications * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary + * number of pattern parts), as well as direct equality. More sophisticated matching * can be implemented by overriding the {@code isAllowed} method. *

Alternatively, specify a list of allowed fields. * @param disallowedFields array of field names @@ -772,10 +774,11 @@ protected void checkAllowedFields(MutablePropertyValues mpvs) { /** * Return if the given field is allowed for binding. * Invoked for each passed-in property value. - *

The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, - * as well as direct equality, in the specified lists of allowed fields and - * disallowed fields. A field matching a disallowed pattern will not be accepted - * even if it also happens to match a pattern in the allowed list. + *

The default implementation checks for "xxx*", "*xxx", "*xxx*" and "xxx*yyy" + * matches (with an arbitrary number of pattern parts), as well as direct equality, + * in the specified lists of allowed fields and disallowed fields. A field matching + * a disallowed pattern will not be accepted even if it also happens to match a + * pattern in the allowed list. *

Can be overridden in subclasses. * @param field the field to check * @return if the field is allowed From 3f7dec0b0dccdbc84897c4c10e6c35ff152d63d9 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 23 Nov 2021 10:52:05 +0000 Subject: [PATCH 20/78] Polishing contribution Closes gh-699 --- .../validation/DataBinder.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 9ae262ee070e..612dfc5622a2 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -419,11 +419,12 @@ public boolean isIgnoreInvalidFields() { /** * Register fields that should be allowed for binding. Default is all fields. - * Restrict this for example to avoid unwanted modifications by malicious users - * when binding HTTP request parameters. - *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary - * number of pattern parts), as well as direct equality. More sophisticated matching - * can be implemented by overriding the {@code isAllowed} method. + * Restrict this for example to avoid unwanted modifications by malicious + * users when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an + * arbitrary number of pattern parts), as well as direct equality. More + * sophisticated matching can be implemented by overriding the + * {@code isAllowed} method. *

Alternatively, specify a list of disallowed fields. * @param allowedFields array of field names * @see #setDisallowedFields @@ -443,12 +444,13 @@ public String[] getAllowedFields() { } /** - * Register fields that should not be allowed for binding. Default is none. - * Mark fields as disallowed for example to avoid unwanted modifications - * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary - * number of pattern parts), as well as direct equality. More sophisticated matching - * can be implemented by overriding the {@code isAllowed} method. + * Register fields that should not be allowed for binding. Default + * is none. Mark fields as disallowed for example to avoid unwanted + * modifications by malicious users when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an + * arbitrary number of pattern parts), as well as direct equality. + * More sophisticated matching can be implemented by overriding the + * {@code isAllowed} method. *

Alternatively, specify a list of allowed fields. * @param disallowedFields array of field names * @see #setAllowedFields From 913cc079af07fbb58ea09e73d3fb538714b6a1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?/usr/local/=CE=95=CE=A8=CE=97=CE=95=CE=9B=CE=A9=CE=9D?= Date: Tue, 3 Nov 2015 12:30:34 +0100 Subject: [PATCH 21/78] TagWriter can write empty attribute See gh-910 --- .../web/servlet/tags/form/TagWriter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java index 87cedb540223..29b62c2b936a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java @@ -97,6 +97,19 @@ public void writeAttribute(String attributeName, String attributeValue) throws J this.writer.append(" ").append(attributeName).append("=\"") .append(attributeValue).append("\""); } + + /** + * Write an empty HTML attribute with the specified name. + *

Be sure to write all attributes before writing + * any inner text or nested tags. + * @throws IllegalStateException if the opening tag is closed + */ + public void writeAttribute(String attributeName) throws JspException { + if (currentState().isBlockTag()) { + throw new IllegalStateException("Cannot write attributes after opening tag is closed."); + } + this.writer.append(" ").append(attributeName); + } /** * Write an HTML attribute if the supplied value is not {@code null} From 0d7c5626936d07446f4ba3f10df2fcbf8d68c551 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 24 Nov 2021 12:42:04 +0000 Subject: [PATCH 22/78] Polishing contribution Closes gh-910 --- .../web/servlet/tags/form/TagWriter.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java index 29b62c2b936a..a8167bcbf34a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,12 +97,11 @@ public void writeAttribute(String attributeName, String attributeValue) throws J this.writer.append(" ").append(attributeName).append("=\"") .append(attributeValue).append("\""); } - + /** - * Write an empty HTML attribute with the specified name. - *

Be sure to write all attributes before writing - * any inner text or nested tags. - * @throws IllegalStateException if the opening tag is closed + * Variant of {@link #writeAttribute(String, String)} for writing empty HTML + * attributes without a value such as {@code required}. + * @since 5.3.14 */ public void writeAttribute(String attributeName) throws JspException { if (currentState().isBlockTag()) { From ce0aed216b5cc04fbf52ba7a9007c185593e5625 Mon Sep 17 00:00:00 2001 From: Andreas Grub Date: Wed, 24 Nov 2021 07:47:21 +0100 Subject: [PATCH 23/78] Add getter for RequestMappingInfo.BuilderConfiguration This improves support for programmatic registration of mappings to use the same config as that of the RequestMappingHandlerMapping. See gh-27723 --- .../annotation/RequestMappingHandlerMapping.java | 11 +++++++++++ .../annotation/RequestMappingHandlerMappingTests.java | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 1da7ca1ed8b3..6bebaa18df0b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -246,6 +246,17 @@ public List getFileExtensions() { return this.config.getFileExtensions(); } + /** + * Get the configuration to build {@link RequestMappingInfo} + * instances. This is useful for programmatic registration of + * additional mappings following the same configuration as {@link + * #createRequestMappingInfo(RequestMapping, RequestCondition)}. + * + * @return builder configuration to be supplied into {@link RequestMappingInfo.Builder#options}. + */ + public RequestMappingInfo.BuilderConfiguration getRequestMappingInfoBuilderConfiguration() { + return this.config; + } /** * {@inheritDoc} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index f60e69af0e3b..2e6149bc9695 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -79,6 +79,17 @@ static Stream pathPatternsArguments() { return Stream.of(Arguments.of(mapping1, wac1), Arguments.of(mapping2, wac2)); } + @Test + void getRequestMappingInfoBuilderConfiguration() { + RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); + handlerMapping.setApplicationContext(new StaticWebApplicationContext()); + + RequestMappingInfo.BuilderConfiguration beforeAfterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); + assertThat(beforeAfterPropertiesSet).isNotNull(); + handlerMapping.afterPropertiesSet(); + RequestMappingInfo.BuilderConfiguration afterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); + assertThat(afterPropertiesSet).isNotNull().isNotSameAs(beforeAfterPropertiesSet); + } @Test @SuppressWarnings("deprecation") From 829bed03af2f92b37eb45732e5f2b5113f433bdf Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 24 Nov 2021 13:03:12 +0000 Subject: [PATCH 24/78] Polishing contribution Closes gh-27723 --- .../RequestMappingHandlerMapping.java | 30 ++++++++++++++----- .../RequestMappingHandlerMappingTests.java | 24 ++++++++------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 6bebaa18df0b..c3b264158891 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -187,7 +187,6 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) { @Override @SuppressWarnings("deprecation") public void afterPropertiesSet() { - this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); @@ -247,17 +246,19 @@ public List getFileExtensions() { } /** - * Get the configuration to build {@link RequestMappingInfo} - * instances. This is useful for programmatic registration of - * additional mappings following the same configuration as {@link - * #createRequestMappingInfo(RequestMapping, RequestCondition)}. - * - * @return builder configuration to be supplied into {@link RequestMappingInfo.Builder#options}. + * Obtain a {@link RequestMappingInfo.BuilderConfiguration} that can reflects + * the internal configuration of this {@code HandlerMapping} and can be used + * to set {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)}. + *

This is useful for programmatic registration of request mappings via + * {@link #registerHandlerMethod(Object, Method, RequestMappingInfo)}. + * @return the builder configuration that reflects the internal state + * @since 5.3.14 */ - public RequestMappingInfo.BuilderConfiguration getRequestMappingInfoBuilderConfiguration() { + public RequestMappingInfo.BuilderConfiguration getBuilderConfiguration() { return this.config; } + /** * {@inheritDoc} *

Expects a handler to have either a type-level @{@link Controller} @@ -401,6 +402,19 @@ public void registerMapping(RequestMappingInfo mapping, Object handler, Method m updateConsumesCondition(mapping, method); } + /** + * {@inheritDoc} + *

Note: To create the {@link RequestMappingInfo}, + * please use {@link #getBuilderConfiguration()} and set the options on + * {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)} + * to match how this {@code HandlerMapping} is configured. This + * is important for example to ensure use of + * {@link org.springframework.web.util.pattern.PathPattern} or + * {@link org.springframework.util.PathMatcher} based matching. + * @param handler the bean name of the handler or the handler instance + * @param method the method to register + * @param mapping the mapping conditions associated with the handler method + */ @Override protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { super.registerHandlerMethod(handler, method, mapping); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index 2e6149bc9695..9ed74e07817e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,15 +80,15 @@ static Stream pathPatternsArguments() { } @Test - void getRequestMappingInfoBuilderConfiguration() { - RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); - handlerMapping.setApplicationContext(new StaticWebApplicationContext()); + void builderConfiguration() { + RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); + mapping.setApplicationContext(new StaticWebApplicationContext()); - RequestMappingInfo.BuilderConfiguration beforeAfterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); - assertThat(beforeAfterPropertiesSet).isNotNull(); - handlerMapping.afterPropertiesSet(); - RequestMappingInfo.BuilderConfiguration afterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); - assertThat(afterPropertiesSet).isNotNull().isNotSameAs(beforeAfterPropertiesSet); + RequestMappingInfo.BuilderConfiguration config = mapping.getBuilderConfiguration(); + assertThat(config).isNotNull(); + + mapping.afterPropertiesSet(); + assertThat(mapping.getBuilderConfiguration()).isNotNull().isNotSameAs(config); } @Test @@ -99,7 +99,8 @@ void useRegisteredSuffixPatternMatch() { handlerMapping.setApplicationContext(new StaticWebApplicationContext()); Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); - org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); + org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = + new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); ContentNegotiationManager manager = new ContentNegotiationManager(strategy); handlerMapping.setContentNegotiationManager(manager); @@ -115,7 +116,8 @@ void useRegisteredSuffixPatternMatch() { @SuppressWarnings("deprecation") void useRegisteredSuffixPatternMatchInitialization() { Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); - org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); + org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = + new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); ContentNegotiationManager manager = new ContentNegotiationManager(strategy); final Set extensions = new HashSet<>(); From 341f4882ed9026661d8c2818dc90c9c84b1e69e1 Mon Sep 17 00:00:00 2001 From: Mateusz Date: Wed, 28 Nov 2018 12:21:21 +0100 Subject: [PATCH 25/78] Clarify behaviour of AnnotationBeanNameGenerator with acronyms Name transformation is delegated to Introspector#decapitalize, which states "but in the (unusual) special case when there is more than one character and both the first and second characters are upper case, we leave it alone.". This commit clarifies this behavior. See gh-2030 --- .../context/annotation/AnnotationBeanNameGenerator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java index 4beee8680642..bb13d3944a10 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java @@ -47,10 +47,11 @@ * *

If the annotation's value doesn't indicate a bean name, an appropriate * name will be built based on the short name of the class (with the first - * letter lower-cased). For example: + * letter lower-cased). If two first letters of class name are uppercase, bean name will be unchanged. For example: * *

com.xyz.FooServiceImpl -> fooServiceImpl
- * + *
com.xyz.URLFooServiceImpl -> URLFooServiceImpl
+ * * @author Juergen Hoeller * @author Mark Fisher * @since 2.5 From a1b2695c3a24017c258515faf84041420dd9f6d6 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 24 Nov 2021 16:00:11 +0100 Subject: [PATCH 26/78] Polish "Clarify behaviour of AnnotationBeanNameGenerator with acronyms" See gh-2030 --- .../context/annotation/AnnotationBeanNameGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java index bb13d3944a10..54ac7379e751 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ * *

If the annotation's value doesn't indicate a bean name, an appropriate * name will be built based on the short name of the class (with the first - * letter lower-cased). If two first letters of class name are uppercase, bean name will be unchanged. For example: + * letter lower-cased), unless the two first letters are uppercase. For example: * *

com.xyz.FooServiceImpl -> fooServiceImpl
*
com.xyz.URLFooServiceImpl -> URLFooServiceImpl
From d019c1f82b107d13dc0ab8d7d78f85274a9d6619 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 24 Nov 2021 21:34:23 +0100 Subject: [PATCH 27/78] Polish See gh-2030 --- .../context/annotation/AnnotationBeanNameGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java index 54ac7379e751..688087c8e770 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java @@ -51,7 +51,7 @@ * *
com.xyz.FooServiceImpl -> fooServiceImpl
*
com.xyz.URLFooServiceImpl -> URLFooServiceImpl
- * + * * @author Juergen Hoeller * @author Mark Fisher * @since 2.5 From e209a460ca35b90daba6a923f1bdabc4bfdbaaa8 Mon Sep 17 00:00:00 2001 From: diguage Date: Sat, 12 Jan 2019 22:32:46 +0800 Subject: [PATCH 28/78] Fix reference to CountingBeforeAdvice See gh-22246 --- .../aop/config/AopNamespaceHandlerEventTests-context.xml | 4 ++-- .../AopNamespaceHandlerEventTests-directPointcutEvents.xml | 2 +- .../AopNamespaceHandlerEventTests-pointcutRefEvents.xml | 2 +- ...NamespaceHandlerPointcutErrorTests-pointcutDuplication.xml | 2 +- .../AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml index 64f222cdd372..4a290477bb9d 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml @@ -16,9 +16,9 @@ - + - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml index 852f7479377e..c06d3188a98e 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml @@ -9,6 +9,6 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml index 8300b280512d..c1f5180aaceb 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml @@ -10,6 +10,6 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml index 33693b7c1c40..6cb11d7e966b 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml @@ -12,7 +12,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml index 3d2037805e41..b18d0d435e83 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml @@ -12,7 +12,7 @@ - + From 27257fb82e82861b01a4108ac46124c8adb32029 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Sun, 28 Nov 2021 10:52:16 +0100 Subject: [PATCH 29/78] Polish "Fix reference to CountingBeforeAdvice" See gh-22246 --- .../aop/config/AopNamespaceHandlerEventTests-context.xml | 4 ++-- .../AopNamespaceHandlerEventTests-directPointcutEvents.xml | 2 +- .../AopNamespaceHandlerEventTests-pointcutRefEvents.xml | 2 +- ...NamespaceHandlerPointcutErrorTests-pointcutDuplication.xml | 2 +- .../AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml index 4a290477bb9d..6acd17a85671 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml @@ -16,9 +16,9 @@ - + - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml index c06d3188a98e..2f2024ada6cd 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-directPointcutEvents.xml @@ -9,6 +9,6 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml index c1f5180aaceb..0b12eb945e5d 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-pointcutRefEvents.xml @@ -10,6 +10,6 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml index 6cb11d7e966b..7c8f47124e6b 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml @@ -12,7 +12,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml index b18d0d435e83..847f864eb114 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml @@ -12,7 +12,7 @@ - + From 445c0def0ce741e70d716da74862e03a4524ab3f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Nov 2021 17:13:57 +0000 Subject: [PATCH 30/78] Update docs on heartbeats with simple broker Closes gh-27746 --- src/docs/asciidoc/web/websocket.adoc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/docs/asciidoc/web/websocket.adoc b/src/docs/asciidoc/web/websocket.adoc index e4ee1e49c055..2a8042403d5c 100644 --- a/src/docs/asciidoc/web/websocket.adoc +++ b/src/docs/asciidoc/web/websocket.adoc @@ -1439,8 +1439,11 @@ See <>. If configured with a task scheduler, the simple broker supports https://stomp.github.io/stomp-specification-1.2.html#Heart-beating[STOMP heartbeats]. -For that, you can declare your own scheduler or use the one that is automatically -declared and used internally. The following example shows how to declare your own scheduler: +To configure a scheduler, you can declare your own `TaskScheduler` bean and set it through +the `MessageBrokerRegistry`. Alternatively, you can use the one that is automatically +declared in the built-in WebSocket configuration, however, you'll' need `@Lazy` to avoid +a cycle between the built-in WebSocket configuration and your +`WebSocketMessageBrokerConfigurer`. For example: [source,java,indent=0,subs="verbatim,quotes"] ---- @@ -1451,13 +1454,12 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private TaskScheduler messageBrokerTaskScheduler; @Autowired - public void setMessageBrokerTaskScheduler(TaskScheduler taskScheduler) { + public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) { this.messageBrokerTaskScheduler = taskScheduler; } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/queue/", "/topic/") .setHeartbeatValue(new long[] {10000, 20000}) .setTaskScheduler(this.messageBrokerTaskScheduler); From 0d478ca8dd55d080e656831f9abc7fa794d7acfb Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Nov 2021 17:37:37 +0000 Subject: [PATCH 31/78] Add link to WebSocket scope from the Spring Core section Closes gh-25172 --- src/docs/asciidoc/core/core-beans.adoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/docs/asciidoc/core/core-beans.adoc b/src/docs/asciidoc/core/core-beans.adoc index 22cdf42ce530..72138867377e 100644 --- a/src/docs/asciidoc/core/core-beans.adoc +++ b/src/docs/asciidoc/core/core-beans.adoc @@ -2969,6 +2969,8 @@ When using annotation-driven components or Java configuration, you can use the ---- + + [[beans-factory-scopes-application]] ==== Application Scope @@ -3012,6 +3014,17 @@ following example shows how to do so: + +[[beans-factory-scopes-websocket]] +==== WebSocket Scope + +WebSocket scope is associated with the lifecycle of a WebSocket session and applies to +STOMP over WebSocket applications, see +<> for more details. + + + + [[beans-factory-scopes-other-injection]] ==== Scoped Beans as Dependencies From 40d2058b9756189f6208d5389056032f6088fd28 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 30 Nov 2021 12:22:31 +0100 Subject: [PATCH 32/78] Upgrade to JUnit 5.8.2 Closes gh-27744 --- build.gradle | 6 +++--- spring-test/spring-test.gradle | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 6021fa574ddf..90251e0e8fac 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ configure(allprojects) { project -> mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.31" mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2" mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2" - mavenBom "org.junit:junit-bom:5.8.1" + mavenBom "org.junit:junit-bom:5.8.2" } dependencies { dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') { @@ -383,9 +383,9 @@ configure([rootProject] + javaProjects) { project -> "https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/", "https://projectreactor.io/docs/test/release/api/", "https://junit.org/junit4/javadoc/4.13.2/", - // Disabling linking to JUnit 5.8.1, since the `package-list` file no longer exists due to + // Disabling linking to JUnit 5.8.2, since the `package-list` file no longer exists due to // https://github.com/junit-team/junit5/commit/67ad4e545518b0ce2b0e7c96df31a669866d5003. - // "https://junit.org/junit5/docs/5.8.1/api/", + // "https://junit.org/junit5/docs/5.8.2/api/", "https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/", "https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/", "https://r2dbc.io/spec/0.8.5.RELEASE/api/" diff --git a/spring-test/spring-test.gradle b/spring-test/spring-test.gradle index 0bc316636a7a..58ca0680ad44 100644 --- a/spring-test/spring-test.gradle +++ b/spring-test/spring-test.gradle @@ -60,9 +60,6 @@ dependencies { testImplementation("org.hibernate:hibernate-core") testImplementation("org.hibernate:hibernate-validator") testImplementation("javax.validation:validation-api") - testImplementation("org.junit.platform:junit-platform-runner") { - exclude group: "junit", module: "junit" - } testImplementation("org.junit.platform:junit-platform-testkit") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("com.thoughtworks.xstream:xstream") From 2a5713f389e305300d7659887d38a89aa4e7e91e Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 29 Nov 2021 16:48:36 +0100 Subject: [PATCH 33/78] Always copy ServerResponse headers Prior to this commit, ServerResponse headers would only be written if there were no existing headers with the same name, thus making it impossible to overwrite existing headers. With the changes in this commit, headers are always written. Closes gh-27741 --- .../server/DefaultServerResponseBuilder.java | 4 +--- .../DefaultServerResponseBuilderTests.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index da71fbe565a1..2d406d39d46c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -362,9 +362,7 @@ private void writeStatusAndHeaders(ServerHttpResponse response) { private static void copy(MultiValueMap src, MultiValueMap dst) { if (!src.isEmpty()) { - src.entrySet().stream() - .filter(entry -> !dst.containsKey(entry.getKey())) - .forEach(entry -> dst.put(entry.getKey(), entry.getValue())); + dst.putAll(src); } } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java index 38b295463ca4..c3780d2c443d 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java @@ -320,6 +320,23 @@ public void copyCookies() { assertThat(serverResponse.block().cookies().isEmpty()).isFalse(); } + @Test + public void overwriteHeaders() { + ServerResponse serverResponse = + ServerResponse.ok().headers(headers -> headers.set("Foo", "Bar")).build().block(); + assertThat(serverResponse).isNotNull(); + + MockServerWebExchange mockExchange = MockServerWebExchange + .builder(MockServerHttpRequest.get("https://example.org")) + .build(); + MockServerHttpResponse response = mockExchange.getResponse(); + response.getHeaders().set("Foo", "Baz"); + + serverResponse.writeTo(mockExchange, EMPTY_CONTEXT).block(); + + assertThat(response.getHeaders().getFirst("Foo")).isEqualTo("Bar"); + } + @Test public void build() { From 8b89128c7bec7c1e902ba74d2b6c268f3011194a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 2 Dec 2021 11:53:43 +0000 Subject: [PATCH 34/78] Update createException example for WebClient Closes gh-27645 --- .../web/reactive/function/client/WebClient.java | 4 ++-- src/docs/asciidoc/web/webflux-webclient.adoc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 7e8219c1678a..9097000ccc60 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -530,7 +530,7 @@ interface RequestHeadersSpec> { * return response.bodyToMono(ErrorContainer.class); * } * else { - * return Mono.error(response.createException()); + * return response.createException(); * } * }); * @@ -562,7 +562,7 @@ interface RequestHeadersSpec> { * return response.bodyToMono(ErrorContainer.class).flux(); * } * else { - * return Flux.error(response.createException()); + * return response.createException().flux(); * } * }); * diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index 3099ecb02d26..3c2d18c56129 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -559,7 +559,7 @@ depending on the response status: } else { // Turn to error - return response.createException().flatMap(Mono::error); + return response.createException(); } }); ---- From 6582787678817ac3c2d88daf2b0bd83505abfdc4 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 2 Dec 2021 12:28:36 +0000 Subject: [PATCH 35/78] Apply resources after application HttpClient mapper Closes gh-27749 --- .../reactive/ReactorClientHttpConnector.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpConnector.java b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpConnector.java index 8112ecf6442e..0fdfaab69155 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpConnector.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpConnector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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,18 +73,21 @@ public ReactorClientHttpConnector() { * @since 5.1 */ public ReactorClientHttpConnector(ReactorResourceFactory factory, Function mapper) { - this.httpClient = defaultInitializer.andThen(mapper).apply(initHttpClient(factory)); + ConnectionProvider provider = factory.getConnectionProvider(); + Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?"); + this.httpClient = defaultInitializer.andThen(mapper).andThen(applyLoopResources(factory)) + .apply(HttpClient.create(provider)); } - @SuppressWarnings("deprecation") - private static HttpClient initHttpClient(ReactorResourceFactory resourceFactory) { - ConnectionProvider provider = resourceFactory.getConnectionProvider(); - LoopResources resources = resourceFactory.getLoopResources(); - Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?"); - Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?"); - return HttpClient.create(provider).tcpConfiguration(tcpClient -> tcpClient.runOn(resources)); + private static Function applyLoopResources(ReactorResourceFactory factory) { + return httpClient -> { + LoopResources resources = factory.getLoopResources(); + Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?"); + return httpClient.runOn(resources); + }; } + /** * Constructor with a pre-configured {@code HttpClient} instance. * @param httpClient the client to use From b77b45434f5c8fd73be39ffe5b1e52ebc641411b Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 2 Dec 2021 15:11:15 +0100 Subject: [PATCH 36/78] Change createException to createError See gh-27645 --- .../springframework/web/reactive/function/client/WebClient.java | 2 +- src/docs/asciidoc/web/webflux-webclient.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 9097000ccc60..530324d42b64 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -530,7 +530,7 @@ interface RequestHeadersSpec> { * return response.bodyToMono(ErrorContainer.class); * } * else { - * return response.createException(); + * return response.createError(); * } * }); * diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index 3c2d18c56129..d07ff5b3514b 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -559,7 +559,7 @@ depending on the response status: } else { // Turn to error - return response.createException(); + return response.createError(); } }); ---- From be6eeafe78194f9ca7cbf871310674e5188466e3 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 2 Dec 2021 15:59:51 +0100 Subject: [PATCH 37/78] Revert change createException to createError createError is not available in 5.3. See gh-27645 --- .../springframework/web/reactive/function/client/WebClient.java | 2 +- src/docs/asciidoc/web/webflux-webclient.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 530324d42b64..9097000ccc60 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -530,7 +530,7 @@ interface RequestHeadersSpec> { * return response.bodyToMono(ErrorContainer.class); * } * else { - * return response.createError(); + * return response.createException(); * } * }); * diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index d07ff5b3514b..3c2d18c56129 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -559,7 +559,7 @@ depending on the response status: } else { // Turn to error - return response.createError(); + return response.createException(); } }); ---- From 5649a6f8ef335735480616cbeb4dd87efb371adf Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 3 Dec 2021 09:21:21 +0000 Subject: [PATCH 38/78] Update exchangeToMono Javadoc This time showing a more representative example. See gh-27645 --- .../web/reactive/function/client/WebClient.java | 14 ++++---------- src/docs/asciidoc/web/webflux-webclient.adoc | 11 ++--------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index 9097000ccc60..1df8c06ffbaa 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -519,18 +519,15 @@ interface RequestHeadersSpec> { * scenarios, for example to decode the response differently depending * on the response status: *

-		 * Mono<Object> entityMono = client.get()
+		 * Mono<Person> entityMono = client.get()
 		 *     .uri("/persons/1")
 		 *     .accept(MediaType.APPLICATION_JSON)
 		 *     .exchangeToMono(response -> {
 		 *         if (response.statusCode().equals(HttpStatus.OK)) {
 		 *             return response.bodyToMono(Person.class);
 		 *         }
-		 *         else if (response.statusCode().is4xxClientError()) {
-		 *             return response.bodyToMono(ErrorContainer.class);
-		 *         }
 		 *         else {
-		 *             return response.createException();
+		 *             return response.createException().flatMap(Mono::error);
 		 *         }
 		 *     });
 		 * 
@@ -551,18 +548,15 @@ interface RequestHeadersSpec> { * scenarios, for example to decode the response differently depending * on the response status: *

-		 * Mono<Object> entityMono = client.get()
+		 * Flux<Person> entityMono = client.get()
 		 *     .uri("/persons")
 		 *     .accept(MediaType.APPLICATION_JSON)
 		 *     .exchangeToFlux(response -> {
 		 *         if (response.statusCode().equals(HttpStatus.OK)) {
 		 *             return response.bodyToFlux(Person.class);
 		 *         }
-		 *         else if (response.statusCode().is4xxClientError()) {
-		 *             return response.bodyToMono(ErrorContainer.class).flux();
-		 *         }
 		 *         else {
-		 *             return response.createException().flux();
+		 *             return response.createException().flatMapMany(Mono::error);
 		 *         }
 		 *     });
 		 * 
diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index 3c2d18c56129..e93d1214046e 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -546,20 +546,16 @@ depending on the response status: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- - Mono entityMono = client.get() + Mono entityMono = client.get() .uri("/persons/1") .accept(MediaType.APPLICATION_JSON) .exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.OK)) { return response.bodyToMono(Person.class); } - else if (response.statusCode().is4xxClientError()) { - // Suppress error status code - return response.bodyToMono(ErrorContainer.class); - } else { // Turn to error - return response.createException(); + return response.createException().flatMap(Mono::error); } }); ---- @@ -573,9 +569,6 @@ val entity = client.get() if (response.statusCode() == HttpStatus.OK) { return response.awaitBody() } - else if (response.statusCode().is4xxClientError) { - return response.awaitBody() - } else { throw response.createExceptionAndAwait() } From 69df27a99ff191cbdfc9ecf7c19f3ac3e87888af Mon Sep 17 00:00:00 2001 From: Ingmar van Dijk Date: Mon, 6 Jul 2020 09:13:07 +0200 Subject: [PATCH 39/78] Throw 404 ResponseStatusException when no routes found This commit makes it possible to customize 404 responses generated by RouterFunctionWebHandler, by throwing an ResponseStatusException instead of returning a standard 404 response. See gh-25358 --- .../function/server/RouterFunctions.java | 12 +++------- .../function/server/RouterFunctionsTests.java | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java index 30157153d4ae..8b241f70cd9b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java @@ -32,10 +32,12 @@ import reactor.core.publisher.Mono; import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.util.Assert; import org.springframework.web.reactive.result.view.ViewResolver; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebHandler; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; @@ -1231,9 +1233,6 @@ public List viewResolvers() { private static class RouterFunctionWebHandler implements WebHandler { - private static final HandlerFunction NOT_FOUND_HANDLER = - request -> ServerResponse.notFound().build(); - private final HandlerStrategies strategies; private final RouterFunction routerFunction; @@ -1249,7 +1248,7 @@ public Mono handle(ServerWebExchange exchange) { ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders()); addAttributes(exchange, request); return this.routerFunction.route(request) - .defaultIfEmpty(notFound()) + .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))) .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))) .flatMap(response -> wrapException(() -> response.writeTo(exchange, new HandlerStrategiesResponseContext(this.strategies)))); @@ -1261,11 +1260,6 @@ private void addAttributes(ServerWebExchange exchange, ServerRequest request) { attributes.put(REQUEST_ATTRIBUTE, request); } - @SuppressWarnings("unchecked") - private static HandlerFunction notFound() { - return (HandlerFunction) NOT_FOUND_HANDLER; - } - private static Mono wrapException(Supplier> supplier) { try { return supplier.get(); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java index 46b29bdf6cd4..ad6bfbdca9f7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java @@ -16,14 +16,17 @@ package org.springframework.web.reactive.function.server; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; @@ -191,6 +194,27 @@ public void toHttpHandlerHandlerResponseStatusException() { assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } + @Test + public void toHttpHandlerRouteNotFoundReturnsResponseStatusException() { + HandlerFunction handlerFunction = request -> ServerResponse.accepted().build(); + RouterFunction routerFunction = + RouterFunctions.route(RequestPredicates.GET("/path"), handlerFunction); + + HandlerStrategies handlerStrategies = HandlerStrategies.empty().exceptionHandler((exchange, ex) -> { + exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND); + DataBuffer buffer = exchange.getResponse().bufferFactory().wrap("Custom response".getBytes(StandardCharsets.UTF_8)); + return exchange.getResponse().writeWith(Flux.just(buffer)); + }).build(); + HttpHandler result = RouterFunctions.toHttpHandler(routerFunction, handlerStrategies); + assertThat(result).isNotNull(); + + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("https://localhost").build(); + MockServerHttpResponse httpResponse = new MockServerHttpResponse(); + result.handle(httpRequest, httpResponse).block(); + assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(httpResponse.getBodyAsString().block()).isEqualTo("Custom response"); + } + @Test public void toHttpHandlerHandlerReturnResponseStatusExceptionInResponseWriteTo() { HandlerFunction handlerFunction = From b84fe99d07f9cb1261950121e8da7133bc645302 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 3 Dec 2021 12:00:23 +0100 Subject: [PATCH 40/78] Polish "Throw 404 ResponseStatusException when no routes found" See gh-25358 --- .../web/reactive/function/server/RouterFunctions.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java index 8b241f70cd9b..739fc17a1c33 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java @@ -1248,7 +1248,7 @@ public Mono handle(ServerWebExchange exchange) { ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders()); addAttributes(exchange, request); return this.routerFunction.route(request) - .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))) + .switchIfEmpty(createNotFoundError()) .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))) .flatMap(response -> wrapException(() -> response.writeTo(exchange, new HandlerStrategiesResponseContext(this.strategies)))); @@ -1260,6 +1260,11 @@ private void addAttributes(ServerWebExchange exchange, ServerRequest request) { attributes.put(REQUEST_ATTRIBUTE, request); } + private Mono createNotFoundError() { + return Mono.defer(() -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, + "No matching router function"))); + } + private static Mono wrapException(Supplier> supplier) { try { return supplier.get(); From 70974e006e0263d8c7f8a35c5ee6b743523cafe7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 3 Dec 2021 22:32:00 +0100 Subject: [PATCH 41/78] Upgrade to Tomcat 9.0.55, Undertow 2.2.13, RxJava 3.1.3, SmallRye Mutiny 1.2, Joda-Time 2.10.13, JRuby 9.2.20.1, HtmlUnit 2.55, Checkstyle 9.2 --- build.gradle | 20 +++++++++---------- .../upgrade/TomcatRequestUpgradeStrategy.java | 1 + .../TomcatRequestUpgradeStrategy.java | 3 ++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 90251e0e8fac..ff029ad98709 100644 --- a/build.gradle +++ b/build.gradle @@ -67,8 +67,8 @@ configure(allprojects) { project -> dependency "io.reactivex:rxjava:1.3.8" dependency "io.reactivex:rxjava-reactive-streams:1.2.1" dependency "io.reactivex.rxjava2:rxjava:2.2.21" - dependency "io.reactivex.rxjava3:rxjava:3.1.1" - dependency "io.smallrye.reactive:mutiny:1.1.1" + dependency "io.reactivex.rxjava3:rxjava:3.1.3" + dependency "io.smallrye.reactive:mutiny:1.2.0" dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE" dependency "com.caucho:hessian:4.0.63" @@ -128,18 +128,18 @@ configure(allprojects) { project -> dependency "org.webjars:webjars-locator-core:0.48" dependency "org.webjars:underscorejs:1.8.3" - dependencySet(group: 'org.apache.tomcat', version: '9.0.54') { + dependencySet(group: 'org.apache.tomcat', version: '9.0.55') { entry 'tomcat-util' entry('tomcat-websocket') { exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" exclude group: "org.apache.tomcat", name: "tomcat-servlet-api" } } - dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.54') { + dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.55') { entry 'tomcat-embed-core' entry 'tomcat-embed-websocket' } - dependencySet(group: 'io.undertow', version: '2.2.12.Final') { + dependencySet(group: 'io.undertow', version: '2.2.13.Final') { entry 'undertow-core' entry('undertow-websockets-jsr') { exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" @@ -164,7 +164,7 @@ configure(allprojects) { project -> dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1' dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" - dependency "org.jruby:jruby:9.2.19.0" + dependency "org.jruby:jruby:9.2.20.1" dependency "org.python:jython-standalone:2.7.1" dependency "org.mozilla:rhino:1.7.11" @@ -206,10 +206,10 @@ configure(allprojects) { project -> } dependency "io.mockk:mockk:1.12.0" - dependency("net.sourceforge.htmlunit:htmlunit:2.54.0") { + dependency("net.sourceforge.htmlunit:htmlunit:2.55.0") { exclude group: "commons-logging", name: "commons-logging" } - dependency("org.seleniumhq.selenium:htmlunit-driver:2.54.0") { + dependency("org.seleniumhq.selenium:htmlunit-driver:2.55.0") { exclude group: "commons-logging", name: "commons-logging" } dependency("org.seleniumhq.selenium:selenium-java:3.141.59") { @@ -237,7 +237,7 @@ configure(allprojects) { project -> dependency "com.ibm.websphere:uow:6.0.2.17" dependency "com.jamonapi:jamon:2.82" - dependency "joda-time:joda-time:2.10.10" + dependency "joda-time:joda-time:2.10.13" dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.9" dependency "org.javamoney:moneta:1.3" @@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project -> } checkstyle { - toolVersion = "9.0" + toolVersion = "9.2" configDirectory.set(rootProject.file("src/checkstyle")) } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java index f918410dd5ee..1d2c641c9bea 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java @@ -123,6 +123,7 @@ public Integer getMaxBinaryMessageBufferSize() { } + @SuppressWarnings("deprecation") // for old doUpgrade variant in Tomcat 9.0.55 @Override public Mono upgrade(ServerWebExchange exchange, WebSocketHandler handler, @Nullable String subProtocol, Supplier handshakeInfoFactory){ diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java index c3c6a191609d..4f685ad372bd 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,7 @@ public String[] getSupportedVersions() { return new String[] {"13"}; } + @SuppressWarnings("deprecation") // for old doUpgrade variant in Tomcat 9.0.55 @Override public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) From 7c834d98c2037b5cca0b1de461545b7de1b2db6c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 3 Dec 2021 22:32:26 +0100 Subject: [PATCH 42/78] Upgrade to ASM master (including early support for Java 19 bytecode) Closes gh-27740 --- .../src/main/java/org/springframework/asm/ClassReader.java | 2 +- spring-core/src/main/java/org/springframework/asm/Opcodes.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/asm/ClassReader.java b/spring-core/src/main/java/org/springframework/asm/ClassReader.java index e5c2113993ff..1b14e0e02a38 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassReader.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassReader.java @@ -194,7 +194,7 @@ public ClassReader( this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V18) { + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V19) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } diff --git a/spring-core/src/main/java/org/springframework/asm/Opcodes.java b/spring-core/src/main/java/org/springframework/asm/Opcodes.java index fbd0c4db640e..dc7bbce285c2 100644 --- a/spring-core/src/main/java/org/springframework/asm/Opcodes.java +++ b/spring-core/src/main/java/org/springframework/asm/Opcodes.java @@ -283,6 +283,7 @@ public interface Opcodes { int V16 = 0 << 16 | 60; int V17 = 0 << 16 | 61; int V18 = 0 << 16 | 62; + int V19 = 0 << 16 | 63; /** * Version flag indicating that the class is using 'preview' features. From d7e0eed8d26ab0dc882d2bd2257b805f2a4a5f37 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 3 Dec 2021 22:33:50 +0100 Subject: [PATCH 43/78] Clarify getBeanProvider(ResolvableType) semantics for unresolved generics Closes gh-27727 --- .../springframework/beans/factory/BeanFactory.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index e9108e828f54..93c909e047b6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -214,6 +214,7 @@ public interface BeanFactory { /** * Return a provider for the specified bean, allowing for lazy on-demand retrieval * of instances, including availability and uniqueness options. + *

For matching a generic type, consider {@link #getBeanProvider(ResolvableType)}. * @param requiredType type the bean must match; can be an interface or superclass * @return a corresponding provider handle * @since 5.1 @@ -223,13 +224,20 @@ public interface BeanFactory { /** * Return a provider for the specified bean, allowing for lazy on-demand retrieval - * of instances, including availability and uniqueness options. - * @param requiredType type the bean must match; can be a generic type declaration. - * Note that collection types are not supported here, in contrast to reflective + * of instances, including availability and uniqueness options. This variant allows + * for specifying a generic type to match, similar to reflective injection points + * with generic type declarations in method/constructor parameters. + *

Note that collections of beans are not supported here, in contrast to reflective * injection points. For programmatically retrieving a list of beans matching a * specific type, specify the actual bean type as an argument here and subsequently * use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options. + *

Also, generics matching is strict here, as per the Java assignment rules. + * For lenient fallback matching with unchecked semantics (similar to the ´unchecked´ + * Java compiler warning), consider calling {@link #getBeanProvider(Class)} with the + * raw type as a second step if no full generic match is + * {@link ObjectProvider#getIfAvailable() available} with this variant. * @return a corresponding provider handle + * @param requiredType type the bean must match; can be a generic type declaration * @since 5.1 * @see ObjectProvider#iterator() * @see ObjectProvider#stream() From 14f24f43d7a3da9796cdbe50dbb06b60e2278644 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 3 Dec 2021 22:36:31 +0100 Subject: [PATCH 44/78] Polishing --- .../InstantiationAwareBeanPostProcessor.java | 6 ++--- ...AbstractFallbackJCacheOperationSource.java | 4 ++-- .../AbstractFallbackCacheOperationSource.java | 4 ++-- .../ThreadPoolExecutorFactoryBean.java | 4 +--- .../client/match/ContentRequestMatchers.java | 4 ++-- ...actFallbackTransactionAttributeSource.java | 4 ++-- .../AbstractListenerWriteProcessor.java | 2 +- .../web/filter/DelegatingFilterProxy.java | 22 ++++++++----------- .../web/filter/OncePerRequestFilter.java | 4 +--- .../MediaTypeNotSupportedStatusException.java | 4 ++-- .../util/ContentCachingRequestWrapper.java | 1 - .../handler/HandlerMappingIntrospector.java | 15 ++++++------- 12 files changed, 31 insertions(+), 43 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index c4e21122ed81..03e192e72e4c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author 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,9 +34,7 @@ * *

NOTE: This interface is a special purpose interface, mainly for * internal use within the framework. It is recommended to implement the plain - * {@link BeanPostProcessor} interface as far as possible, or to derive from - * {@link InstantiationAwareBeanPostProcessorAdapter} in order to be shielded - * from extensions to this interface. + * {@link BeanPostProcessor} interface as far as possible. * * @author Juergen Hoeller * @author Rod Johnson diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java index 3edf2a2c153a..8b20e4b14822 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author 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,7 +79,7 @@ public JCacheOperation getCacheOperation(Method method, @Nullable Class ta @Nullable private JCacheOperation computeCacheOperation(Method method, @Nullable Class targetClass) { - // Don't allow no-public methods as required. + // Don't allow non-public methods, as configured. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java index 8de528875fb2..d20993ae27a7 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,7 +124,7 @@ protected Object getCacheKey(Method method, @Nullable Class targetClass) { @Nullable private Collection computeCacheOperations(Method method, @Nullable Class targetClass) { - // Don't allow no-public methods as required. + // Don't allow non-public methods, as configured. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index d95fef71f22a..3a853297b397 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -26,9 +26,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; import org.springframework.lang.Nullable; /** @@ -65,7 +63,7 @@ */ @SuppressWarnings("serial") public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport - implements FactoryBean, InitializingBean, DisposableBean { + implements FactoryBean { private int corePoolSize = 1; diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java index ec883b02a41c..fed5808a1ff2 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -193,7 +193,7 @@ private RequestMatcher formData(MultiValueMap expectedMap, boole *

  • {@link Resource} - content from a file *
  • {@code byte[]} - other raw content * - *

    Note: This method uses the Apache Commons File Upload + *

    Note: This method uses the Apache Commons FileUpload * library to parse the multipart data and it must be on the test classpath. * @param expectedMap the expected multipart values * @since 5.3 diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java index 80983806eded..c19cf2c10670 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -163,7 +163,7 @@ protected Object getCacheKey(Method method, @Nullable Class targetClass) { */ @Nullable protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class targetClass) { - // Don't allow no-public methods as required. + // Don't allow non-public methods, as configured. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerWriteProcessor.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerWriteProcessor.java index 92d7b41846b5..e94e3dcc334c 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerWriteProcessor.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerWriteProcessor.java @@ -254,7 +254,7 @@ protected void dataReceived(T data) { * the next item from the upstream, write Publisher. *

    The default implementation is a no-op. * @deprecated originally introduced for Undertow to stop write notifications - * when no data is available, but deprecated as of as of 5.0.6 since constant + * when no data is available, but deprecated as of 5.0.6 since constant * switching on every requested item causes a significant slowdown. */ @Deprecated diff --git a/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java b/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java index bfbe62739dce..0dbc22408240 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java +++ b/spring-web/src/main/java/org/springframework/web/filter/DelegatingFilterProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,9 +54,9 @@ * of the {@code Filter.init} and {@code Filter.destroy} lifecycle methods * on the target bean, letting the servlet container manage the filter lifecycle. * - *

    As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally accept - * constructor parameters when using Servlet 3.0's instance-based filter registration - * methods, usually in conjunction with Spring 3.1's + *

    As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally + * accept constructor parameters when using a Servlet container's instance-based filter + * registration methods, usually in conjunction with Spring's * {@link org.springframework.web.WebApplicationInitializer} SPI. These constructors allow * for providing the delegate Filter bean directly, or providing the application context * and bean name to fetch, avoiding the need to look up the application context from the @@ -100,8 +100,7 @@ public class DelegatingFilterProxy extends GenericFilterBean { /** - * Create a new {@code DelegatingFilterProxy}. For traditional (pre-Servlet 3.0) use - * in {@code web.xml}. + * Create a new {@code DelegatingFilterProxy}. For traditional use in {@code web.xml}. * @see #setTargetBeanName(String) */ public DelegatingFilterProxy() { @@ -111,8 +110,7 @@ public DelegatingFilterProxy() { * Create a new {@code DelegatingFilterProxy} with the given {@link Filter} delegate. * Bypasses entirely the need for interacting with a Spring application context, * specifying the {@linkplain #setTargetBeanName target bean name}, etc. - *

    For use in Servlet 3.0+ environments where instance-based registration of - * filters is supported. + *

    For use with instance-based registration of filters. * @param delegate the {@code Filter} instance that this proxy will delegate to and * manage the lifecycle for (must not be {@code null}). * @see #doFilter(ServletRequest, ServletResponse, FilterChain) @@ -130,9 +128,8 @@ public DelegatingFilterProxy(Filter delegate) { * bean from the Spring {@code WebApplicationContext} found in the {@code ServletContext} * (either the 'root' application context or the context named by * {@link #setContextAttribute}). - *

    For use in Servlet 3.0+ environments where instance-based registration of - * filters is supported. - *

    The target bean must implement the standard Servlet Filter. + *

    For use with instance-based registration of filters. + *

    The target bean must implement the standard Servlet Filter interface. * @param targetBeanName name of the target filter bean to look up in the Spring * application context (must not be {@code null}). * @see #findWebApplicationContext() @@ -145,8 +142,7 @@ public DelegatingFilterProxy(String targetBeanName) { /** * Create a new {@code DelegatingFilterProxy} that will retrieve the named target * bean from the given Spring {@code WebApplicationContext}. - *

    For use in Servlet 3.0+ environments where instance-based registration of - * filters is supported. + *

    For use with instance-based registration of filters. *

    The target bean must implement the standard Servlet Filter interface. *

    The given {@code WebApplicationContext} may or may not be refreshed when passed * in. If it has not, and if the context implements {@link ConfigurableApplicationContext}, diff --git a/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java b/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java index 0f5e1a3831ad..271560f930ca 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java @@ -98,12 +98,10 @@ public final void doFilter(ServletRequest request, ServletResponse response, Fil boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) { - // Proceed without invoking this filter... filterChain.doFilter(request, response); } else if (hasAlreadyFilteredAttribute) { - if (DispatcherType.ERROR.equals(request.getDispatcherType())) { doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain); return; @@ -197,7 +195,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce * setting up thread locals or to perform final processing at the very end. *

    Note that although a filter can be mapped to handle specific dispatcher * types via {@code web.xml} or in Java through the {@code ServletContext}, - * servlet containers may enforce different defaults with regards to + * servlet containers may enforce different defaults with respect to * dispatcher types. This flag enforces the design intent of the filter. *

    The default return value is "true", which means the filter will not be * invoked during subsequent async dispatches. If "false", the filter will diff --git a/spring-web/src/main/java/org/springframework/web/server/MediaTypeNotSupportedStatusException.java b/spring-web/src/main/java/org/springframework/web/server/MediaTypeNotSupportedStatusException.java index 690e7a136b0f..f9cd1700a435 100644 --- a/spring-web/src/main/java/org/springframework/web/server/MediaTypeNotSupportedStatusException.java +++ b/spring-web/src/main/java/org/springframework/web/server/MediaTypeNotSupportedStatusException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ * @author Rossen Stoyanchev * @since 5.0 * @deprecated in favor of {@link UnsupportedMediaTypeStatusException}, - * with this class never thrown by Spring code and to be removed in 5.3 + * with this class never thrown by Spring code and to be removed in 6.0 */ @Deprecated @SuppressWarnings("serial") diff --git a/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java b/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java index faa0ccbf6431..104f762b4539 100644 --- a/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java +++ b/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java @@ -48,7 +48,6 @@ *

    Used e.g. by {@link org.springframework.web.filter.AbstractRequestLoggingFilter}. * Note: As of Spring Framework 5.0, this wrapper is built on the Servlet 3.1 API. * - * * @author Juergen Hoeller * @author Brian Clozel * @since 4.1.3 diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java index 81d38fb3b8c7..7ee392b3514a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java @@ -103,14 +103,6 @@ public HandlerMappingIntrospector(ApplicationContext context) { } - /** - * Return the configured or detected {@code HandlerMapping}s. - */ - public List getHandlerMappings() { - return (this.handlerMappings != null ? this.handlerMappings : Collections.emptyList()); - } - - @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; @@ -125,6 +117,13 @@ public void afterPropertiesSet() { } } + /** + * Return the configured or detected {@code HandlerMapping}s. + */ + public List getHandlerMappings() { + return (this.handlerMappings != null ? this.handlerMappings : Collections.emptyList()); + } + /** * Find the {@link HandlerMapping} that would handle the given request and From 9261766677727387a7c43d7382362863b6a602c8 Mon Sep 17 00:00:00 2001 From: "Katada, Junya" Date: Sun, 17 May 2020 00:07:21 +0900 Subject: [PATCH 45/78] Fix for ModelAndView.status not working with RedirectView --- .../web/servlet/DispatcherServlet.java | 1 + ...tAnnotationControllerHandlerMethodTests.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 869b4c6979c5..cd4dc70872b3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -1395,6 +1395,7 @@ protected void render(ModelAndView mv, HttpServletRequest request, HttpServletRe } try { if (mv.getStatus() != null) { + request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus()); response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 6957bdfc828e..aeaf049adb23 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1897,6 +1897,18 @@ void modelAndViewWithStatus(boolean usePathPatterns) throws Exception { assertThat(response.getForwardedUrl()).isEqualTo("view"); } + @PathPatternsParameterizedTest + void modelAndViewWithStatusForRedirect(boolean usePathPatterns) throws Exception { + initDispatcherServlet(ModelAndViewController.class, usePathPatterns); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/redirect"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getServlet().service(request, response); + + assertThat(response.getStatus()).isEqualTo(307); + assertThat(response.getRedirectedUrl()).isEqualTo("/path"); + } + @PathPatternsParameterizedTest // SPR-14796 void modelAndViewWithStatusInExceptionHandler(boolean usePathPatterns) throws Exception { initDispatcherServlet(ModelAndViewController.class, usePathPatterns); @@ -3872,6 +3884,11 @@ public ModelAndView methodWithHttpStatus(MyEntity object) { return new ModelAndView("view", HttpStatus.UNPROCESSABLE_ENTITY); } + @RequestMapping("/redirect") + public ModelAndView methodWithHttpStatusForRedirect(MyEntity object) { + return new ModelAndView("redirect:/path", HttpStatus.TEMPORARY_REDIRECT); + } + @RequestMapping("/exception") public void raiseException() throws Exception { throw new TestException(); From 99c7608ffe726fea4d6a1dce30005a8273cb305e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 8 Dec 2021 11:24:30 +0000 Subject: [PATCH 46/78] Replace both EOL and control characters --- .../core/log/LogFormatUtils.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java index 51ffd98cf0ab..9a2693e2ce6b 100644 --- a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java +++ b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java @@ -17,6 +17,7 @@ package org.springframework.core.log; import java.util.function.Function; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; @@ -35,10 +36,15 @@ */ public abstract class LogFormatUtils { + private static final Pattern NEWLINE_PATTERN = Pattern.compile("[\n\r]"); + + private static final Pattern CONTROL_CHARACTER_PATTERN = Pattern.compile("\\p{Cc}"); + + /** * Convenience variant of {@link #formatValue(Object, int, boolean)} that * limits the length of a log message to 100 characters and also replaces - * newline characters if {@code limitLength} is set to "true". + * newline and control characters if {@code limitLength} is set to "true". * @param value the value to format * @param limitLength whether to truncate the value at a length of 100 * @return the formatted value @@ -53,10 +59,13 @@ public static String formatValue(@Nullable Object value, boolean limitLength) { * compacting it into a single line when {@code replaceNewLines} is set. * @param value the value to be formatted * @param maxLength the max length, after which to truncate, or -1 for unlimited - * @param replaceNewlines whether to replace newline characters with placeholders + * @param replaceNewlinesAndControlCharacters whether to replace newline and + * control characters with placeholders * @return the formatted value */ - public static String formatValue(@Nullable Object value, int maxLength, boolean replaceNewlines) { + public static String formatValue( + @Nullable Object value, int maxLength, boolean replaceNewlinesAndControlCharacters) { + if (value == null) { return ""; } @@ -70,8 +79,9 @@ public static String formatValue(@Nullable Object value, int maxLength, boolean if (maxLength != -1) { result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result); } - if (replaceNewlines) { - result = result.replace("\n", "").replace("\r", ""); + if (replaceNewlinesAndControlCharacters) { + result = NEWLINE_PATTERN.matcher(result).replaceAll(""); + result = CONTROL_CHARACTER_PATTERN.matcher(result).replaceAll("?"); } if (value instanceof CharSequence) { result = "\"" + result + "\""; From 57a5370eec4dce7739aa467d2a496c53091bf940 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 9 Dec 2021 14:43:00 +0100 Subject: [PATCH 47/78] Mention explicitly that @EventListener should be put on public methods Closes gh-27777 --- src/docs/asciidoc/core/core-beans.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/core/core-beans.adoc b/src/docs/asciidoc/core/core-beans.adoc index 72138867377e..786615be14fb 100644 --- a/src/docs/asciidoc/core/core-beans.adoc +++ b/src/docs/asciidoc/core/core-beans.adoc @@ -10695,7 +10695,7 @@ architectures that build upon the well-known Spring programming model. [[context-functionality-events-annotation]] ==== Annotation-based Event Listeners -You can register an event listener on any method of a managed bean by using the +You can register an event listener on any `public` method of a managed bean by using the `@EventListener` annotation. The `BlockedListNotifier` can be rewritten as follows: [source,java,indent=0,subs="verbatim,quotes",role="primary"] From 98ce171b30a09cadbf37593a1448ad011f75a7b6 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 9 Dec 2021 15:18:56 +0100 Subject: [PATCH 48/78] Revert "Mention explicitly that @EventListener should be put on public methods" This reverts commit 57a5370eec4dce7739aa467d2a496c53091bf940. See gh-27777 --- src/docs/asciidoc/core/core-beans.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/asciidoc/core/core-beans.adoc b/src/docs/asciidoc/core/core-beans.adoc index 786615be14fb..72138867377e 100644 --- a/src/docs/asciidoc/core/core-beans.adoc +++ b/src/docs/asciidoc/core/core-beans.adoc @@ -10695,7 +10695,7 @@ architectures that build upon the well-known Spring programming model. [[context-functionality-events-annotation]] ==== Annotation-based Event Listeners -You can register an event listener on any `public` method of a managed bean by using the +You can register an event listener on any method of a managed bean by using the `@EventListener` annotation. The `BlockedListNotifier` can be rewritten as follows: [source,java,indent=0,subs="verbatim,quotes",role="primary"] From e9083d7d2053fea3919bfeb9057e9fdba4049119 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 9 Dec 2021 14:52:34 +0000 Subject: [PATCH 49/78] Apply LogFormatUtils in more places --- .../springframework/web/util/UrlPathHelper.java | 4 ++-- .../reactive/resource/PathResourceResolver.java | 15 +++++++++------ .../web/reactive/resource/ResourceWebHandler.java | 10 +++++++--- .../ExtendedServletRequestDataBinder.java | 7 +++---- .../method/annotation/ReactiveTypeHandler.java | 4 ++-- .../servlet/resource/PathResourceResolver.java | 15 +++++++++------ .../resource/ResourceHttpRequestHandler.java | 10 +++++++--- .../server/support/AbstractHandshakeHandler.java | 12 ++++++++---- .../sockjs/support/AbstractSockJsService.java | 7 +++++-- .../transport/TransportHandlingSockJsService.java | 7 ++++--- 10 files changed, 56 insertions(+), 35 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 685f376b426a..ac43d5394c9c 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -578,8 +578,8 @@ private String decodeInternal(HttpServletRequest request, String source) { return UriUtils.decode(source, enc); } catch (UnsupportedCharsetException ex) { - if (logger.isWarnEnabled()) { - logger.warn("Could not decode request string [" + source + "] with encoding '" + enc + + if (logger.isDebugEnabled()) { + logger.debug("Could not decode request string [" + source + "] with encoding '" + enc + "': falling back to platform default encoding; exception message: " + ex.getMessage()); } return URLDecoder.decode(source); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/PathResourceResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/PathResourceResolver.java index dd86b8e9a264..23d5eaaa55f3 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/PathResourceResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/PathResourceResolver.java @@ -29,6 +29,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.core.log.LogFormatUtils; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; @@ -119,11 +120,12 @@ protected Mono getResource(String resourcePath, Resource location) { return Mono.just(resource); } else if (logger.isWarnEnabled()) { - Resource[] allowedLocations = getAllowedLocations(); - logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " + - "but resource \"" + resource.getURL() + "\" is neither under the " + - "current location \"" + location.getURL() + "\" nor under any of the " + - "allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]")); + Resource[] allowed = getAllowedLocations(); + logger.warn(LogFormatUtils.formatValue( + "Resource path \"" + resourcePath + "\" was successfully resolved " + + "but resource \"" + resource.getURL() + "\" is neither under the " + + "current location \"" + location.getURL() + "\" nor under any of the " + + "allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true)); } } return Mono.empty(); @@ -199,7 +201,8 @@ private boolean isInvalidEncodedPath(String resourcePath) { try { String decodedPath = URLDecoder.decode(resourcePath, "UTF-8"); if (decodedPath.contains("../") || decodedPath.contains("..\\")) { - logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath); + logger.warn(LogFormatUtils.formatValue( + "Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true)); return true; } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index e6f0640a7022..7c4db588b450 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -39,6 +39,7 @@ import org.springframework.core.codec.Hints; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -572,7 +573,8 @@ private boolean isInvalidEncodedPath(String path) { protected boolean isInvalidPath(String path) { if (path.contains("WEB-INF") || path.contains("META-INF")) { if (logger.isWarnEnabled()) { - logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true)); } return true; } @@ -580,14 +582,16 @@ protected boolean isInvalidPath(String path) { String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path); if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) { if (logger.isWarnEnabled()) { - logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true)); } return true; } } if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) { if (logger.isWarnEnabled()) { - logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true)); } return true; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinder.java index a0db1cac45a2..2a7489b98176 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExtendedServletRequestDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,9 +69,8 @@ protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) if (uriVars != null) { uriVars.forEach((name, value) -> { if (mpvs.contains(name)) { - if (logger.isWarnEnabled()) { - logger.warn("Skipping URI variable '" + name + - "' because request contains bind value with same name."); + if (logger.isDebugEnabled()) { + logger.debug("URI variable '" + name + "' overridden by request bind value."); } } else { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java index ec3e9a47081c..107fc4cd7b86 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ReactiveTypeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -202,7 +202,7 @@ private void logExecutorWarning(MethodParameter returnType) { "-------------------------------\n" + "Controller:\t" + returnType.getContainingClass().getName() + "\n" + "Method:\t\t" + returnType.getMethod().getName() + "\n" + - "Returning:\t" + ResolvableType.forMethodParameter(returnType).toString() + "\n" + + "Returning:\t" + ResolvableType.forMethodParameter(returnType) + "\n" + "!!!"); this.taskExecutorWarning = false; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java index 2b97ac60895f..cc042115d88d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/PathResourceResolver.java @@ -33,6 +33,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.server.PathContainer; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; @@ -190,11 +191,12 @@ protected Resource getResource(String resourcePath, Resource location) throws IO return resource; } else if (logger.isWarnEnabled()) { - Resource[] allowedLocations = getAllowedLocations(); - logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " + - "but resource \"" + resource.getURL() + "\" is neither under the " + - "current location \"" + location.getURL() + "\" nor under any of the " + - "allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]")); + Resource[] allowed = getAllowedLocations(); + logger.warn(LogFormatUtils.formatValue( + "Resource path \"" + resourcePath + "\" was successfully resolved " + + "but resource \"" + resource.getURL() + "\" is neither under " + + "the current location \"" + location.getURL() + "\" nor under any of " + + "the allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true)); } } return null; @@ -297,7 +299,8 @@ private boolean isInvalidEncodedPath(String resourcePath) { try { String decodedPath = URLDecoder.decode(resourcePath, "UTF-8"); if (decodedPath.contains("../") || decodedPath.contains("..\\")) { - logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath); + logger.warn(LogFormatUtils.formatValue( + "Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true)); return true; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 7855a1171cb3..ba1b0ef6cd88 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -39,6 +39,7 @@ import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRange; @@ -734,7 +735,8 @@ private boolean isInvalidEncodedPath(String path) { protected boolean isInvalidPath(String path) { if (path.contains("WEB-INF") || path.contains("META-INF")) { if (logger.isWarnEnabled()) { - logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true)); } return true; } @@ -742,14 +744,16 @@ protected boolean isInvalidPath(String path) { String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path); if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) { if (logger.isWarnEnabled()) { - logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true)); } return true; } } if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) { if (logger.isWarnEnabled()) { - logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]"); + logger.warn(LogFormatUtils.formatValue( + "Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true)); } return true; } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java index 8ca56429a4e8..4c9fa3ea2dd9 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java @@ -29,6 +29,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.context.Lifecycle; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; @@ -299,7 +300,8 @@ public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse r protected void handleInvalidUpgradeHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException { if (logger.isErrorEnabled()) { - logger.error("Handshake failed due to invalid Upgrade header: " + request.getHeaders().getUpgrade()); + logger.error(LogFormatUtils.formatValue( + "Handshake failed due to invalid Upgrade header: " + request.getHeaders().getUpgrade(), -1, true)); } response.setStatusCode(HttpStatus.BAD_REQUEST); response.getBody().write("Can \"Upgrade\" only to \"WebSocket\".".getBytes(StandardCharsets.UTF_8)); @@ -307,7 +309,8 @@ protected void handleInvalidUpgradeHeader(ServerHttpRequest request, ServerHttpR protected void handleInvalidConnectHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException { if (logger.isErrorEnabled()) { - logger.error("Handshake failed due to invalid Connection header " + request.getHeaders().getConnection()); + logger.error(LogFormatUtils.formatValue( + "Handshake failed due to invalid Connection header" + request.getHeaders().getConnection(), -1, true)); } response.setStatusCode(HttpStatus.BAD_REQUEST); response.getBody().write("\"Connection\" must be \"upgrade\".".getBytes(StandardCharsets.UTF_8)); @@ -331,8 +334,9 @@ protected String[] getSupportedVersions() { protected void handleWebSocketVersionNotSupported(ServerHttpRequest request, ServerHttpResponse response) { if (logger.isErrorEnabled()) { String version = request.getHeaders().getFirst("Sec-WebSocket-Version"); - logger.error("Handshake failed due to unsupported WebSocket version: " + version + - ". Supported versions: " + Arrays.toString(getSupportedVersions())); + logger.error(LogFormatUtils.formatValue( + "Handshake failed due to unsupported WebSocket version: " + version + + ". Supported versions: " + Arrays.toString(getSupportedVersions()), -1, true)); } response.setStatusCode(HttpStatus.UPGRADE_REQUIRED); response.getHeaders().set(WebSocketHttpHeaders.SEC_WEBSOCKET_VERSION, diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java index 84502556bd65..39f6d2436b19 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -377,7 +378,8 @@ public final void handleRequest(ServerHttpRequest request, ServerHttpResponse re if (sockJsPath == null) { if (logger.isWarnEnabled()) { - logger.warn("Expected SockJS path. Failing request: " + request.getURI()); + logger.warn(LogFormatUtils.formatValue( + "Expected SockJS path. Failing request: " + request.getURI(), -1, true)); } response.setStatusCode(HttpStatus.NOT_FOUND); return; @@ -447,7 +449,8 @@ else if (requestInfo != null) { String[] pathSegments = StringUtils.tokenizeToStringArray(sockJsPath.substring(1), "/"); if (pathSegments.length != 3) { if (logger.isWarnEnabled()) { - logger.warn("Invalid SockJS path '" + sockJsPath + "' - required to have 3 path segments"); + logger.warn(LogFormatUtils.formatValue("Invalid SockJS path '" + sockJsPath + "' - " + + "required to have 3 path segments", -1, true)); } if (requestInfo != null) { logger.debug("Ignoring transport request: " + requestInfo); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java index 8968980ef6ae..18224e3184f4 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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 java.util.concurrent.ScheduledFuture; import org.springframework.context.Lifecycle; +import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; @@ -234,7 +235,7 @@ protected void handleTransportRequest(ServerHttpRequest request, ServerHttpRespo TransportType transportType = TransportType.fromValue(transport); if (transportType == null) { if (logger.isWarnEnabled()) { - logger.warn("Unknown transport type for " + request.getURI()); + logger.warn(LogFormatUtils.formatValue("Unknown transport type for " + request.getURI(), -1, true)); } response.setStatusCode(HttpStatus.NOT_FOUND); return; @@ -243,7 +244,7 @@ protected void handleTransportRequest(ServerHttpRequest request, ServerHttpRespo TransportHandler transportHandler = this.handlers.get(transportType); if (transportHandler == null) { if (logger.isWarnEnabled()) { - logger.warn("No TransportHandler for " + request.getURI()); + logger.warn(LogFormatUtils.formatValue("No TransportHandler for " + request.getURI(), -1, true)); } response.setStatusCode(HttpStatus.NOT_FOUND); return; From 6cc9538ab9a1434f1b210f598e20d3d882b583f8 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 10 Dec 2021 07:48:52 +0100 Subject: [PATCH 50/78] Start building against Reactor 2020.0.14 snapshots See gh-27793 --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ff029ad98709..be9b492ad257 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ configure(allprojects) { project -> imports { mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5" mavenBom "io.netty:netty-bom:4.1.70.Final" - mavenBom "io.projectreactor:reactor-bom:2020.0.13" + mavenBom "io.projectreactor:reactor-bom:2020.0.14-SNAPSHOT" mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10" mavenBom "io.rsocket:rsocket-bom:1.1.1" mavenBom "org.eclipse.jetty:jetty-bom:9.4.44.v20210927" @@ -292,6 +292,7 @@ configure(allprojects) { project -> repositories { mavenCentral() maven { url "https://repo.spring.io/libs-spring-framework-build" } + maven { url "https://repo.spring.io/snapshot" } // reactor } } configurations.all { From ad7cdc5ce970c3df3297dc4456b511ded9d3e2b1 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 10 Dec 2021 13:12:44 +0100 Subject: [PATCH 51/78] Fix regression for null varargs in SpEL expressions A regression was introduced in gh-27582. Specifically, when null is supplied as the single argument for a varargs parameter in a method or function in a SpEL expression, ReflectionHelper currently throws a NullPointerException instead of leaving the null value unchanged. This commit fixes this regression. Closes gh-27719 --- .../spel/support/ReflectionHelper.java | 19 +++--- .../spel/MethodInvocationTests.java | 58 ++++++++++++++----- .../expression/spel/TestScenarioCreator.java | 32 ++++------ .../spel/VariableAndFunctionTests.java | 40 ++++++++----- .../spel/testresources/Inventor.java | 19 +++--- 5 files changed, 99 insertions(+), 69 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index 821c0b246ae6..d202a61a8c1b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -290,17 +290,18 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe Object argument = arguments[varargsPosition]; TypeDescriptor targetType = new TypeDescriptor(methodParam); TypeDescriptor sourceType = TypeDescriptor.forObject(argument); - // If the argument type is equal to the varargs element type, there is no need - // to convert it or wrap it in an array. For example, using StringToArrayConverter - // to convert a String containing a comma would result in the String being split - // and repackaged in an array when it should be used as-is. - if (!sourceType.equals(targetType.getElementTypeDescriptor())) { + // If the argument is null or the argument type is equal to the varargs element type, + // there is no need to convert it or wrap it in an array. For example, using + // StringToArrayConverter to convert a String containing a comma would result in the + // String being split and repackaged in an array when it should be used as-is. + if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) { arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType); } - // Three outcomes of the above if-block: - // 1) the input argument was correct type but not wrapped in an array, and nothing was done. - // 2) the input argument was already compatible (i.e., array of valid type), and nothing was done. - // 3) the input argument was the wrong type and got converted and wrapped in an array. + // Possible outcomes of the above if-block: + // 1) the input argument was null, and nothing was done. + // 2) the input argument was correct type but not wrapped in an array, and nothing was done. + // 3) the input argument was already compatible (i.e., array of valid type), and nothing was done. + // 4) the input argument was the wrong type and got converted and wrapped in an array. if (argument != arguments[varargsPosition] && !isFirstEntryInArray(argument, arguments[varargsPosition])) { conversionOccurred = true; // case 3 diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index b2cde1f10ff3..c003a07636b5 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java @@ -234,26 +234,33 @@ public void testAddingMethodResolvers() { @Test public void testVarargsInvocation01() { - // Calling 'public int aVarargsMethod(String... strings)' - returns number of arguments - evaluate("aVarargsMethod('a','b','c')", 3, Integer.class); - evaluate("aVarargsMethod('a')", 1, Integer.class); - evaluate("aVarargsMethod()", 0, Integer.class); - evaluate("aVarargsMethod(1,2,3)", 3, Integer.class); // all need converting to strings - evaluate("aVarargsMethod(1)", 1, Integer.class); // needs string conversion - evaluate("aVarargsMethod(1,'a',3.0d)", 3, Integer.class); // first and last need conversion - evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class); + // Calling 'public String aVarargsMethod(String... strings)' + evaluate("aVarargsMethod('a','b','c')", "[a, b, c]", String.class); + evaluate("aVarargsMethod('a')", "[a]", String.class); + evaluate("aVarargsMethod()", "[]", String.class); + evaluate("aVarargsMethod(1,2,3)", "[1, 2, 3]", String.class); // all need converting to strings + evaluate("aVarargsMethod(1)", "[1]", String.class); // needs string conversion + evaluate("aVarargsMethod(1,'a',3.0d)", "[1, a, 3.0]", String.class); // first and last need conversion + evaluate("aVarargsMethod(new String[]{'a','b','c'})", "[a, b, c]", String.class); + evaluate("aVarargsMethod(new String[]{})", "[]", String.class); + evaluate("aVarargsMethod(null)", "[null]", String.class); + evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class); + evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class); } @Test public void testVarargsInvocation02() { - // Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int + length_of_strings - evaluate("aVarargsMethod2(5,'a','b','c')", 8, Integer.class); - evaluate("aVarargsMethod2(2,'a')", 3, Integer.class); - evaluate("aVarargsMethod2(4)", 4, Integer.class); - evaluate("aVarargsMethod2(8,2,3)", 10, Integer.class); - evaluate("aVarargsMethod2(9)", 9, Integer.class); - evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class); - evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class); + // Calling 'public String aVarargsMethod2(int i, String... strings)' + evaluate("aVarargsMethod2(5,'a','b','c')", "5-[a, b, c]", String.class); + evaluate("aVarargsMethod2(2,'a')", "2-[a]", String.class); + evaluate("aVarargsMethod2(4)", "4-[]", String.class); + evaluate("aVarargsMethod2(8,2,3)", "8-[2, 3]", String.class); + evaluate("aVarargsMethod2(2,'a',3.0d)", "2-[a, 3.0]", String.class); + evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", "8-[a, b, c]", String.class); + evaluate("aVarargsMethod2(8,new String[]{})", "8-[]", String.class); + evaluate("aVarargsMethod2(8,null)", "8-[null]", String.class); + evaluate("aVarargsMethod2(8,null,'a')", "8-[null, a]", String.class); + evaluate("aVarargsMethod2(8,'a',null,'b')", "8-[a, null, b]", String.class); } @Test @@ -284,6 +291,25 @@ public void testVarargsInvocation03() { evaluate("aVarargsMethod3('foo', 'bar,baz')", "foo-bar,baz", String.class); } + @Test + public void testVarargsOptionalInvocation() { + // Calling 'public String optionalVarargsMethod(Optional... values)' + evaluate("optionalVarargsMethod()", "[]", String.class); + evaluate("optionalVarargsMethod(new String[0])", "[]", String.class); + evaluate("optionalVarargsMethod('a')", "[Optional[a]]", String.class); + evaluate("optionalVarargsMethod('a','b','c')", "[Optional[a], Optional[b], Optional[c]]", String.class); + evaluate("optionalVarargsMethod(9)", "[Optional[9]]", String.class); + evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class); + evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class); + evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class); + // The following should actually evaluate to [Optional.empty] instead of [null], + // but ReflectionHelper.convertArguments() currently does not provide explicit + // Optional support for a single argument passed to a varargs array. + evaluate("optionalVarargsMethod(null)", "[null]", String.class); + evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class); + evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class); + } + @Test public void testInvocationOnNullContextObject() { evaluateAndCheckError("null.toString()",SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java index eb60acd79808..4bb3f7da0cc3 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.expression.spel; +import java.util.Arrays; import java.util.GregorianCalendar; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -51,10 +52,10 @@ private static void populateFunctions(StandardEvaluationContext testContext) { TestScenarioCreator.class.getDeclaredMethod("reverseInt", Integer.TYPE, Integer.TYPE, Integer.TYPE)); testContext.registerFunction("reverseString", TestScenarioCreator.class.getDeclaredMethod("reverseString", String.class)); - testContext.registerFunction("varargsFunctionReverseStringsAndMerge", - TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge", String[].class)); - testContext.registerFunction("varargsFunctionReverseStringsAndMerge2", - TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge2", Integer.TYPE, String[].class)); + testContext.registerFunction("varargsFunction", + TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class)); + testContext.registerFunction("varargsFunction2", + TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", Integer.TYPE, String[].class)); } catch (Exception ex) { throw new IllegalStateException(ex); @@ -108,25 +109,12 @@ public static String reverseString(String input) { return backwards.toString(); } - public static String varargsFunctionReverseStringsAndMerge(String... strings) { - StringBuilder sb = new StringBuilder(); - if (strings != null) { - for (int i = strings.length - 1; i >= 0; i--) { - sb.append(strings[i]); - } - } - return sb.toString(); + public static String varargsFunction(String... strings) { + return Arrays.toString(strings); } - public static String varargsFunctionReverseStringsAndMerge2(int j, String... strings) { - StringBuilder sb = new StringBuilder(); - sb.append(j); - if (strings != null) { - for (int i = strings.length - 1; i >= 0; i--) { - sb.append(strings[i]); - } - } - return sb.toString(); + public static String varargsFunction2(int i, String... strings) { + return String.valueOf(i) + "-" + Arrays.toString(strings); } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java index 3271e45ea6d1..71c266acd801 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java @@ -59,21 +59,33 @@ public void testFunctionAccess02() { @Test public void testCallVarargsFunction() { - evaluate("#varargsFunctionReverseStringsAndMerge('a,b')", "a,b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('a', 'b,c', 'd')", "db,ca", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('a','b','c')", "cba", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('a')", "a", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge()", "", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge('b',25)", "25b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge(25)", "25", String.class); + evaluate("#varargsFunction()", "[]", String.class); + evaluate("#varargsFunction(new String[0])", "[]", String.class); + evaluate("#varargsFunction('a')", "[a]", String.class); + evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class); + // Conversion from int to String + evaluate("#varargsFunction(25)", "[25]", String.class); + evaluate("#varargsFunction('b',25)", "[b, 25]", String.class); + // Strings that contain a comma + evaluate("#varargsFunction('a,b')", "[a,b]", String.class); + evaluate("#varargsFunction('a', 'x,y', 'd')", "[a, x,y, d]", String.class); + // null values + evaluate("#varargsFunction(null)", "[null]", String.class); + evaluate("#varargsFunction('a',null,'b')", "[a, null, b]", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(1, 'a,b')", "1a,b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(1, 'a', 'b,c', 'd')", "1db,ca", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(2,'a')", "2a", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(3)", "3", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", String.class); - evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class); + evaluate("#varargsFunction2(9)", "9-[]", String.class); + evaluate("#varargsFunction2(9, new String[0])", "9-[]", String.class); + evaluate("#varargsFunction2(9,'a')", "9-[a]", String.class); + evaluate("#varargsFunction2(9,'a','b','c')", "9-[a, b, c]", String.class); + // Conversion from int to String + evaluate("#varargsFunction2(9,25)", "9-[25]", String.class); + evaluate("#varargsFunction2(9,'b',25)", "9-[b, 25]", String.class); + // Strings that contain a comma: + evaluate("#varargsFunction2(9, 'a,b')", "9-[a,b]", String.class); + evaluate("#varargsFunction2(9, 'a', 'x,y', 'd')", "9-[a, x,y, d]", String.class); + // null values + evaluate("#varargsFunction2(9,null)", "9-[null]", String.class); + evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class); } @Test diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java b/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java index 34960d982fe3..282622cf7d2d 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java @@ -17,11 +17,13 @@ package org.springframework.expression.spel.testresources; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.springframework.util.ObjectUtils; @@ -191,16 +193,17 @@ public String joinThreeStrings(String a, String b, String c) { return a + b + c; } - public int aVarargsMethod(String... strings) { - if (strings == null) - return 0; - return strings.length; + public String aVarargsMethod(String... strings) { + return Arrays.toString(strings); } - public int aVarargsMethod2(int i, String... strings) { - if (strings == null) - return i; - return strings.length + i; + public String aVarargsMethod2(int i, String... strings) { + return String.valueOf(i) + "-" + Arrays.toString(strings); + } + + @SuppressWarnings("unchecked") + public String optionalVarargsMethod(Optional... values) { + return Arrays.toString(values); } public String aVarargsMethod3(String str1, String... strings) { From b2e94f611fded8bf9c907de4ba5d9eb1fba10ef0 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 10 Dec 2021 13:53:28 +0100 Subject: [PATCH 52/78] Convert single null argument to Optional.empty() in SpEL varargs expression Prior to this commit, if a single null value was passed to a method with a varargs array of type java.util.Optional, that null value was passed unmodified. On the contrary, a null passed with additional values to such a method resulted in the null being converted to Optional.empty(). This commit ensures that a single null value is also converted to Optional.empty() for such SpEL expressions. Closes gh-27795 --- .../spel/support/ReflectionHelper.java | 29 ++++++++++++------- .../spel/MethodInvocationTests.java | 5 +--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index d202a61a8c1b..e9b62ec2adbb 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -20,6 +20,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.List; +import java.util.Optional; import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; @@ -290,21 +291,29 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe Object argument = arguments[varargsPosition]; TypeDescriptor targetType = new TypeDescriptor(methodParam); TypeDescriptor sourceType = TypeDescriptor.forObject(argument); - // If the argument is null or the argument type is equal to the varargs element type, - // there is no need to convert it or wrap it in an array. For example, using - // StringToArrayConverter to convert a String containing a comma would result in the - // String being split and repackaged in an array when it should be used as-is. - if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) { + if (argument == null) { + // Perform the equivalent of GenericConversionService.convertNullSource() for a single argument. + if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) { + arguments[varargsPosition] = Optional.empty(); + conversionOccurred = true; + } + } + // If the argument type is equal to the varargs element type, there is no need to + // convert it or wrap it in an array. For example, using StringToArrayConverter to + // convert a String containing a comma would result in the String being split and + // repackaged in an array when it should be used as-is. + else if (!sourceType.equals(targetType.getElementTypeDescriptor())) { arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType); } - // Possible outcomes of the above if-block: + // Possible outcomes of the above if-else block: // 1) the input argument was null, and nothing was done. - // 2) the input argument was correct type but not wrapped in an array, and nothing was done. - // 3) the input argument was already compatible (i.e., array of valid type), and nothing was done. - // 4) the input argument was the wrong type and got converted and wrapped in an array. + // 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty(). + // 3) the input argument was correct type but not wrapped in an array, and nothing was done. + // 4) the input argument was already compatible (i.e., array of valid type), and nothing was done. + // 5) the input argument was the wrong type and got converted and wrapped in an array. if (argument != arguments[varargsPosition] && !isFirstEntryInArray(argument, arguments[varargsPosition])) { - conversionOccurred = true; // case 3 + conversionOccurred = true; // case 5 } } // Otherwise, convert remaining arguments to the varargs element type. diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index c003a07636b5..6a94550104d3 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java @@ -302,10 +302,7 @@ public void testVarargsOptionalInvocation() { evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class); evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class); evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class); - // The following should actually evaluate to [Optional.empty] instead of [null], - // but ReflectionHelper.convertArguments() currently does not provide explicit - // Optional support for a single argument passed to a varargs array. - evaluate("optionalVarargsMethod(null)", "[null]", String.class); + evaluate("optionalVarargsMethod(null)", "[Optional.empty]", String.class); evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class); evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class); } From b1352e1833b6f92b210cc83ff9d48553be969b3e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 10 Dec 2021 15:05:56 +0100 Subject: [PATCH 53/78] Upgrade to Log4j2 2.15.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index be9b492ad257..24b93577a4c5 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ configure(allprojects) { project -> mavenBom "org.junit:junit-bom:5.8.2" } dependencies { - dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') { + dependencySet(group: 'org.apache.logging.log4j', version: '2.15.0') { entry 'log4j-api' entry 'log4j-core' entry 'log4j-jul' From 00375df4e8acc97f88c96cfaf5e4c0c44e7f8960 Mon Sep 17 00:00:00 2001 From: xay <1580319665@qq.com> Date: Sun, 12 Dec 2021 20:07:17 +0800 Subject: [PATCH 54/78] Fix javadoc reference to ThrowsAdvice See gh-27804 --- .../aop/framework/adapter/ThrowsAdviceAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java index dd557215c563..1bf091ce408e 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java @@ -25,7 +25,7 @@ import org.springframework.aop.ThrowsAdvice; /** - * Adapter to enable {@link org.springframework.aop.MethodBeforeAdvice} + * Adapter to enable {@link org.springframework.aop.ThrowsAdvice} * to be used in the Spring AOP framework. * * @author Rod Johnson From 31b8587ce61b2a37268eeaf2c83bd57b65ccd141 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Sun, 12 Dec 2021 16:30:06 +0100 Subject: [PATCH 55/78] Update copyright year of changed file See gh-27804 --- .../aop/framework/adapter/ThrowsAdviceAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java index 1bf091ce408e..91450ac1db44 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 9e09d6cde498f26a71e183cbf6a3afad0ed2f575 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Sun, 12 Dec 2021 16:32:35 +0100 Subject: [PATCH 56/78] Polish "Fix javadoc reference to ThrowsAdvice" See gh-27804 --- .../aop/framework/adapter/ThrowsAdviceAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java index 91450ac1db44..a6b09c63cbbe 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceAdapter.java @@ -25,8 +25,8 @@ import org.springframework.aop.ThrowsAdvice; /** - * Adapter to enable {@link org.springframework.aop.ThrowsAdvice} - * to be used in the Spring AOP framework. + * Adapter to enable {@link org.springframework.aop.ThrowsAdvice} to be used + * in the Spring AOP framework. * * @author Rod Johnson * @author Juergen Hoeller From c50a5096a04c2a198da21192333fa05d1d85e70e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 13 Dec 2021 16:18:25 +0100 Subject: [PATCH 57/78] Upgrade to Netty 4.1.72.Final --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 24b93577a4c5..367edd2b90ba 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ configure(allprojects) { project -> dependencyManagement { imports { mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5" - mavenBom "io.netty:netty-bom:4.1.70.Final" + mavenBom "io.netty:netty-bom:4.1.72.Final" mavenBom "io.projectreactor:reactor-bom:2020.0.14-SNAPSHOT" mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10" mavenBom "io.rsocket:rsocket-bom:1.1.1" From 8422d9d22f4156831a92b5e01950306880135c0f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 14 Dec 2021 13:44:48 +0100 Subject: [PATCH 58/78] Add default methods to CachingConfigurer This commit adds default methods to CachingConfigurer and JCacheConfigurer and removes the reference to their respective support classes as they are now irrelevant. Closes gh-27811 --- .../cache/jcache/config/JCacheConfigurer.java | 10 ++++----- .../jcache/JCacheEhCacheAnnotationTests.java | 6 ++--- .../jcache/config/JCacheJavaConfigTests.java | 8 +++---- .../interceptor/JCacheErrorHandlerTests.java | 6 ++--- .../interceptor/JCacheKeyGeneratorTests.java | 8 +++---- .../cache/annotation/CachingConfigurer.java | 22 ++++++++++++------- .../cache/CacheReproTests.java | 6 ++--- .../config/EnableCachingIntegrationTests.java | 6 ++--- .../cache/config/EnableCachingTests.java | 10 ++++----- .../ExpressionCachingIntegrationTests.java | 6 ++--- .../interceptor/CacheErrorHandlerTests.java | 6 ++--- .../interceptor/CachePutEvaluationTests.java | 6 ++--- .../CacheResolverCustomizationTests.java | 6 ++--- .../interceptor/CacheSyncFailureTests.java | 6 ++--- 14 files changed, 59 insertions(+), 53 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/JCacheConfigurer.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/JCacheConfigurer.java index 989e720aeb99..039729a02d50 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/JCacheConfigurer.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/JCacheConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,7 @@ *

    To be implemented by classes annotated with * {@link org.springframework.cache.annotation.EnableCaching} that wish * or need to specify explicitly how exception caches are resolved for - * annotation-driven cache management. Consider extending {@link JCacheConfigurerSupport}, - * which provides a stub implementation of all interface methods. + * annotation-driven cache management. * *

    See {@link org.springframework.cache.annotation.EnableCaching} for * general examples and context; see {@link #exceptionCacheResolver()} for @@ -36,7 +35,6 @@ * @author Stephane Nicoll * @since 4.1 * @see CachingConfigurer - * @see JCacheConfigurerSupport * @see org.springframework.cache.annotation.EnableCaching */ public interface JCacheConfigurer extends CachingConfigurer { @@ -60,6 +58,8 @@ public interface JCacheConfigurer extends CachingConfigurer { * See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples. */ @Nullable - CacheResolver exceptionCacheResolver(); + default CacheResolver exceptionCacheResolver() { + return null; + } } diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java index 188cc293c974..e73dd79a222d 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKeyGenerator; @@ -104,7 +104,7 @@ public void testEvictAllEarlyWithTransaction() { @Configuration @EnableCaching - static class EnableCachingConfig extends CachingConfigurerSupport { + static class EnableCachingConfig implements CachingConfigurer { @Autowired CachingProvider cachingProvider; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java index 5c12aceac022..1dcc12540d1e 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -180,7 +180,7 @@ public CacheResolver exceptionCacheResolver() { @Configuration @EnableCaching - public static class EmptyConfigSupportConfig extends JCacheConfigurerSupport { + public static class EmptyConfigSupportConfig implements JCacheConfigurer { @Bean public CacheManager cm() { return new NoOpCacheManager(); @@ -190,7 +190,7 @@ public CacheManager cm() { @Configuration @EnableCaching - static class FullCachingConfigSupport extends JCacheConfigurerSupport { + static class FullCachingConfigSupport implements JCacheConfigurer { @Override @Bean @@ -220,7 +220,7 @@ public CacheResolver exceptionCacheResolver() { @Configuration @EnableCaching - static class NoExceptionCacheResolverConfig extends JCacheConfigurerSupport { + static class NoExceptionCacheResolverConfig implements JCacheConfigurer { @Override @Bean diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheErrorHandlerTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheErrorHandlerTests.java index 3b341a0c4df0..561c0af54d1b 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheErrorHandlerTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheErrorHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author 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,7 +34,7 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.SimpleKeyGenerator; -import org.springframework.cache.jcache.config.JCacheConfigurerSupport; +import org.springframework.cache.jcache.config.JCacheConfigurer; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -141,7 +141,7 @@ public void clearFail() { @Configuration @EnableCaching - static class Config extends JCacheConfigurerSupport { + static class Config implements JCacheConfigurer { @Bean @Override diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheKeyGeneratorTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheKeyGeneratorTests.java index dd5497764b0c..35db912df971 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheKeyGeneratorTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/JCacheKeyGeneratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author 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,7 +34,7 @@ import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKey; import org.springframework.cache.interceptor.SimpleKeyGenerator; -import org.springframework.cache.jcache.config.JCacheConfigurerSupport; +import org.springframework.cache.jcache.config.JCacheConfigurer; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -97,7 +97,7 @@ public void getFiltered() { @Configuration @EnableCaching - static class Config extends JCacheConfigurerSupport { + static class Config implements JCacheConfigurer { @Bean @Override @@ -151,7 +151,7 @@ private void expect(Object... params) { @Override public Object generate(Object target, Method method, Object... params) { assertThat(Arrays.equals(expectedParams, params)).as("Unexpected parameters: expected: " - + Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue(); + + Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue(); return new SimpleKey(params); } } diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java index b7a609e9c238..e3dbe023f189 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,7 @@ * Interface to be implemented by @{@link org.springframework.context.annotation.Configuration * Configuration} classes annotated with @{@link EnableCaching} that wish or need to * specify explicitly how caches are resolved and how keys are generated for annotation-driven - * cache management. Consider extending {@link CachingConfigurerSupport}, which provides a - * stub implementation of all interface methods. + * cache management. * *

    See @{@link EnableCaching} for general examples and context; see * {@link #cacheManager()}, {@link #cacheResolver()} and {@link #keyGenerator()} @@ -37,7 +36,6 @@ * @author Stephane Nicoll * @since 3.1 * @see EnableCaching - * @see CachingConfigurerSupport */ public interface CachingConfigurer { @@ -64,7 +62,9 @@ public interface CachingConfigurer { * See @{@link EnableCaching} for more complete examples. */ @Nullable - CacheManager cacheManager(); + default CacheManager cacheManager() { + return null; + } /** * Return the {@link CacheResolver} bean to use to resolve regular caches for @@ -89,7 +89,9 @@ public interface CachingConfigurer { * See {@link EnableCaching} for more complete examples. */ @Nullable - CacheResolver cacheResolver(); + default CacheResolver cacheResolver() { + return null; + } /** * Return the key generator bean to use for annotation-driven cache management. @@ -110,7 +112,9 @@ public interface CachingConfigurer { * See @{@link EnableCaching} for more complete examples. */ @Nullable - KeyGenerator keyGenerator(); + default KeyGenerator keyGenerator() { + return null; + } /** * Return the {@link CacheErrorHandler} to use to handle cache-related errors. @@ -133,6 +137,8 @@ public interface CachingConfigurer { * See @{@link EnableCaching} for more complete examples. */ @Nullable - CacheErrorHandler errorHandler(); + default CacheErrorHandler errorHandler() { + return null; + } } diff --git a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java index 4c81623f507e..b08c6f895a06 100644 --- a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java +++ b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; @@ -306,7 +306,7 @@ public Object getNeverCache(String key) { @Configuration @EnableCaching - public static class Spr13081Config extends CachingConfigurerSupport { + public static class Spr13081Config implements CachingConfigurer { @Bean @Override diff --git a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java index 858d9c0d5b92..6e10f5d04c64 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -124,7 +124,7 @@ private Cache getCache() { @Configuration - static class SharedConfig extends CachingConfigurerSupport { + static class SharedConfig implements CachingConfigurer { @Override @Bean diff --git a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java index ea7717478968..8c41ef04570e 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java @@ -22,7 +22,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheInterceptor; @@ -150,7 +150,7 @@ public void bothSetOnlyResolverIsUsed() { @Configuration @EnableCaching - static class EnableCachingConfig extends CachingConfigurerSupport { + static class EnableCachingConfig implements CachingConfigurer { @Override @Bean @@ -227,7 +227,7 @@ public CacheManager cm2() { @Configuration @EnableCaching - static class MultiCacheManagerConfigurer extends CachingConfigurerSupport { + static class MultiCacheManagerConfigurer implements CachingConfigurer { @Bean public CacheManager cm1() { @@ -253,7 +253,7 @@ public KeyGenerator keyGenerator() { @Configuration @EnableCaching - static class EmptyConfigSupportConfig extends CachingConfigurerSupport { + static class EmptyConfigSupportConfig implements CachingConfigurer { @Bean public CacheManager cm() { @@ -264,7 +264,7 @@ public CacheManager cm() { @Configuration @EnableCaching - static class FullCachingConfig extends CachingConfigurerSupport { + static class FullCachingConfig implements CachingConfigurer { @Override @Bean diff --git a/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java b/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java index ae109aec5c56..5ba071381fc7 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachePut; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.ConfigurableApplicationContext; @@ -122,7 +122,7 @@ public String getId() { @Configuration @EnableCaching - static class SharedConfig extends CachingConfigurerSupport { + static class SharedConfig implements CachingConfigurer { @Override @Bean diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java index cfcd9cd713ae..90e26dd51f39 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.cache.support.SimpleValueWrapper; @@ -170,7 +170,7 @@ public void clearFailProperException() { @Configuration @EnableCaching - static class Config extends CachingConfigurerSupport { + static class Config implements CachingConfigurer { @Bean @Override diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CachePutEvaluationTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CachePutEvaluationTests.java index d17630a8ed29..c257534c6fb0 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CachePutEvaluationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CachePutEvaluationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.ConfigurableApplicationContext; @@ -106,7 +106,7 @@ public void getAndPut() { @Configuration @EnableCaching - static class Config extends CachingConfigurerSupport { + static class Config implements CachingConfigurer { @Bean @Override diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheResolverCustomizationTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheResolverCustomizationTests.java index ca2e76fc36ff..86f0223402f9 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheResolverCustomizationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheResolverCustomizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -149,7 +149,7 @@ public void unknownCacheResolver() { @Configuration @EnableCaching - static class Config extends CachingConfigurerSupport { + static class Config implements CachingConfigurer { @Override @Bean diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheSyncFailureTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheSyncFailureTests.java index 2a3eccc6db7b..de4776adae2e 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheSyncFailureTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheSyncFailureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; -import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -133,7 +133,7 @@ public Object syncWithTwoGetOperations(Object arg1) { @Configuration @EnableCaching - static class Config extends CachingConfigurerSupport { + static class Config implements CachingConfigurer { @Override @Bean From b06d267232f3951e4d4bdbfbb04c4ce1d83f3b8c Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 14 Dec 2021 13:56:28 +0100 Subject: [PATCH 59/78] Remove references to AsyncConfigurerSupport Closes gh-27812 --- .../scheduling/annotation/AsyncConfigurer.java | 8 +------- .../AsyncAnnotationBeanPostProcessorTests.java | 8 ++++---- ...gTestExecutionListenerIntegrationTests.java | 18 +++++++++++------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java index eab9744cf340..488297171ba6 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,11 +28,6 @@ * {@link AsyncUncaughtExceptionHandler} instance used to process exception thrown from * async method with {@code void} return type. * - *

    Consider using {@link AsyncConfigurerSupport} providing default implementations for - * both methods if only one element needs to be customized. Furthermore, backward compatibility - * of this interface will be insured in case new customization options are introduced - * in the future. - * *

    See @{@link EnableAsync} for usage examples. * * @author Chris Beams @@ -40,7 +35,6 @@ * @since 3.1 * @see AbstractAsyncConfiguration * @see EnableAsync - * @see AsyncConfigurerSupport */ public interface AsyncConfigurer { diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java index 477a97dec3df..2599d1a3cefb 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -218,8 +218,8 @@ public void handleExceptionWithListenableFuture() { private void assertFutureWithException(Future result, TestableAsyncUncaughtExceptionHandler exceptionHandler) { assertThatExceptionOfType(ExecutionException.class).isThrownBy( - result::get) - .withCauseExactlyInstanceOf(UnsupportedOperationException.class); + result::get) + .withCauseExactlyInstanceOf(UnsupportedOperationException.class); assertThat(exceptionHandler.isCalled()).as("handler should never be called with Future return type").isFalse(); } @@ -343,7 +343,7 @@ public void execute(Runnable r) { @Configuration @EnableAsync - static class ConfigWithExceptionHandler extends AsyncConfigurerSupport { + static class ConfigWithExceptionHandler implements AsyncConfigurer { @Bean public ITestBean target() { diff --git a/spring-test/src/test/java/org/springframework/test/context/event/EventPublishingTestExecutionListenerIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/event/EventPublishingTestExecutionListenerIntegrationTests.java index c3edfeb83869..7cba9a363509 100644 --- a/spring-test/src/test/java/org/springframework/test/context/event/EventPublishingTestExecutionListenerIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/event/EventPublishingTestExecutionListenerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.AsyncConfigurerSupport; +import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; @@ -73,13 +73,17 @@ public class EventPublishingTestExecutionListenerIntegrationTests { private static final CountDownLatch countDownLatch = new CountDownLatch(1); private final TestContextManager testContextManager = new TestContextManager(ExampleTestCase.class); + private final TestContext testContext = testContextManager.getTestContext(); + // Note that the following invocation of getApplicationContext() forces eager // loading of the test's ApplicationContext which consequently results in the // publication of all test execution events. Otherwise, TestContext#publishEvent // would never fire any events for ExampleTestCase. private final TestExecutionListener listener = testContext.getApplicationContext().getBean(TestExecutionListener.class); + private final Object testInstance = new ExampleTestCase(); + private final Method traceableTestMethod = ReflectionUtils.findMethod(ExampleTestCase.class, "traceableTest"); @@ -127,8 +131,8 @@ public void beforeTestMethodAnnotationWithFailingCondition() throws Exception { public void beforeTestMethodAnnotationWithFailingEventListener() throws Exception { Method method = ReflectionUtils.findMethod(ExampleTestCase.class, "testWithFailingEventListener"); assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> - testContextManager.beforeTestMethod(testInstance, method)) - .withMessageContaining("Boom!"); + testContextManager.beforeTestMethod(testInstance, method)) + .withMessageContaining("Boom!"); verify(listener, only()).beforeTestMethod(testContext); } @@ -149,7 +153,7 @@ public void beforeTestMethodAnnotationWithFailingAsyncEventListener() throws Exc verify(listener, only()).beforeTestMethod(testContext); assertThat(TrackingAsyncUncaughtExceptionHandler.asyncException.getMessage()) - .startsWith("Asynchronous exception for test method [" + methodName + "] in thread [" + THREAD_NAME_PREFIX); + .startsWith("Asynchronous exception for test method [" + methodName + "] in thread [" + THREAD_NAME_PREFIX); } @Test @@ -211,7 +215,7 @@ public void testWithFailingAsyncEventListener() { @Configuration @EnableAsync(proxyTargetClass = true) - static class TestEventListenerConfiguration extends AsyncConfigurerSupport { + static class TestEventListenerConfiguration implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { @@ -306,7 +310,7 @@ static class AsyncTestEventComponent { public void beforeTestMethodWithAsyncFailure(BeforeTestMethodEvent event) throws Exception { this.listener.beforeTestMethod(event.getSource()); throw new RuntimeException(String.format("Asynchronous exception for test method [%s] in thread [%s]", - event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName())); + event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName())); } } From 8b80d38c339717cc66edc9f56ac8b503da50e179 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 14 Dec 2021 15:33:55 +0100 Subject: [PATCH 60/78] Upgrade to Reactor 2020.0.14 Closes gh-27793 --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 367edd2b90ba..e1cd581786ad 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ configure(allprojects) { project -> imports { mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5" mavenBom "io.netty:netty-bom:4.1.72.Final" - mavenBom "io.projectreactor:reactor-bom:2020.0.14-SNAPSHOT" + mavenBom "io.projectreactor:reactor-bom:2020.0.14" mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10" mavenBom "io.rsocket:rsocket-bom:1.1.1" mavenBom "org.eclipse.jetty:jetty-bom:9.4.44.v20210927" @@ -292,7 +292,6 @@ configure(allprojects) { project -> repositories { mavenCentral() maven { url "https://repo.spring.io/libs-spring-framework-build" } - maven { url "https://repo.spring.io/snapshot" } // reactor } } configurations.all { From c44447f622b35331e6e3a41eb7f8578fcd62b7c1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:01 +0100 Subject: [PATCH 61/78] Avoid early initialization of empty interceptor names Closes gh-12238 --- .../aop/framework/ProxyFactoryBean.java | 12 ++---- .../aop/framework/ProxyFactoryBeanTests.java | 38 +++++++++++++++++-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java index 6c9efc49f0d7..5e1ed1603fa6 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -421,11 +421,7 @@ private boolean isNamedBeanAnAdvisorOrAdvice(String beanName) { * are unaffected by such changes. */ private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException { - if (this.advisorChainInitialized) { - return; - } - - if (!ObjectUtils.isEmpty(this.interceptorNames)) { + if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) { if (this.beanFactory == null) { throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " + "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames)); @@ -464,9 +460,9 @@ private synchronized void initializeAdvisorChain() throws AopConfigException, Be addAdvisorOnChainCreation(advice); } } - } - this.advisorChainInitialized = true; + this.advisorChainInitialized = true; + } } diff --git a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java index 57df369b68eb..91d85d35dadd 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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,10 +65,10 @@ import static org.assertj.core.api.Assertions.assertThatIOException; /** - * @since 13.03.2003 * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams + * @since 13.03.2003 */ public class ProxyFactoryBeanTests { @@ -633,20 +633,50 @@ public void testFrozenFactoryBean() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(FROZEN_CONTEXT, CLASS)); - Advised advised = (Advised)bf.getBean("frozen"); + Advised advised = (Advised) bf.getBean("frozen"); assertThat(advised.isFrozen()).as("The proxy should be frozen").isTrue(); } @Test - public void testDetectsInterfaces() throws Exception { + public void testDetectsInterfaces() { ProxyFactoryBean fb = new ProxyFactoryBean(); fb.setTarget(new TestBean()); fb.addAdvice(new DebugInterceptor()); fb.setBeanFactory(new DefaultListableBeanFactory()); + ITestBean proxy = (ITestBean) fb.getObject(); assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue(); } + @Test + public void testWithInterceptorNames() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerSingleton("debug", new DebugInterceptor()); + + ProxyFactoryBean fb = new ProxyFactoryBean(); + fb.setTarget(new TestBean()); + fb.setInterceptorNames("debug"); + fb.setBeanFactory(bf); + + Advised proxy = (Advised) fb.getObject(); + assertThat(proxy.getAdvisorCount()).isEqualTo(1); + } + + @Test + public void testWithLateInterceptorNames() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerSingleton("debug", new DebugInterceptor()); + + ProxyFactoryBean fb = new ProxyFactoryBean(); + fb.setTarget(new TestBean()); + fb.setBeanFactory(bf); + fb.getObject(); + + fb.setInterceptorNames("debug"); + Advised proxy = (Advised) fb.getObject(); + assertThat(proxy.getAdvisorCount()).isEqualTo(1); + } + /** * Fires only on void methods. Saves list of methods intercepted. From d7b9270672a141cd5b046fbdeb8a22f269c73a6f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:13 +0100 Subject: [PATCH 62/78] Clarify SchedulerFactoryBean's LocalDataSourceJobStore overriding Includes clarification of interface-level cache annotations for target-class proxies. Closes gh-27709 See gh-27726 --- .../quartz/LocalDataSourceJobStore.java | 3 ++ .../quartz/SchedulerFactoryBean.java | 4 +- .../config/EnableCachingIntegrationTests.java | 43 +++++++++++++++++++ src/docs/asciidoc/integration.adoc | 32 +++++++------- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java index e0b12f443f91..e4b09c0798fb 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java @@ -39,6 +39,7 @@ * Subclass of Quartz's {@link JobStoreCMT} class that delegates to a Spring-managed * {@link DataSource} instead of using a Quartz-managed JDBC connection pool. * This JobStore will be used if SchedulerFactoryBean's "dataSource" property is set. + * You may also configure it explicitly, possibly as a custom subclass of this class. * *

    Supports both transactional and non-transactional DataSource access. * With a non-XA DataSource and local Spring transactions, a single DataSource @@ -58,6 +59,8 @@ * @since 1.1 * @see SchedulerFactoryBean#setDataSource * @see SchedulerFactoryBean#setNonTransactionalDataSource + * @see SchedulerFactoryBean#getConfigTimeDataSource() + * @see SchedulerFactoryBean#getConfigTimeNonTransactionalDataSource() * @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection */ diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java index e0982a2e5ff9..15185bba6c37 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java @@ -310,9 +310,11 @@ public void setTaskExecutor(Executor taskExecutor) { /** * Set the default {@link DataSource} to be used by the Scheduler. - * If set, this will override corresponding settings in Quartz properties. *

    Note: If this is set, the Quartz settings should not define * a job store "dataSource" to avoid meaningless double configuration. + * Also, do not define a "org.quartz.jobStore.class" property at all. + * (You may explicitly define Spring's {@link LocalDataSourceJobStore} + * but that's the default when using this method anyway.) *

    A Spring-specific subclass of Quartz' JobStoreCMT will be used. * It is therefore strongly recommended to perform all operations on * the Scheduler within Spring-managed (or plain JTA) transactions. diff --git a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java index 6e10f5d04c64..6a7e1127aecd 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java @@ -83,6 +83,19 @@ private void fooGetSimple(FooService service) { assertCacheHit(key, value, cache); } + @Test + public void barServiceWithCacheableInterfaceCglib() { + this.context = new AnnotationConfigApplicationContext(BarConfigCglib.class); + BarService service = this.context.getBean(BarService.class); + Cache cache = getCache(); + + Object key = new Object(); + assertCacheMiss(key, cache); + + Object value = service.getSimple(key); + assertCacheHit(key, value, cache); + } + @Test public void beanConditionOff() { this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class); @@ -185,6 +198,36 @@ public Object getWithCondition(Object key) { } + @Configuration + @Import(SharedConfig.class) + @EnableCaching(proxyTargetClass = true) + static class BarConfigCglib { + + @Bean + public BarService barService() { + return new BarServiceImpl(); + } + } + + + interface BarService { + + @Cacheable(cacheNames = "testCache") + Object getSimple(Object key); + } + + + static class BarServiceImpl implements BarService { + + private final AtomicLong counter = new AtomicLong(); + + @Override + public Object getSimple(Object key) { + return this.counter.getAndIncrement(); + } + } + + @Configuration @Import(FooConfig.class) @EnableCaching diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index f2ec53ad0aed..9ea8f646d7d5 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -5396,6 +5396,7 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron |=== + [[scheduling-quartz]] === Using the Quartz Scheduler @@ -5451,7 +5452,6 @@ has it applied automatically: protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException { // do the actual work } - } ---- @@ -5571,11 +5571,19 @@ seconds and one running every morning at 6 AM. To finalize everything, we need t ---- -More properties are available for the `SchedulerFactoryBean`, such as the calendars -used by the job details, properties to customize Quartz with, and others. See the -{api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`] +More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the +job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See +the {api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`] javadoc for more information. +NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath, +based on Quartz property keys, as with regular Quartz configuration. Please note that many +`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file; +it is therefore not recommended to specify values at both levels. For example, do not set +an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource, +or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which +is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`. + @@ -5877,7 +5885,6 @@ is updated in the cache. The following example shows how to use the `sync` attri ---- <1> Using the `sync` attribute. - NOTE: This is an optional feature, and your favorite cache library may not support it. All `CacheManager` implementations provided by the core framework support it. See the documentation of your cache provider for more details. @@ -6035,7 +6042,6 @@ all entries from the `books` cache: ---- <1> Using the `allEntries` attribute to evict all entries from the cache. - This option comes in handy when an entire cache region needs to be cleared out. Rather than evicting each entry (which would take a long time, since it is inefficient), all the entries are removed in one operation, as the preceding example shows. @@ -6094,7 +6100,6 @@ comes into play. The following examples uses `@CacheConfig` to set the name of t ---- <1> Using `@CacheConfig` to set the name of the cache. - `@CacheConfig` is a class-level annotation that allows sharing the cache names, the custom `KeyGenerator`, the custom `CacheManager`, and the custom `CacheResolver`. Placing this annotation on the class does not turn on any caching operation. @@ -6235,13 +6240,11 @@ if you need to annotate non-public methods, as it changes the bytecode itself. **** TIP: Spring recommends that you only annotate concrete classes (and methods of concrete -classes) with the `@Cache{asterisk}` annotation, as opposed to annotating interfaces. -You certainly can place the `@Cache{asterisk}` annotation on an interface (or an interface -method), but this works only as you would expect it to if you use interface-based proxies. -The fact that Java annotations are not inherited from interfaces means that, if you use -class-based proxies (`proxy-target-class="true"`) or the weaving-based aspect -(`mode="aspectj"`), the caching settings are not recognized by the proxying and weaving -infrastructure, and the object is not wrapped in a caching proxy. +classes) with the `@Cache{asterisk}` annotations, as opposed to annotating interfaces. +You certainly can place an `@Cache{asterisk}` annotation on an interface (or an interface +method), but this works only if you use the proxy mode (`mode="proxy"`). If you use the +weaving-based aspect (`mode="aspectj"`), the caching settings are not recognized on +interface-level declarations by the weaving infrastructure. NOTE: In proxy mode (the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the @@ -6378,7 +6381,6 @@ to customize the factory for each cache operation, as the following example show ---- <1> Customizing the factory for this operation. - NOTE: For all referenced classes, Spring tries to locate a bean with the given type. If more than one match exists, a new instance is created and can use the regular bean lifecycle callbacks, such as dependency injection. From 0802581aff7e3ec5b00f7c2f44e1af3d5574461b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:26 +0100 Subject: [PATCH 63/78] Unit test for identifying type variable argument See gh-27748 --- .../core/ResolvableTypeTests.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index ba58532c74f2..3ca17918b53e 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1159,6 +1159,20 @@ void isAssignableFromForComplexWildcards() throws Exception { assertThatResolvableType(complex4).isNotAssignableFrom(complex3); } + @Test + void identifyTypeVariable() throws Exception { + Method method = ClassArguments.class.getMethod("typedArgumentFirst", Class.class, Class.class, Class.class); + ResolvableType returnType = ResolvableType.forMethodReturnType(method, ClassArguments.class); + + ResolvableType arg0 = ResolvableType.forMethodParameter(method, 0, ClassArguments.class); + ResolvableType arg1 = ResolvableType.forMethodParameter(method, 1, ClassArguments.class); + ResolvableType arg2 = ResolvableType.forMethodParameter(method, 2, ClassArguments.class); + + assertThat(returnType.getType().equals(arg0.as(Class.class).getGeneric(0).getType())).isTrue(); + assertThat(returnType.getType().equals(arg1.as(Class.class).getGeneric(0).getType())).isFalse(); + assertThat(returnType.getType().equals(arg2.as(Class.class).getGeneric(0).getType())).isFalse(); + } + @Test void hashCodeAndEquals() throws Exception { ResolvableType forClass = ResolvableType.forClass(List.class); @@ -1427,6 +1441,10 @@ interface Methods { } + interface TypedMethods extends Methods { + } + + static class AssignmentBase { public O o; @@ -1479,7 +1497,9 @@ static class Assignment extends AssignmentBase { } - interface TypedMethods extends Methods { + interface ClassArguments { + + T typedArgumentFirst(Class arg0, Class arg1, Class arg2); } From ac581bed92181ce8ddfe629e94629a52e3dc972a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:42 +0100 Subject: [PATCH 64/78] Avoid NPE against null value from toString call Closes gh-27782 --- .../java/org/springframework/core/log/LogFormatUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java index 9a2693e2ce6b..550a202255c2 100644 --- a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java +++ b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.Log; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Utility methods for formatting and logging messages. @@ -71,10 +72,10 @@ public static String formatValue( } String result; try { - result = value.toString(); + result = ObjectUtils.nullSafeToString(value); } catch (Throwable ex) { - result = ex.toString(); + result = ObjectUtils.nullSafeToString(ex); } if (maxLength != -1) { result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result); From 1af21bb4513e27add1b32a024fd10e9e69253776 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:46:59 +0100 Subject: [PATCH 65/78] Declare serialVersionUID on DefaultAopProxyFactory Closes gh-27784 --- .../springframework/aop/framework/DefaultAopProxyFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java index 3e8c838704c4..8692371d3a8c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java @@ -45,9 +45,10 @@ * @see AdvisedSupport#setProxyTargetClass * @see AdvisedSupport#setInterfaces */ -@SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { + private static final long serialVersionUID = 7930414337282325166L; + @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { From ca84559588a26c4acb48cd0110ded9a89b43852e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:47:12 +0100 Subject: [PATCH 66/78] Provide findAnnotationOnBean variant with allowFactoryBeanInit flag Closes gh-27796 --- .../beans/factory/ListableBeanFactory.java | 24 ++++++++++++++++++- .../support/DefaultListableBeanFactory.java | 17 +++++++++---- .../support/StaticListableBeanFactory.java | 13 ++++++++-- .../support/AbstractApplicationContext.java | 10 ++++++++ .../setup/StubWebApplicationContext.java | 11 ++++++++- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java index 389f19c9e488..2642e72d7ae5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -353,9 +353,31 @@ Map getBeansOfType(@Nullable Class type, boolean includeNonSin * @since 3.0 * @see #getBeanNamesForAnnotation * @see #getBeansWithAnnotation + * @see #getType(String) */ @Nullable A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException; + /** + * Find an {@link Annotation} of {@code annotationType} on the specified bean, + * traversing its interfaces and super classes if no annotation can be found on + * the given class itself, as well as checking the bean's factory method (if any). + * @param beanName the name of the bean to look for annotations on + * @param annotationType the type of annotation to look for + * (at class, interface or factory method level of the specified bean) + * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized + * just for the purpose of determining its object type + * @return the annotation of the given type if found, or {@code null} otherwise + * @throws NoSuchBeanDefinitionException if there is no bean with the given name + * @since 5.3.14 + * @see #getBeanNamesForAnnotation + * @see #getBeansWithAnnotation + * @see #getType(String, boolean) + */ + @Nullable + A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException; + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 048612fbed73..b019ef5361f3 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -730,14 +730,23 @@ public Map getBeansWithAnnotation(Class an public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { - return findMergedAnnotationOnBean(beanName, annotationType) + return findAnnotationOnBean(beanName, annotationType, true); + } + + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + return findMergedAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit) .synthesize(MergedAnnotation::isPresent).orElse(null); } private MergedAnnotation findMergedAnnotationOnBean( - String beanName, Class annotationType) { + String beanName, Class annotationType, boolean allowFactoryBeanInit) { - Class beanType = getType(beanName); + Class beanType = getType(beanName, allowFactoryBeanInit); if (beanType != null) { MergedAnnotation annotation = MergedAnnotations.from(beanType, SearchStrategy.TYPE_HIERARCHY).get(annotationType); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index a5430120dfdb..1105ead3e12f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -459,7 +459,16 @@ public Map getBeansWithAnnotation(Class an public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { - Class beanType = getType(beanName); + return findAnnotationOnBean(beanName, annotationType, true); + } + + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + Class beanType = getType(beanName, allowFactoryBeanInit); return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null); } diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index aca706e82c1f..38243b17944b 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -1331,6 +1331,16 @@ public A findAnnotationOnBean(String beanName, Class a return getBeanFactory().findAnnotationOnBean(beanName, annotationType); } + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + assertBeanFactoryActive(); + return getBeanFactory().findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit); + } + //--------------------------------------------------------------------- // Implementation of HierarchicalBeanFactory interface diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java index c5271e8d18e8..4a64b49d9943 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -314,6 +314,15 @@ public A findAnnotationOnBean(String beanName, Class a return this.beanFactory.findAnnotationOnBean(beanName, annotationType); } + @Override + @Nullable + public A findAnnotationOnBean( + String beanName, Class annotationType, boolean allowFactoryBeanInit) + throws NoSuchBeanDefinitionException { + + return this.beanFactory.findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit); + } + //--------------------------------------------------------------------- // Implementation of HierarchicalBeanFactory interface From 4b7d9b1f9c38c60ac43a752ed1f244abbb1000e2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:48:07 +0100 Subject: [PATCH 67/78] Avoid compilation warning for test class with serializable base class --- .../concurrent/ThreadPoolExecutorFactoryBeanTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java index d350341a8c46..8f81b987c8aa 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBeanTests.java @@ -76,6 +76,7 @@ void executorWithPrestartAllCoreThreads() { verify(threadPoolExecutor).prestartAllCoreThreads(); } + @Configuration static class ExecutorConfig { @@ -83,9 +84,10 @@ static class ExecutorConfig { ThreadPoolExecutorFactoryBean executor() { return new ThreadPoolExecutorFactoryBean(); } - } + + @SuppressWarnings("serial") private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean { @Override From 0ebb1c5baabd280c1587e3be83c223e951f76133 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 16:48:40 +0100 Subject: [PATCH 68/78] Upgrade to Tomcat 9.0.56, Undertow 2.2.14, Apache HttpClient 5.1.2 & HttpAsyncClient 4.1.5, Mockito 4.1 --- build.gradle | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index e1cd581786ad..992aa6391a25 100644 --- a/build.gradle +++ b/build.gradle @@ -128,41 +128,41 @@ configure(allprojects) { project -> dependency "org.webjars:webjars-locator-core:0.48" dependency "org.webjars:underscorejs:1.8.3" - dependencySet(group: 'org.apache.tomcat', version: '9.0.55') { + dependencySet(group: 'org.apache.tomcat', version: '9.0.56') { entry 'tomcat-util' entry('tomcat-websocket') { - exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" exclude group: "org.apache.tomcat", name: "tomcat-servlet-api" + exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" } } - dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.55') { + dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.56') { entry 'tomcat-embed-core' entry 'tomcat-embed-websocket' } - dependencySet(group: 'io.undertow', version: '2.2.13.Final') { + dependencySet(group: 'io.undertow', version: '2.2.14.Final') { entry 'undertow-core' - entry('undertow-websockets-jsr') { - exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" - } entry('undertow-servlet') { exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec" exclude group: "org.jboss.spec.javax.annotation", name: "jboss-annotations-api_1.3_spec" } + entry('undertow-websockets-jsr') { + exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" + } } - dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') { - entry 'okhttp' - entry 'mockwebserver' - } + dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" + dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.2' + dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.2' dependency("org.apache.httpcomponents:httpclient:4.5.13") { exclude group: "commons-logging", name: "commons-logging" } - dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") { + dependency("org.apache.httpcomponents:httpasyncclient:4.1.5") { exclude group: "commons-logging", name: "commons-logging" } - dependency 'org.apache.httpcomponents.client5:httpclient5:5.1' - dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1' - dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" + dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') { + entry 'okhttp' + entry 'mockwebserver' + } dependency "org.jruby:jruby:9.2.20.1" dependency "org.python:jython-standalone:2.7.1" @@ -198,7 +198,7 @@ configure(allprojects) { project -> exclude group: "org.hamcrest", name: "hamcrest-core" } } - dependencySet(group: 'org.mockito', version: '4.0.0') { + dependencySet(group: 'org.mockito', version: '4.1.0') { entry('mockito-core') { exclude group: "org.hamcrest", name: "hamcrest-core" } From f191cf4eb34ceac5d1bf48cca86b6487509b0474 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 18:32:32 +0100 Subject: [PATCH 69/78] Revised comment on explicit LocalDataSourceJobStore configuration See gh-27709 --- .../scheduling/quartz/LocalDataSourceJobStore.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java index e4b09c0798fb..f6042bee9347 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,8 @@ * Subclass of Quartz's {@link JobStoreCMT} class that delegates to a Spring-managed * {@link DataSource} instead of using a Quartz-managed JDBC connection pool. * This JobStore will be used if SchedulerFactoryBean's "dataSource" property is set. - * You may also configure it explicitly, possibly as a custom subclass of this class. + * You may also configure it explicitly, possibly as a custom subclass of this + * {@code LocalDataSourceJobStore} or as an equivalent {@code JobStoreCMT} variant. * *

    Supports both transactional and non-transactional DataSource access. * With a non-XA DataSource and local Spring transactions, a single DataSource From 79804d92c29a71daf1cbdc64cd355f90b01b2765 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 14 Dec 2021 18:36:05 +0100 Subject: [PATCH 70/78] Upgrade to Protobuf 3.19.1, Gson 2.8.9, Woodstox 6.2.7, Apache Johnzon 1.2.15, Caffeine 2.9.3 --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 992aa6391a25..8b51d040e5c1 100644 --- a/build.gradle +++ b/build.gradle @@ -73,17 +73,17 @@ configure(allprojects) { project -> dependency "com.caucho:hessian:4.0.63" dependency "com.fasterxml:aalto-xml:1.3.0" - dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") { + dependency("com.fasterxml.woodstox:woodstox-core:6.2.7") { exclude group: "stax", name: "stax-api" } - dependency "com.google.code.gson:gson:2.8.8" - dependency "com.google.protobuf:protobuf-java-util:3.18.0" + dependency "com.google.code.gson:gson:2.8.9" + dependency "com.google.protobuf:protobuf-java-util:3.19.1" dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4" dependency("com.thoughtworks.xstream:xstream:1.4.18") { exclude group: "xpp3", name: "xpp3_min" exclude group: "xmlpull", name: "xmlpull" } - dependency "org.apache.johnzon:johnzon-jsonb:1.2.14" + dependency "org.apache.johnzon:johnzon-jsonb:1.2.15" dependency("org.codehaus.jettison:jettison:1.3.8") { exclude group: "stax", name: "stax-api" } @@ -95,7 +95,7 @@ configure(allprojects) { project -> dependency "org.yaml:snakeyaml:1.29" dependency "com.h2database:h2:1.4.200" - dependency "com.github.ben-manes.caffeine:caffeine:2.9.2" + dependency "com.github.ben-manes.caffeine:caffeine:2.9.3" dependency "com.github.librepdf:openpdf:1.3.26" dependency "com.rometools:rome:1.16.0" dependency "commons-io:commons-io:2.5" From 4c2e0ee5ffb9fd2e6bf56c4da36f4e2d68f0258a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 15 Dec 2021 09:49:57 +0100 Subject: [PATCH 71/78] Upgrade to Log4j2 2.16.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8b51d040e5c1..f1ba25cf6789 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ configure(allprojects) { project -> mavenBom "org.junit:junit-bom:5.8.2" } dependencies { - dependencySet(group: 'org.apache.logging.log4j', version: '2.15.0') { + dependencySet(group: 'org.apache.logging.log4j', version: '2.16.0') { entry 'log4j-api' entry 'log4j-core' entry 'log4j-jul' From c7642422c3556e7e12279ce92bd7b8ece35cf200 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 14 Dec 2021 09:45:24 +0100 Subject: [PATCH 72/78] Stop resolving CachingConfigurer instances eagerly Closes gh-27751 --- .../AspectJEnableCachingIsolatedTests.java | 8 +- .../config/AbstractJCacheConfiguration.java | 16 ++-- .../AbstractCachingConfiguration.java | 75 ++++++++++++++----- .../cache/config/EnableCachingTests.java | 8 +- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java index 7ca1037efbc4..4235f801df73 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author 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.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; @@ -107,10 +106,7 @@ public void multipleCachingConfigurers() { try { load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class); } - catch (BeanCreationException ex) { - Throwable root = ex.getRootCause(); - boolean condition = root instanceof IllegalStateException; - assertThat(condition).isTrue(); + catch (IllegalStateException ex) { assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue(); } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java index 1cd5ce612f30..4d2af4b9461b 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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.springframework.beans.factory.config.BeanDefinition; import org.springframework.cache.annotation.AbstractCachingConfiguration; -import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource; import org.springframework.cache.jcache.interceptor.JCacheOperationSource; @@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu @Override - protected void useCachingConfigurer(CachingConfigurer config) { - super.useCachingConfigurer(config); - if (config instanceof JCacheConfigurer) { - this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver; - } + protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) { + super.useCachingConfigurer(cachingConfigurerSupplier); + this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> { + if (config instanceof JCacheConfigurer) { + return ((JCacheConfigurer) config).exceptionCacheResolver(); + } + return null; + }); } @Bean(name = "jCacheOperationSource") diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java index c49d8d20c6e0..740ad3096b2b 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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,9 +16,12 @@ package org.springframework.cache.annotation; -import java.util.Collection; +import java.util.List; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.CacheErrorHandler; @@ -30,6 +33,7 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; +import org.springframework.util.function.SingletonSupplier; /** * Abstract base {@code @Configuration} class providing common structure @@ -70,29 +74,60 @@ public void setImportMetadata(AnnotationMetadata importMetadata) { } } - @Autowired(required = false) - void setConfigurers(Collection configurers) { - if (CollectionUtils.isEmpty(configurers)) { - return; - } - if (configurers.size() > 1) { - throw new IllegalStateException(configurers.size() + " implementations of " + - "CachingConfigurer were found when only 1 was expected. " + - "Refactor the configuration such that CachingConfigurer is " + - "implemented only once or not at all."); - } - CachingConfigurer configurer = configurers.iterator().next(); - useCachingConfigurer(configurer); + @Autowired + void setConfigurers(ObjectProvider configurers) { + Supplier cachingConfigurer = () -> { + List candidates = configurers.stream().collect(Collectors.toList()); + if (CollectionUtils.isEmpty(candidates)) { + return null; + } + if (candidates.size() > 1) { + throw new IllegalStateException(candidates.size() + " implementations of " + + "CachingConfigurer were found when only 1 was expected. " + + "Refactor the configuration such that CachingConfigurer is " + + "implemented only once or not at all."); + } + return candidates.get(0); + }; + useCachingConfigurer(new CachingConfigurerSupplier(cachingConfigurer)); } /** * Extract the configuration from the nominated {@link CachingConfigurer}. */ - protected void useCachingConfigurer(CachingConfigurer config) { - this.cacheManager = config::cacheManager; - this.cacheResolver = config::cacheResolver; - this.keyGenerator = config::keyGenerator; - this.errorHandler = config::errorHandler; + protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) { + this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager); + this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver); + this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator); + this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler); + } + + + protected static class CachingConfigurerSupplier { + + private final Supplier supplier; + + public CachingConfigurerSupplier(Supplier supplier) { + this.supplier = SingletonSupplier.of(supplier); + } + + /** + * Adapt the {@link CachingConfigurer} supplier to another supplier + * provided by the specified mapping function. If the underlying + * {@link CachingConfigurer} is {@code null}, {@code null} is returned + * and the mapping function is not invoked. + * @param provider the provider to use to adapt the supplier + * @param the type of the supplier + * @return another supplier mapped by the specified function + */ + @Nullable + public Supplier adapt(Function provider) { + return () -> { + CachingConfigurer cachingConfigurer = this.supplier.get(); + return (cachingConfigurer != null) ? provider.apply(cachingConfigurer) : null; + }; + } + } } diff --git a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java index 8c41ef04570e..b7cb5f1755a8 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.cache.CacheManager; @@ -107,11 +106,8 @@ public void multipleCachingConfigurers() { try { ctx.refresh(); } - catch (BeanCreationException ex) { - Throwable root = ex.getRootCause(); - boolean condition = root instanceof IllegalStateException; - assertThat(condition).isTrue(); - assertThat(root.getMessage().contains("implementations of CachingConfigurer")).isTrue(); + catch (IllegalStateException ex) { + assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue(); } } From de10bb69cb129a45bba41c90b8a2e18d8305b764 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 15 Dec 2021 10:53:09 +0100 Subject: [PATCH 73/78] Stop resolving AsyncConfigurer instances eagerly Closes gh-27808 --- .../AbstractAsyncConfiguration.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java index 60955d50077a..2558ab5684c5 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author 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,11 +16,14 @@ package org.springframework.scheduling.annotation; -import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportAware; @@ -28,6 +31,7 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; +import org.springframework.util.function.SingletonSupplier; /** * Abstract base {@code Configuration} class providing common structure for enabling @@ -65,17 +69,27 @@ public void setImportMetadata(AnnotationMetadata importMetadata) { /** * Collect any {@link AsyncConfigurer} beans through autowiring. */ - @Autowired(required = false) - void setConfigurers(Collection configurers) { - if (CollectionUtils.isEmpty(configurers)) { - return; - } - if (configurers.size() > 1) { - throw new IllegalStateException("Only one AsyncConfigurer may exist"); - } - AsyncConfigurer configurer = configurers.iterator().next(); - this.executor = configurer::getAsyncExecutor; - this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler; + @Autowired + void setConfigurers(ObjectProvider configurers) { + Supplier asyncConfigurer = SingletonSupplier.of(() -> { + List candidates = configurers.stream().collect(Collectors.toList()); + if (CollectionUtils.isEmpty(candidates)) { + return null; + } + if (candidates.size() > 1) { + throw new IllegalStateException("Only one AsyncConfigurer may exist"); + } + return candidates.get(0); + }); + this.executor = adapt(asyncConfigurer, AsyncConfigurer::getAsyncExecutor); + this.exceptionHandler = adapt(asyncConfigurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler); + } + + private Supplier adapt(Supplier supplier, Function provider) { + return () -> { + AsyncConfigurer asyncConfigurer = supplier.get(); + return (asyncConfigurer != null) ? provider.apply(asyncConfigurer) : null; + }; } } From 1885ab3e07eba47d19e6e8505abda721bb692417 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Dec 2021 17:58:28 +0100 Subject: [PATCH 74/78] Polishing --- .../annotation/AbstractCachingConfiguration.java | 2 +- .../core/type/StandardAnnotationMetadata.java | 2 +- .../type/classreading/SimpleAnnotationMetadata.java | 11 ++++++----- .../SimpleAnnotationMetadataReadingVisitor.java | 5 ++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java index 740ad3096b2b..403c6e9dcee3 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java @@ -124,7 +124,7 @@ public CachingConfigurerSupplier(Supplier supplier) { public Supplier adapt(Function provider) { return () -> { CachingConfigurer cachingConfigurer = this.supplier.get(); - return (cachingConfigurer != null) ? provider.apply(cachingConfigurer) : null; + return (cachingConfigurer != null ? provider.apply(cachingConfigurer) : null); }; } diff --git a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java index 60f0a26ff40e..fddd853af521 100644 --- a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java @@ -162,7 +162,7 @@ public Set getAnnotatedMethods(String annotationName) { throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex); } } - return annotatedMethods != null ? annotatedMethods : Collections.emptySet(); + return (annotatedMethods != null ? annotatedMethods : Collections.emptySet()); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java index 5f1ebf4bb51e..c80bc172a270 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java @@ -127,6 +127,11 @@ public String[] getMemberClassNames() { return this.memberClassNames.clone(); } + @Override + public MergedAnnotations getAnnotations() { + return this.annotations; + } + @Override public Set getAnnotationTypes() { Set annotationTypes = this.annotationTypes; @@ -149,13 +154,9 @@ public Set getAnnotatedMethods(String annotationName) { annotatedMethods.add(annotatedMethod); } } - return annotatedMethods != null ? annotatedMethods : Collections.emptySet(); + return (annotatedMethods != null ? annotatedMethods : Collections.emptySet()); } - @Override - public MergedAnnotations getAnnotations() { - return this.annotations; - } @Override public boolean equals(@Nullable Object obj) { diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java index 6422cda99105..a951194ae7dc 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,8 +99,7 @@ public void visitOuterClass(String owner, String name, String desc) { } @Override - public void visitInnerClass(String name, @Nullable String outerName, String innerName, - int access) { + public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) { if (outerName != null) { String className = toClassName(name); String outerClassName = toClassName(outerName); From 0b6a54dcc77ea09a4dafc0dd7a04c34553f1a317 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Dec 2021 17:58:59 +0100 Subject: [PATCH 75/78] Upgrade to R2DBC Arabba-SR11, Kotlin 1.5.32, Jackson 2.12.6 --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index f1ba25cf6789..23057804e45d 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ plugins { id 'io.spring.nohttp' version '0.0.10' id "io.freefair.aspectj" version '6.2.0' apply false id 'org.jetbrains.dokka' version '1.5.0' apply false - id 'org.jetbrains.kotlin.jvm' version '1.5.31' apply false - id "org.jetbrains.kotlin.plugin.serialization" version "1.5.31" apply false + id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false + id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false id 'org.asciidoctor.jvm.convert' version '3.3.2' id 'org.asciidoctor.jvm.pdf' version '3.3.2' id "org.unbroken-dome.xjc" version '2.0.0' apply false @@ -27,13 +27,13 @@ configure(allprojects) { project -> dependencyManagement { imports { - mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5" + mavenBom "com.fasterxml.jackson:jackson-bom:2.12.6" mavenBom "io.netty:netty-bom:4.1.72.Final" mavenBom "io.projectreactor:reactor-bom:2020.0.14" - mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10" + mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR11" mavenBom "io.rsocket:rsocket-bom:1.1.1" mavenBom "org.eclipse.jetty:jetty-bom:9.4.44.v20210927" - mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.31" + mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32" mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2" mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2" mavenBom "org.junit:junit-bom:5.8.2" @@ -353,12 +353,12 @@ configure([rootProject] + javaProjects) { project -> testImplementation("io.mockk:mockk") testImplementation("org.assertj:assertj-core") // Pull in the latest JUnit 5 Launcher API to ensure proper support in IDEs. - testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-suite-engine") testRuntimeOnly("org.apache.logging.log4j:log4j-core") - testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl") testRuntimeOnly("org.apache.logging.log4j:log4j-jul") + testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl") // JSR-305 only used for non-required meta-annotations compileOnly("com.google.code.findbugs:jsr305") testCompileOnly("com.google.code.findbugs:jsr305") From d665977787a31230c2a908fe695fb8fa3af254ee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Dec 2021 21:50:30 +0100 Subject: [PATCH 76/78] Polishing --- .../annotation/AbstractCachingConfiguration.java | 6 +++--- .../context/annotation/AnnotationConfigUtils.java | 8 ++++---- .../annotation/AbstractAsyncConfiguration.java | 12 ++++++------ .../AbstractTransactionManagementConfiguration.java | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java index 403c6e9dcee3..d6473e28e67b 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java @@ -67,7 +67,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware { @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableCaching = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false)); + importMetadata.getAnnotationAttributes(EnableCaching.class.getName())); if (this.enableCaching == null) { throw new IllegalArgumentException( "@EnableCaching is not present on importing class " + importMetadata.getClassName()); @@ -76,7 +76,7 @@ public void setImportMetadata(AnnotationMetadata importMetadata) { @Autowired void setConfigurers(ObjectProvider configurers) { - Supplier cachingConfigurer = () -> { + Supplier configurer = () -> { List candidates = configurers.stream().collect(Collectors.toList()); if (CollectionUtils.isEmpty(candidates)) { return null; @@ -89,7 +89,7 @@ void setConfigurers(ObjectProvider configurers) { } return candidates.get(0); }; - useCachingConfigurer(new CachingConfigurerSupplier(cachingConfigurer)); + useCachingConfigurer(new CachingConfigurerSupplier(configurer)); } /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java index 91b1719b7083..0c6e55670953 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -282,7 +282,7 @@ static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, Class< @Nullable static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annotationClassName) { - return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName, false)); + return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName)); } static Set attributesForRepeatable(AnnotationMetadata metadata, @@ -298,10 +298,10 @@ static Set attributesForRepeatable( Set result = new LinkedHashSet<>(); // Direct annotation present? - addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false)); + addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName)); // Container annotation present? - Map container = metadata.getAnnotationAttributes(containerClassName, false); + Map container = metadata.getAnnotationAttributes(containerClassName); if (container != null && container.containsKey("value")) { for (Map containedAttributes : (Map[]) container.get("value")) { addAttributesIfNotNull(result, containedAttributes); diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java index 2558ab5684c5..53d7749982cf 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java @@ -59,7 +59,7 @@ public abstract class AbstractAsyncConfiguration implements ImportAware { @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableAsync = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false)); + importMetadata.getAnnotationAttributes(EnableAsync.class.getName())); if (this.enableAsync == null) { throw new IllegalArgumentException( "@EnableAsync is not present on importing class " + importMetadata.getClassName()); @@ -71,7 +71,7 @@ public void setImportMetadata(AnnotationMetadata importMetadata) { */ @Autowired void setConfigurers(ObjectProvider configurers) { - Supplier asyncConfigurer = SingletonSupplier.of(() -> { + Supplier configurer = SingletonSupplier.of(() -> { List candidates = configurers.stream().collect(Collectors.toList()); if (CollectionUtils.isEmpty(candidates)) { return null; @@ -81,14 +81,14 @@ void setConfigurers(ObjectProvider configurers) { } return candidates.get(0); }); - this.executor = adapt(asyncConfigurer, AsyncConfigurer::getAsyncExecutor); - this.exceptionHandler = adapt(asyncConfigurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler); + this.executor = adapt(configurer, AsyncConfigurer::getAsyncExecutor); + this.exceptionHandler = adapt(configurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler); } private Supplier adapt(Supplier supplier, Function provider) { return () -> { - AsyncConfigurer asyncConfigurer = supplier.get(); - return (asyncConfigurer != null) ? provider.apply(asyncConfigurer) : null; + AsyncConfigurer configurer = supplier.get(); + return (configurer != null ? provider.apply(configurer) : null); }; } diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.java index 460105d9ea56..0e885081fa23 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ public abstract class AbstractTransactionManagementConfiguration implements Impo @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableTx = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false)); + importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName())); if (this.enableTx == null) { throw new IllegalArgumentException( "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName()); From 31a4c274b7f34cc4fd66bb5c90864ce3ae45fe8e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 15 Dec 2021 21:51:05 +0100 Subject: [PATCH 77/78] Upgrade to SnakeYAML 1.30, MockK 1.12.1, Jetty Reactive HttpClient 1.1.10 --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 23057804e45d..5144dbccf8cd 100644 --- a/build.gradle +++ b/build.gradle @@ -92,7 +92,7 @@ configure(allprojects) { project -> entry 'jibx-run' } dependency "org.ogce:xpp3:1.1.6" - dependency "org.yaml:snakeyaml:1.29" + dependency "org.yaml:snakeyaml:1.30" dependency "com.h2database:h2:1.4.200" dependency "com.github.ben-manes.caffeine:caffeine:2.9.3" @@ -150,7 +150,7 @@ configure(allprojects) { project -> } } - dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9" + dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.10" dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.2' dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.2' dependency("org.apache.httpcomponents:httpclient:4.5.13") { @@ -204,7 +204,7 @@ configure(allprojects) { project -> } entry 'mockito-junit-jupiter' } - dependency "io.mockk:mockk:1.12.0" + dependency "io.mockk:mockk:1.12.1" dependency("net.sourceforge.htmlunit:htmlunit:2.55.0") { exclude group: "commons-logging", name: "commons-logging" From 29185a3d28fa5e9c1b4821ffe519ef6f56b51962 Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 16 Dec 2021 08:35:04 +0000 Subject: [PATCH 78/78] Release v5.3.14 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bc834bbefe0d..63caba700222 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.3.14-SNAPSHOT +version=5.3.14 org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy