Compare commits

...

52 Commits

Author SHA1 Message Date
Spring Builds 03e695ad5f Release v5.3.17 2022-03-17 10:38:12 +00:00
Sam Brannen 29d98285be Add warning to "enclosing classes" search strategy for MergedAnnotations
This commit adds a warning to the Javadoc for the
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES search strategy in
MergedAnnotations with regard to the scope of the search
algorithm.

See gh-28079
2022-03-16 19:23:27 +01:00
Sam Brannen ad708780ed Polish Javadoc for MergedAnnotations 2022-03-16 19:23:27 +01:00
Stephane Nicoll 8789b34ef8 Upgrade to Tomcat 9.0.60 2022-03-16 18:07:38 +01:00
Sam Brannen c9cd53f469 Revert "Deprecate "enclosing classes" search strategy for MergedAnnotations"
This reverts commit 5689395678.

See gh-28079
2022-03-16 15:27:52 +01:00
Sam Brannen c462fe30ed Use Named arguments in parameterized tests 2022-03-16 14:45:47 +01:00
Sam Brannen 8a0c4caff6 Polish Javadoc 2022-03-16 14:33:52 +01:00
Juergen Hoeller 46363c337b Avoid confusing debug log entries with DataClassRowMapper
Closes gh-28179
2022-03-16 11:21:43 +01:00
Juergen Hoeller c1261f2860 Fix accidental exclusion of BeanMapEmitter class
Closes gh-28110
2022-03-16 11:20:49 +01:00
Juergen Hoeller c4c124b16d Upgrade to Log4J 2.17.2, SLF4J 1.7.36, Netty 4.1.75, SmallRye Mutiny 1.4, Hibernate Validator 6.2.3, Mockito 4.4, HtmlUnit 2.59 2022-03-16 11:19:57 +01:00
rstoyanchev ee7f60000e Polishing contribution
Closes gh-28105
2022-03-16 05:56:47 +00:00
azzureman f1fdc11fab Support custom HTTP status in MockClientHttpResponse
See gh-28105
2022-03-16 05:56:23 +00:00
rstoyanchev 21d61316be Unwrap MappingJacksonValue before selecting ObjectMapper
See gh-28045
2022-03-16 05:55:58 +00:00
rstoyanchev cb39b07088 Polishing Jackson encoder tests 2022-03-16 05:55:23 +00:00
danthonywalker 50771237cc Fix ListableBeanFactory#findAnnotationOnBean extension return type
Closes gh-26908
2022-03-15 18:33:35 +01:00
Sam Brannen 9fbf5dc945 Use String#lastIndexOf(int) where possible 2022-03-15 17:03:20 +01:00
Stephane Nicoll a683e9e81b Upgrade to Reactor 2020.0.17
Closes gh-28064
2022-03-15 16:00:48 +01:00
Sam Brannen 6e2e45d18f Apply @SafeVarargs to ManagedMap.ofEntries(...) 2022-03-14 20:24:57 +01:00
Sam Brannen 887389d341 Clarify behavior for generics support in BeanUtils.copyProperties()
Since Spring Framework 5.3, BeanUtils.copyProperties() honors generics
in the source and target property types (see gh-24187); however, this
refinement of the contract was not properly documented prior to this
commit. In addition, the refinement can be a breaking change for users
who were relying on the previous unreliable behavior.

This commit therefore clarifies the behavior for generics support in
BeanUtils.copyProperties() and introduces a table of example matches
and mismatches when generics are involved.

Closes gh-27259
2022-03-12 18:12:23 +01:00
Sam Brannen d9c22e657f Document the effect of @DirtiesContext on test execution events
See gh-27757
2022-03-12 16:05:14 +01:00
Lee, Kyutae 8a510db00d Polish Javadoc for Environment
Closes gh-28170
2022-03-12 14:02:26 +01:00
Stephane Nicoll 9eec6d0834 Start building against Reactor 2020.0.17 snapshots
See gh-28064
2022-03-11 16:18:28 +01:00
Arjen Poutsma a3e23cd5fc Fix unwrapping logic for ResponseEntity<Flux>
This commit makes sure that the response returned by coroutine handler
methods that return ResponseEntity<Flux> is unwrapped correctly.

Closes gh-27809
2022-03-11 11:56:13 +01:00
Sam Brannen af977c0891 Improve documentation for @EnabledIf and @DisabledIf test support
Closes gh-28157
2022-03-10 19:27:41 +01:00
Stephane Nicoll 1bbcd706be Upgrade Ubuntu version in CI image 2022-03-10 10:09:55 +01:00
Sam Brannen 83ac659158 Improve diagnostics in SpEL for large array creation
Attempting to create a large array in a SpEL expression can result in
an OutOfMemoryError. Although the JVM recovers from that, the error
message is not very helpful to the user.

This commit improves the diagnostics in SpEL for large array creation
by throwing a SpelEvaluationException with a meaningful error message
in order to improve diagnostics for the user.

Closes gh-28145
2022-03-09 11:07:45 +01:00
Sam Brannen a2f02dbfc0 Improve documentation for TestContext events
This commit improves the documentation for test execution events,
especially with regard to the fact that, by default, a
BeforeTestClassEvent is not published for the first test class using a
particular ApplicationContext.

This commit also introduces tests that verify the default behavior and
the ability to change the default behavior with a custom
TestExecutionListener that eagerly loads the context.

Closes gh-27757
2022-03-06 17:51:49 +01:00
Sam Brannen 8cbb188455 Fix links to Spring Security in the reference guide
Closes gh-28135
2022-03-05 14:53:52 +01:00
Sam Brannen bc973d7001 Fix broken link to "CORS WebFilter" in ref docs 2022-03-05 14:36:52 +01:00
Sam Brannen fa3130d716 Document that TX rollback rules may result in unintentional matches
Closes gh-28125
2022-03-04 16:39:11 +01:00
Sam Brannen b3e5f86277 Polish rollback rule support 2022-03-03 16:20:13 +01:00
Sam Brannen 340f41af6d Suppress warnings in Gradle build 2022-03-02 17:28:45 +01:00
Sam Brannen 25aa295c2c Rename test class to adhere to conventions 2022-03-02 17:25:37 +01:00
Sam Brannen 67b91b2390 Polish RollbackRuleTests
See gh-28098
2022-03-01 19:10:33 +01:00
Sam Brannen d67034f99b Document semantics for externally managed init/destroy methods
This commit introduces Javadoc to explain the difference between
init/destroy method names when such methods are private, namely that a
private method is registered via its qualified method name; whereas, a
non-private method is registered via its simple name.

See gh-28083
2022-03-01 16:18:46 +01:00
Sam Brannen dcdea986f6 Polish init/destroy lifecycle method tests
See gh-28083
2022-03-01 15:43:25 +01:00
Sam Brannen a524857bd5 Fix init/destroy lifecycle method tests
See gh-28083
2022-03-01 15:23:15 +01:00
Vikey Chen af14eea1ef Introduce tests for gh-28083 2022-03-01 15:22:51 +01:00
Sam Brannen f96872404d Ensure private init/destroy method is invoked only once
Closes gh-28083
2022-03-01 15:03:10 +01:00
Sam Brannen a7d5fbfbea Fix log messages for init/destroy method registration 2022-03-01 15:03:10 +01:00
Sam Brannen 84de100fc6 Polishing 2022-02-28 17:15:43 +01:00
Sam Brannen beab8ab4e7 Test claims regarding SpEL support for T(Character)
See gh-28112
2022-02-28 17:15:43 +01:00
Brian Clozel 6f41180cc5 Align AsyncRestTemplate error logging with RestTemplate
Prior to this commit, `AsyncRestTemplate` would log errors (including
simple 404s) with WARN level. Such errors are quite common and should
not clutter logs.

This commit aligns the logging strategy with RestTemplate, using the
DEBUG level for such cases.

Fixes gh-28049
2022-02-28 16:37:07 +01:00
Arjen Poutsma 7e2106b850 Refactor roll forward in CronField
Before this commit, CronField.Type::rollForward added temporal units
to reach the higher order field. This caused issues with DST, where
the added amount of hours was either too small or too large.

This commit refactors the implementation so that it now adds one to the
higher order field, and reset the current field to the minimum value.

Closes gh-28095
2022-02-24 14:47:02 +01:00
Stephane Nicoll 453c6d41f7 Fix Objenesis version
See gh-28100
2022-02-24 10:54:52 +01:00
Sam Brannen 84b4cebb39 Fix (@)since tag in SpelMessage
See gh-28043
2022-02-19 16:54:16 +01:00
Sam Brannen 5689395678 Deprecate "enclosing classes" search strategy for MergedAnnotations
The TYPE_HIERARCHY_AND_ENCLOSING_CLASSES search strategy for
MergedAnnotations was originally introduced to support @Nested test
classes in JUnit Jupiter (see #23378).

However, while implementing #19930, we determined that the
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES search strategy unfortunately
could not be used since it does not allow the user to control when to
recurse up the enclosing class hierarchy. For example, this search
strategy will automatically search on enclosing classes for static
nested classes as well as for inner classes, when the user probably
only wants one such category of "enclosing class" to be searched.
Consequently, TestContextAnnotationUtils was introduced in the Spring
TestContext Framework to address the shortcomings of the
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES search strategy.

Since this search strategy is unlikely to be useful to general users,
the team has decided to deprecate this search strategy in Spring
Framework 5.3.x and remove it in 6.0.

Closes gh-28079
2022-02-19 16:51:00 +01:00
Sam Brannen 071c2988d5 Suppress deprecation warnings in tests in build 2022-02-18 16:18:13 +01:00
Sam Brannen 94af2ca06b Recover from error during SpEL MIXED mode compilation
Prior to this commit, SpEL was able to recover from an error that
occurred while running a CompiledExpression; however, SpEL was not able
to recover from an error that occurred while compiling the expression
(such as a java.lang.VerifyError). The latter can occur when multiple
threads concurrently change types involved in the expression, such as
the concrete type of a custom variable registered via
EvaluationContext.setVariable(...), which can result in SpEL generating
invalid bytecode.

This commit addresses this issue by catching exceptions thrown while
compiling an expression and updating the `failedAttempts` and
`interpretedCount` counters accordingly. If an exception is caught
while operating in SpelCompilerMode.IMMEDIATE mode, the exception will
be propagated via a SpelEvaluationException with a new
SpelMessage.EXCEPTION_COMPILING_EXPRESSION error category.

Closes gh-28043
2022-02-18 15:31:59 +01:00
Arjen Poutsma ff20a06876 Added .sdkmanrc file
This commit adds a .sdkmanrc file, so that we can automatically switch
to JDK 8 when building the 5.3. branch.
2022-02-17 11:51:14 +01:00
Stephane Nicoll 2ffefbb211 Downgrade to concourse-release-scripts 0.3.2
This commit reverts partially "0ab054c7b943d65bb9034d1d7987f556e9d54d05"
as 0.3.3 is breaking promition.
2022-02-17 09:44:22 +01:00
Spring Builds 4571626839 Next development version (v5.3.17-SNAPSHOT) 2022-02-17 07:45:38 +00:00
109 changed files with 3843 additions and 2554 deletions
+3
View File
@@ -0,0 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=8.0.322-librca
+11 -11
View File
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.6"
mavenBom "io.netty:netty-bom:4.1.74.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.16"
mavenBom "io.netty:netty-bom:4.1.75.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.17"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR12"
mavenBom "io.rsocket:rsocket-bom:1.1.1"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.45.v20220203"
@@ -39,13 +39,13 @@ configure(allprojects) { project ->
mavenBom "org.junit:junit-bom:5.8.2"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.17.1') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.17.2') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
entry 'log4j-slf4j-impl'
}
dependency "org.slf4j:slf4j-api:1.7.35"
dependency "org.slf4j:slf4j-api:1.7.36"
dependency("com.google.code.findbugs:findbugs:3.0.1") {
exclude group: "dom4j", name: "dom4j"
}
@@ -68,7 +68,7 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.3"
dependency "io.smallrye.reactive:mutiny:1.3.1"
dependency "io.smallrye.reactive:mutiny:1.4.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
@@ -124,18 +124,18 @@ configure(allprojects) { project ->
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.33.Final"
dependency "org.hibernate:hibernate-validator:6.2.2.Final"
dependency "org.hibernate:hibernate-validator:6.2.3.Final"
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.58') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.60') {
entry 'tomcat-util'
entry('tomcat-websocket') {
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.58') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.60') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
@@ -198,7 +198,7 @@ configure(allprojects) { project ->
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '4.3.1') {
dependencySet(group: 'org.mockito', version: '4.4.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.12.1"
dependency("net.sourceforge.htmlunit:htmlunit:2.58.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.59.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.58.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.59.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220113
FROM ubuntu:focal-20220302
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1 -1
View File
@@ -14,7 +14,7 @@ rm -rf /var/lib/apt/lists/*
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.3/concourse-release-scripts-0.3.3.jar
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar
###########################################################
# JAVA
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.16-SNAPSHOT
version=5.3.17
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -691,7 +691,25 @@ public abstract class BeanUtils {
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* consider using a full {@link BeanWrapper}.
* <p>As of Spring Framework 5.3, this method honors generic type information
* when matching properties in the source and target objects.
* <p>The following table provides a non-exhaustive set of examples of source
* and target property types that can be copied as well as source and target
* property types that cannot be copied.
* <table border="1">
* <tr><th>source property type</th><th>target property type</th><th>copy supported</th></tr>
* <tr><td>{@code Integer}</td><td>{@code Integer}</td><td>yes</td></tr>
* <tr><td>{@code Integer}</td><td>{@code Number}</td><td>yes</td></tr>
* <tr><td>{@code List<Integer>}</td><td>{@code List<Integer>}</td><td>yes</td></tr>
* <tr><td>{@code List<?>}</td><td>{@code List<?>}</td><td>yes</td></tr>
* <tr><td>{@code List<Integer>}</td><td>{@code List<?>}</td><td>yes</td></tr>
* <tr><td>{@code List<Integer>}</td><td>{@code List<? extends Number>}</td><td>yes</td></tr>
* <tr><td>{@code String}</td><td>{@code Integer}</td><td>no</td></tr>
* <tr><td>{@code Number}</td><td>{@code Integer}</td><td>no</td></tr>
* <tr><td>{@code List<Integer>}</td><td>{@code List<Long>}</td><td>no</td></tr>
* <tr><td>{@code List<Integer>}</td><td>{@code List<Number>}</td><td>no</td></tr>
* </table>
* @param source the source bean
* @param target the target bean
* @throws BeansException if the copying failed
@@ -708,7 +726,10 @@ public abstract class BeanUtils {
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* consider using a full {@link BeanWrapper}.
* <p>As of Spring Framework 5.3, this method honors generic type information
* when matching properties in the source and target objects. See the
* documentation for {@link #copyProperties(Object, Object)} for details.
* @param source the source bean
* @param target the target bean
* @param editable the class (or interface) to restrict property setting to
@@ -726,7 +747,10 @@ public abstract class BeanUtils {
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* consider using a full {@link BeanWrapper}.
* <p>As of Spring Framework 5.3, this method honors generic type information
* when matching properties in the source and target objects. See the
* documentation for {@link #copyProperties(Object, Object)} for details.
* @param source the source bean
* @param target the target bean
* @param ignoreProperties array of property names to ignore
@@ -743,7 +767,8 @@ public abstract class BeanUtils {
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>As of Spring Framework 5.3, this method honors generic type information
* when matching properties in the source and target objects.
* when matching properties in the source and target objects. See the
* documentation for {@link #copyProperties(Object, Object)} for details.
* @param source the source bean
* @param target the target bean
* @param editable the class (or interface) to restrict property setting to
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -302,7 +302,7 @@ public class InitDestroyAnnotationBeanPostProcessor
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
checkedInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element);
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
}
}
}
@@ -313,7 +313,7 @@ public class InitDestroyAnnotationBeanPostProcessor
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
checkedDestroyMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element);
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
}
}
}
@@ -1844,7 +1844,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
@@ -1868,7 +1868,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
@@ -52,6 +52,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Costin Leau
* @author Stephane Nicoll
* @author Sam Brannen
* @since 2.0
* @see AbstractBeanFactory
* @see org.springframework.beans.factory.DisposableBean
@@ -109,12 +110,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
this.beanName = beanName;
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.invokeDisposableBean = (bean instanceof DisposableBean &&
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
!beanDefinition.hasAnyExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
if (destroyMethodName != null &&
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
!beanDefinition.hasAnyExternallyManagedDestroyMethod(destroyMethodName)) {
this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName));
if (!this.invokeAutoCloseable) {
@@ -67,6 +67,7 @@ public class ManagedMap<K, V> extends LinkedHashMap<K, V> implements Mergeable,
* @return a {@code Map} containing the specified mappings
* @since 5.3.16
*/
@SafeVarargs
@SuppressWarnings("unchecked")
public static <K,V> ManagedMap<K,V> ofEntries(Entry<? extends K, ? extends V>... entries) {
ManagedMap<K,V > map = new ManagedMap<>();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ import org.springframework.util.Assert;
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @see GenericBeanDefinition
* @see ChildBeanDefinition
*/
@@ -436,7 +437,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Check whether the given method or field is an externally managed configuration member.
* Determine if the given method or field is an externally managed configuration member.
*/
public boolean isExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
@@ -446,7 +447,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Return all externally managed configuration methods and fields (as an immutable Set).
* Get all externally managed configuration methods and fields (as an immutable Set).
* @since 5.3.11
*/
public Set<Member> getExternallyManagedConfigMembers() {
@@ -458,7 +459,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Register an externally managed configuration initialization method.
* Register an externally managed configuration initialization method &mdash;
* for example, a method annotated with JSR-250's
* {@link javax.annotation.PostConstruct} annotation.
* <p>The supplied {@code initMethod} may be the
* {@linkplain Method#getName() simple method name} for non-private methods or the
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
* qualified method name} for {@code private} methods. A qualified name is
* necessary for {@code private} methods in order to disambiguate between
* multiple private methods with the same name within a class hierarchy.
*/
public void registerExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
@@ -470,7 +479,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Check whether the given method name indicates an externally managed initialization method.
* Determine if the given method name indicates an externally managed
* initialization method.
* <p>See {@link #registerExternallyManagedInitMethod} for details
* regarding the format for the supplied {@code initMethod}.
*/
public boolean isExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
@@ -479,8 +491,40 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
}
/**
* Determine if the given method name indicates an externally managed
* initialization method, regardless of method visibility.
* <p>In contrast to {@link #isExternallyManagedInitMethod(String)}, this
* method also returns {@code true} if there is a {@code private} externally
* managed initialization method that has been
* {@linkplain #registerExternallyManagedInitMethod(String) registered}
* using a qualified method name instead of a simple method name.
* @since 5.3.17
*/
boolean hasAnyExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
if (isExternallyManagedInitMethod(initMethod)) {
return true;
}
if (this.externallyManagedInitMethods != null) {
for (String candidate : this.externallyManagedInitMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(initMethod)) {
return true;
}
}
}
}
return false;
}
}
/**
* Return all externally managed initialization methods (as an immutable Set).
* <p>See {@link #registerExternallyManagedInitMethod} for details
* regarding the format for the initialization methods in the returned set.
* @since 5.3.11
*/
public Set<String> getExternallyManagedInitMethods() {
@@ -492,7 +536,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Register an externally managed configuration destruction method.
* Register an externally managed configuration destruction method &mdash;
* for example, a method annotated with JSR-250's
* {@link javax.annotation.PreDestroy} annotation.
* <p>The supplied {@code destroyMethod} may be the
* {@linkplain Method#getName() simple method name} for non-private methods or the
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
* qualified method name} for {@code private} methods. A qualified name is
* necessary for {@code private} methods in order to disambiguate between
* multiple private methods with the same name within a class hierarchy.
*/
public void registerExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
@@ -504,7 +556,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Check whether the given method name indicates an externally managed destruction method.
* Determine if the given method name indicates an externally managed
* destruction method.
* <p>See {@link #registerExternallyManagedDestroyMethod} for details
* regarding the format for the supplied {@code destroyMethod}.
*/
public boolean isExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
@@ -514,7 +569,39 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
/**
* Return all externally managed destruction methods (as an immutable Set).
* Determine if the given method name indicates an externally managed
* destruction method, regardless of method visibility.
* <p>In contrast to {@link #isExternallyManagedDestroyMethod(String)}, this
* method also returns {@code true} if there is a {@code private} externally
* managed destruction method that has been
* {@linkplain #registerExternallyManagedDestroyMethod(String) registered}
* using a qualified method name instead of a simple method name.
* @since 5.3.17
*/
boolean hasAnyExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
if (isExternallyManagedDestroyMethod(destroyMethod)) {
return true;
}
if (this.externallyManagedDestroyMethods != null) {
for (String candidate : this.externallyManagedDestroyMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(destroyMethod)) {
return true;
}
}
}
}
return false;
}
}
/**
* Get all externally managed destruction methods (as an immutable Set).
* <p>See {@link #registerExternallyManagedDestroyMethod} for details
* regarding the format for the destruction methods in the returned set.
* @since 5.3.11
*/
public Set<String> getExternallyManagedDestroyMethods() {
@@ -64,6 +64,6 @@ inline fun <reified T : Annotation> ListableBeanFactory.getBeansWithAnnotation()
* @author Sebastien Deleuze
* @since 5.0
*/
inline fun <reified T : Annotation> ListableBeanFactory.findAnnotationOnBean(beanName:String): Annotation? =
inline fun <reified T : Annotation> ListableBeanFactory.findAnnotationOnBean(beanName:String): T? =
findAnnotationOnBean(beanName, T::class.java)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -203,8 +203,25 @@ class BeanUtilsTests {
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
}
/**
* {@code Integer} can be copied to {@code Number}.
*/
@Test
void copyPropertiesHonorsGenericTypeMatches() {
void copyPropertiesFromSubTypeToSuperType() {
IntegerHolder integerHolder = new IntegerHolder();
integerHolder.setNumber(42);
NumberHolder numberHolder = new NumberHolder();
BeanUtils.copyProperties(integerHolder, numberHolder);
assertThat(integerHolder.getNumber()).isEqualTo(42);
assertThat(numberHolder.getNumber()).isEqualTo(42);
}
/**
* {@code List<Integer>} can be copied to {@code List<Integer>}.
*/
@Test
void copyPropertiesHonorsGenericTypeMatchesFromIntegerToInteger() {
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
integerListHolder1.getList().add(42);
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
@@ -214,6 +231,68 @@ class BeanUtilsTests {
assertThat(integerListHolder2.getList()).containsOnly(42);
}
/**
* {@code List<?>} can be copied to {@code List<?>}.
*/
@Test
void copyPropertiesHonorsGenericTypeMatchesFromWildcardToWildcard() {
List<?> list = Arrays.asList("foo", 42);
WildcardListHolder1 wildcardListHolder1 = new WildcardListHolder1();
wildcardListHolder1.setList(list);
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
assertThat(wildcardListHolder2.getList()).isEmpty();
BeanUtils.copyProperties(wildcardListHolder1, wildcardListHolder2);
assertThat(wildcardListHolder1.getList()).isEqualTo(list);
assertThat(wildcardListHolder2.getList()).isEqualTo(list);
}
/**
* {@code List<Integer>} can be copied to {@code List<?>}.
*/
@Test
void copyPropertiesHonorsGenericTypeMatchesFromIntegerToWildcard() {
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
integerListHolder1.getList().add(42);
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
BeanUtils.copyProperties(integerListHolder1, wildcardListHolder2);
assertThat(integerListHolder1.getList()).containsOnly(42);
assertThat(wildcardListHolder2.getList()).isEqualTo(Arrays.asList(42));
}
/**
* {@code List<Integer>} can be copied to {@code List<? extends Number>}.
*/
@Test
void copyPropertiesHonorsGenericTypeMatchesForUpperBoundedWildcard() {
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
integerListHolder1.getList().add(42);
NumberUpperBoundedWildcardListHolder numberListHolder = new NumberUpperBoundedWildcardListHolder();
BeanUtils.copyProperties(integerListHolder1, numberListHolder);
assertThat(integerListHolder1.getList()).containsOnly(42);
assertThat(numberListHolder.getList()).hasSize(1);
assertThat(numberListHolder.getList().contains(Integer.valueOf(42))).isTrue();
}
/**
* {@code Number} can NOT be copied to {@code Integer}.
*/
@Test
void copyPropertiesDoesNotCopyeFromSuperTypeToSubType() {
NumberHolder numberHolder = new NumberHolder();
numberHolder.setNumber(Integer.valueOf(42));
IntegerHolder integerHolder = new IntegerHolder();
BeanUtils.copyProperties(numberHolder, integerHolder);
assertThat(numberHolder.getNumber()).isEqualTo(42);
assertThat(integerHolder.getNumber()).isNull();
}
/**
* {@code List<Integer>} can NOT be copied to {@code List<Long>}.
*/
@Test
void copyPropertiesDoesNotHonorGenericTypeMismatches() {
IntegerListHolder1 integerListHolder = new IntegerListHolder1();
@@ -225,6 +304,20 @@ class BeanUtilsTests {
assertThat(longListHolder.getList()).isEmpty();
}
/**
* {@code List<Integer>} can NOT be copied to {@code List<Number>}.
*/
@Test
void copyPropertiesDoesNotHonorGenericTypeMismatchesFromSubTypeToSuperType() {
IntegerListHolder1 integerListHolder = new IntegerListHolder1();
integerListHolder.getList().add(42);
NumberListHolder numberListHolder = new NumberListHolder();
BeanUtils.copyProperties(integerListHolder, numberListHolder);
assertThat(integerListHolder.getList()).containsOnly(42);
assertThat(numberListHolder.getList()).isEmpty();
}
@Test // gh-26531
void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception {
Order original = new Order("test", Arrays.asList("foo", "bar"));
@@ -413,6 +506,90 @@ class BeanUtilsTests {
}
@SuppressWarnings("unused")
private static class NumberHolder {
private Number number;
public Number getNumber() {
return number;
}
public void setNumber(Number number) {
this.number = number;
}
}
@SuppressWarnings("unused")
private static class IntegerHolder {
private Integer number;
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}
@SuppressWarnings("unused")
private static class WildcardListHolder1 {
private List<?> list = new ArrayList<>();
public List<?> getList() {
return list;
}
public void setList(List<?> list) {
this.list = list;
}
}
@SuppressWarnings("unused")
private static class WildcardListHolder2 {
private List<?> list = new ArrayList<>();
public List<?> getList() {
return list;
}
public void setList(List<?> list) {
this.list = list;
}
}
@SuppressWarnings("unused")
private static class NumberUpperBoundedWildcardListHolder {
private List<? extends Number> list = new ArrayList<>();
public List<? extends Number> getList() {
return list;
}
public void setList(List<? extends Number> list) {
this.list = list;
}
}
@SuppressWarnings("unused")
private static class NumberListHolder {
private List<Number> list = new ArrayList<>();
public List<Number> getList() {
return list;
}
public void setList(List<Number> list) {
this.list = list;
}
}
@SuppressWarnings("unused")
private static class IntegerListHolder1 {
@@ -16,9 +16,11 @@
package org.springframework.beans.factory
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test
import kotlin.reflect.full.createInstance
/**
* Mock object based tests for ListableBeanFactory Kotlin extensions
@@ -77,10 +79,12 @@ class ListableBeanFactoryExtensionsTests {
verify { lbf.getBeansWithAnnotation(Bar::class.java) }
}
@Suppress("UNUSED_VARIABLE")
@Test
fun `findAnnotationOnBean with String and reified type parameters`() {
val name = "bar"
lbf.findAnnotationOnBean<Bar>(name)
every { lbf.findAnnotationOnBean(name, Bar::class.java) } returns Bar::class.createInstance()
val annotation: Bar? = lbf.findAnnotationOnBean(name)
verify { lbf.findAnnotationOnBean(name, Bar::class.java) }
}
@@ -18,6 +18,7 @@ package org.springframework.scheduling.support;
import java.time.DateTimeException;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.ValueRange;
import java.util.function.BiFunction;
@@ -168,22 +169,25 @@ abstract class CronField {
* day-of-month, month, day-of-week.
*/
protected enum Type {
NANO(ChronoField.NANO_OF_SECOND),
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
HOUR(ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_MONTH(ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
MONTH(ChronoField.MONTH_OF_YEAR, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
HOUR(ChronoField.HOUR_OF_DAY, ChronoUnit.DAYS, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_MONTH(ChronoField.DAY_OF_MONTH, ChronoUnit.MONTHS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
private final ChronoField field;
private final ChronoUnit higherOrder;
private final ChronoField[] lowerOrders;
Type(ChronoField field, ChronoField... lowerOrders) {
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
this.field = field;
this.higherOrder = higherOrder;
this.lowerOrders = lowerOrders;
}
@@ -266,17 +270,9 @@ abstract class CronField {
* @return the rolled forward temporal
*/
public <T extends Temporal & Comparable<? super T>> T rollForward(T temporal) {
int current = get(temporal);
ValueRange range = temporal.range(this.field);
long amount = range.getMaximum() - current + 1;
T result = this.field.getBaseUnit().addTo(temporal, amount);
current = get(result);
range = result.range(this.field);
// adjust for daylight savings
if (current != range.getMinimum()) {
result = this.field.adjustInto(result, range.getMinimum());
}
return result;
T result = this.higherOrder.addTo(temporal, 1);
ValueRange range = result.range(this.field);
return this.field.adjustInto(result, range.getMinimum());
}
/**
@@ -0,0 +1,279 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests which verify expected <em>init</em> and <em>destroy</em> bean lifecycle
* behavior as requested in
* <a href="https://github.com/spring-projects/spring-framework/issues/8455" target="_blank">SPR-3775</a>.
*
* <p>Specifically, combinations of the following are tested:
* <ul>
* <li>{@link InitializingBean} &amp; {@link DisposableBean} interfaces</li>
* <li>Custom {@link RootBeanDefinition#getInitMethodName() init} &amp;
* {@link RootBeanDefinition#getDestroyMethodName() destroy} methods</li>
* <li>JSR 250's {@link javax.annotation.PostConstruct @PostConstruct} &amp;
* {@link javax.annotation.PreDestroy @PreDestroy} annotations</li>
* </ul>
*
* @author Sam Brannen
* @since 2.5
*/
class InitDestroyMethodLifecycleTests {
@Test
void initDestroyMethods() {
Class<?> beanClass = InitDestroyBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
InitDestroyBean bean = beanFactory.getBean(InitDestroyBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy");
}
@Test
void initializingDisposableInterfaces() {
Class<?> beanClass = CustomInitializingDisposableBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
CustomInitializingDisposableBean bean = beanFactory.getBean(CustomInitializingDisposableBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet", "customInit");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy", "customDestroy");
}
@Test
void initializingDisposableInterfacesWithShadowedMethods() {
Class<?> beanClass = InitializingDisposableWithShadowedMethodsBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
InitializingDisposableWithShadowedMethodsBean bean = beanFactory.getBean(InitializingDisposableWithShadowedMethodsBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("InitializingBean.afterPropertiesSet");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("DisposableBean.destroy");
}
@Test
void jsr250Annotations() {
Class<?> beanClass = CustomAnnotatedInitDestroyBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
CustomAnnotatedInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedInitDestroyBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("postConstruct", "afterPropertiesSet", "customInit");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("preDestroy", "destroy", "customDestroy");
}
@Test
void jsr250AnnotationsWithShadowedMethods() {
Class<?> beanClass = CustomAnnotatedInitDestroyWithShadowedMethodsBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
CustomAnnotatedInitDestroyWithShadowedMethodsBean bean = beanFactory.getBean(CustomAnnotatedInitDestroyWithShadowedMethodsBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.afterPropertiesSet", "customInit");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.destroy", "customDestroy");
}
@Test
void jsr250AnnotationsWithCustomPrivateInitDestroyMethods() {
Class<?> beanClass = CustomAnnotatedPrivateInitDestroyBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit1", "customDestroy1");
CustomAnnotatedPrivateInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedPrivateInitDestroyBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.privateCustomInit1", "afterPropertiesSet");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.privateCustomDestroy1", "destroy");
}
@Test
void jsr250AnnotationsWithCustomSameMethodNames() {
Class<?> beanClass = CustomAnnotatedPrivateSameNameInitDestroyBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit1", "customDestroy1");
CustomAnnotatedPrivateSameNameInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedPrivateSameNameInitDestroyBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.privateCustomInit1", "@PostConstruct.sameNameCustomInit1", "afterPropertiesSet");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.sameNameCustomDestroy1", "@PreDestroy.privateCustomDestroy1", "destroy");
}
@Test
void allLifecycleMechanismsAtOnce() {
Class<?> beanClass = AllInOneBean.class;
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
AllInOneBean bean = beanFactory.getBean(AllInOneBean.class);
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet");
beanFactory.destroySingletons();
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy");
}
private static DefaultListableBeanFactory createBeanFactoryAndRegisterBean(Class<?> beanClass,
String initMethodName, String destroyMethodName) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("lifecycleTestBean", beanDefinition);
return beanFactory;
}
static class InitDestroyBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("InitializingBean.afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
this.destroyMethods.add("DisposableBean.destroy");
}
}
static class CustomInitDestroyBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
public void customInit() throws Exception {
this.initMethods.add("customInit");
}
public void customDestroy() throws Exception {
this.destroyMethods.add("customDestroy");
}
}
static class CustomAnnotatedPrivateInitDestroyBean extends CustomInitializingDisposableBean {
@PostConstruct
private void customInit1() throws Exception {
this.initMethods.add("@PostConstruct.privateCustomInit1");
}
@PreDestroy
private void customDestroy1() throws Exception {
this.destroyMethods.add("@PreDestroy.privateCustomDestroy1");
}
}
static class CustomAnnotatedPrivateSameNameInitDestroyBean extends CustomAnnotatedPrivateInitDestroyBean {
@PostConstruct
@SuppressWarnings("unused")
private void customInit1() throws Exception {
this.initMethods.add("@PostConstruct.sameNameCustomInit1");
}
@PreDestroy
@SuppressWarnings("unused")
private void customDestroy1() throws Exception {
this.destroyMethods.add("@PreDestroy.sameNameCustomDestroy1");
}
}
static class CustomInitializingDisposableBean extends CustomInitDestroyBean
implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
static class CustomAnnotatedInitDestroyBean extends CustomInitializingDisposableBean {
@PostConstruct
public void postConstruct() throws Exception {
this.initMethods.add("postConstruct");
}
@PreDestroy
public void preDestroy() throws Exception {
this.destroyMethods.add("preDestroy");
}
}
static class CustomAnnotatedInitDestroyWithShadowedMethodsBean extends CustomInitializingDisposableBean {
@PostConstruct
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("@PostConstruct.afterPropertiesSet");
}
@PreDestroy
@Override
public void destroy() throws Exception {
this.destroyMethods.add("@PreDestroy.destroy");
}
}
static class AllInOneBean implements InitializingBean, DisposableBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
@PostConstruct
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
@PreDestroy
@Override
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
}
@@ -1,272 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* <p>
* JUnit-3.8-based unit test which verifies expected <em>init</em> and
* <em>destroy</em> bean lifecycle behavior as requested in <a
* href="https://opensource.atlassian.com/projects/spring/browse/SPR-3775"
* target="_blank">SPR-3775</a>.
* </p>
* <p>
* Specifically, combinations of the following are tested:
* </p>
* <ul>
* <li>{@link InitializingBean} &amp; {@link DisposableBean} interfaces</li>
* <li>Custom {@link RootBeanDefinition#getInitMethodName() init} &amp;
* {@link RootBeanDefinition#getDestroyMethodName() destroy} methods</li>
* <li>JSR 250's {@link javax.annotation.PostConstruct @PostConstruct} &amp;
* {@link javax.annotation.PreDestroy @PreDestroy} annotations</li>
* </ul>
*
* @author Sam Brannen
* @since 2.5
*/
public class Spr3775InitDestroyLifecycleTests {
private static final Log logger = LogFactory.getLog(Spr3775InitDestroyLifecycleTests.class);
/** LIFECYCLE_TEST_BEAN. */
private static final String LIFECYCLE_TEST_BEAN = "lifecycleTestBean";
private void debugMethods(Class<?> clazz, String category, List<String> methodNames) {
if (logger.isDebugEnabled()) {
logger.debug(clazz.getSimpleName() + ": " + category + ": " + methodNames);
}
}
private void assertMethodOrdering(Class<?> clazz, String category, List<String> expectedMethods,
List<String> actualMethods) {
debugMethods(clazz, category, actualMethods);
assertThat(ObjectUtils.nullSafeEquals(expectedMethods, actualMethods)).as("Verifying " + category + ": expected<" + expectedMethods + "> but got<" + actualMethods + ">.").isTrue();
}
private DefaultListableBeanFactory createBeanFactoryAndRegisterBean(final Class<?> beanClass,
final String initMethodName, final String destroyMethodName) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition(LIFECYCLE_TEST_BEAN, beanDefinition);
return beanFactory;
}
@Test
public void testInitDestroyMethods() {
final Class<?> beanClass = InitDestroyBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
"afterPropertiesSet", "destroy");
final InitDestroyBean bean = (InitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
}
@Test
public void testInitializingDisposableInterfaces() {
final Class<?> beanClass = CustomInitializingDisposableBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
"customDestroy");
final CustomInitializingDisposableBean bean = (CustomInitializingDisposableBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet", "customInit"),
bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy", "customDestroy"),
bean.destroyMethods);
}
@Test
public void testInitializingDisposableInterfacesWithShadowedMethods() {
final Class<?> beanClass = InitializingDisposableWithShadowedMethodsBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
"afterPropertiesSet", "destroy");
final InitializingDisposableWithShadowedMethodsBean bean = (InitializingDisposableWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("InitializingBean.afterPropertiesSet"),
bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("DisposableBean.destroy"), bean.destroyMethods);
}
@Test
public void testJsr250Annotations() {
final Class<?> beanClass = CustomAnnotatedInitDestroyBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
"customDestroy");
final CustomAnnotatedInitDestroyBean bean = (CustomAnnotatedInitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("postConstruct", "afterPropertiesSet",
"customInit"), bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("preDestroy", "destroy", "customDestroy"),
bean.destroyMethods);
}
@Test
public void testJsr250AnnotationsWithShadowedMethods() {
final Class<?> beanClass = CustomAnnotatedInitDestroyWithShadowedMethodsBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
"customDestroy");
final CustomAnnotatedInitDestroyWithShadowedMethodsBean bean = (CustomAnnotatedInitDestroyWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods",
Arrays.asList("@PostConstruct.afterPropertiesSet", "customInit"), bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("@PreDestroy.destroy", "customDestroy"),
bean.destroyMethods);
}
@Test
public void testAllLifecycleMechanismsAtOnce() {
final Class<?> beanClass = AllInOneBean.class;
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
"afterPropertiesSet", "destroy");
final AllInOneBean bean = (AllInOneBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
beanFactory.destroySingletons();
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
}
public static class InitDestroyBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
public static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("InitializingBean.afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
this.destroyMethods.add("DisposableBean.destroy");
}
}
public static class CustomInitDestroyBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
public void customInit() throws Exception {
this.initMethods.add("customInit");
}
public void customDestroy() throws Exception {
this.destroyMethods.add("customDestroy");
}
}
public static class CustomInitializingDisposableBean extends CustomInitDestroyBean
implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
@Override
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
public static class CustomAnnotatedInitDestroyBean extends CustomInitializingDisposableBean {
@PostConstruct
public void postConstruct() throws Exception {
this.initMethods.add("postConstruct");
}
@PreDestroy
public void preDestroy() throws Exception {
this.destroyMethods.add("preDestroy");
}
}
public static class CustomAnnotatedInitDestroyWithShadowedMethodsBean extends CustomInitializingDisposableBean {
@PostConstruct
@Override
public void afterPropertiesSet() throws Exception {
this.initMethods.add("@PostConstruct.afterPropertiesSet");
}
@PreDestroy
@Override
public void destroy() throws Exception {
this.destroyMethods.add("@PreDestroy.destroy");
}
}
public static class AllInOneBean implements InitializingBean, DisposableBean {
final List<String> initMethods = new ArrayList<>();
final List<String> destroyMethods = new ArrayList<>();
@Override
@PostConstruct
public void afterPropertiesSet() throws Exception {
this.initMethods.add("afterPropertiesSet");
}
@Override
@PreDestroy
public void destroy() throws Exception {
this.destroyMethods.add("destroy");
}
}
}
@@ -1344,6 +1344,14 @@ class CronExpressionTests {
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
cronExpression = CronExpression.parse("0 5 0 * * *");
last = ZonedDateTime.parse("2019-10-27T01:05+02:00[Europe/Amsterdam]");
expected = ZonedDateTime.parse("2019-10-28T00:05+01:00[Europe/Amsterdam]");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
@Test
+2 -1
View File
@@ -76,7 +76,8 @@ jar {
dependsOn cglibRepackJar
from(zipTree(cglibRepackJar.archivePath)) {
include "org/springframework/cglib/**"
exclude "org/springframework/cglib/beans/BeanMap*.class"
exclude "org/springframework/cglib/beans/BeanMap.class"
exclude "org/springframework/cglib/beans/BeanMap\$*.class"
exclude "org/springframework/cglib/core/AbstractClassGenerator*.class"
exclude "org/springframework/cglib/core/AsmApi*.class"
exclude "org/springframework/cglib/core/KeyFactory.class"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,7 +85,7 @@ import org.springframework.util.Assert;
* </tr>
* </table>
*
* <p>{@link MergedAnnotations} can be obtained {@linkplain #from(AnnotatedElement)
* <p>{@code MergedAnnotations} can be obtained {@linkplain #from(AnnotatedElement)
* from} any Java {@link AnnotatedElement}. They may also be used for sources that
* don't use reflection (such as those that directly parse bytecode).
*
@@ -94,7 +94,7 @@ import org.springframework.util.Assert;
* example, {@link SearchStrategy#TYPE_HIERARCHY} will search both superclasses and
* implemented interfaces.
*
* <p>From a {@link MergedAnnotations} instance you can either
* <p>From a {@code MergedAnnotations} instance you can either
* {@linkplain #get(String) get} a single annotation, or {@linkplain #stream()
* stream all annotations} or just those that match {@linkplain #stream(String)
* a specific type}. You can also quickly tell if an annotation
@@ -138,7 +138,7 @@ import org.springframework.util.Assert;
public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>> {
/**
* Determine if the specified annotation is either directly present or
* Determine if the specified annotation type is either directly present or
* meta-present.
* <p>Equivalent to calling {@code get(annotationType).isPresent()}.
* @param annotationType the annotation type to check
@@ -147,7 +147,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
<A extends Annotation> boolean isPresent(Class<A> annotationType);
/**
* Determine if the specified annotation is either directly present or
* Determine if the specified annotation type is either directly present or
* meta-present.
* <p>Equivalent to calling {@code get(annotationType).isPresent()}.
* @param annotationType the fully qualified class name of the annotation type
@@ -157,7 +157,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
boolean isPresent(String annotationType);
/**
* Determine if the specified annotation is directly present.
* Determine if the specified annotation type is directly present.
* <p>Equivalent to calling {@code get(annotationType).isDirectlyPresent()}.
* @param annotationType the annotation type to check
* @return {@code true} if the annotation is directly present
@@ -165,7 +165,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
<A extends Annotation> boolean isDirectlyPresent(Class<A> annotationType);
/**
* Determine if the specified annotation is directly present.
* Determine if the specified annotation type is directly present.
* <p>Equivalent to calling {@code get(annotationType).isDirectlyPresent()}.
* @param annotationType the fully qualified class name of the annotation type
* to check
@@ -256,8 +256,8 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Stream all annotations and meta-annotations that match the specified
* type. The resulting stream follows the same ordering rules as
* {@link #stream()}.
* type.
* <p>The resulting stream follows the same ordering rules as {@link #stream()}.
* @param annotationType the annotation type to match
* @return a stream of matching annotations
*/
@@ -265,8 +265,8 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Stream all annotations and meta-annotations that match the specified
* type. The resulting stream follows the same ordering rules as
* {@link #stream()}.
* type.
* <p>The resulting stream follows the same ordering rules as {@link #stream()}.
* @param annotationType the fully qualified class name of the annotation type
* to match
* @return a stream of matching annotations
@@ -275,7 +275,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Stream all annotations and meta-annotations contained in this collection.
* The resulting stream is ordered first by the
* <p>The resulting stream is ordered first by the
* {@linkplain MergedAnnotation#getAggregateIndex() aggregate index} and then
* by the annotation distance (with the closest annotations first). This ordering
* means that, for most use-cases, the most suitable annotations appear
@@ -287,13 +287,13 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Create a new {@link MergedAnnotations} instance containing all
* annotations and meta-annotations from the specified element. The
* resulting instance will not include any inherited annotations. If you
* want to include those as well you should use
* annotations and meta-annotations from the specified element.
* <p>The resulting instance will not include any inherited annotations. If
* you want to include those as well you should use
* {@link #from(AnnotatedElement, SearchStrategy)} with an appropriate
* {@link SearchStrategy}.
* @param element the source element
* @return a {@link MergedAnnotations} instance containing the element's
* @return a {@code MergedAnnotations} instance containing the element's
* annotations
*/
static MergedAnnotations from(AnnotatedElement element) {
@@ -306,7 +306,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* depending on the {@link SearchStrategy}, related inherited elements.
* @param element the source element
* @param searchStrategy the search strategy to use
* @return a {@link MergedAnnotations} instance containing the merged
* @return a {@code MergedAnnotations} instance containing the merged
* element annotations
*/
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy) {
@@ -321,7 +321,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* @param searchStrategy the search strategy to use
* @param repeatableContainers the repeatable containers that may be used by
* the element annotations or the meta-annotations
* @return a {@link MergedAnnotations} instance containing the merged
* @return a {@code MergedAnnotations} instance containing the merged
* element annotations
*/
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
@@ -340,7 +340,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* the element annotations or the meta-annotations
* @param annotationFilter an annotation filter used to restrict the
* annotations considered
* @return a {@link MergedAnnotations} instance containing the merged
* @return a {@code MergedAnnotations} instance containing the merged
* annotations for the supplied element
*/
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
@@ -355,7 +355,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* Create a new {@link MergedAnnotations} instance from the specified
* annotations.
* @param annotations the annotations to include
* @return a {@link MergedAnnotations} instance containing the annotations
* @return a {@code MergedAnnotations} instance containing the annotations
* @see #from(Object, Annotation...)
*/
static MergedAnnotations from(Annotation... annotations) {
@@ -369,7 +369,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* for information and logging. It does not need to <em>actually</em>
* contain the specified annotations, and it will not be searched.
* @param annotations the annotations to include
* @return a {@link MergedAnnotations} instance containing the annotations
* @return a {@code MergedAnnotations} instance containing the annotations
* @see #from(Annotation...)
* @see #from(AnnotatedElement)
*/
@@ -386,7 +386,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* @param annotations the annotations to include
* @param repeatableContainers the repeatable containers that may be used by
* meta-annotations
* @return a {@link MergedAnnotations} instance containing the annotations
* @return a {@code MergedAnnotations} instance containing the annotations
*/
static MergedAnnotations from(Object source, Annotation[] annotations, RepeatableContainers repeatableContainers) {
return from(source, annotations, repeatableContainers, AnnotationFilter.PLAIN);
@@ -403,7 +403,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* meta-annotations
* @param annotationFilter an annotation filter used to restrict the
* annotations considered
* @return a {@link MergedAnnotations} instance containing the annotations
* @return a {@code MergedAnnotations} instance containing the annotations
*/
static MergedAnnotations from(Object source, Annotation[] annotations,
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
@@ -416,16 +416,16 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Create a new {@link MergedAnnotations} instance from the specified
* collection of directly present annotations. This method allows a
* {@link MergedAnnotations} instance to be created from annotations that
* {@code MergedAnnotations} instance to be created from annotations that
* are not necessarily loaded using reflection. The provided annotations
* must all be {@link MergedAnnotation#isDirectlyPresent() directly present}
* and must have an {@link MergedAnnotation#getAggregateIndex() aggregate
* index} of {@code 0}.
* <p>The resulting {@link MergedAnnotations} instance will contain both the
* specified annotations, and any meta-annotations that can be read using
* <p>The resulting {@code MergedAnnotations} instance will contain both the
* specified annotations and any meta-annotations that can be read using
* reflection.
* @param annotations the annotations to include
* @return a {@link MergedAnnotations} instance containing the annotations
* @return a {@code MergedAnnotations} instance containing the annotations
* @see MergedAnnotation#of(ClassLoader, Object, Class, java.util.Map)
*/
static MergedAnnotations of(Collection<MergedAnnotation<?>> annotations) {
@@ -435,7 +435,8 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Search strategies supported by
* {@link MergedAnnotations#from(AnnotatedElement, SearchStrategy)}.
* {@link MergedAnnotations#from(AnnotatedElement, SearchStrategy)} and
* variants of that method.
*
* <p>Each strategy creates a different set of aggregates that will be
* combined to create the final {@link MergedAnnotations}.
@@ -451,39 +452,49 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
/**
* Find all directly declared annotations as well as any
* {@link Inherited @Inherited} superclass annotations. This strategy
* is only really useful when used with {@link Class} types since the
* {@link Inherited @Inherited} annotation is ignored for all other
* {@linkplain AnnotatedElement annotated elements}. This strategy does
* not search implemented interfaces.
* {@link Inherited @Inherited} superclass annotations.
* <p>This strategy is only really useful when used with {@link Class}
* types since the {@link Inherited @Inherited} annotation is ignored for
* all other {@linkplain AnnotatedElement annotated elements}.
* <p>This strategy does not search implemented interfaces.
*/
INHERITED_ANNOTATIONS,
/**
* Find all directly declared and superclass annotations. This strategy
* is similar to {@link #INHERITED_ANNOTATIONS} except the annotations
* do not need to be meta-annotated with {@link Inherited @Inherited}.
* This strategy does not search implemented interfaces.
* Find all directly declared and superclass annotations.
* <p>This strategy is similar to {@link #INHERITED_ANNOTATIONS} except
* the annotations do not need to be meta-annotated with
* {@link Inherited @Inherited}.
* <p>This strategy does not search implemented interfaces.
*/
SUPERCLASS,
/**
* Perform a full search of the entire type hierarchy, including
* superclasses and implemented interfaces. Superclass annotations do
* not need to be meta-annotated with {@link Inherited @Inherited}.
* superclasses and implemented interfaces.
* <p>Superclass annotations do not need to be meta-annotated with
* {@link Inherited @Inherited}.
*/
TYPE_HIERARCHY,
/**
* Perform a full search of the entire type hierarchy on the source
* <em>and</em> any enclosing classes. This strategy is similar to
* {@link #TYPE_HIERARCHY} except that {@linkplain Class#getEnclosingClass()
* enclosing classes} are also searched. Superclass annotations do not
* need to be meta-annotated with {@link Inherited @Inherited}. When
* searching a {@link Method} source, this strategy is identical to
* {@link #TYPE_HIERARCHY}.
* <em>and</em> any enclosing classes.
* <p>This strategy is similar to {@link #TYPE_HIERARCHY} except that
* {@linkplain Class#getEnclosingClass() enclosing classes} are also
* searched.
* <p>Superclass and enclosing class annotations do not need to be
* meta-annotated with {@link Inherited @Inherited}.
* <p>When searching a {@link Method} source, this strategy is identical
* to {@link #TYPE_HIERARCHY}.
* <p><strong>WARNING:</strong> This strategy searches recursively for
* annotations on the enclosing class for any source type, regardless
* whether the source type is an <em>inner class</em>, a {@code static}
* nested class, or a nested interface. Thus, it may find more annotations
* than you would expect.
*/
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES
}
}
@@ -34,9 +34,9 @@ package org.springframework.core.env;
* <p><em>Properties</em> play an important role in almost all applications, and may
* originate from a variety of sources: properties files, JVM system properties, system
* environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,
* Maps, and so on. The role of the environment object with relation to properties is to
* provide the user with a convenient service interface for configuring property sources
* and resolving properties from them.
* Maps, and so on. The role of the {@code Environment} object with relation to properties
* is to provide the user with a convenient service interface for configuring property
* sources and resolving properties from them.
*
* <p>Beans managed within an {@code ApplicationContext} may register to be {@link
* org.springframework.context.EnvironmentAware EnvironmentAware} or {@code @Inject} the
@@ -50,7 +50,7 @@ package org.springframework.core.env;
* as of Spring 3.1 is registered by default when using
* {@code <context:property-placeholder/>}.
*
* <p>Configuration of the environment object must be done through the
* <p>Configuration of the {@code Environment} object must be done through the
* {@code ConfigurableEnvironment} interface, returned from all
* {@code AbstractApplicationContext} subclass {@code getEnvironment()} methods. See
* {@link ConfigurableEnvironment} Javadoc for usage examples demonstrating manipulation
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,6 +65,8 @@ public abstract class StringUtils {
private static final String FOLDER_SEPARATOR = "/";
private static final char FOLDER_SEPARATOR_CHAR = '/';
private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
private static final String TOP_PATH = "..";
@@ -581,7 +583,7 @@ public abstract class StringUtils {
return null;
}
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR_CHAR);
return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
}
@@ -602,7 +604,7 @@ public abstract class StringUtils {
return null;
}
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR_CHAR);
if (folderIndex > extIndex) {
return null;
}
@@ -622,7 +624,7 @@ public abstract class StringUtils {
return path;
}
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR_CHAR);
if (folderIndex > extIndex) {
return path;
}
@@ -639,11 +641,11 @@ public abstract class StringUtils {
* @return the full file path that results from applying the relative path
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR_CHAR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
newPath += FOLDER_SEPARATOR_CHAR;
}
return newPath + relativePath;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
class DataBufferTests extends AbstractDataBufferAllocatingTests {
@ParameterizedDataBufferAllocatingTest
void byteCountsAndPositions(String displayName, DataBufferFactory bufferFactory) {
void byteCountsAndPositions(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(2);
@@ -78,7 +78,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readPositionSmallerThanZero(String displayName, DataBufferFactory bufferFactory) {
void readPositionSmallerThanZero(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -92,7 +92,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readPositionGreaterThanWritePosition(String displayName, DataBufferFactory bufferFactory) {
void readPositionGreaterThanWritePosition(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -106,7 +106,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writePositionSmallerThanReadPosition(String displayName, DataBufferFactory bufferFactory) {
void writePositionSmallerThanReadPosition(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(2);
@@ -122,7 +122,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writePositionGreaterThanCapacity(String displayName, DataBufferFactory bufferFactory) {
void writePositionGreaterThanCapacity(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -136,7 +136,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeAndRead(String displayName, DataBufferFactory bufferFactory) {
void writeAndRead(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(5);
@@ -157,7 +157,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeNullString(String displayName, DataBufferFactory bufferFactory) {
void writeNullString(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -171,7 +171,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeNullCharset(String displayName, DataBufferFactory bufferFactory) {
void writeNullCharset(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -185,7 +185,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeEmptyString(String displayName, DataBufferFactory bufferFactory) {
void writeEmptyString(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -197,7 +197,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeUtf8String(String displayName, DataBufferFactory bufferFactory) {
void writeUtf8String(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(6);
@@ -211,7 +211,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeUtf8StringOutGrowsCapacity(String displayName, DataBufferFactory bufferFactory) {
void writeUtf8StringOutGrowsCapacity(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(5);
@@ -225,7 +225,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeIsoString(String displayName, DataBufferFactory bufferFactory) {
void writeIsoString(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -239,7 +239,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeMultipleUtf8String(String displayName, DataBufferFactory bufferFactory) {
void writeMultipleUtf8String(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -261,7 +261,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void toStringNullCharset(String displayName, DataBufferFactory bufferFactory) {
void toStringNullCharset(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -275,7 +275,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void toStringUtf8(String displayName, DataBufferFactory bufferFactory) {
void toStringUtf8(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
String spring = "Spring";
@@ -290,7 +290,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void toStringSection(String displayName, DataBufferFactory bufferFactory) {
void toStringSection(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
String spring = "Spring";
@@ -305,7 +305,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void inputStream(String displayName, DataBufferFactory bufferFactory) throws Exception {
void inputStream(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(4);
@@ -339,7 +339,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void inputStreamReleaseOnClose(String displayName, DataBufferFactory bufferFactory) throws Exception {
void inputStreamReleaseOnClose(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -357,7 +357,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void outputStream(String displayName, DataBufferFactory bufferFactory) throws Exception {
void outputStream(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(4);
@@ -377,7 +377,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void expand(String displayName, DataBufferFactory bufferFactory) {
void expand(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -391,7 +391,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void increaseCapacity(String displayName, DataBufferFactory bufferFactory) {
void increaseCapacity(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -404,7 +404,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void decreaseCapacityLowReadPosition(String displayName, DataBufferFactory bufferFactory) {
void decreaseCapacityLowReadPosition(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(2);
@@ -416,7 +416,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void decreaseCapacityHighReadPosition(String displayName, DataBufferFactory bufferFactory) {
void decreaseCapacityHighReadPosition(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(2);
@@ -429,7 +429,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void capacityLessThanZero(String displayName, DataBufferFactory bufferFactory) {
void capacityLessThanZero(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -443,7 +443,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeByteBuffer(String displayName, DataBufferFactory bufferFactory) {
void writeByteBuffer(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer1 = createDataBuffer(1);
@@ -472,7 +472,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeDataBuffer(String displayName, DataBufferFactory bufferFactory) {
void writeDataBuffer(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer1 = createDataBuffer(1);
@@ -495,7 +495,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void asByteBuffer(String displayName, DataBufferFactory bufferFactory) {
void asByteBuffer(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(4);
@@ -516,7 +516,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void asByteBufferIndexLength(String displayName, DataBufferFactory bufferFactory) {
void asByteBufferIndexLength(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -536,7 +536,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void byteBufferContainsDataBufferChanges(String displayName, DataBufferFactory bufferFactory) {
void byteBufferContainsDataBufferChanges(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer dataBuffer = createDataBuffer(1);
@@ -552,7 +552,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void dataBufferContainsByteBufferChanges(String displayName, DataBufferFactory bufferFactory) {
void dataBufferContainsByteBufferChanges(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer dataBuffer = createDataBuffer(1);
@@ -568,7 +568,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void emptyAsByteBuffer(String displayName, DataBufferFactory bufferFactory) {
void emptyAsByteBuffer(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(1);
@@ -580,7 +580,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void indexOf(String displayName, DataBufferFactory bufferFactory) {
void indexOf(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -602,7 +602,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void lastIndexOf(String displayName, DataBufferFactory bufferFactory) {
void lastIndexOf(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -633,7 +633,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void slice(String displayName, DataBufferFactory bufferFactory) {
void slice(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -662,7 +662,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void retainedSlice(String displayName, DataBufferFactory bufferFactory) {
void retainedSlice(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(3);
@@ -691,7 +691,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void spr16351(String displayName, DataBufferFactory bufferFactory) {
void spr16351(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = createDataBuffer(6);
@@ -711,7 +711,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void join(String displayName, DataBufferFactory bufferFactory) {
void join(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer composite = this.bufferFactory.join(Arrays.asList(stringBuffer("a"),
@@ -726,7 +726,7 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void getByte(String displayName, DataBufferFactory bufferFactory) {
void getByte(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer buffer = stringBuffer("abc");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
@ParameterizedDataBufferAllocatingTest
void readInputStream(String displayName, DataBufferFactory bufferFactory) {
void readInputStream(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> flux = DataBufferUtils.readInputStream(
@@ -86,7 +86,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readByteChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readByteChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -98,7 +98,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readByteChannelError(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readByteChannelError(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
ReadableByteChannel channel = mock(ReadableByteChannel.class);
@@ -121,7 +121,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readByteChannelCancel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readByteChannelCancel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -136,7 +136,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAsynchronousFileChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAsynchronousFileChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -148,7 +148,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAsynchronousFileChannelPosition(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAsynchronousFileChannelPosition(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -163,7 +163,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAsynchronousFileChannelError(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAsynchronousFileChannelError(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
AsynchronousFileChannel channel = mock(AsynchronousFileChannel.class);
@@ -195,7 +195,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAsynchronousFileChannelCancel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAsynchronousFileChannelCancel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -210,7 +210,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest // gh-22107
void readAsynchronousFileChannelCancelWithoutDemand(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAsynchronousFileChannelCancelWithoutDemand(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
URI uri = this.resource.getURI();
@@ -224,7 +224,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readPath(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readPath(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> flux = DataBufferUtils.read(this.resource.getFile().toPath(), super.bufferFactory, 3);
@@ -233,7 +233,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readResource(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readResource(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> flux = DataBufferUtils.read(this.resource, super.bufferFactory, 3);
@@ -242,7 +242,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readResourcePosition(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readResourcePosition(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> flux = DataBufferUtils.read(this.resource, 9, super.bufferFactory, 3);
@@ -264,7 +264,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readResourcePositionAndTakeUntil(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readResourcePositionAndTakeUntil(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Resource resource = new ClassPathResource("DataBufferUtilsTests.txt", getClass());
@@ -281,7 +281,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readByteArrayResourcePositionAndTakeUntil(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readByteArrayResourcePositionAndTakeUntil(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Resource resource = new ByteArrayResource("foobarbazqux" .getBytes());
@@ -298,7 +298,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeOutputStream(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeOutputStream(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -315,7 +315,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeWritableByteChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -332,7 +332,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannelErrorInFlux(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeWritableByteChannelErrorInFlux(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -355,7 +355,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannelErrorInWrite(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeWritableByteChannelErrorInWrite(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -383,7 +383,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannelCancel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeWritableByteChannelCancel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -407,7 +407,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeAsynchronousFileChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeAsynchronousFileChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -439,7 +439,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeAsynchronousFileChannelErrorInFlux(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeAsynchronousFileChannelErrorInFlux(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -465,7 +465,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
@ParameterizedDataBufferAllocatingTest
@SuppressWarnings("unchecked")
void writeAsynchronousFileChannelErrorInWrite(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeAsynchronousFileChannelErrorInWrite(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -506,7 +506,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writeAsynchronousFileChannelCanceled(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writeAsynchronousFileChannelCanceled(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -531,7 +531,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void writePath(String displayName, DataBufferFactory bufferFactory) throws Exception {
void writePath(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -548,7 +548,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAndWriteByteChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAndWriteByteChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Path source = Paths.get(
@@ -582,7 +582,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void readAndWriteAsynchronousFileChannel(String displayName, DataBufferFactory bufferFactory) throws Exception {
void readAndWriteAsynchronousFileChannel(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
Path source = Paths.get(
@@ -623,7 +623,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void takeUntilByteCount(String displayName, DataBufferFactory bufferFactory) {
void takeUntilByteCount(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> result = DataBufferUtils.takeUntilByteCount(
@@ -637,7 +637,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void takeUntilByteCountCanceled(String displayName, DataBufferFactory bufferFactory) {
void takeUntilByteCountCanceled(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -654,7 +654,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void takeUntilByteCountError(String displayName, DataBufferFactory bufferFactory) {
void takeUntilByteCountError(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -671,7 +671,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void takeUntilByteCountExact(String displayName, DataBufferFactory bufferFactory) {
void takeUntilByteCountExact(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -690,7 +690,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void skipUntilByteCount(String displayName, DataBufferFactory bufferFactory) {
void skipUntilByteCount(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -708,7 +708,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void skipUntilByteCountCancelled(String displayName, DataBufferFactory bufferFactory) {
void skipUntilByteCountCancelled(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -724,7 +724,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void skipUntilByteCountErrorInFlux(String displayName, DataBufferFactory bufferFactory) {
void skipUntilByteCountErrorInFlux(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -738,7 +738,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void skipUntilByteCountShouldSkipAll(String displayName, DataBufferFactory bufferFactory) {
void skipUntilByteCountShouldSkipAll(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -753,7 +753,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void releaseConsumer(String displayName, DataBufferFactory bufferFactory) {
void releaseConsumer(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -776,7 +776,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void SPR16070(String displayName, DataBufferFactory bufferFactory) throws Exception {
void SPR16070(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
ReadableByteChannel channel = mock(ReadableByteChannel.class);
@@ -807,7 +807,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void join(String displayName, DataBufferFactory bufferFactory) {
void join(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -825,7 +825,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void joinWithLimit(String displayName, DataBufferFactory bufferFactory) {
void joinWithLimit(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -855,7 +855,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void joinErrors(String displayName, DataBufferFactory bufferFactory) {
void joinErrors(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -869,7 +869,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void joinCanceled(String displayName, DataBufferFactory bufferFactory) {
void joinCanceled(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
Flux<DataBuffer> source = Flux.concat(
@@ -885,7 +885,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void matcher(String displayName, DataBufferFactory bufferFactory) {
void matcher(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
@@ -903,7 +903,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void matcher2(String displayName, DataBufferFactory bufferFactory) {
void matcher2(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foooobar");
@@ -923,7 +923,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void matcher3(String displayName, DataBufferFactory bufferFactory) {
void matcher3(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foooobar");
@@ -943,7 +943,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void propagateContextByteChannel(String displayName, DataBufferFactory bufferFactory) throws IOException {
void propagateContextByteChannel(DataBufferFactory bufferFactory) throws IOException {
Path path = Paths.get(this.resource.getURI());
try (SeekableByteChannel out = Files.newByteChannel(this.tempFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
Flux<DataBuffer> result = DataBufferUtils.read(path, bufferFactory, 1024, StandardOpenOption.READ)
@@ -967,7 +967,7 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void propagateContextAsynchronousFileChannel(String displayName, DataBufferFactory bufferFactory) throws IOException {
void propagateContextAsynchronousFileChannel(DataBufferFactory bufferFactory) throws IOException {
Path path = Paths.get(this.resource.getURI());
try (AsynchronousFileChannel out = AsynchronousFileChannel.open(this.tempFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
Flux<DataBuffer> result = DataBufferUtils.read(path, bufferFactory, 1024, StandardOpenOption.READ)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class DataBufferTestUtilsTests extends AbstractDataBufferAllocatingTests {
@ParameterizedDataBufferAllocatingTest
void dumpBytes(String displayName, DataBufferFactory bufferFactory) {
void dumpBytes(DataBufferFactory bufferFactory) {
this.bufferFactory = bufferFactory;
DataBuffer buffer = this.bufferFactory.allocateBuffer(4);
@@ -47,7 +47,7 @@ class DataBufferTestUtilsTests extends AbstractDataBufferAllocatingTests {
}
@ParameterizedDataBufferAllocatingTest
void dumpString(String displayName, DataBufferFactory bufferFactory) {
void dumpString(DataBufferFactory bufferFactory) {
this.bufferFactory = bufferFactory;
DataBuffer buffer = this.bufferFactory.allocateBuffer(4);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,7 +81,7 @@ public abstract class AbstractEncoderTests<E extends Encoder<?>> extends Abstrac
* Helper methods that tests for a variety of encoding scenarios. This methods
* invokes:
* <ul>
* <li>{@link #testEncode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
* <li>{@link #testEncode(Publisher, ResolvableType, MimeType, Map, Consumer)}</li>
* <li>{@link #testEncodeError(Publisher, ResolvableType, MimeType, Map)}</li>
* <li>{@link #testEncodeCancel(Publisher, ResolvableType, MimeType, Map)}</li>
* <li>{@link #testEncodeEmpty(ResolvableType, MimeType, Map)}</li>
@@ -94,30 +94,32 @@ public abstract class AbstractEncoderTests<E extends Encoder<?>> extends Abstrac
*/
protected <T> void testEncodeAll(Publisher<? extends T> input, Class<? extends T> inputClass,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) {
testEncodeAll(input, ResolvableType.forClass(inputClass), stepConsumer, null, null);
testEncodeAll(input, ResolvableType.forClass(inputClass), null, null, stepConsumer);
}
/**
* Helper methods that tests for a variety of decoding scenarios. This methods
* invokes:
* <ul>
* <li>{@link #testEncode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
* <li>{@link #testEncode(Publisher, ResolvableType, MimeType, Map, Consumer)}</li>
* <li>{@link #testEncodeError(Publisher, ResolvableType, MimeType, Map)}</li>
* <li>{@link #testEncodeCancel(Publisher, ResolvableType, MimeType, Map)}</li>
* <li>{@link #testEncodeEmpty(ResolvableType, MimeType, Map)}</li>
* </ul>
*
* @param <T> the output type
* @param input the input to be provided to the encoder
* @param inputType the input type
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
* @param mimeType the mime type to use for decoding. May be {@code null}.
* @param hints the hints used for decoding. May be {@code null}.
* @param <T> the output type
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
*/
protected <T> void testEncodeAll(Publisher<? extends T> input, ResolvableType inputType,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
testEncode(input, inputType, stepConsumer, mimeType, hints);
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) {
testEncode(input, inputType, mimeType, hints, stepConsumer);
testEncodeError(input, inputType, mimeType, hints);
testEncodeCancel(input, inputType, mimeType, hints);
testEncodeEmpty(inputType, mimeType, hints);
@@ -133,25 +135,25 @@ public abstract class AbstractEncoderTests<E extends Encoder<?>> extends Abstrac
*/
protected <T> void testEncode(Publisher<? extends T> input, Class<? extends T> inputClass,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) {
testEncode(input, ResolvableType.forClass(inputClass), stepConsumer, null, null);
testEncode(input, ResolvableType.forClass(inputClass), null, null, stepConsumer);
}
/**
* Test a standard {@link Encoder#encode encode} scenario.
*
* @param <T> the output type
* @param input the input to be provided to the encoder
* @param inputType the input type
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
* @param mimeType the mime type to use for decoding. May be {@code null}.
* @param hints the hints used for decoding. May be {@code null}.
* @param <T> the output type
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
*/
protected <T> void testEncode(Publisher<? extends T> input, ResolvableType inputType,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints,
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) {
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType,
mimeType, hints);
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, mimeType, hints);
StepVerifier.FirstStep<DataBuffer> step = StepVerifier.create(result);
stepConsumer.accept(step);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ import org.springframework.core.io.buffer.NettyDataBufferFactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
@@ -161,20 +162,20 @@ public abstract class AbstractDataBufferAllocatingTests {
@SuppressWarnings("deprecation") // PooledByteBufAllocator no longer supports tinyCacheSize.
public static Stream<Arguments> dataBufferFactories() {
return Stream.of(
arguments("NettyDataBufferFactory - UnpooledByteBufAllocator - preferDirect = true",
new NettyDataBufferFactory(new UnpooledByteBufAllocator(true))),
arguments("NettyDataBufferFactory - UnpooledByteBufAllocator - preferDirect = false",
new NettyDataBufferFactory(new UnpooledByteBufAllocator(false))),
arguments(named("NettyDataBufferFactory - UnpooledByteBufAllocator - preferDirect = true",
new NettyDataBufferFactory(new UnpooledByteBufAllocator(true)))),
arguments(named("NettyDataBufferFactory - UnpooledByteBufAllocator - preferDirect = false",
new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)))),
// 1) Disable caching for reliable leak detection, see https://github.com/netty/netty/issues/5275
// 2) maxOrder is 4 (vs default 11) but can be increased if necessary
arguments("NettyDataBufferFactory - PooledByteBufAllocator - preferDirect = true",
new NettyDataBufferFactory(new PooledByteBufAllocator(true, 1, 1, 4096, 4, 0, 0, 0, true))),
arguments("NettyDataBufferFactory - PooledByteBufAllocator - preferDirect = false",
new NettyDataBufferFactory(new PooledByteBufAllocator(false, 1, 1, 4096, 4, 0, 0, 0, true))),
arguments("DefaultDataBufferFactory - preferDirect = true",
new DefaultDataBufferFactory(true)),
arguments("DefaultDataBufferFactory - preferDirect = false",
new DefaultDataBufferFactory(false))
arguments(named("NettyDataBufferFactory - PooledByteBufAllocator - preferDirect = true",
new NettyDataBufferFactory(new PooledByteBufAllocator(true, 1, 1, 4096, 4, 0, 0, 0, true)))),
arguments(named("NettyDataBufferFactory - PooledByteBufAllocator - preferDirect = false",
new NettyDataBufferFactory(new PooledByteBufAllocator(false, 1, 1, 4096, 4, 0, 0, 0, true)))),
arguments(named("DefaultDataBufferFactory - preferDirect = true",
new DefaultDataBufferFactory(true))),
arguments(named("DefaultDataBufferFactory - preferDirect = false",
new DefaultDataBufferFactory(false)))
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@@ -22,8 +22,8 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
/**
* A method resolver attempts locate a method and returns a command executor that can be
* used to invoke that method. The command executor will be cached but if it 'goes stale'
* A method resolver attempts to locate a method and returns a command executor that can be
* used to invoke that method. The command executor will be cached, but if it 'goes stale'
* the resolvers will be called again.
*
* @author Andy Clement
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,10 +27,11 @@ import java.text.MessageFormat;
* <p>When a message is formatted, it will have this kind of form, capturing the prefix
* and the error kind:
*
* <pre class="code">EL1004E: Type cannot be found 'String'</pre>
* <pre class="code">EL1005E: Type cannot be found 'String'</pre>
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public enum SpelMessage {
@@ -255,7 +256,15 @@ public enum SpelMessage {
/** @since 4.3.17 */
FLAWED_PATTERN(Kind.ERROR, 1073,
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it");
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"),
/** @since 5.3.17 */
EXCEPTION_COMPILING_EXPRESSION(Kind.ERROR, 1074,
"An exception occurred while compiling an expression"),
/** @since 5.3.17 */
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
"Array declares too many elements, exceeding the threshold of ''{0}''");
private final Kind kind;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,10 +53,18 @@ import org.springframework.util.Assert;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class ConstructorReference extends SpelNodeImpl {
/**
* Maximum number of elements permitted in an array declaration, applying
* to one-dimensional as well as multi-dimensional arrays.
* @since 5.3.17
*/
private static final int MAX_ARRAY_ELEMENTS = 256 * 1024; // 256K
private final boolean isArrayConstructor;
@Nullable
@@ -259,14 +267,19 @@ public class ConstructorReference extends SpelNodeImpl {
// Shortcut for 1-dimensional
TypedValue o = this.dimensions[0].getTypedValue(state);
int arraySize = ExpressionUtils.toInt(typeConverter, o);
checkNumElements(arraySize);
newArray = Array.newInstance(componentType, arraySize);
}
else {
// Multi-dimensional - hold onto your hat!
int[] dims = new int[this.dimensions.length];
long numElements = 1;
for (int d = 0; d < this.dimensions.length; d++) {
TypedValue o = this.dimensions[d].getTypedValue(state);
dims[d] = ExpressionUtils.toInt(typeConverter, o);
int arraySize = ExpressionUtils.toInt(typeConverter, o);
dims[d] = arraySize;
numElements *= arraySize;
checkNumElements(numElements);
}
newArray = Array.newInstance(componentType, dims);
}
@@ -327,6 +340,13 @@ public class ConstructorReference extends SpelNodeImpl {
return new TypedValue(newArray);
}
private void checkNumElements(long numElements) {
if (numElements >= MAX_ARRAY_ELEMENTS) {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED, MAX_ARRAY_ELEMENTS);
}
}
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer, Class<?> componentType) {
@@ -200,8 +200,7 @@ public class MethodReference extends SpelNodeImpl {
EvaluationContext evaluationContext) throws SpelEvaluationException {
AccessException accessException = null;
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
for (MethodResolver methodResolver : methodResolvers) {
for (MethodResolver methodResolver : evaluationContext.getMethodResolvers()) {
try {
MethodExecutor methodExecutor = methodResolver.resolve(
evaluationContext, targetObject, this.name, argumentTypes);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,8 @@ public final class SpelCompiler implements Opcodes {
return ReflectionUtils.accessibleConstructor(clazz).newInstance();
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to instantiate CompiledExpression", ex);
throw new IllegalStateException("Failed to instantiate CompiledExpression for expression: " +
expression.toStringAST(), ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ import org.springframework.util.Assert;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class SpelExpression implements Expression {
@@ -522,17 +523,34 @@ public class SpelExpression implements Expression {
// Compiled by another thread before this thread got into the sync block
return true;
}
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
compiledAst = compiler.compile(this.ast);
if (compiledAst != null) {
// Successfully compiled
this.compiledAst = compiledAst;
return true;
try {
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
compiledAst = compiler.compile(this.ast);
if (compiledAst != null) {
// Successfully compiled
this.compiledAst = compiledAst;
return true;
}
else {
// Failed to compile
this.failedAttempts.incrementAndGet();
return false;
}
}
else {
catch (Exception ex) {
// Failed to compile
this.failedAttempts.incrementAndGet();
return false;
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.compiledAst = null;
this.interpretedCount.set(0);
return false;
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_COMPILING_EXPRESSION);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -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.
@@ -41,16 +41,17 @@ import static org.assertj.core.api.Assertions.within;
/**
* Test the examples specified in the documentation.
*
* NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that
* <p>NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that
* you need to update the documentation too !
*
* @author Andy Clement
*/
@SuppressWarnings("rawtypes")
public class SpelDocumentationTests extends AbstractExpressionTests {
class SpelDocumentationTests extends AbstractExpressionTests {
static Inventor tesla ;
static Inventor pupin ;
static Inventor tesla;
static Inventor pupin;
static {
GregorianCalendar c = new GregorianCalendar();
@@ -65,53 +66,24 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
pupin.setPlaceOfBirth(new PlaceOfBirth("Idvor"));
}
static class IEEE {
private String name;
public Inventor[] Members = new Inventor[1];
public List Members2 = new ArrayList();
public Map<String,Object> officers = new HashMap<>();
public List<Map<String, Object>> reverse = new ArrayList<>();
@SuppressWarnings("unchecked")
IEEE() {
officers.put("president",pupin);
List linv = new ArrayList();
linv.add(tesla);
officers.put("advisors",linv);
Members2.add(tesla);
Members2.add(pupin);
reverse.add(officers);
}
public boolean isMember(String name) {
return true;
}
public String getName() { return name; }
public void setName(String n) { this.name = n; }
}
@Test
public void testMethodInvocation() {
void methodInvocation() {
evaluate("'Hello World'.concat('!')","Hello World!",String.class);
}
@Test
public void testBeanPropertyAccess() {
void beanPropertyAccess() {
evaluate("new String('Hello World'.bytes)","Hello World",String.class);
}
@Test
public void testArrayLengthAccess() {
void arrayLengthAccess() {
evaluate("'Hello World'.bytes.length",11,Integer.class);
}
@Test
public void testRootObject() throws Exception {
void rootObject() throws Exception {
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
@@ -129,7 +101,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
}
@Test
public void testEqualityCheck() throws Exception {
void equalityCheck() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -143,13 +115,13 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// Section 7.4.1
@Test
public void testXMLBasedConfig() {
void xmlBasedConfig() {
evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class);
}
// Section 7.5
@Test
public void testLiterals() throws Exception {
void literals() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World"
@@ -169,7 +141,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
}
@Test
public void testPropertyAccess() throws Exception {
void propertyAccess() throws Exception {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
assertThat(year).isEqualTo(1856);
@@ -179,12 +151,12 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
}
@Test
public void testPropertyNavigation() throws Exception {
void propertyNavigation() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
// Inventions Array
StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
// teslaContext.setRootObject(tesla);
// teslaContext.setRootObject(tesla);
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
@@ -206,9 +178,8 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
assertThat(invention).isEqualTo("Wireless communication");
}
@Test
public void testDictionaryAccess() throws Exception {
void dictionaryAccess() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
// Officer's Dictionary
@@ -233,7 +204,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.3
@Test
public void testMethodInvocation2() throws Exception {
void methodInvocation2() throws Exception {
// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
assertThat(c).isEqualTo("bc");
@@ -248,7 +219,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.4.1
@Test
public void testRelationalOperators() throws Exception {
void relationalOperators() throws Exception {
boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class);
assertThat(result).isTrue();
// evaluates to false
@@ -261,7 +232,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
}
@Test
public void testOtherOperators() throws Exception {
void otherOperators() throws Exception {
// evaluates to false
boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
assertThat(falseValue).isFalse();
@@ -278,7 +249,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.4.2
@Test
public void testLogicalOperators() throws Exception {
void logicalOperators() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
@@ -319,7 +290,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.4.3
@Test
public void testNumericalOperators() throws Exception {
void numericalOperators() throws Exception {
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
assertThat(two).isEqualTo(2);
@@ -363,7 +334,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.5
@Test
public void testAssignment() throws Exception {
void assignment() throws Exception {
Inventor inventor = new Inventor();
StandardEvaluationContext inventorContext = new StandardEvaluationContext();
inventorContext.setRootObject(inventor);
@@ -381,7 +352,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.6
@Test
public void testTypes() throws Exception {
void types() throws Exception {
Class<?> dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
assertThat(dateClass).isEqualTo(Date.class);
boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
@@ -391,7 +362,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.7
@Test
public void testConstructors() throws Exception {
void constructors() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
Inventor einstein =
@@ -404,7 +375,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.8
@Test
public void testVariables() throws Exception {
void variables() throws Exception {
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("newName", "Mike Tesla");
@@ -416,9 +387,9 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
assertThat(tesla.getFoo()).isEqualTo("Mike Tesla");
}
@SuppressWarnings("unchecked")
@Test
public void testSpecialVariables() throws Exception {
@SuppressWarnings("unchecked")
void specialVariables() throws Exception {
// create an array of integers
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17);
@@ -435,7 +406,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.9
@Test
public void testFunctions() throws Exception {
void functions() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverseString", StringUtils.class.getDeclaredMethod("reverseString", String.class));
@@ -447,7 +418,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.10
@Test
public void testTernary() throws Exception {
void ternary() throws Exception {
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
assertThat(falseString).isEqualTo("falseExp");
@@ -468,9 +439,9 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.11
@SuppressWarnings("unchecked")
@Test
public void testSelection() throws Exception {
@SuppressWarnings("unchecked")
void selection() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
List<Inventor> list = (List<Inventor>) parser.parseExpression("Members2.?[nationality == 'Serbian']").getValue(societyContext);
@@ -481,7 +452,7 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
// 7.5.12
@Test
public void testTemplating() throws Exception {
void templating() throws Exception {
String randomPhrase =
parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class);
assertThat(randomPhrase.startsWith("random number")).isTrue();
@@ -505,14 +476,39 @@ public class SpelDocumentationTests extends AbstractExpressionTests {
}
}
static class IEEE {
private String name;
public Inventor[] Members = new Inventor[1];
public List Members2 = new ArrayList();
public Map<String,Object> officers = new HashMap<>();
public List<Map<String, Object>> reverse = new ArrayList<>();
@SuppressWarnings("unchecked")
IEEE() {
officers.put("president",pupin);
List linv = new ArrayList();
linv.add(tesla);
officers.put("advisors",linv);
Members2.add(tesla);
Members2.add(pupin);
reverse.add(officers);
}
public boolean isMember(String name) {
return true;
}
public String getName() { return name; }
public void setName(String n) { this.name = n; }
}
static class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
return new StringBuilder(input).reverse().toString();
}
}
@@ -30,7 +30,7 @@ import org.springframework.expression.spel.testresources.PlaceOfBirth;
* <li>The root context object is an Inventor instance {@link Inventor}
* </ul>
*/
public class TestScenarioCreator {
class TestScenarioCreator {
public static StandardEvaluationContext getTestEvaluationContext() {
StandardEvaluationContext testContext = new StandardEvaluationContext();
@@ -76,6 +76,21 @@ class SpelCompilerTests {
assertThat(expression.getValue(context)).asInstanceOf(BOOLEAN).isTrue();
}
@Test // gh-28043
void changingRegisteredVariableTypeDoesNotResultInFailureInMixedMode() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.MIXED, null);
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression sharedExpression = parser.parseExpression("#bean.value");
StandardEvaluationContext context = new StandardEvaluationContext();
Object[] beans = new Object[] {new Bean1(), new Bean2(), new Bean3(), new Bean4()};
IntStream.rangeClosed(1, 1_000_000).parallel().forEach(count -> {
context.setVariable("bean", beans[count % 4]);
assertThat(sharedExpression.getValue(context)).asString().startsWith("1");
});
}
static class OrderedComponent implements Ordered {
@@ -121,4 +136,28 @@ class SpelCompilerTests {
boolean hasSomeProperty();
}
public static class Bean1 {
public String getValue() {
return "11";
}
}
public static class Bean2 {
public Integer getValue() {
return 111;
}
}
public static class Bean3 {
public Float getValue() {
return 1.23f;
}
}
public static class Bean4 {
public Character getValue() {
return '1';
}
}
}
@@ -341,12 +341,6 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
"Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
}
}
else {
// No PropertyDescriptor found
if (rowNumber == 0 && logger.isDebugEnabled()) {
logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'");
}
}
}
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -320,7 +320,7 @@ public class ReactorNettyTcpClient<P> implements TcpOperations<P> {
TcpConnection<P> connection = new ReactorNettyTcpConnection<>(inbound, outbound, codec, completionSink);
scheduler.schedule(() -> this.connectionHandler.afterConnected(connection));
inbound.withConnection(conn -> conn.addHandler(new StompMessageDecoder<>(codec)));
inbound.withConnection(conn -> conn.addHandlerFirst(new StompMessageDecoder<>(codec)));
inbound.receiveObject()
.cast(Message.class)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -419,6 +419,7 @@ public class OpenEntityManagerInViewTests {
private final CountDownLatch latch = new CountDownLatch(1);
@Override
@SuppressWarnings("deprecation")
public void execute(Runnable task, long startTimeout) {
Runnable decoratedTask = () -> {
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.springframework.util.Assert;
*/
public class MockClientHttpResponse extends MockHttpInputMessage implements ClientHttpResponse {
private final HttpStatus status;
private final int statusCode;
/**
@@ -41,7 +41,17 @@ public class MockClientHttpResponse extends MockHttpInputMessage implements Clie
public MockClientHttpResponse(byte[] body, HttpStatus statusCode) {
super(body);
Assert.notNull(statusCode, "HttpStatus is required");
this.status = statusCode;
this.statusCode = statusCode.value();
}
/**
* Variant of {@link #MockClientHttpResponse(byte[], HttpStatus)} with a
* custom HTTP status code.
* @since 5.3.17
*/
public MockClientHttpResponse(byte[] body, int statusCode) {
super(body);
this.statusCode = statusCode;
}
/**
@@ -50,23 +60,34 @@ public class MockClientHttpResponse extends MockHttpInputMessage implements Clie
public MockClientHttpResponse(InputStream body, HttpStatus statusCode) {
super(body);
Assert.notNull(statusCode, "HttpStatus is required");
this.status = statusCode;
this.statusCode = statusCode.value();
}
/**
* Variant of {@link #MockClientHttpResponse(InputStream, HttpStatus)} with a
* custom HTTP status code.
* @since 5.3.17
*/
public MockClientHttpResponse(InputStream body, int statusCode) {
super(body);
this.statusCode = statusCode;
}
@Override
public HttpStatus getStatusCode() throws IOException {
return this.status;
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(this.statusCode);
}
@Override
public int getRawStatusCode() throws IOException {
return this.status.value();
public int getRawStatusCode() {
return this.statusCode;
}
@Override
public String getStatusText() throws IOException {
return this.status.getReasonPhrase();
public String getStatusText() {
HttpStatus status = HttpStatus.resolve(this.statusCode);
return (status != null ? status.getReasonPhrase() : "");
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,16 +17,12 @@
package org.springframework.test.context.event;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListener}
* that publishes test execution events to the
* {@code TestExecutionListener} that publishes test execution events to the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for the currently executing test. Events are only published if the
* {@code ApplicationContext} {@linkplain TestContext#hasApplicationContext()
* has already been loaded}.
* for the currently executing test.
*
* <h3>Supported Events</h3>
* <ul>
@@ -41,11 +37,36 @@ import org.springframework.test.context.support.AbstractTestExecutionListener;
*
* <p>These events may be consumed for various reasons, such as resetting <em>mock</em>
* beans or tracing test execution. One advantage of consuming test events rather
* than implementing a custom {@link TestExecutionListener} is that test events
* may be consumed by any Spring bean registered in the test {@code ApplicationContext},
* and such beans may benefit directly from dependency injection and other features
* of the {@code ApplicationContext}. In contrast, a {@link TestExecutionListener}
* is not a bean in the {@code ApplicationContext}.
* than implementing a custom {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListener} is that test events may be consumed by any Spring bean
* registered in the test {@code ApplicationContext}, and such beans may benefit
* directly from dependency injection and other features of the {@code ApplicationContext}.
* In contrast, a {@code TestExecutionListener} is not a bean in the {@code ApplicationContext}.
*
* <p>Note that the {@code EventPublishingTestExecutionListener} is registered by
* default; however, it only publishes events if the {@code ApplicationContext}
* {@linkplain TestContext#hasApplicationContext() has already been loaded}. This
* prevents the {@code ApplicationContext} from being loaded unnecessarily or too
* early. Consequently, a {@code BeforeTestClassEvent} will not be published until
* after the {@code ApplicationContext} has been loaded by another
* {@code TestExecutionListener}. For example, with the default set of
* {@code TestExecutionListeners} registered, a {@code BeforeTestClassEvent} will
* not be published for the first test class that uses a particular test
* {@code ApplicationContext}, but a {@code BeforeTestClassEvent} will be published
* for any subsequent test class in the same test suite that uses the same test
* {@code ApplicationContext} since the context will already have been loaded
* when subsequent test classes run (as long as the context has not been removed
* from the {@link org.springframework.test.context.cache.ContextCache ContextCache}
* via {@link org.springframework.test.annotation.DirtiesContext @DirtiesContext}
* or the max-size eviction policy). If you wish to ensure that a
* {@code BeforeTestClassEvent} is published for every test class, you need to
* register a {@code TestExecutionListener} that loads the {@code ApplicationContext}
* in the {@link org.springframework.test.context.TestExecutionListener#beforeTestClass
* beforeTestClass} callback, and that {@code TestExecutionListener} must be registered
* before the {@code EventPublishingTestExecutionListener}. Similarly, if
* {@code @DirtiesContext} is used to remove the {@code ApplicationContext} from
* the context cache after the last test method in a given test class, the
* {@code AfterTestClassEvent} will not be published for that test class.
*
* <h3>Exception Handling</h3>
* <p>By default, if a test event listener throws an exception while consuming
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
* for this annotation to have an effect &mdash; for example, via
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
* by default.
*
* @author Frank Scheffler
* @author Sam Brannen
@@ -1,4 +1,4 @@
/**
* Test event annotations for the <em>Spring TestContext Framework</em>.
* Test execution event annotations for the <em>Spring TestContext Framework</em>.
*/
package org.springframework.test.context.event.annotation;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,12 +50,23 @@ import org.springframework.core.annotation.AliasFor;
* public {@literal @}interface DisabledOnMac {}
* </pre>
*
* <p>Please note that {@code @DisabledOnMac} is meant only as an example of what
* is possible. If you have that exact use case, please use the built-in
* {@link org.junit.jupiter.api.condition.DisabledOnOs @DisabledOnOs(MAC)} support
* in JUnit Jupiter.
*
* <p>Since JUnit 5.7, JUnit Jupiter also has a condition annotation named
* {@link org.junit.jupiter.api.condition.DisabledIf @DisabledIf}. Thus, if you
* wish to use Spring's {@code @DisabledIf} support make sure you import the
* annotation type from the correct package.
*
* @author Sam Brannen
* @author Tadaya Tsuyukubo
* @since 5.0
* @see SpringExtension
* @see EnabledIf
* @see org.junit.jupiter.api.Disabled
* @see org.junit.jupiter.api.condition.DisabledIf
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,11 +50,22 @@ import org.springframework.core.annotation.AliasFor;
* public {@literal @}interface EnabledOnMac {}
* </pre>
*
* <p>Please note that {@code @EnabledOnMac} is meant only as an example of what
* is possible. If you have that exact use case, please use the built-in
* {@link org.junit.jupiter.api.condition.EnabledOnOs @EnabledOnOs(MAC)} support
* in JUnit Jupiter.
*
* <p>Since JUnit 5.7, JUnit Jupiter also has a condition annotation named
* {@link org.junit.jupiter.api.condition.EnabledIf @EnabledIf}. Thus, if you
* wish to use Spring's {@code @EnabledIf} support make sure you import the
* annotation type from the correct package.
*
* @author Sam Brannen
* @since 5.0
* @see SpringExtension
* @see DisabledIf
* @see org.junit.jupiter.api.Disabled
* @see org.junit.jupiter.api.condition.EnabledIf
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
package org.springframework.test.web.client.response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
@@ -40,7 +39,7 @@ import org.springframework.util.Assert;
*/
public class DefaultResponseCreator implements ResponseCreator {
private HttpStatus statusCode;
private final int statusCode;
private byte[] content = new byte[0];
@@ -56,6 +55,15 @@ public class DefaultResponseCreator implements ResponseCreator {
*/
protected DefaultResponseCreator(HttpStatus statusCode) {
Assert.notNull(statusCode, "HttpStatus must not be null");
this.statusCode = statusCode.value();
}
/**
* Protected constructor.
* Use static factory methods in {@link MockRestResponseCreators}.
* @since 5.3.17
*/
protected DefaultResponseCreator(int statusCode) {
this.statusCode = statusCode;
}
@@ -111,14 +119,9 @@ public class DefaultResponseCreator implements ResponseCreator {
@Override
public ClientHttpResponse createResponse(@Nullable ClientHttpRequest request) throws IOException {
MockClientHttpResponse response;
if (this.contentResource != null) {
InputStream stream = this.contentResource.getInputStream();
response = new MockClientHttpResponse(stream, this.statusCode);
}
else {
response = new MockClientHttpResponse(this.content, this.statusCode);
}
MockClientHttpResponse response = (this.contentResource != null ?
new MockClientHttpResponse(this.contentResource.getInputStream(), this.statusCode) :
new MockClientHttpResponse(this.content, this.statusCode));
response.getHeaders().putAll(this.headers);
return response;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,6 +117,15 @@ public abstract class MockRestResponseCreators {
return new DefaultResponseCreator(status);
}
/**
* Variant of {@link #withStatus(HttpStatus)} for a custom HTTP status code.
* @param status the response status
* @since 5.3.17
*/
public static DefaultResponseCreator withRawStatus(int status) {
return new DefaultResponseCreator(status);
}
/**
* {@code ResponseCreator} with an internal application {@code IOException}.
* <p>For example, one could use this to simulate a {@code SocketTimeoutException}.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,9 +19,9 @@ package org.springframework.mock.http.server.reactive;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -31,6 +31,8 @@ import org.springframework.http.HttpMethod;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Named.named;
/**
* Unit tests for {@link MockServerHttpRequest}.
@@ -63,21 +65,23 @@ class MockServerHttpRequestTests {
assertThat(request.getURI().toString()).isEqualTo("/foo%20bar?a=b&name%20A=value%20A1&name%20A=value%20A2&name%20B=value%20B1");
}
@ParameterizedTest
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource
void httpMethodNotNullOrEmpty(Executable executable) {
Exception ex = Assertions.assertThrows(IllegalArgumentException.class, executable);
assertThat(ex.getMessage()).contains("HTTP method is required.");
void httpMethodNotNullOrEmpty(ThrowingCallable callable) {
assertThatIllegalArgumentException()
.isThrownBy(callable)
.withMessageContaining("HTTP method is required.");
}
static Stream<Executable> httpMethodNotNullOrEmpty() {
static Stream<Named<ThrowingCallable>> httpMethodNotNullOrEmpty() {
String uriTemplate = "/foo bar?a=b";
return Stream.of(
() -> MockServerHttpRequest.method(null, UriComponentsBuilder.fromUriString(uriTemplate).build("")).build(),
() -> MockServerHttpRequest.method((HttpMethod) null, uriTemplate).build(),
() -> MockServerHttpRequest.method((String) null, uriTemplate).build(),
() -> MockServerHttpRequest.method("", uriTemplate).build(),
() -> MockServerHttpRequest.method(" ", uriTemplate).build()
named("null HttpMethod, URI", () -> MockServerHttpRequest.method(null, UriComponentsBuilder.fromUriString(uriTemplate).build("")).build()),
named("null HttpMethod, uriTemplate", () -> MockServerHttpRequest.method((HttpMethod) null, uriTemplate).build()),
named("null String, uriTemplate", () -> MockServerHttpRequest.method((String) null, uriTemplate).build()),
named("empty String, uriTemplate", () -> MockServerHttpRequest.method("", uriTemplate).build()),
named("blank String, uriTemplate", () -> MockServerHttpRequest.method(" ", uriTemplate).build())
);
}
}
@@ -40,6 +40,7 @@ import org.springframework.test.context.web.WebTestContextBootstrapper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.mock;
import static org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper;
@@ -104,9 +105,9 @@ class BootstrapUtilsTests {
/**
* @since 5.3
*/
@ParameterizedTest(name = "{0}")
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource
void resolveTestContextBootstrapperInEnclosingClassHierarchy(String name, Class<?> testClass, Class<?> expectedBootstrapper) {
void resolveTestContextBootstrapperInEnclosingClassHierarchy(Class<?> testClass, Class<?> expectedBootstrapper) {
assertBootstrapper(testClass, expectedBootstrapper);
}
@@ -129,7 +130,7 @@ class BootstrapUtilsTests {
}
private static Arguments args(Class<?> testClass, Class<? extends TestContextBootstrapper> expectedBootstrapper) {
return arguments(testClass.getSimpleName(), testClass, expectedBootstrapper);
return arguments(named(testClass.getSimpleName(), testClass), expectedBootstrapper);
}
/**
@@ -0,0 +1,237 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.event;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.MethodMode;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.event.annotation.AfterTestClass;
import org.springframework.test.context.event.annotation.AfterTestExecution;
import org.springframework.test.context.event.annotation.AfterTestMethod;
import org.springframework.test.context.event.annotation.BeforeTestClass;
import org.springframework.test.context.event.annotation.BeforeTestExecution;
import org.springframework.test.context.event.annotation.BeforeTestMethod;
import org.springframework.test.context.event.annotation.PrepareTestInstance;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
/**
* Tests for the {@link EventPublishingTestExecutionListener} which verify
* behavior for test context events when {@link DirtiesContext @DirtiesContext}
* is used.
*
* @author Sam Brannen
* @since 5.3.17
* @see https://github.com/spring-projects/spring-framework/issues/27757
*/
class DirtiesContextEventPublishingTests {
private static final List<Class<? extends TestContextEvent>> events = new ArrayList<>();
@BeforeEach
@AfterEach
void resetEvents() {
events.clear();
}
@Test
void classLevelDirtiesContext() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(ClassLevelDirtiesContextTestCase.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
assertThat(events).containsExactly(//
// BeforeTestClassEvent.class -- always missing for 1st test class by default
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class //
);
}
@Test
void methodLevelAfterMethodDirtiesContext() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(MethodLevelAfterMethodDirtiesContextTestCase.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
assertThat(events).containsExactly(//
// BeforeTestClassEvent.class -- always missing for 1st test class by default
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class //
// AfterTestClassEvent.class -- missing b/c of @DirtiestContext "after method" at the method level
);
}
@Test
void methodLevelAfterMethodDirtiesContextWithSubsequentTestMethod() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(MethodLevelAfterMethodDirtiesContextWithSubsequentTestMethodTestCase.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
assertThat(events).containsExactly(//
// BeforeTestClassEvent.class -- always missing for 1st test class by default
// test1()
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
// test2()
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class // b/c @DirtiestContext is not applied for test2()
);
}
@Test
void methodLevelBeforeMethodDirtiesContext() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(MethodLevelBeforeMethodDirtiesContextTestCase.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
assertThat(events).containsExactly(//
// BeforeTestClassEvent.class -- always missing for 1st test class by default
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class // b/c @DirtiestContext happens "before method" at the method level
);
}
@SpringJUnitConfig(Config.class)
// add unique property to get a unique ApplicationContext
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = class-level")
@DirtiesContext
static class ClassLevelDirtiesContextTestCase {
@Test
void test() {
}
}
@SpringJUnitConfig(Config.class)
// add unique property to get a unique ApplicationContext
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-after-method")
static class MethodLevelAfterMethodDirtiesContextTestCase {
@Test
@DirtiesContext
void test1() {
}
}
@SpringJUnitConfig(Config.class)
// add unique property to get a unique ApplicationContext
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-after-method-with-subsequent-test-method")
@TestMethodOrder(DisplayName.class)
static class MethodLevelAfterMethodDirtiesContextWithSubsequentTestMethodTestCase {
@Test
@DirtiesContext
void test1() {
}
@Test
void test2() {
}
}
@SpringJUnitConfig(Config.class)
// add unique property to get a unique ApplicationContext
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-before-method")
static class MethodLevelBeforeMethodDirtiesContextTestCase {
@Test
@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD)
void test() {
}
}
@Configuration
static class Config {
@BeforeTestClass
public void beforeTestClass(BeforeTestClassEvent e) {
events.add(e.getClass());
}
@PrepareTestInstance
public void prepareTestInstance(PrepareTestInstanceEvent e) {
events.add(e.getClass());
}
@BeforeTestMethod
public void beforeTestMethod(BeforeTestMethodEvent e) {
events.add(e.getClass());
}
@BeforeTestExecution
public void beforeTestExecution(BeforeTestExecutionEvent e) {
events.add(e.getClass());
}
@AfterTestExecution
public void afterTestExecution(AfterTestExecutionEvent e) {
events.add(e.getClass());
}
@AfterTestMethod
public void afterTestMethod(AfterTestMethodEvent e) {
events.add(e.getClass());
}
@AfterTestClass
public void afterTestClass(AfterTestClassEvent e) {
events.add(e.getClass());
}
}
}
@@ -0,0 +1,190 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.event;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.context.event.annotation.AfterTestClass;
import org.springframework.test.context.event.annotation.AfterTestExecution;
import org.springframework.test.context.event.annotation.AfterTestMethod;
import org.springframework.test.context.event.annotation.BeforeTestClass;
import org.springframework.test.context.event.annotation.BeforeTestExecution;
import org.springframework.test.context.event.annotation.BeforeTestMethod;
import org.springframework.test.context.event.annotation.PrepareTestInstance;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
/**
* Tests for the {@link EventPublishingTestExecutionListener} which verify that
* a {@link BeforeTestClassEvent} can be eagerly published; whereas, such an
* event is not published by default for the first run of a test class for a
* specific {@code ApplicationContext}.
*
* @author Sam Brannen
* @since 5.3.17
* @see https://github.com/spring-projects/spring-framework/issues/27757
*/
class EagerTestExecutionEventPublishingTests {
private static final List<Class<? extends TestContextEvent>> events = new ArrayList<>();
@BeforeEach
@AfterEach
void resetEvents() {
events.clear();
}
@Test
void beforeTestClassEventIsNotPublishedByDefaultForFirstTestClass() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(LazyTestCase1.class), selectClass(LazyTestCase2.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
assertThat(events).containsExactly(//
// 1st test class
// BeforeTestClassEvent.class -- missing for 1st test class
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class, //
// 2nd test class
BeforeTestClassEvent.class, //
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class//
);
}
@Test
void beforeTestClassEventIsPublishedForAllTestClassesIfCustomListenerEagerlyLoadsContext() {
EngineTestKit.engine("junit-jupiter")//
.selectors(selectClass(EagerTestCase1.class), selectClass(EagerTestCase2.class))//
.execute()//
.testEvents()//
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
assertThat(events).containsExactly(//
// 1st test class
BeforeTestClassEvent.class, //
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class, //
// 2nd test class
BeforeTestClassEvent.class, //
PrepareTestInstanceEvent.class, //
BeforeTestMethodEvent.class, //
BeforeTestExecutionEvent.class, //
AfterTestExecutionEvent.class, //
AfterTestMethodEvent.class, //
AfterTestClassEvent.class//
);
}
@SpringJUnitConfig(Config.class)
static class LazyTestCase1 {
@Test
void test() {
}
}
static class LazyTestCase2 extends LazyTestCase1 {
}
@TestExecutionListeners(listeners = EagerLoadingTestExecutionListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
static class EagerTestCase1 extends LazyTestCase1 {
}
static class EagerTestCase2 extends EagerTestCase1 {
}
@Configuration
static class Config {
@BeforeTestClass
public void beforeTestClass(BeforeTestClassEvent e) {
events.add(e.getClass());
}
@PrepareTestInstance
public void prepareTestInstance(PrepareTestInstanceEvent e) {
events.add(e.getClass());
}
@BeforeTestMethod
public void beforeTestMethod(BeforeTestMethodEvent e) {
events.add(e.getClass());
}
@BeforeTestExecution
public void beforeTestExecution(BeforeTestExecutionEvent e) {
events.add(e.getClass());
}
@AfterTestExecution
public void afterTestExecution(AfterTestExecutionEvent e) {
events.add(e.getClass());
}
@AfterTestMethod
public void afterTestMethod(AfterTestMethodEvent e) {
events.add(e.getClass());
}
@AfterTestClass
public void afterTestClass(AfterTestClassEvent e) {
events.add(e.getClass());
}
}
@Order(0)
static class EagerLoadingTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestClass(TestContext testContext) {
testContext.getApplicationContext();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,10 +27,10 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
@@ -53,13 +53,11 @@ class GenericXmlContextLoaderResourceLocationsTests {
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("contextConfigurationLocationsData")
void assertContextConfigurationLocations(String testClassName, String[] expectedLocations) throws Exception {
Class<?> testClass = ClassUtils.forName(getClass().getName() + "$" + testClassName, getClass().getClassLoader());
final ContextConfiguration contextConfig = testClass.getAnnotation(ContextConfiguration.class);
final ContextLoader contextLoader = new GenericXmlContextLoader();
final String[] configuredLocations = (String[]) AnnotationUtils.getValue(contextConfig);
final String[] processedLocations = contextLoader.processLocations(testClass, configuredLocations);
void assertContextConfigurationLocations(Class<?> testClass, String[] expectedLocations) throws Exception {
ContextConfiguration contextConfig = testClass.getAnnotation(ContextConfiguration.class);
ContextLoader contextLoader = new GenericXmlContextLoader();
String[] configuredLocations = (String[]) AnnotationUtils.getValue(contextConfig);
String[] processedLocations = contextLoader.processLocations(testClass, configuredLocations);
if (logger.isDebugEnabled()) {
logger.debug("----------------------------------------------------------------------");
@@ -73,29 +71,32 @@ class GenericXmlContextLoaderResourceLocationsTests {
static Stream<Arguments> contextConfigurationLocationsData() {
return Stream.of(
arguments(ClasspathNonExistentDefaultLocationsTestCase.class.getSimpleName(), array()),
args(ClasspathNonExistentDefaultLocationsTestCase.class, array()),
arguments(ClasspathExistentDefaultLocationsTestCase.class.getSimpleName(), array(
args(ClasspathExistentDefaultLocationsTestCase.class, array(
"classpath:org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$ClasspathExistentDefaultLocationsTestCase-context.xml")),
arguments(ImplicitClasspathLocationsTestCase.class.getSimpleName(),
args(ImplicitClasspathLocationsTestCase.class,
array("classpath:/org/springframework/test/context/support/context1.xml",
"classpath:/org/springframework/test/context/support/context2.xml")),
arguments(ExplicitClasspathLocationsTestCase.class.getSimpleName(), array("classpath:context.xml")),
args(ExplicitClasspathLocationsTestCase.class, array("classpath:context.xml")),
arguments(ExplicitFileLocationsTestCase.class.getSimpleName(),
array("file:/testing/directory/context.xml")),
args(ExplicitFileLocationsTestCase.class, array("file:/testing/directory/context.xml")),
arguments(ExplicitUrlLocationsTestCase.class.getSimpleName(), array("https://example.com/context.xml")),
args(ExplicitUrlLocationsTestCase.class, array("https://example.com/context.xml")),
arguments(ExplicitMixedPathTypesLocationsTestCase.class.getSimpleName(),
args(ExplicitMixedPathTypesLocationsTestCase.class,
array("classpath:/org/springframework/test/context/support/context1.xml", "classpath:context2.xml",
"classpath:/context3.xml", "file:/testing/directory/context.xml",
"https://example.com/context.xml"))
);
}
private static Arguments args(Class<?> testClass, String[] expectedLocations) {
return arguments(named(testClass.getSimpleName(), testClass), expectedLocations);
}
private static String[] array(String... elements) {
return elements;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -128,6 +128,15 @@ class ResponseCreatorsTests {
assertThat(StreamUtils.copyToByteArray(response.getBody()).length).isEqualTo(0);
}
@Test
void withCustomStatus() throws Exception {
DefaultResponseCreator responseCreator = MockRestResponseCreators.withRawStatus(454);
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
assertThat(response.getRawStatusCode()).isEqualTo(454);
assertThat(response.getStatusText()).isEmpty();
}
@Test
void withException() {
ResponseCreator responseCreator = MockRestResponseCreators.withException(new SocketTimeoutException());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,17 +37,57 @@ import org.springframework.transaction.TransactionDefinition;
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction">Transaction Management</a>
* section of the reference manual.
*
* <p>This annotation type is generally directly comparable to Spring's
* <p>This annotation is generally directly comparable to Spring's
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
* convert the data to the latter class, so that Spring's transaction support code
* does not have to know about annotations. If no custom rollback rules apply,
* the transaction will roll back on {@link RuntimeException} and {@link Error}
* but not on checked exceptions.
* convert this annotation's attributes to properties in {@code RuleBasedTransactionAttribute},
* so that Spring's transaction support code does not have to know about annotations.
*
* <p>For specific information about the semantics of this annotation's attributes,
* consult the {@link org.springframework.transaction.TransactionDefinition} and
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
* <h3>Attribute Semantics</h3>
*
* <p>If no custom rollback rules are configured in this annotation, the transaction
* will roll back on {@link RuntimeException} and {@link Error} but not on checked
* exceptions.
*
* <p>Rollback rules determine if a transaction should be rolled back when a given
* exception is thrown, and the rules are based on patterns. A pattern can be a
* fully qualified class name or a substring of a fully qualified class name for
* an exception type (which must be a subclass of {@code Throwable}), with no
* wildcard support at present. For example, a value of
* {@code "javax.servlet.ServletException"} or {@code "ServletException"} will
* match {@code javax.servlet.ServletException} and its subclasses.
*
* <p>Rollback rules may be configured via {@link #rollbackFor}/{@link #noRollbackFor}
* and {@link #rollbackForClassName}/{@link #noRollbackForClassName}, which allow
* patterns to be specified as {@link Class} references or {@linkplain String
* strings}, respectively. When an exception type is specified as a class reference
* its fully qualified name will be used as the pattern. Consequently,
* {@code @Transactional(rollbackFor = example.CustomException.class)} is equivalent
* to {@code @Transactional(rollbackForClassName = "example.CustomException")}.
*
* <p><strong>WARNING:</strong> You must carefully consider how specific the pattern
* is and whether to include package information (which isn't mandatory). For example,
* {@code "Exception"} will match nearly anything and will probably hide other
* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
* were meant to define a rule for all checked exceptions. With more unique
* exception names such as {@code "BaseBusinessException"} there is likely no
* need to use the fully qualified class name for the exception pattern. Furthermore,
* rollback rules may result in unintentional matches for similarly named exceptions
* and nested classes. This is due to the fact that a thrown exception is considered
* to be a match for a given rollback rule if the name of thrown exception contains
* the exception pattern configured for the rollback rule. For example, given a
* rule configured to match on {@code com.example.CustomException}, that rule
* would match against an exception named
* {@code com.example.CustomExceptionV2} (an exception in the same package as
* {@code CustomException} but with an additional suffix) or an exception named
* {@code com.example.CustomException$AnotherException}
* (an exception declared as a nested class in {@code CustomException}).
*
* <p>For specific information about the semantics of other attributes in this
* annotation, consult the {@link org.springframework.transaction.TransactionDefinition}
* and {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
*
* <h3>Transaction Management</h3>
*
* <p>This annotation commonly works with thread-bound transactions managed by a
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
@@ -167,37 +207,33 @@ public @interface Transactional {
boolean readOnly() default false;
/**
* Defines zero (0) or more exception {@link Class classes}, which must be
* Defines zero (0) or more exception {@linkplain Class classes}, which must be
* subclasses of {@link Throwable}, indicating which exception types must cause
* a transaction rollback.
* <p>By default, a transaction will be rolling back on {@link RuntimeException}
* <p>By default, a transaction will be rolled back on {@link RuntimeException}
* and {@link Error} but not on checked exceptions (business exceptions). See
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
* for a detailed explanation.
* <p>This is the preferred way to construct a rollback rule (in contrast to
* {@link #rollbackForClassName}), matching the exception class and its subclasses.
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
* {@link #rollbackForClassName}), matching the exception type, its subclasses,
* and its nested classes. See the {@linkplain Transactional class-level javadocs}
* for further details on rollback rule semantics and warnings regarding possible
* unintentional matches.
* @see #rollbackForClassName
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class)
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* Defines zero (0) or more exception names (for exceptions which must be a
* Defines zero (0) or more exception name patterns (for exceptions which must be a
* subclass of {@link Throwable}), indicating which exception types must cause
* a transaction rollback.
* <p>This can be a substring of a fully qualified class name, with no wildcard
* support at present. For example, a value of {@code "ServletException"} would
* match {@code javax.servlet.ServletException} and its subclasses.
* <p><b>NB:</b> Consider carefully how specific the pattern is and whether
* to include package information (which isn't mandatory). For example,
* {@code "Exception"} will match nearly anything and will probably hide other
* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
* were meant to define a rule for all checked exceptions. With more unusual
* {@link Exception} names such as {@code "BaseBusinessException"} there is no
* need to use a FQN.
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.
* <p>See the {@linkplain Transactional class-level javadocs} for further details
* on rollback rule semantics, patterns, and warnings regarding possible
* unintentional matches.
* @see #rollbackFor
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String)
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
String[] rollbackForClassName() default {};
@@ -206,23 +242,26 @@ public @interface Transactional {
* Defines zero (0) or more exception {@link Class Classes}, which must be
* subclasses of {@link Throwable}, indicating which exception types must
* <b>not</b> cause a transaction rollback.
* <p>This is the preferred way to construct a rollback rule (in contrast
* to {@link #noRollbackForClassName}), matching the exception class and
* its subclasses.
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.
* <p>This is the preferred way to construct a rollback rule (in contrast to
* {@link #noRollbackForClassName}), matching the exception type, its subclasses,
* and its nested classes. See the {@linkplain Transactional class-level javadocs}
* for further details on rollback rule semantics and warnings regarding possible
* unintentional matches.
* @see #noRollbackForClassName
* @see org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class)
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* Defines zero (0) or more exception names (for exceptions which must be a
* Defines zero (0) or more exception name patterns (for exceptions which must be a
* subclass of {@link Throwable}) indicating which exception types must <b>not</b>
* cause a transaction rollback.
* <p>See the description of {@link #rollbackForClassName} for further
* information on how the specified names are treated.
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.
* <p>See the {@linkplain Transactional class-level javadocs} for further details
* on rollback rule semantics, patterns, and warnings regarding possible
* unintentional matches.
* @see #noRollbackFor
* @see org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String)
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
String[] noRollbackForClassName() default {};
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ package org.springframework.transaction.interceptor;
* to the {@code RollbackRuleAttribute} superclass.
*
* @author Rod Johnson
* @author Sam Brannen
* @since 09.04.2003
*/
@SuppressWarnings("serial")
@@ -28,22 +29,28 @@ public class NoRollbackRuleAttribute extends RollbackRuleAttribute {
/**
* Create a new instance of the {@code NoRollbackRuleAttribute} class
* for the supplied {@link Throwable} class.
* @param clazz the {@code Throwable} class
* for the given {@code exceptionType}.
* @param exceptionType exception type; must be {@link Throwable} or a subclass
* of {@code Throwable}
* @throws IllegalArgumentException if the supplied {@code exceptionType} is
* not a {@code Throwable} type or is {@code null}
* @see RollbackRuleAttribute#RollbackRuleAttribute(Class)
*/
public NoRollbackRuleAttribute(Class<?> clazz) {
super(clazz);
public NoRollbackRuleAttribute(Class<?> exceptionType) {
super(exceptionType);
}
/**
* Create a new instance of the {@code NoRollbackRuleAttribute} class
* for the supplied {@code exceptionName}.
* @param exceptionName the exception name pattern
* for the supplied {@code exceptionPattern}.
* @param exceptionPattern the exception name pattern; can also be a fully
* package-qualified class name
* @throws IllegalArgumentException if the supplied {@code exceptionPattern}
* is {@code null} or empty
* @see RollbackRuleAttribute#RollbackRuleAttribute(String)
*/
public NoRollbackRuleAttribute(String exceptionName) {
super(exceptionName);
public NoRollbackRuleAttribute(String exceptionPattern) {
super(exceptionPattern);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,13 +22,29 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Rule determining whether or not a given exception (and any subclasses)
* should cause a rollback.
* Rule determining whether or not a given exception should cause a rollback.
*
* <p>Multiple such rules can be applied to determine whether a transaction
* should commit or rollback after an exception has been thrown.
*
* <p>Each rule is based on an exception pattern which can be a fully qualified
* class name or a substring of a fully qualified class name for an exception
* type (which must be a subclass of {@code Throwable}), with no wildcard support
* at present. For example, a value of {@code "javax.servlet.ServletException"}
* or {@code "ServletException"} would match {@code javax.servlet.ServletException}
* and its subclasses.
*
* <p>An exception pattern can be specified as a {@link Class} reference or a
* {@link String} in {@link #RollbackRuleAttribute(Class)} and
* {@link #RollbackRuleAttribute(String)}, respectively. When an exception type
* is specified as a class reference its fully qualified name will be used as the
* pattern. See the javadocs for
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
* for further details on rollback rule semantics, patterns, and warnings regarding
* possible unintentional matches.
*
* @author Rod Johnson
* @author Sam Brannen
* @since 09.04.2003
* @see NoRollbackRuleAttribute
*/
@@ -36,7 +52,7 @@ import org.springframework.util.Assert;
public class RollbackRuleAttribute implements Serializable{
/**
* The {@link RollbackRuleAttribute rollback rule} for
* The {@linkplain RollbackRuleAttribute rollback rule} for
* {@link RuntimeException RuntimeExceptions}.
*/
public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS =
@@ -48,79 +64,90 @@ public class RollbackRuleAttribute implements Serializable{
* This way does multiple string comparisons, but how often do we decide
* whether to roll back a transaction following an exception?
*/
private final String exceptionName;
private final String exceptionPattern;
/**
* Create a new instance of the {@code RollbackRuleAttribute} class.
* Create a new instance of the {@code RollbackRuleAttribute} class
* for the given {@code exceptionType}.
* <p>This is the preferred way to construct a rollback rule that matches
* the supplied {@link Exception} class, its subclasses, and its nested classes.
* @param clazz throwable class; must be {@link Throwable} or a subclass
* the supplied exception type, its subclasses, and its nested classes.
* <p>See the javadocs for
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
* for further details on rollback rule semantics, patterns, and warnings regarding
* possible unintentional matches.
* @param exceptionType exception type; must be {@link Throwable} or a subclass
* of {@code Throwable}
* @throws IllegalArgumentException if the supplied {@code clazz} is
* @throws IllegalArgumentException if the supplied {@code exceptionType} is
* not a {@code Throwable} type or is {@code null}
*/
public RollbackRuleAttribute(Class<?> clazz) {
Assert.notNull(clazz, "'clazz' cannot be null");
if (!Throwable.class.isAssignableFrom(clazz)) {
public RollbackRuleAttribute(Class<?> exceptionType) {
Assert.notNull(exceptionType, "'exceptionType' cannot be null");
if (!Throwable.class.isAssignableFrom(exceptionType)) {
throw new IllegalArgumentException(
"Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable");
"Cannot construct rollback rule from [" + exceptionType.getName() + "]: it's not a Throwable");
}
this.exceptionName = clazz.getName();
this.exceptionPattern = exceptionType.getName();
}
/**
* Create a new instance of the {@code RollbackRuleAttribute} class
* for the given {@code exceptionName}.
* <p>This can be a substring, with no wildcard support at present. A value
* of "ServletException" would match
* {@code javax.servlet.ServletException} and subclasses, for example.
* <p><b>NB:</b> Consider carefully how specific the pattern is, and
* whether to include package information (which is not mandatory). For
* example, "Exception" will match nearly anything, and will probably hide
* other rules. "java.lang.Exception" would be correct if "Exception" was
* meant to define a rule for all checked exceptions. With more unusual
* exception names such as "BaseBusinessException" there's no need to use a
* fully package-qualified name.
* @param exceptionName the exception name pattern; can also be a fully
* for the given {@code exceptionPattern}.
* <p>See the javadocs for
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
* for further details on rollback rule semantics, patterns, and warnings regarding
* possible unintentional matches.
* @param exceptionPattern the exception name pattern; can also be a fully
* package-qualified class name
* @throws IllegalArgumentException if the supplied
* {@code exceptionName} is {@code null} or empty
* @throws IllegalArgumentException if the supplied {@code exceptionPattern}
* is {@code null} or empty
*/
public RollbackRuleAttribute(String exceptionName) {
Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty");
this.exceptionName = exceptionName;
public RollbackRuleAttribute(String exceptionPattern) {
Assert.hasText(exceptionPattern, "'exceptionPattern' cannot be null or empty");
this.exceptionPattern = exceptionPattern;
}
/**
* Return the pattern for the exception name.
* Get the configured exception name pattern that this rule uses for matching.
* @see #getDepth(Throwable)
*/
public String getExceptionName() {
return this.exceptionName;
return this.exceptionPattern;
}
/**
* Return the depth of the superclass matching.
* <p>{@code 0} means {@code ex} matches exactly. Returns
* {@code -1} if there is no match. Otherwise, returns depth with the
* lowest depth winning.
* Return the depth of the superclass matching, with the following semantics.
* <ul>
* <li>{@code -1} means this rule does not match the supplied {@code exception}.</li>
* <li>{@code 0} means this rule matches the supplied {@code exception} directly.</li>
* <li>Any other positive value means this rule matches the supplied {@code exception}
* within the superclass hierarchy, where the value is the number of levels in the
* class hierarchy between the supplied {@code exception} and the exception against
* which this rule matches directly.</li>
* </ul>
* <p>When comparing roll back rules that match against a given exception, a rule
* with a lower matching depth wins. For example, a direct match ({@code depth == 0})
* wins over a match in the superclass hierarchy ({@code depth > 0}).
* <p>A match against a nested exception type or similarly named exception type
* will return a depth signifying a match at the corresponding level in the
* class hierarchy as if there had been a direct match.
*/
public int getDepth(Throwable ex) {
return getDepth(ex.getClass(), 0);
public int getDepth(Throwable exception) {
return getDepth(exception.getClass(), 0);
}
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
private int getDepth(Class<?> exceptionType, int depth) {
if (exceptionType.getName().contains(this.exceptionPattern)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
if (exceptionType == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
return getDepth(exceptionType.getSuperclass(), depth + 1);
}
@@ -133,17 +160,17 @@ public class RollbackRuleAttribute implements Serializable{
return false;
}
RollbackRuleAttribute rhs = (RollbackRuleAttribute) other;
return this.exceptionName.equals(rhs.exceptionName);
return this.exceptionPattern.equals(rhs.exceptionPattern);
}
@Override
public int hashCode() {
return this.exceptionName.hashCode();
return this.exceptionPattern.hashCode();
}
@Override
public String toString() {
return "RollbackRuleAttribute with pattern [" + this.exceptionName + "]";
return "RollbackRuleAttribute with pattern [" + this.exceptionPattern + "]";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,13 @@ import org.springframework.core.NestedRuntimeException;
*/
@SuppressWarnings("serial")
class MyRuntimeException extends NestedRuntimeException {
public MyRuntimeException() {
super("");
}
public MyRuntimeException(String msg) {
super(msg);
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.interceptor;
import java.io.IOException;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.FatalBeanException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Unit tests for the {@link RollbackRuleAttribute} class.
*
* @author Rod Johnson
* @author Rick Evans
* @author Chris Beams
* @author Sam Brannen
* @since 09.04.2003
*/
class RollbackRuleAttributeTests {
@Nested
class ExceptionPatternTests {
@Test
void constructorPreconditions() {
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((String) null));
}
@Test
void notFound() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(IOException.class.getName());
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(-1);
}
@Test
void alwaysFoundForThrowable() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class.getName());
assertThat(rr.getDepth(new MyRuntimeException())).isGreaterThan(0);
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
}
@Test
void foundImmediatelyWhenDirectMatch() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
}
@Test
void foundImmediatelyWhenExceptionThrownIsNestedTypeOfRegisteredException() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class.getName());
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
}
@Test
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class.getName());
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
}
@Test
void foundInSuperclassHierarchy() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
// Exception -> RuntimeException -> NestedRuntimeException -> MyRuntimeException
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
}
}
@Nested
class ExceptionTypeTests {
@Test
void constructorPreconditions() {
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute(Object.class));
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((Class<?>) null));
}
@Test
void notFound() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(IOException.class);
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(-1);
}
@Test
void alwaysFoundForThrowable() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class);
assertThat(rr.getDepth(new MyRuntimeException())).isGreaterThan(0);
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
}
@Test
void foundImmediatelyWhenDirectMatch() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
}
@Test
void foundImmediatelyWhenExceptionThrownIsNestedTypeOfRegisteredException() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
}
@Test
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class);
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
}
@Test
void foundInSuperclassHierarchy() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
// Exception -> RuntimeException -> NestedRuntimeException -> MyRuntimeException
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
}
}
@SuppressWarnings("serial")
static class EnclosingException extends RuntimeException {
@SuppressWarnings("serial")
static class NestedException extends RuntimeException {
}
}
@SuppressWarnings("serial")
static class MyException extends RuntimeException {
}
// Name intentionally starts with MyException (including package) but does
// NOT extend MyException.
@SuppressWarnings("serial")
static class MyException2 extends RuntimeException {
}
}
@@ -1,103 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.interceptor;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.FatalBeanException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Unit tests for the {@link RollbackRuleAttribute} class.
*
* @author Rod Johnson
* @author Rick Evans
* @author Chris Beams
* @author Sam Brannen
* @since 09.04.2003
*/
class RollbackRuleTests {
@Test
void foundImmediatelyWithString() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
}
@Test
void foundImmediatelyWithClass() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
}
@Test
void notFound() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(java.io.IOException.class.getName());
assertThat(rr.getDepth(new MyRuntimeException(""))).isEqualTo(-1);
}
@Test
void ancestry() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
// Exception -> Runtime -> NestedRuntime -> MyRuntimeException
assertThat(rr.getDepth(new MyRuntimeException(""))).isEqualTo(3);
}
@Test
void alwaysTrueForThrowable() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class.getName());
assertThat(rr.getDepth(new MyRuntimeException(""))).isGreaterThan(0);
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
}
@Test
void ctorArgMustBeAThrowableClassWithNonThrowableType() {
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute(Object.class));
}
@Test
void ctorArgMustBeAThrowableClassWithNullThrowableType() {
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((Class<?>) null));
}
@Test
void ctorArgExceptionStringNameVersionWithNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((String) null));
}
@Test
void foundEnclosedExceptionWithEnclosingException() {
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
assertThat(rr.getDepth(new EnclosingException.EnclosedException())).isEqualTo(0);
}
@SuppressWarnings("serial")
static class EnclosingException extends RuntimeException {
@SuppressWarnings("serial")
static class EnclosedException extends RuntimeException {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,13 +35,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @since 09.04.2003
*/
public class RuleBasedTransactionAttributeTests {
class RuleBasedTransactionAttributeTests {
@Test
public void testDefaultRule() {
void defaultRule() {
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute();
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isTrue();
assertThat(rta.rollbackOn(new MyRuntimeException())).isTrue();
assertThat(rta.rollbackOn(new Exception())).isFalse();
assertThat(rta.rollbackOn(new IOException())).isFalse();
}
@@ -50,20 +50,20 @@ public class RuleBasedTransactionAttributeTests {
* Test one checked exception that should roll back.
*/
@Test
public void testRuleForRollbackOnChecked() {
void ruleForRollbackOnChecked() {
List<RollbackRuleAttribute> list = new ArrayList<>();
list.add(new RollbackRuleAttribute(IOException.class.getName()));
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isTrue();
assertThat(rta.rollbackOn(new MyRuntimeException())).isTrue();
assertThat(rta.rollbackOn(new Exception())).isFalse();
// Check that default behaviour is overridden
assertThat(rta.rollbackOn(new IOException())).isTrue();
}
@Test
public void testRuleForCommitOnUnchecked() {
void ruleForCommitOnUnchecked() {
List<RollbackRuleAttribute> list = new ArrayList<>();
list.add(new NoRollbackRuleAttribute(MyRuntimeException.class.getName()));
list.add(new RollbackRuleAttribute(IOException.class.getName()));
@@ -71,14 +71,14 @@ public class RuleBasedTransactionAttributeTests {
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
// Check default behaviour is overridden
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
assertThat(rta.rollbackOn(new Exception())).isFalse();
// Check that default behaviour is overridden
assertThat(rta.rollbackOn(new IOException())).isTrue();
}
@Test
public void testRuleForSelectiveRollbackOnCheckedWithString() {
void ruleForSelectiveRollbackOnCheckedWithString() {
List<RollbackRuleAttribute> l = new ArrayList<>();
l.add(new RollbackRuleAttribute(java.rmi.RemoteException.class.getName()));
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, l);
@@ -86,7 +86,7 @@ public class RuleBasedTransactionAttributeTests {
}
@Test
public void testRuleForSelectiveRollbackOnCheckedWithClass() {
void ruleForSelectiveRollbackOnCheckedWithClass() {
List<RollbackRuleAttribute> l = Collections.singletonList(new RollbackRuleAttribute(RemoteException.class));
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, l);
doTestRuleForSelectiveRollbackOnChecked(rta);
@@ -105,7 +105,7 @@ public class RuleBasedTransactionAttributeTests {
* when Exception prompts a rollback.
*/
@Test
public void testRuleForCommitOnSubclassOfChecked() {
void ruleForCommitOnSubclassOfChecked() {
List<RollbackRuleAttribute> list = new ArrayList<>();
// Note that it's important to ensure that we have this as
// a FQN: otherwise it will match everything!
@@ -120,20 +120,20 @@ public class RuleBasedTransactionAttributeTests {
}
@Test
public void testRollbackNever() {
void rollbackNever() {
List<RollbackRuleAttribute> list = new ArrayList<>();
list.add(new NoRollbackRuleAttribute("Throwable"));
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
assertThat(rta.rollbackOn(new Throwable())).isFalse();
assertThat(rta.rollbackOn(new RuntimeException())).isFalse();
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
assertThat(rta.rollbackOn(new Exception())).isFalse();
assertThat(rta.rollbackOn(new IOException())).isFalse();
}
@Test
public void testToStringMatchesEditor() {
void toStringMatchesEditor() {
List<RollbackRuleAttribute> list = new ArrayList<>();
list.add(new NoRollbackRuleAttribute("Throwable"));
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
@@ -144,7 +144,7 @@ public class RuleBasedTransactionAttributeTests {
assertThat(rta.rollbackOn(new Throwable())).isFalse();
assertThat(rta.rollbackOn(new RuntimeException())).isFalse();
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
assertThat(rta.rollbackOn(new Exception())).isFalse();
assertThat(rta.rollbackOn(new IOException())).isFalse();
}
@@ -153,7 +153,7 @@ public class RuleBasedTransactionAttributeTests {
* See <a href="https://forum.springframework.org/showthread.php?t=41350">this forum post</a>.
*/
@Test
public void testConflictingRulesToDetermineExactContract() {
void conflictingRulesToDetermineExactContract() {
List<RollbackRuleAttribute> list = new ArrayList<>();
list.add(new NoRollbackRuleAttribute(MyBusinessWarningException.class));
list.add(new RollbackRuleAttribute(MyBusinessException.class));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.transaction.interceptor;
import java.io.IOException;
import org.junit.jupiter.api.Test;
@@ -27,72 +26,65 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests to check conversion from String to TransactionAttribute.
* Tests to check conversion from String to TransactionAttribute using
* a {@link TransactionAttributeEditor}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 26.04.2003
*/
public class TransactionAttributeEditorTests {
class TransactionAttributeEditorTests {
private final TransactionAttributeEditor pe = new TransactionAttributeEditor();
@Test
public void testNull() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void nullText() {
pe.setAsText(null);
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta == null).isTrue();
assertThat(pe.getValue()).isNull();
}
@Test
public void testEmptyString() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void emptyString() {
pe.setAsText("");
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta == null).isTrue();
assertThat(pe.getValue()).isNull();
}
@Test
public void testValidPropagationCodeOnly() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void validPropagationCodeOnly() {
pe.setAsText("PROPAGATION_REQUIRED");
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta != null).isTrue();
assertThat(ta.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED).isTrue();
assertThat(ta.getIsolationLevel() == TransactionDefinition.ISOLATION_DEFAULT).isTrue();
boolean condition = !ta.isReadOnly();
assertThat(condition).isTrue();
assertThat(ta).isNotNull();
assertThat(ta.getPropagationBehavior()).isEqualTo(TransactionDefinition.PROPAGATION_REQUIRED);
assertThat(ta.getIsolationLevel()).isEqualTo(TransactionDefinition.ISOLATION_DEFAULT);
assertThat(ta.isReadOnly()).isFalse();
}
@Test
public void testInvalidPropagationCodeOnly() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void invalidPropagationCodeOnly() {
// should have failed with bogus propagation code
assertThatIllegalArgumentException().isThrownBy(() ->
pe.setAsText("XXPROPAGATION_REQUIRED"));
assertThatIllegalArgumentException().isThrownBy(() -> pe.setAsText("XXPROPAGATION_REQUIRED"));
}
@Test
public void testValidPropagationCodeAndIsolationCode() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void validPropagationCodeAndIsolationCode() {
pe.setAsText("PROPAGATION_REQUIRED, ISOLATION_READ_UNCOMMITTED");
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta != null).isTrue();
assertThat(ta.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED).isTrue();
assertThat(ta.getIsolationLevel() == TransactionDefinition.ISOLATION_READ_UNCOMMITTED).isTrue();
assertThat(ta).isNotNull();
assertThat(ta.getPropagationBehavior()).isEqualTo(TransactionDefinition.PROPAGATION_REQUIRED);
assertThat(ta.getIsolationLevel()).isEqualTo(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
}
@Test
public void testValidPropagationAndIsolationCodesAndInvalidRollbackRule() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void validPropagationAndIsolationCodesAndInvalidRollbackRule() {
// should fail with bogus rollback rule
assertThatIllegalArgumentException().isThrownBy(() ->
pe.setAsText("PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED,XXX"));
assertThatIllegalArgumentException()
.isThrownBy(() -> pe.setAsText("PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED,XXX"));
}
@Test
public void testValidPropagationCodeAndIsolationCodeAndRollbackRules1() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void validPropagationCodeAndIsolationCodeAndRollbackRules1() {
pe.setAsText("PROPAGATION_MANDATORY,ISOLATION_REPEATABLE_READ,timeout_10,-IOException,+MyRuntimeException");
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta).isNotNull();
@@ -104,13 +96,11 @@ public class TransactionAttributeEditorTests {
assertThat(ta.rollbackOn(new Exception())).isFalse();
// Check for our bizarre customized rollback rules
assertThat(ta.rollbackOn(new IOException())).isTrue();
boolean condition = !ta.rollbackOn(new MyRuntimeException(""));
assertThat(condition).isTrue();
assertThat(ta.rollbackOn(new MyRuntimeException())).isFalse();
}
@Test
public void testValidPropagationCodeAndIsolationCodeAndRollbackRules2() {
TransactionAttributeEditor pe = new TransactionAttributeEditor();
void validPropagationCodeAndIsolationCodeAndRollbackRules2() {
pe.setAsText("+IOException,readOnly,ISOLATION_READ_COMMITTED,-MyRuntimeException,PROPAGATION_SUPPORTS");
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(ta).isNotNull();
@@ -122,18 +112,17 @@ public class TransactionAttributeEditorTests {
assertThat(ta.rollbackOn(new Exception())).isFalse();
// Check for our bizarre customized rollback rules
assertThat(ta.rollbackOn(new IOException())).isFalse();
assertThat(ta.rollbackOn(new MyRuntimeException(""))).isTrue();
assertThat(ta.rollbackOn(new MyRuntimeException())).isTrue();
}
@Test
public void testDefaultTransactionAttributeToString() {
void defaultTransactionAttributeToString() {
DefaultTransactionAttribute source = new DefaultTransactionAttribute();
source.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
source.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
source.setTimeout(10);
source.setReadOnly(true);
TransactionAttributeEditor pe = new TransactionAttributeEditor();
pe.setAsText(source.toString());
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(source).isEqualTo(ta);
@@ -151,7 +140,7 @@ public class TransactionAttributeEditorTests {
}
@Test
public void testRuleBasedTransactionAttributeToString() {
void ruleBasedTransactionAttributeToString() {
RuleBasedTransactionAttribute source = new RuleBasedTransactionAttribute();
source.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
source.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
@@ -160,7 +149,6 @@ public class TransactionAttributeEditorTests {
source.getRollbackRules().add(new RollbackRuleAttribute("IllegalArgumentException"));
source.getRollbackRules().add(new NoRollbackRuleAttribute("IllegalStateException"));
TransactionAttributeEditor pe = new TransactionAttributeEditor();
pe.setAsText(source.toString());
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
assertThat(source).isEqualTo(ta);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -196,22 +196,26 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory,
ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
ObjectMapper mapper = selectObjectMapper(valueType, mimeType);
if (mapper == null) {
throw new IllegalStateException("No ObjectMapper for " + valueType);
}
Class<?> jsonView = null;
FilterProvider filters = null;
if (value instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) value;
value = container.getValue();
valueType = ResolvableType.forInstance(value);
jsonView = container.getSerializationView();
filters = container.getFilters();
}
ObjectMapper mapper = selectObjectMapper(valueType, mimeType);
if (mapper == null) {
throw new IllegalStateException("No ObjectMapper for " + valueType);
}
ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, jsonView, hints);
if (filters != null) {
writer = writer.with(filters);
}
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
try {
JsonEncoding encoding = getJsonEncoding(mimeType);
@@ -559,9 +559,9 @@ public class AsyncRestTemplate extends org.springframework.http.client.support.I
}
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
if (logger.isDebugEnabled()) {
try {
logger.warn("Async " + method.name() + " request for \"" + url + "\" resulted in " +
logger.debug("Async " + method.name() + " request for \"" + url + "\" resulted in " +
response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException ex) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -54,6 +55,7 @@ import org.springframework.lang.NonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Named.named;
/**
* @author Arjen Poutsma
@@ -202,22 +204,22 @@ public class ClientHttpConnectorTests {
@Target(ElementType.METHOD)
// Do not auto-close arguments since HttpComponentsClientHttpConnector implements
// AutoCloseable and is shared between parameterized test invocations.
@ParameterizedTest(autoCloseArguments = false)
@ParameterizedTest(name = "{0}", autoCloseArguments = false)
@MethodSource("org.springframework.http.client.reactive.ClientHttpConnectorTests#connectors")
public @interface ParameterizedConnectorTest {
}
static List<ClientHttpConnector> connectors() {
static List<Named<ClientHttpConnector>> connectors() {
return Arrays.asList(
new ReactorClientHttpConnector(),
new JettyClientHttpConnector(),
new HttpComponentsClientHttpConnector()
named("Reactor Netty", new ReactorClientHttpConnector()),
named("Jetty", new JettyClientHttpConnector()),
named("HttpComponents", new HttpComponentsClientHttpConnector())
);
}
static List<Arguments> methodsWithConnectors() {
List<Arguments> result = new ArrayList<>();
for (ClientHttpConnector connector : connectors()) {
for (Named<ClientHttpConnector> connector : connectors()) {
for (HttpMethod method : HttpMethod.values()) {
result.add(Arguments.of(connector, method));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
@ParameterizedDataBufferAllocatingTest
void canWrite(String displayName, DataBufferFactory bufferFactory) {
void canWrite(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
assertThat(this.messageWriter.canWrite(forClass(Object.class), null)).isTrue();
@@ -73,7 +73,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest
void writeServerSentEvent(String displayName, DataBufferFactory bufferFactory) {
void writeServerSentEvent(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
ServerSentEvent<?> event = ServerSentEvent.builder().data("bar").id("c42").event("foo")
@@ -91,7 +91,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest
void writeString(String displayName, DataBufferFactory bufferFactory) {
void writeString(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
@@ -106,7 +106,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest
void writeMultiLineString(String displayName, DataBufferFactory bufferFactory) {
void writeMultiLineString(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
@@ -121,7 +121,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest // SPR-16516
void writeStringWithCustomCharset(String displayName, DataBufferFactory bufferFactory) {
void writeStringWithCustomCharset(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
@@ -142,7 +142,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest
void writePojo(String displayName, DataBufferFactory bufferFactory) {
void writePojo(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
@@ -161,7 +161,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest // SPR-14899
void writePojoWithPrettyPrint(String displayName, DataBufferFactory bufferFactory) {
void writePojoWithPrettyPrint(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().indentOutput(true).build();
@@ -187,7 +187,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
}
@ParameterizedDataBufferAllocatingTest // SPR-16516, SPR-16539
void writePojoWithCustomEncoding(String displayName, DataBufferFactory bufferFactory) {
void writePojoWithCustomEncoding(DataBufferFactory bufferFactory) {
super.bufferFactory = bufferFactory;
MockServerHttpResponse outputMessage = new MockServerHttpResponse(super.bufferFactory);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,12 +95,12 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
new Pojo("foofoo", "barbar"),
new Pojo("foofoofoo", "barbarbar"));
testEncodeAll(input, ResolvableType.forClass(Pojo.class), step -> step
testEncodeAll(input, ResolvableType.forClass(Pojo.class), APPLICATION_STREAM_JSON, null, step -> step
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n"))
.consumeNextWith(expectString("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}\n"))
.consumeNextWith(expectString("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}\n"))
.verifyComplete(),
APPLICATION_STREAM_JSON, null);
.verifyComplete()
);
}
@Test // SPR-15866
@@ -168,15 +168,15 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
new Pojo("foofoofoo", "barbarbar")
);
testEncode(input, ResolvableType.forClass(Pojo.class), step -> step
testEncode(input, ResolvableType.forClass(Pojo.class), barMediaType, null, step -> step
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n")
.andThen(DataBufferUtils::release))
.consumeNextWith(expectString("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}\n")
.andThen(DataBufferUtils::release))
.consumeNextWith(expectString("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}\n")
.andThen(DataBufferUtils::release))
.verifyComplete(),
barMediaType, null);
.verifyComplete()
);
}
@Test
@@ -190,11 +190,10 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
ResolvableType type = ResolvableType.forClass(JacksonViewBean.class);
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView1.class);
testEncode(input, type, step -> step
.consumeNextWith(expectString("{\"withView1\":\"with\"}")
.andThen(DataBufferUtils::release))
.verifyComplete(),
null, hints);
testEncode(input, type, null, hints, step -> step
.consumeNextWith(expectString("{\"withView1\":\"with\"}").andThen(DataBufferUtils::release))
.verifyComplete()
);
}
@Test
@@ -208,11 +207,10 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
ResolvableType type = ResolvableType.forClass(JacksonViewBean.class);
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView3.class);
testEncode(input, type, step -> step
.consumeNextWith(expectString("{\"withoutView\":\"without\"}")
.andThen(DataBufferUtils::release))
.verifyComplete(),
null, hints);
testEncode(input, type, null, hints, step -> step
.consumeNextWith(expectString("{\"withoutView\":\"without\"}").andThen(DataBufferUtils::release))
.verifyComplete()
);
}
@Test
@@ -227,11 +225,33 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
ResolvableType type = ResolvableType.forClass(MappingJacksonValue.class);
testEncode(Mono.just(jacksonValue), type, step -> step
.consumeNextWith(expectString("{\"withView1\":\"with\"}")
.andThen(DataBufferUtils::release))
.verifyComplete(),
null, Collections.emptyMap());
testEncode(Mono.just(jacksonValue), type, null, Collections.emptyMap(), step -> step
.consumeNextWith(expectString("{\"withView1\":\"with\"}").andThen(DataBufferUtils::release))
.verifyComplete()
);
}
@Test // gh-28045
public void jacksonValueUnwrappedBeforeObjectMapperSelection() {
JacksonViewBean bean = new JacksonViewBean();
bean.setWithView1("with");
bean.setWithView2("with");
bean.setWithoutView("without");
MappingJacksonValue jacksonValue = new MappingJacksonValue(bean);
jacksonValue.setSerializationView(MyJacksonView1.class);
ResolvableType type = ResolvableType.forClass(MappingJacksonValue.class);
MediaType halMediaType = MediaType.parseMediaType("application/hal+json");
ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true);
this.encoder.registerObjectMappersForType(JacksonViewBean.class, map -> map.put(halMediaType, mapper));
testEncode(Mono.just(jacksonValue), type, halMediaType, Collections.emptyMap(), step -> step
.consumeNextWith(expectString("{\n \"withView1\" : \"with\"\n}").andThen(DataBufferUtils::release))
.verifyComplete()
);
}
@Test // gh-22771
@@ -252,11 +272,12 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
@Test
public void encodeAscii() {
Mono<Object> input = Mono.just(new Pojo("foo", "bar"));
MimeType mimeType = new MimeType("application", "json", StandardCharsets.US_ASCII);
testEncode(input, ResolvableType.forClass(Pojo.class), step -> step
testEncode(input, ResolvableType.forClass(Pojo.class), mimeType, null, step -> step
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}"))
.verifyComplete(),
new MimeType("application", "json", StandardCharsets.US_ASCII), null);
.verifyComplete()
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -108,17 +108,13 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTests<Jackson2Smil
}
@Test
public void encodeError() throws Exception {
public void encodeError() {
Mono<Pojo> input = Mono.error(new InputException());
testEncode(input, Pojo.class, step -> step
.expectError(InputException.class)
.verify());
testEncode(input, Pojo.class, step -> step.expectError(InputException.class).verify());
}
@Test
public void encodeAsStream() throws Exception {
public void encodeAsStream() {
Pojo pojo1 = new Pojo("foo", "bar");
Pojo pojo2 = new Pojo("foofoo", "barbar");
Pojo pojo3 = new Pojo("foofoofoo", "barbarbar");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.springframework.core.ResolvableType.forClass;
import static org.springframework.core.io.buffer.DataBufferUtils.release;
@@ -62,7 +63,7 @@ import static org.springframework.core.io.buffer.DataBufferUtils.release;
/**
* @author Arjen Poutsma
*/
public class DefaultPartHttpMessageReaderTests {
class DefaultPartHttpMessageReaderTests {
private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer iaculis metus id vestibulum nullam.";
@@ -73,12 +74,12 @@ public class DefaultPartHttpMessageReaderTests {
private static final DataBufferFactory bufferFactory = new NettyDataBufferFactory(new PooledByteBufAllocator());
@ParameterizedDefaultPartHttpMessageReaderTest
public void canRead(String displayName, DefaultPartHttpMessageReader reader) {
void canRead(DefaultPartHttpMessageReader reader) {
assertThat(reader.canRead(forClass(Part.class), MediaType.MULTIPART_FORM_DATA)).isTrue();
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void simple(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void simple(DefaultPartHttpMessageReader reader) throws InterruptedException {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "simple-boundary");
@@ -96,7 +97,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void noHeaders(String displayName, DefaultPartHttpMessageReader reader) {
void noHeaders(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("no-header.multipart", getClass()), "boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -110,7 +111,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void noEndBoundary(String displayName, DefaultPartHttpMessageReader reader) {
void noEndBoundary(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("no-end-boundary.multipart", getClass()), "boundary");
@@ -122,7 +123,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void garbage(String displayName, DefaultPartHttpMessageReader reader) {
void garbage(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("garbage-1.multipart", getClass()), "boundary");
@@ -134,7 +135,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void noEndHeader(String displayName, DefaultPartHttpMessageReader reader) {
void noEndHeader(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("no-end-header.multipart", getClass()), "boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -145,7 +146,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void noEndBody(String displayName, DefaultPartHttpMessageReader reader) {
void noEndBody(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("no-end-body.multipart", getClass()), "boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -156,7 +157,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void cancelPart(String displayName, DefaultPartHttpMessageReader reader) {
void cancelPart(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "simple-boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -168,7 +169,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void cancelBody(String displayName, DefaultPartHttpMessageReader reader) throws Exception {
void cancelBody(DefaultPartHttpMessageReader reader) throws Exception {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "simple-boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -185,7 +186,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void cancelBodyThenPart(String displayName, DefaultPartHttpMessageReader reader) {
void cancelBodyThenPart(DefaultPartHttpMessageReader reader) {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "simple-boundary");
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
@@ -197,25 +198,25 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void firefox(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void firefox(DefaultPartHttpMessageReader reader) throws InterruptedException {
testBrowser(reader, new ClassPathResource("firefox.multipart", getClass()),
"---------------------------18399284482060392383840973206");
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void chrome(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void chrome(DefaultPartHttpMessageReader reader) throws InterruptedException {
testBrowser(reader, new ClassPathResource("chrome.multipart", getClass()),
"----WebKitFormBoundaryEveBLvRT65n21fwU");
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void safari(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void safari(DefaultPartHttpMessageReader reader) throws InterruptedException {
testBrowser(reader, new ClassPathResource("safari.multipart", getClass()),
"----WebKitFormBoundaryG8fJ50opQOML0oGD");
}
@Test
public void tooManyParts() throws InterruptedException {
void tooManyParts() throws InterruptedException {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "simple-boundary");
@@ -235,7 +236,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void quotedBoundary(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void quotedBoundary(DefaultPartHttpMessageReader reader) throws InterruptedException {
MockServerHttpRequest request = createRequest(
new ClassPathResource("simple.multipart", getClass()), "\"simple-boundary\"");
@@ -253,7 +254,7 @@ public class DefaultPartHttpMessageReaderTests {
}
@ParameterizedDefaultPartHttpMessageReaderTest
public void utf8Headers(String displayName, DefaultPartHttpMessageReader reader) throws InterruptedException {
void utf8Headers(DefaultPartHttpMessageReader reader) throws InterruptedException {
MockServerHttpRequest request = createRequest(
new ClassPathResource("utf8.multipart", getClass()), "\"simple-boundary\"");
@@ -272,7 +273,7 @@ public class DefaultPartHttpMessageReaderTests {
// gh-27612
@Test
public void exceedHeaderLimit() throws InterruptedException {
void exceedHeaderLimit() throws InterruptedException {
Flux<DataBuffer> body = DataBufferUtils
.readByteChannel((new ClassPathResource("files.multipart", getClass()))::readableChannel, bufferFactory, 282);
@@ -410,10 +411,10 @@ public class DefaultPartHttpMessageReaderTests {
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("org.springframework.http.codec.multipart.DefaultPartHttpMessageReaderTests#messageReaders()")
public @interface ParameterizedDefaultPartHttpMessageReaderTest {
@interface ParameterizedDefaultPartHttpMessageReaderTest {
}
public static Stream<Arguments> messageReaders() {
static Stream<Arguments> messageReaders() {
DefaultPartHttpMessageReader streaming = new DefaultPartHttpMessageReader();
streaming.setStreaming(true);
@@ -426,11 +427,9 @@ public class DefaultPartHttpMessageReaderTests {
onDisk.setMaxInMemorySize(100);
return Stream.of(
arguments("streaming", streaming),
arguments("in-memory", inMemory),
arguments("on-disk", onDisk)
);
arguments(named("streaming", streaming)),
arguments(named("in-memory", inMemory)),
arguments(named("on-disk", onDisk)));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,8 +71,8 @@ public class Jaxb2XmlEncoderTests extends AbstractEncoderTests<Jaxb2XmlEncoder>
Mono<Pojo> input = Mono.just(new Pojo("foofoo", "barbar"));
testEncode(input, Pojo.class, step -> step
.consumeNextWith(
expectXml("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" +
.consumeNextWith(expectXml(
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" +
"<pojo><bar>barbar</bar><foo>foofoo</foo></pojo>"))
.verifyComplete());
}
@@ -80,10 +80,7 @@ public class Jaxb2XmlEncoderTests extends AbstractEncoderTests<Jaxb2XmlEncoder>
@Test
public void encodeError() {
Flux<Pojo> input = Flux.error(RuntimeException::new);
testEncode(input, Pojo.class, step -> step
.expectError(RuntimeException.class)
.verify());
testEncode(input, Pojo.class, step -> step.expectError(RuntimeException.class).verify());
}
@Test
@@ -91,9 +88,11 @@ public class Jaxb2XmlEncoderTests extends AbstractEncoderTests<Jaxb2XmlEncoder>
Mono<Container> input = Mono.just(new Container());
testEncode(input, Pojo.class, step -> step
.consumeNextWith(
expectXml("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" +
"<container><foo><name>name1</name></foo><bar><title>title1</title></bar></container>"))
.consumeNextWith(expectXml(
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" +
"<container>" +
"<foo><name>name1</name></foo><bar><title>title1</title></bar>" +
"</container>"))
.verifyComplete());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
@@ -50,24 +51,24 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
class HeadersAdaptersTests {
@ParameterizedHeadersTest
void getWithUnknownHeaderShouldReturnNull(String displayName, MultiValueMap<String, String> headers) {
void getWithUnknownHeaderShouldReturnNull(MultiValueMap<String, String> headers) {
assertThat(headers.get("Unknown")).isNull();
}
@ParameterizedHeadersTest
void getFirstWithUnknownHeaderShouldReturnNull(String displayName, MultiValueMap<String, String> headers) {
void getFirstWithUnknownHeaderShouldReturnNull(MultiValueMap<String, String> headers) {
assertThat(headers.getFirst("Unknown")).isNull();
}
@ParameterizedHeadersTest
void sizeWithMultipleValuesForHeaderShouldCountHeaders(String displayName, MultiValueMap<String, String> headers) {
void sizeWithMultipleValuesForHeaderShouldCountHeaders(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
headers.add("TestHeader", "second");
assertThat(headers.size()).isEqualTo(1);
}
@ParameterizedHeadersTest
void keySetShouldNotDuplicateHeaderNames(String displayName, MultiValueMap<String, String> headers) {
void keySetShouldNotDuplicateHeaderNames(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
headers.add("OtherHeader", "test");
headers.add("TestHeader", "second");
@@ -75,13 +76,13 @@ class HeadersAdaptersTests {
}
@ParameterizedHeadersTest
void containsKeyShouldBeCaseInsensitive(String displayName, MultiValueMap<String, String> headers) {
void containsKeyShouldBeCaseInsensitive(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
assertThat(headers.containsKey("testheader")).isTrue();
}
@ParameterizedHeadersTest
void addShouldKeepOrdering(String displayName, MultiValueMap<String, String> headers) {
void addShouldKeepOrdering(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
headers.add("TestHeader", "second");
assertThat(headers.getFirst("TestHeader")).isEqualTo("first");
@@ -89,7 +90,7 @@ class HeadersAdaptersTests {
}
@ParameterizedHeadersTest
void putShouldOverrideExisting(String displayName, MultiValueMap<String, String> headers) {
void putShouldOverrideExisting(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
headers.put("TestHeader", Arrays.asList("override"));
assertThat(headers.getFirst("TestHeader")).isEqualTo("override");
@@ -97,7 +98,7 @@ class HeadersAdaptersTests {
}
@ParameterizedHeadersTest
void nullValuesShouldNotFail(String displayName, MultiValueMap<String, String> headers) {
void nullValuesShouldNotFail(MultiValueMap<String, String> headers) {
headers.add("TestHeader", null);
assertThat(headers.getFirst("TestHeader")).isNull();
headers.set("TestHeader", null);
@@ -105,7 +106,7 @@ class HeadersAdaptersTests {
}
@ParameterizedHeadersTest
void shouldReflectChangesOnKeyset(String displayName, MultiValueMap<String, String> headers) {
void shouldReflectChangesOnKeyset(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
assertThat(headers.keySet()).hasSize(1);
headers.keySet().removeIf("TestHeader"::equals);
@@ -113,7 +114,7 @@ class HeadersAdaptersTests {
}
@ParameterizedHeadersTest
void shouldFailIfHeaderRemovedFromKeyset(String displayName, MultiValueMap<String, String> headers) {
void shouldFailIfHeaderRemovedFromKeyset(MultiValueMap<String, String> headers) {
headers.add("TestHeader", "first");
assertThat(headers.keySet()).hasSize(1);
Iterator<String> names = headers.keySet().iterator();
@@ -132,11 +133,11 @@ class HeadersAdaptersTests {
static Stream<Arguments> headers() {
return Stream.of(
arguments("Map", CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH))),
arguments("Netty", new NettyHeadersAdapter(new DefaultHttpHeaders())),
arguments("Tomcat", new TomcatHeadersAdapter(new MimeHeaders())),
arguments("Undertow", new UndertowHeadersAdapter(new HeaderMap())),
arguments("Jetty", new JettyHeadersAdapter(new HttpFields()))
arguments(named("Map", CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH)))),
arguments(named("Netty", new NettyHeadersAdapter(new DefaultHttpHeaders()))),
arguments(named("Tomcat", new TomcatHeadersAdapter(new MimeHeaders()))),
arguments(named("Undertow", new UndertowHeadersAdapter(new HeaderMap()))),
arguments(named("Jetty", new JettyHeadersAdapter(new HttpFields())))
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,11 @@
package org.springframework.web.client;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.http.HttpHeaders;
@@ -27,6 +30,8 @@ import org.springframework.http.client.ClientHttpResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.http.HttpStatus.BAD_GATEWAY;
@@ -65,7 +70,7 @@ class DefaultResponseErrorHandlerHttpStatusTests {
assertThat(this.handler.hasError(this.response)).isTrue();
}
@ParameterizedTest(name = "[{index}] error: [{0}], exception: [{1}]")
@ParameterizedTest(name = "[{index}] error: {0}, exception: {1}")
@DisplayName("handleError() throws an exception")
@MethodSource("errorCodes")
void handleErrorException(HttpStatus httpStatus, Class<? extends Throwable> expectedExceptionClass) throws Exception {
@@ -78,27 +83,33 @@ class DefaultResponseErrorHandlerHttpStatusTests {
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> this.handler.handleError(this.response));
}
static Object[][] errorCodes() {
return new Object[][]{
static Stream<Arguments> errorCodes() {
return Stream.of(
// 4xx
{BAD_REQUEST, HttpClientErrorException.BadRequest.class},
{UNAUTHORIZED, HttpClientErrorException.Unauthorized.class},
{FORBIDDEN, HttpClientErrorException.Forbidden.class},
{NOT_FOUND, HttpClientErrorException.NotFound.class},
{METHOD_NOT_ALLOWED, HttpClientErrorException.MethodNotAllowed.class},
{NOT_ACCEPTABLE, HttpClientErrorException.NotAcceptable.class},
{CONFLICT, HttpClientErrorException.Conflict.class},
{TOO_MANY_REQUESTS, HttpClientErrorException.TooManyRequests.class},
{UNPROCESSABLE_ENTITY, HttpClientErrorException.UnprocessableEntity.class},
{I_AM_A_TEAPOT, HttpClientErrorException.class},
args(BAD_REQUEST, HttpClientErrorException.BadRequest.class),
args(UNAUTHORIZED, HttpClientErrorException.Unauthorized.class),
args(FORBIDDEN, HttpClientErrorException.Forbidden.class),
args(NOT_FOUND, HttpClientErrorException.NotFound.class),
args(METHOD_NOT_ALLOWED, HttpClientErrorException.MethodNotAllowed.class),
args(NOT_ACCEPTABLE, HttpClientErrorException.NotAcceptable.class),
args(CONFLICT, HttpClientErrorException.Conflict.class),
args(TOO_MANY_REQUESTS, HttpClientErrorException.TooManyRequests.class),
args(UNPROCESSABLE_ENTITY, HttpClientErrorException.UnprocessableEntity.class),
args(I_AM_A_TEAPOT, HttpClientErrorException.class),
// 5xx
{INTERNAL_SERVER_ERROR, HttpServerErrorException.InternalServerError.class},
{NOT_IMPLEMENTED, HttpServerErrorException.NotImplemented.class},
{BAD_GATEWAY, HttpServerErrorException.BadGateway.class},
{SERVICE_UNAVAILABLE, HttpServerErrorException.ServiceUnavailable.class},
{GATEWAY_TIMEOUT, HttpServerErrorException.GatewayTimeout.class},
{HTTP_VERSION_NOT_SUPPORTED, HttpServerErrorException.class}
};
args(INTERNAL_SERVER_ERROR, HttpServerErrorException.InternalServerError.class),
args(NOT_IMPLEMENTED, HttpServerErrorException.NotImplemented.class),
args(BAD_GATEWAY, HttpServerErrorException.BadGateway.class),
args(SERVICE_UNAVAILABLE, HttpServerErrorException.ServiceUnavailable.class),
args(GATEWAY_TIMEOUT, HttpServerErrorException.GatewayTimeout.class),
args(HTTP_VERSION_NOT_SUPPORTED, HttpServerErrorException.class)
);
}
private static Arguments args(HttpStatus httpStatus, Class<? extends Throwable> exceptionType) {
return arguments(
named(String.valueOf(httpStatus.value()), httpStatus),
named(exceptionType.getSimpleName(), exceptionType));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonView;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.params.ParameterizedTest;
@@ -58,6 +59,7 @@ import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Named.named;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.MediaType.MULTIPART_MIXED;
@@ -88,12 +90,12 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests {
}
@SuppressWarnings("deprecation")
static Stream<ClientHttpRequestFactory> clientHttpRequestFactories() {
static Stream<Named<ClientHttpRequestFactory>> clientHttpRequestFactories() {
return Stream.of(
new SimpleClientHttpRequestFactory(),
new HttpComponentsClientHttpRequestFactory(),
new org.springframework.http.client.Netty4ClientHttpRequestFactory(),
new OkHttp3ClientHttpRequestFactory()
named("JDK", new SimpleClientHttpRequestFactory()),
named("HttpComponents", new HttpComponentsClientHttpRequestFactory()),
named("Netty", new org.springframework.http.client.Netty4ClientHttpRequestFactory()),
named("OkHttp", new OkHttp3ClientHttpRequestFactory())
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.lang.annotation.Target;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -34,6 +35,7 @@ import org.springframework.web.util.UrlPathHelper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Named.named;
/**
* Unit tests for {@link UrlBasedCorsConfigurationSource}.
@@ -42,62 +44,35 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/
class UrlBasedCorsConfigurationSourceTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest
@MethodSource("pathPatternsArguments")
@interface PathPatternsParameterizedTest {
}
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
return Stream.of(
requestUri -> {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
ServletRequestPathUtils.parseAndCache(request);
return request;
},
requestUri -> {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
UrlPathHelper.defaultInstance.getLookupPathForRequest(request);
return request;
}
);
}
private final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
@PathPatternsParameterizedTest
void empty(Function<String, MockHttpServletRequest> requestFactory) {
CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
assertThat(source.getCorsConfiguration(requestFactory.apply("/bar/test.html"))).isNull();
}
@PathPatternsParameterizedTest
void registerAndMatch(Function<String, MockHttpServletRequest> requestFactory) {
CorsConfiguration config = new CorsConfiguration();
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/bar/**", config);
source.registerCorsConfiguration("/bar/**", config);
MockHttpServletRequest request = requestFactory.apply("/foo/test.html");
assertThat(configSource.getCorsConfiguration(request)).isNull();
assertThat(source.getCorsConfiguration(request)).isNull();
request = requestFactory.apply("/bar/test.html");
assertThat(configSource.getCorsConfiguration(request)).isEqualTo(config);
assertThat(source.getCorsConfiguration(request)).isEqualTo(config);
}
@Test
void unmodifiableConfigurationsMap() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.getCorsConfigurations().put("/**", new CorsConfiguration());
});
.isThrownBy(() -> source.getCorsConfigurations().put("/**", new CorsConfiguration()));
}
@Test
void allowInitLookupPath() {
CorsConfiguration config = new CorsConfiguration();
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
@@ -108,4 +83,29 @@ class UrlBasedCorsConfigurationSourceTests {
source.setAllowInitLookupPath(false);
assertThatIllegalArgumentException().isThrownBy(() -> source.getCorsConfiguration(request));
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("pathPatternsArguments")
private @interface PathPatternsParameterizedTest {
}
@SuppressWarnings("unused")
private static Stream<Named<Function<String, MockHttpServletRequest>>> pathPatternsArguments() {
return Stream.of(
named("ServletRequestPathUtils", requestUri -> {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
ServletRequestPathUtils.parseAndCache(request);
return request;
}),
named("UrlPathHelper", requestUri -> {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
UrlPathHelper.defaultInstance.getLookupPathForRequest(request);
return request;
})
);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,25 +65,23 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
Pojo("foofoo", "barbar"),
Pojo("foofoofoo", "barbarbar")
)
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> ->
step
.consumeNextWith(expectString("[" +
"{\"foo\":\"foo\",\"bar\":\"bar\"}," +
"{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," +
"{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) })
.verifyComplete()
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> -> step
.consumeNextWith(expectString("[" +
"{\"foo\":\"foo\",\"bar\":\"bar\"}," +
"{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," +
"{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) })
.verifyComplete()
})
}
@Test
fun encodeMono() {
val input = Mono.just(Pojo("foo", "bar"))
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> ->
step
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) })
.verifyComplete()
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> -> step
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) })
.verifyComplete()
})
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.params.ParameterizedTest;
@@ -36,6 +37,8 @@ import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpServerErrorException;
import static org.junit.jupiter.api.Named.named;
public abstract class AbstractHttpHandlerIntegrationTests {
/**
@@ -121,12 +124,12 @@ public abstract class AbstractHttpHandlerIntegrationTests {
public @interface ParameterizedHttpServerTest {
}
static Stream<HttpServer> httpServers() {
static Stream<Named<HttpServer>> httpServers() {
return Stream.of(
new JettyHttpServer(),
new ReactorHttpServer(),
new TomcatHttpServer(),
new UndertowHttpServer()
named("Jetty", new JettyHttpServer()),
named("Reactor Netty", new ReactorHttpServer()),
named("Tomcat", new TomcatHttpServer()),
named("Undertow", new UndertowHttpServer())
);
}
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Set;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.KotlinDetector;
@@ -130,7 +131,8 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
if (adapter != null) {
publisher = adapter.toPublisher(body);
boolean isUnwrapped = KotlinDetector.isSuspendingFunction(bodyParameter.getMethod()) &&
!COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName());
!COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName()) &&
!Flux.class.equals(bodyType.toClass());
ResolvableType genericType = isUnwrapped ? bodyType : bodyType.getGeneric();
elementType = getElementType(adapter, genericType);
actualElementType = elementType;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ import org.springframework.web.util.UriUtils;
* values from the model or with URI variables from the current request.
*
* <p>By default {@link HttpStatus#SEE_OTHER} is used but alternate status codes
* may be via constructor or setters arguments.
* may be supplied via constructor or setters arguments.
*
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
@@ -74,7 +74,7 @@ public class RedirectView extends AbstractUrlBasedView {
/**
* Create a new {@code RedirectView} with the given redirect URL.
* Status code {@link HttpStatus#SEE_OTHER} is used by default.
* <p>Status code {@link HttpStatus#SEE_OTHER} is used by default.
*/
public RedirectView(String redirectUrl) {
super(redirectUrl);
@@ -109,9 +109,9 @@ public class RedirectView extends AbstractUrlBasedView {
}
/**
* Whether to interpret a given redirect URLs that starts with a slash ("/")
* as relative to the current context path ({@code true}, the default) or to
* the web server root ({@code false}).
* Whether to interpret a given redirect URL that starts with a slash ("/")
* as relative to the current context path ({@code true}, the default) or
* relative to the web server root ({@code false}).
*/
public void setContextRelative(boolean contextRelative) {
this.contextRelative = contextRelative;
@@ -141,7 +141,7 @@ public class RedirectView extends AbstractUrlBasedView {
/**
* Configure one or more hosts associated with the application.
* All other hosts will be considered external hosts.
* <p>All other hosts will be considered external hosts.
* <p>In effect this provides a way turn off encoding for URLs that
* have a host and that host is not listed as a known host.
* <p>If not set (the default) all redirect URLs are encoded.
@@ -301,7 +301,7 @@ public class RedirectView extends AbstractUrlBasedView {
* property is configured and the target URL has a host that does not match.
* @param targetUrl the target redirect URL
* @return {@code true} the target URL has a remote host, {@code false} if it
* the URL does not have a host or the "host" property is not configured.
* the URL does not have a host or the "host" property is not configured
*/
protected boolean isRemoteHost(String targetUrl) {
if (ObjectUtils.isEmpty(this.hosts)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import org.springframework.ui.Model;
* Or controllers may return model attribute(s) and rely on a default view name
* being selected based on the request path.
*
* <p>{@link Rendering} can be used to combine a view name with model attributes,
* <p>{@code Rendering} can be used to combine a view name with model attributes,
* set the HTTP status or headers, and for other more advanced options around
* redirect scenarios.
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -96,7 +96,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
@ParameterizedDataBufferAllocatingTest
void bodyToMonoVoid(String displayName, DataBufferFactory bufferFactory) {
void bodyToMonoVoid(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
this.server.enqueue(new MockResponse()
@@ -114,7 +114,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest // SPR-17482
void bodyToMonoVoidWithoutContentType(String displayName, DataBufferFactory bufferFactory) {
void bodyToMonoVoidWithoutContentType(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
this.server.enqueue(new MockResponse()
@@ -131,7 +131,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest
void onStatusWithBodyNotConsumed(String displayName, DataBufferFactory bufferFactory) {
void onStatusWithBodyNotConsumed(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
RuntimeException ex = new RuntimeException("response error");
@@ -139,7 +139,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest
void onStatusWithBodyConsumed(String displayName, DataBufferFactory bufferFactory) {
void onStatusWithBodyConsumed(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
RuntimeException ex = new RuntimeException("response error");
@@ -147,7 +147,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest // SPR-17473
void onStatusWithMonoErrorAndBodyNotConsumed(String displayName, DataBufferFactory bufferFactory) {
void onStatusWithMonoErrorAndBodyNotConsumed(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
RuntimeException ex = new RuntimeException("response error");
@@ -155,7 +155,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest
void onStatusWithMonoErrorAndBodyConsumed(String displayName, DataBufferFactory bufferFactory) {
void onStatusWithMonoErrorAndBodyConsumed(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
RuntimeException ex = new RuntimeException("response error");
@@ -163,7 +163,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest // gh-23230
void onStatusWithImmediateErrorAndBodyNotConsumed(String displayName, DataBufferFactory bufferFactory) {
void onStatusWithImmediateErrorAndBodyNotConsumed(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
RuntimeException ex = new RuntimeException("response error");
@@ -173,7 +173,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest
void releaseBody(String displayName, DataBufferFactory bufferFactory) {
void releaseBody(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
this.server.enqueue(new MockResponse()
@@ -190,7 +190,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
}
@ParameterizedDataBufferAllocatingTest
void exchangeToBodilessEntity(String displayName, DataBufferFactory bufferFactory) {
void exchangeToBodilessEntity(DataBufferFactory bufferFactory) {
setUp(bufferFactory);
this.server.enqueue(new MockResponse()
@@ -43,6 +43,7 @@ import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -89,16 +90,16 @@ class WebClientIntegrationTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] {displayName} [{0}]")
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("arguments")
@interface ParameterizedWebClientTest {
}
static Stream<ClientHttpConnector> arguments() {
static Stream<Named<ClientHttpConnector>> arguments() {
return Stream.of(
new ReactorClientHttpConnector(),
new JettyClientHttpConnector(),
new HttpComponentsClientHttpConnector()
Named.named("Reactor Netty", new ReactorClientHttpConnector()),
Named.named("Jetty", new JettyClientHttpConnector()),
Named.named("HttpComponents", new HttpComponentsClientHttpConnector())
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,9 +21,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Duration;
import java.util.stream.Stream;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -56,6 +58,7 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.Undert
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Named.named;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM;
/**
@@ -64,31 +67,6 @@ import static org.springframework.http.MediaType.TEXT_EVENT_STREAM;
*/
class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] server [{0}], webClient [{1}]")
@MethodSource("arguments")
protected @interface ParameterizedSseTest {
}
static Object[][] arguments() {
return new Object[][] {
{new JettyHttpServer(), new ReactorClientHttpConnector()},
{new JettyHttpServer(), new JettyClientHttpConnector()},
{new JettyHttpServer(), new HttpComponentsClientHttpConnector()},
{new ReactorHttpServer(), new ReactorClientHttpConnector()},
{new ReactorHttpServer(), new JettyClientHttpConnector()},
{new ReactorHttpServer(), new HttpComponentsClientHttpConnector()},
{new TomcatHttpServer(), new ReactorClientHttpConnector()},
{new TomcatHttpServer(), new JettyClientHttpConnector()},
{new TomcatHttpServer(), new HttpComponentsClientHttpConnector()},
{new UndertowHttpServer(), new ReactorClientHttpConnector()},
{new UndertowHttpServer(), new JettyClientHttpConnector()},
{new UndertowHttpServer(), new HttpComponentsClientHttpConnector()}
};
}
private AnnotationConfigApplicationContext wac;
private WebClient webClient;
@@ -312,4 +290,35 @@ class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] server = {0}, webClient = {1}")
@MethodSource("arguments")
private @interface ParameterizedSseTest {
}
static Stream<Arguments> arguments() {
return Stream.of(
args(new JettyHttpServer(), new ReactorClientHttpConnector()),
args(new JettyHttpServer(), new JettyClientHttpConnector()),
args(new JettyHttpServer(), new HttpComponentsClientHttpConnector()),
args(new ReactorHttpServer(), new ReactorClientHttpConnector()),
args(new ReactorHttpServer(), new JettyClientHttpConnector()),
args(new ReactorHttpServer(), new HttpComponentsClientHttpConnector()),
args(new TomcatHttpServer(), new ReactorClientHttpConnector()),
args(new TomcatHttpServer(), new JettyClientHttpConnector()),
args(new TomcatHttpServer(), new HttpComponentsClientHttpConnector()),
args(new UndertowHttpServer(), new ReactorClientHttpConnector()),
args(new UndertowHttpServer(), new JettyClientHttpConnector()),
args(new UndertowHttpServer(), new HttpComponentsClientHttpConnector())
);
}
private static Arguments args(HttpServer httpServer, ClientHttpConnector connector) {
return Arguments.of(
named(httpServer.getClass().getSimpleName(), httpServer),
named(connector.getClass().getSimpleName(), connector));
}
}
@@ -36,6 +36,8 @@ import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.testfixture.http.server.reactive.bootstrap.HttpServer
import reactor.core.publisher.Flux
import java.time.Duration
class CoroutinesIntegrationTests : AbstractRequestMappingIntegrationTests() {
@@ -110,6 +112,25 @@ class CoroutinesIntegrationTests : AbstractRequestMappingIntegrationTests() {
}
}
@ParameterizedHttpServerTest
fun `Suspending handler method returning ResponseEntity of Flux `(httpServer: HttpServer) {
startServer(httpServer)
val entity = performGet<String>("/entity-flux", HttpHeaders.EMPTY, String::class.java)
assertThat(entity.statusCode).isEqualTo(HttpStatus.OK)
assertThat(entity.body).isEqualTo("01234")
}
@ParameterizedHttpServerTest
fun `Suspending handler method returning ResponseEntity of Flow`(httpServer: HttpServer) {
startServer(httpServer)
val entity = performGet<String>("/entity-flow", HttpHeaders.EMPTY, String::class.java)
assertThat(entity.statusCode).isEqualTo(HttpStatus.OK)
assertThat(entity.body).isEqualTo("foobar")
}
@Configuration
@EnableWebFlux
@ComponentScan(resourcePattern = "**/CoroutinesIntegrationTests*")
@@ -167,6 +188,25 @@ class CoroutinesIntegrationTests : AbstractRequestMappingIntegrationTests() {
throw IllegalStateException()
}
@GetMapping("/entity-flux")
suspend fun entityFlux() : ResponseEntity<Flux<String>> {
val strings = Flux.interval(Duration.ofMillis(100)).take(5)
.map { l -> l.toString() }
delay(1)
return ResponseEntity.ok().body(strings)
}
@GetMapping("/entity-flow")
suspend fun entityFlow() : ResponseEntity<Flow<String>> {
val strings = flow {
emit("foo")
delay(1)
emit("bar")
delay(1)
}
return ResponseEntity.ok().body(strings)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,7 @@
package org.springframework.web.servlet.handler;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
@@ -32,6 +30,7 @@ import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.mock;
/**
@@ -44,19 +43,12 @@ class HandlerMappingTests {
@SuppressWarnings("unused")
private static Stream<Arguments> pathPatternsArguments() {
List<Function<String, MockHttpServletRequest>> factories =
PathPatternsTestUtils.requestArguments().collect(Collectors.toList());
return Stream.of(
Arguments.arguments(new TestHandlerMapping(), factories.get(0)),
Arguments.arguments(new TestHandlerMapping(), factories.get(1))
);
return PathPatternsTestUtils.requestArguments().map(function -> arguments(function, new TestHandlerMapping()));
}
@PathPatternsParameterizedTest
void orderedInterceptors(
TestHandlerMapping mapping, Function<String, MockHttpServletRequest> requestFactory)
throws Exception {
void orderedInterceptors(Function<String, MockHttpServletRequest> requestFactory, TestHandlerMapping mapping) throws Exception {
MappedInterceptor i1 = new MappedInterceptor(new String[] {"/**"}, mock(HandlerInterceptor.class));
HandlerInterceptor i2 = mock(HandlerInterceptor.class);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.springframework.util.PathMatcher;
@@ -47,7 +48,7 @@ class MappedInterceptorTests {
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
private static Stream<Named<Function<String, MockHttpServletRequest>>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
/**
* Annotation for tests parameterized to use either
* {@link org.springframework.web.util.pattern.PathPatternParser} or
@@ -33,7 +36,7 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
// Do not auto-close arguments since ConfigurableWebApplicationContext implements
// AutoCloseable and is shared between parameterized test invocations.
@org.junit.jupiter.params.ParameterizedTest(autoCloseArguments = false)
@org.junit.jupiter.params.provider.MethodSource("pathPatternsArguments")
@ParameterizedTest(name = "[{index}] {0}", autoCloseArguments = false)
@MethodSource("pathPatternsArguments")
public @interface PathPatternsParameterizedTest {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +20,15 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Named;
import org.springframework.lang.Nullable;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
import static org.junit.jupiter.api.Named.named;
/**
* Utility methods to help with parameterized tests for URL pattern matching
* via pre-parsed {@code PathPattern}s or String pattern matching with
@@ -35,22 +39,22 @@ import org.springframework.web.util.UrlPathHelper;
*/
public abstract class PathPatternsTestUtils {
public static Stream<Function<String, MockHttpServletRequest>> requestArguments() {
public static Stream<Named<Function<String, MockHttpServletRequest>>> requestArguments() {
return requestArguments(null);
}
public static Stream<Function<String, MockHttpServletRequest>> requestArguments(@Nullable String contextPath) {
public static Stream<Named<Function<String, MockHttpServletRequest>>> requestArguments(@Nullable String contextPath) {
return Stream.of(
path -> {
named("ServletRequestPathUtils", path -> {
MockHttpServletRequest request = createRequest("GET", contextPath, path);
ServletRequestPathUtils.parseAndCache(request);
return request;
},
path -> {
}),
named("UrlPathHelper", path -> {
MockHttpServletRequest request = createRequest("GET", contextPath, path);
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
return request;
}
})
);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.springframework.ui.ModelMap;
@@ -42,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class UrlFilenameViewControllerTests {
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
private static Stream<Named<Function<String, MockHttpServletRequest>>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
@@ -46,7 +47,7 @@ class WebContentInterceptorTests {
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
private static Stream<Named<Function<String, MockHttpServletRequest>>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,10 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
@@ -49,6 +51,8 @@ import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* Test various scenarios for detecting method-level and method parameter annotations depending
@@ -60,34 +64,39 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class HandlerMethodAnnotationDetectionTests {
static Object[][] handlerTypes() {
return new Object[][] {
{ SimpleController.class, true }, // CGLIB proxy
{ SimpleController.class, false },
static Stream<Arguments> handlerTypes() {
return Stream.of(
args(SimpleController.class, true), // CGLIB proxy
args(SimpleController.class, false),
{ AbstractClassController.class, true }, // CGLIB proxy
{ AbstractClassController.class, false },
args(AbstractClassController.class, true), // CGLIB proxy
args(AbstractClassController.class, false),
{ ParameterizedAbstractClassController.class, true }, // CGLIB proxy
{ ParameterizedAbstractClassController.class, false },
args(ParameterizedAbstractClassController.class, true), // CGLIB proxy
args(ParameterizedAbstractClassController.class, false),
{ ParameterizedSubclassOverridesDefaultMappings.class, true }, // CGLIB proxy
{ ParameterizedSubclassOverridesDefaultMappings.class, false },
args(ParameterizedSubclassOverridesDefaultMappings.class, true), // CGLIB proxy
args(ParameterizedSubclassOverridesDefaultMappings.class, false),
// TODO [SPR-9517] Enable ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass test cases
// { ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, true }, // CGLIB proxy
// { ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, false },
// args(ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, true), // CGLIB proxy
// args(ParameterizedSubclassDoesNotOverrideConcreteImplementationsFromGenericAbstractSuperclass.class, false),
{ InterfaceController.class, true }, // JDK dynamic proxy
{ InterfaceController.class, false },
args(InterfaceController.class, true), // JDK dynamic proxy
args(InterfaceController.class, false),
{ ParameterizedInterfaceController.class, false }, // no AOP
args(ParameterizedInterfaceController.class, false), // no AOP
{ SupportClassController.class, true }, // CGLIB proxy
{ SupportClassController.class, false }
};
args(SupportClassController.class, true), // CGLIB proxy
args(SupportClassController.class, false)
);
}
private static Arguments args(Class<?> controllerType, boolean useAutoProxy) {
return arguments(named(controllerType.getSimpleName(), controllerType), useAutoProxy);
}
private RequestMappingHandlerMapping handlerMapping;
private RequestMappingHandlerAdapter handlerAdapter;
@@ -116,9 +125,9 @@ class HandlerMethodAnnotationDetectionTests {
}
@ParameterizedTest(name = "[{index}] controller [{0}], auto-proxy [{1}]")
@ParameterizedTest(name = "[{index}] controller = {0}, auto-proxy = {1}")
@MethodSource("handlerTypes")
void testRequestMappingMethod(Class<?> controllerType, boolean useAutoProxy) throws Exception {
void requestMappingMethod(Class<?> controllerType, boolean useAutoProxy) throws Exception {
setUp(controllerType, useAutoProxy);
String datePattern = "MM:dd:yyyy";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -136,7 +136,7 @@ public abstract class AbstractHtmlElementTagTests extends AbstractTagTests {
}
protected final void assertBlockTagContains(String output, String desiredContents) {
String contents = output.substring(output.indexOf(">") + 1, output.lastIndexOf("<"));
String contents = output.substring(output.indexOf(">") + 1, output.lastIndexOf('<'));
assertThat(contents.contains(desiredContents)).as("Expected to find '" + desiredContents + "' in the contents of block tag '" + output + "'").isTrue();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.web.servlet.view;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Named;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
@@ -38,59 +40,59 @@ public class DefaultRequestToViewNameTranslatorTests {
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
private static Stream<Named<Function<String, MockHttpServletRequest>>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments("/sundays");
}
@PathPatternsParameterizedTest
void testGetViewNameLeavesLeadingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameLeavesLeadingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/");
this.translator.setStripLeadingSlash(false);
assertViewName(request, "/" + VIEW_NAME);
}
@PathPatternsParameterizedTest
void testGetViewNameLeavesTrailingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameLeavesTrailingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/");
this.translator.setStripTrailingSlash(false);
assertViewName(request, VIEW_NAME + "/");
}
@PathPatternsParameterizedTest
void testGetViewNameLeavesExtensionIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameLeavesExtensionIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + EXTENSION);
this.translator.setStripExtension(false);
assertViewName(request, VIEW_NAME + EXTENSION);
}
@PathPatternsParameterizedTest
void testGetViewNameWithDefaultConfiguration(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithDefaultConfiguration(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + EXTENSION);
assertViewName(request, VIEW_NAME);
}
@PathPatternsParameterizedTest
void testGetViewNameWithCustomSeparator(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithCustomSeparator(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/fiona" + EXTENSION);
this.translator.setSeparator("_");
assertViewName(request, VIEW_NAME + "_fiona");
}
@PathPatternsParameterizedTest
void testGetViewNameWithNoExtension(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithNoExtension(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
assertViewName(request, VIEW_NAME);
}
@PathPatternsParameterizedTest
void testGetViewNameWithSemicolonContent(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithSemicolonContent(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + ";a=A;b=B");
assertViewName(request, VIEW_NAME);
}
@PathPatternsParameterizedTest
void testGetViewNameWithPrefix(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithPrefix(Function<String, MockHttpServletRequest> requestFactory) {
final String prefix = "fiona_";
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setPrefix(prefix);
@@ -98,14 +100,14 @@ public class DefaultRequestToViewNameTranslatorTests {
}
@PathPatternsParameterizedTest
void testGetViewNameWithNullPrefix(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithNullPrefix(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setPrefix(null);
assertViewName(request, VIEW_NAME);
}
@PathPatternsParameterizedTest
void testGetViewNameWithSuffix(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithSuffix(Function<String, MockHttpServletRequest> requestFactory) {
final String suffix = ".fiona";
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setSuffix(suffix);
@@ -113,7 +115,7 @@ public class DefaultRequestToViewNameTranslatorTests {
}
@PathPatternsParameterizedTest
void testGetViewNameWithNullSuffix(Function<String, MockHttpServletRequest> requestFactory) {
void getViewNameWithNullSuffix(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setSuffix(null);
assertViewName(request, VIEW_NAME);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@ import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrate
import org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
@@ -66,15 +67,15 @@ public abstract class AbstractWebSocketIntegrationTests {
static Stream<Arguments> argumentsFactory() {
return Stream.of(
arguments(new JettyWebSocketTestServer(), new JettyWebSocketClient()),
arguments(new TomcatWebSocketTestServer(), new StandardWebSocketClient()),
arguments(new UndertowTestServer(), new StandardWebSocketClient()));
arguments(named("Jetty", new JettyWebSocketTestServer()), named("Jetty", new JettyWebSocketClient())),
arguments(named("Tomcat", new TomcatWebSocketTestServer()), named("Standard", new StandardWebSocketClient())),
arguments(named("Undertow", new UndertowTestServer()), named("Standard", new StandardWebSocketClient())));
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ParameterizedTest(name = "[{index}] server [{0}], client [{1}]")
@ParameterizedTest(name = "[{index}] server = {0}, client = {1}")
@MethodSource("argumentsFactory")
protected @interface ParameterizedWebSocketTest {
}
@@ -104,6 +104,7 @@
<suppress files="ExtractingResponseErrorHandlerTests" checks="MutableException" />
<suppress files="ServletAnnotationControllerHandlerMethodTests" checks="InterfaceIsType" />
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]web[\\/]servlet[\\/]resource[\\/]GzipSupport" checks="IllegalImport" id="bannedJUnitJupiterImports" />
<suppress files="src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]web[\\/]servlet[\\/]handler[\\/]PathPatterns.+" checks="IllegalImport" id="bannedJUnitJupiterImports" />
<!-- spring-websocket -->
<suppress files="web[\\/]socket[\\/]CloseStatus" checks="JavadocStyle" />

Some files were not shown because too many files have changed in this diff Show More