Compare commits

..

84 Commits

Author SHA1 Message Date
Spring Builds 2af9d961e4 Release v5.3.24 2022-11-16 07:51:36 +00:00
Sam Brannen 8e64701cb7 Fix Javadoc formatting issue in TestSocketUtils 2022-11-15 14:47:00 +01:00
Sam Brannen ae9a9e3c06 Document TestSocketUtils in the testing chapter 2022-11-15 14:43:56 +01:00
Juergen Hoeller c94025699f Upgrade to Reactor 2020.0.25
See gh-29464
2022-11-15 13:11:32 +01:00
Juergen Hoeller ec3f59e6fe Allow AutoCloseable dereferences on original AutoCloseable beans
Closes gh-29480
2022-11-14 23:37:05 +01:00
Juergen Hoeller 49ee4a4fdf Deprecated unused findAutowireCandidates template method (removed in 6.0)
See gh-29487
2022-11-14 23:35:47 +01:00
Sam Brannen 698f8995f7 Revise TestSocketUtils and tests
Closes gh-29132
2022-11-14 20:26:58 +01:00
Chris Bono ee51dab1f3 Introduce TestSocketUtils as a replacement for SocketUtils
SocketUtils was officially deprecated in 5.3.16 (gh-28052) and removed
in 6.0 M3 (gh-28054); however, there is still need for a subset of this
functionality in integration tests for testing scenarios in which it is
not possible for the system under test to select its own random port
(or rely on the operating system to provide an ephemeral port).

This commit therefore introduces a scaled down version in the
spring-test module called TestSocketUtils which supports retrieval of a
single TCP port.

See gh-29132
2022-11-14 20:26:58 +01:00
Sam Brannen 743a96b75e Polish SocketUtilsTests 2022-11-14 20:26:58 +01:00
Sébastien Deleuze b766a49a4d Support suspending functions returning Unit in WebFlux
Closes gh-27629
2022-11-13 19:05:29 +01:00
67 3ff308403a Fix two typos in integration.adoc and webflux.adoc
- Change "you ca" to "you can" in webflux.adoc

- English names can be used in "month" field instead of "day-of-month"
  field in cron expressions in integration.adoc

Closes gh-29469
2022-11-11 17:11:16 +01:00
Sam Brannen 27f3feea1a Ensure SpEL ternary and Elvis expressions are enclosed in parentheses in toStringAST()
Prior to this commit, ternary and Elvis expressions enclosed in
parentheses (to account for operator precedence) were properly parsed
and evaluated; however, the corresponding toStringAST() implementations
did not enclose the results in parentheses. Consequently, the string
representation of the ASTs did not reflect the original semantics of
such expressions.

For example, given "(4 % 2 == 0 ? 1 : 0) * 10" as the expression to
parse and evaluate, the result of toStringAST() was previously
"(((4 % 2) == 0) ? 1 : 0 * 10)" instead of
"((((4 % 2) == 0) ? 1 : 0) * 10)", implying that 0 should be multiplied
by 10 instead of multiplying the result of the ternary expression by 10.

This commit addresses this by ensuring that SpEL ternary and Elvis
expressions are enclosed in parentheses in toStringAST().

Closes gh-29463
2022-11-11 17:11:07 +01:00
Sam Brannen b42b785cd1 Polish ParsingTests 2022-11-11 17:10:57 +01:00
rstoyanchev f9d8367379 Exclude URL query from checkpoint in DefaultWebClient
Closes gh-29148
2022-11-11 13:29:36 +00:00
rstoyanchev 09b19d7aa6 Update RestTemplate Javadoc
Closes gh-29311
2022-11-11 12:16:02 +00:00
rstoyanchev 8ade083a05 Filter out null WebSocketSession attributes
Closes gh-29315
2022-11-11 11:20:18 +00:00
rstoyanchev db9e89fba9 Update docs on web controller method return values
Closes gh-29349
2022-11-11 11:03:24 +00:00
Juergen Hoeller d7d44c6c51 Polishing 2022-11-10 18:26:49 +01:00
Juergen Hoeller 330d2a5982 Upgrade to Reactor 2020.0.24
Includes Netty 4.1.85, Tomcat 9.0.68, Jetty 9.4.49, Jetty Reactive HttpClient 1.1.13, Undertow 2.2.21, SmallRye Mutiny 1.8, Log4J 2.19, OpenPDF 1.3.30, Mockito 4.8.1, HtmlUnit 2.66, Checkstyle 10.4

Closes gh-29464
2022-11-10 18:25:55 +01:00
Juergen Hoeller 86fbb8678a Use unquoted value in assertion message in MediaType.checkParameters()
Closes gh-29461
2022-11-10 18:24:57 +01:00
Christoph Dreis 55b258f2de Avoid unnecessary allocations in StompDecoder#unescape
Closes gh-29443
2022-11-08 18:10:54 +01:00
Sam Brannen f4b3333fa8 Avoid reflection for annotation attribute method invocations
As a follow up to 332b25b680, this commit consistently avoids the use of
reflection for annotation attribute method invocations.

See gh-29301
Closes gh-29448
2022-11-08 16:23:33 +01:00
Sam Brannen e5878ab15b Fix precondition assertions 2022-11-08 12:08:08 +01:00
Sam Brannen dedbbf0a79 Avoid warnings in tests 2022-11-08 12:04:56 +01:00
Sam Brannen 5f02323b9c Avoid String allocations with Assert.isTrue() 2022-11-05 14:40:45 +01:00
Sam Brannen 902cdd1a2f Ignore spring-core-test from main branch in nohttp checks 2022-11-05 14:39:50 +01:00
Sam Brannen deabd66939 Avoid String allocations with Assert.state() 2022-11-04 16:24:59 +01:00
Christoph Dreis 2a853aea67 Avoid String allocations in MediaType.checkParameters
Closes gh-29428
2022-11-04 16:24:59 +01:00
Sam Brannen a281d8c3fd Polishing 2022-11-04 16:24:59 +01:00
Sam Brannen 638d91f5f4 Ignore framework-docs from main branch in nohttp check 2022-11-04 16:22:01 +01:00
Sam Brannen 6abfe041b1 Ignore /framework-docs/build folder from main branch 2022-11-02 15:32:16 +01:00
Roy Lee 462d33e68d Fix typo in WebSocket reference doc regarding subscription header
Closes gh-29228
2022-11-02 15:28:43 +01:00
Steve Gerhardt fded79d807 Make SimpleMailMessage setters parameters nullable
Closes gh-29139
2022-11-02 12:18:03 +01:00
Sébastien Deleuze c14cbd07f4 Use non-null DateFormat for CustomDateEditor in tests
Closes gh-29419
2022-11-02 12:01:49 +01:00
Christoph Dreis 58bb6e7181 Reduce allocations caused by producible media types
Closes gh-29412
2022-11-02 11:15:26 +01:00
David Costanzo e1df056059 Fix typo: "as describe in" -> "as described in" 2022-10-31 09:07:39 +01:00
Arjen Poutsma 46329851c1 Ensure state of PartGenerator accepts buffers
This commit makes sure that the current state of the PartGenerator
is able to handle an incoming buffer, possibly requested because of
a request coming from the subscription. All states accept new buffers
except the WritingFileState.

Closes gh-29227
2022-10-27 12:12:57 +02:00
Arjen Poutsma 51d109de30 Updated sdkmanrc 2022-10-27 12:03:34 +02:00
rstoyanchev 3d89acf9ea Adjust checkForLeaks timeout settings
LeakAwareDataBufferFactory#checkForLeaks automatically waits up to 5
sec for buffers to be released, which could be used as a way of
awaiting on some async logic to complete, and as long as buffers are
released, it shouldn't be long. However, the leak test in
LeakAwareDataBufferFactoryTests actually expects to find a leak, and
always ends up waiting the full 5 seconds.

This change, makes the wait configurable, with the no-arg method
using 0 (no waiting). AbstractLeakCheckingTests uses 1 second by
default since ResourceRegionEncoderTests did fail locally. If more
tests need this, we can adjust the settings.
2022-10-24 10:55:39 +01:00
Brian Clozel 9410998897 Add caching headers to If-Unmodified-Since responses
Conditional requests using "If-Unmodified-Since" headers are generally
used as precondition checks for state-changing methods (POST, PUT,
DELETE). See https://datatracker.ietf.org/doc/html/rfc7232#section-3.4
The spec also allows for idempotent methods like GET and HEAD.

Prior to this commit, the "If-Unmodified-Since" processing done in
`checkNotModified` (see `ServletWebRequest` and
`DefaultServerWebExchange`) would only focus on the state changing
methods and not take into account the safe methods. For those cases, the
"ETag" and "Last-Modified" would be missing from the response.

This commit ensures that such headers are added as expected in these
cases.

Fixes gh-29362
2022-10-21 15:11:08 +02:00
Mikk Raudvere 12cc8a9f07 Fix typos
Closes gh-29364
2022-10-21 09:35:38 +02:00
Juergen Hoeller 4c38777064 Explain logger name semantics (see AbstractTraceInterceptor.setLoggerName)
See gh-28678
2022-10-19 14:49:27 +02:00
Vedran Pavic fb291379e4 Avoid use of Commons Logging in LoggingCacheErrorHandler public API
At present, creating LoggingCacheErrorHandler with custom logger requires use of Commons Logging API, as the appropriate constructor expects org.apache.commons.logging.Log instance. As Commons Logging is rarely the logging framework of choice in applications these days, interaction with its API might not be desirable.

This commit adds LoggingCacheErrorHandler constructor that accepts logger name and thus avoids leaking out any details about the underlying logging framework.
2022-10-19 13:48:59 +02:00
Juergen Hoeller 182ba4ac29 Upgrade to Checkstyle 10.3.4 2022-10-18 23:05:10 +02:00
Juergen Hoeller dbcfeb8f4f Aligned with PathMatchingResourcePatternResolverTests on main
See gh-29333
2022-10-18 23:04:44 +02:00
Juergen Hoeller 6f64cfd1e5 Test square brackets with index/key expressions
See gh-27925
2022-10-18 23:04:23 +02:00
Sam Brannen 652781c4a1 Test status quo in 5.3.x for PathMatchingResourcePatternResolver
See gh-29333
2022-10-18 17:51:29 +02:00
Juergen Hoeller 8e25e32eb8 Polishing 2022-10-18 16:17:30 +02:00
Juergen Hoeller 4b0bf16389 Select ambiguous write method based on read method (matching its return type)
Also avoids unnecessary checks in name-based PropertyDescriptor constructor.

See gh-29320
2022-10-18 16:17:03 +02:00
Juergen Hoeller 33023b240f Provide optional SimpleBeanInfoFactory for better introspection performance
Closes gh-29330
2022-10-17 12:25:53 +02:00
Juergen Hoeller c407dc3df8 Reset/rebuild BeanPostProcessorCache within full synchronization
Closes gh-29299
2022-10-17 12:25:35 +02:00
Sébastien Deleuze 1439c5bb8f Document how to use WebJars without webjars-locator-core dependency
Closes gh-29322
2022-10-16 20:28:57 +02:00
Stephane Nicoll 3f42d4de43 Upgrade Ubuntu version in CI image 2022-10-13 11:29:31 +02:00
Sam Brannen 5ac687f4aa Add warning to AnnotatedElementUtils.getMergedRepeatableAnnotations()
See gh-20279
2022-10-11 20:49:19 +02:00
Sam Brannen 332b25b680 Support searches for non-public repeatable annotations
Prior to this commit, searches for non-public repeatable annotations
failed with error messages similar to the following, since the
repeatable annotation's container's `value()` method could not be
invoked via reflection.

JDK 8:

java.lang.IllegalAccessError: tried to access class
org.springframework.core.annotation.NestedRepeatableAnnotationsTests$A
from class com.sun.proxy.$Proxy12

JDK 17:

java.lang.IllegalAccessError: failed to access class
org.springframework.core.annotation.NestedRepeatableAnnotationsTests$A
from class jdk.proxy2.$Proxy12
(org.springframework.core.annotation.NestedRepeatableAnnotationsTests$A
is in unnamed module of loader 'app'; jdk.proxy2.$Proxy12 is in module
jdk.proxy2 of loader 'app')

This commit makes it possible to search for non-public repeatable
annotations by first attempting to invoke the repeatable annotation's
container's `value()` method via the container's InvocationHandler (if
the container is a JDK dynamic proxy) and then falling back to
reflection for the method invocation if an error occurs (such as a
SecurityException).

Closes gh-29301
2022-10-11 19:27:40 +02:00
Sam Brannen 9876701493 Support nesting in AnnotatedElementUtils.getMergedRepeatableAnnotations()
This commit is a follow up to 828f74f71a
and applies to same fix for getMergedRepeatableAnnotations().

See the previous commit for details.

Closes gh-20279
2022-10-11 18:06:00 +02:00
Sam Brannen 828f74f71a Support nesting in AnnotatedElementUtils.findMergedRepeatableAnnotations()
Prior to this commit, the findMergedRepeatableAnnotations() methods in
AnnotatedElementUtils failed to find repeatable annotations declared
on other repeatable annotations (i.e., when one repeatable annotation
type was used as a meta-annotation on a different repeatable annotation
type).

The reason is that
findMergedRepeatableAnnotations(element, annotationType, containerType)
always used RepeatableContainers.of(annotationType, containerType) to
create a RepeatableContainers instance, even if the supplied
containerType was null. Doing so restricts the search to supporting
only repeatable annotations whose container is the supplied
containerType and prevents the search from finding repeatable
annotations declared as meta-annotations on other types of repeatable
annotations.

Note, however, that direct use of the MergedAnnotations API already
supported finding nested repeatable annotations when using
RepeatableContainers.standardRepeatables() or
RepeatableContainers.of(...).and(...).and(...). The latter composes
support for multiple repeatable annotation types and their containers.

This commit addresses the issue for findMergedRepeatableAnnotations()
when the containerType is null or not provided.

However, findMergedRepeatableAnnotations(element, annotationType, containerType)
still suffers from the aforementioned limitation, and the Javadoc has
been updated to make that clear.

Closes gh-20279
2022-10-11 17:21:28 +02:00
Sam Brannen 965dd66f8c Improve Javadoc for RepeatableContainers 2022-10-11 16:41:19 +02:00
Sam Brannen b71d95df71 Document how to switch to the default set of TestExecutionListeners
Closes gh-29281
2022-10-08 16:20:55 +02:00
Sam Brannen b20de758b2 Support custom status code in ExchangeResult for WebTestClient
Prior to this commit, ExchangeResult.assertWithDiagnostics() threw an
IllegalArgumentException for a custom HTTP status code since toString()
invoked getStatus() without a try-catch block.

This commit addresses this issue by introducing a formatStatus() method
that defensively formats the response status, initially trying to
format the HttpStatus and falling back to formatting the raw integer
status code.

Closes gh-29283
2022-10-08 15:45:24 +02:00
Johnny Lim 1c1a0afbed Use AssertJ static imports consistently in 5.3.x
Closes gh-29282
2022-10-08 13:57:21 +02:00
Sam Brannen a599601dd9 Document how to switch to the default set of TestExecutionListeners
Closes gh-29281
2022-10-07 17:58:22 +02:00
Sam Brannen 5eee4673c1 Document limitation of AopTestUtils.getUltimateTargetObject() regarding non-static TargetSource
Closes gh-29276
2022-10-07 16:09:39 +02:00
Sam Brannen 8caed88c14 Test status quo for URI/URL for scanned filesystem resources
See gh-29275
2022-10-07 12:17:27 +02:00
Sam Brannen 3dc60f1627 Backport changes to PathMatchingResourcePatternResolverTests 2022-10-07 11:01:02 +02:00
Sam Brannen fee38175b3 Revert "Test encoding and Unicode normalization for scanned resources"
This reverts commit eb2c1cbcd4.
2022-10-04 14:29:22 +02:00
Sam Brannen eb2c1cbcd4 Test encoding and Unicode normalization for scanned resources
This commit introduces tests which serve as "regression tests" for the
behavior of PathMatchingResourcePatternResolver in Spring Framework
5.3.x with regard to URL-encoding and Unicode normalization of resource
paths.

Specifically, the new tests demonstrate that resource paths do NOT need
to be decoded or normalized in 5.3.x.

See gh-29243
2022-10-04 13:19:59 +02:00
Brian Clozel 8d0ac214f3 Configure more memory for checkstyleNohttp
See https://docs.gradle.org/7.5/release-notes.html#checkstyle-tasks-use-toolchains-and-execute-in-parallel-by-default
2022-10-04 13:08:08 +02:00
Sam Brannen 7241c30141 Backport changes to PathMatchingResourcePatternResolverTests 2022-10-04 11:48:29 +02:00
Brian Clozel 777caef4f7 Upgrade to Gradle 7.5.1 2022-09-30 14:35:31 +02:00
Brian Clozel 9465110917 Polish
See gh-26707
2022-09-26 22:25:44 +02:00
chenrl b71e686cbd Fix ServletContextResource isFile check
Prior to this commit, `ServletContextResource` could rely on
`ServletContext#getRealPath` to check whether a resource exists.
This behavior is not enforced on some Servlet containers, as this method
is only meant to translate virtual paths to real paths, but not
necessarily check for the existence of the file.

See https://bz.apache.org/bugzilla/show_bug.cgi?id=55837#c3 for a
rationale of this behavior in Tomcat.

This commit enforces an additional check, resolving the path as a `File`
and checking that is exists and is a file.

Closes gh-26707
2022-09-26 22:20:15 +02:00
Stephane Nicoll 433a23a0bd Merge pull request #29190 from dreis2211
* pr/29190:
  Polish "Avoid resizing of Maps created by CollectionUtils"
  Avoid resizing of Maps created by CollectionUtils

Closes gh-29190
2022-09-26 09:06:52 +02:00
Stephane Nicoll 7309fe9f2e Polish "Avoid resizing of Maps created by CollectionUtils"
See gh-29190
2022-09-26 09:00:45 +02:00
Christoph Dreis 874a296a76 Avoid resizing of Maps created by CollectionUtils
See gh-29190
2022-09-26 08:54:52 +02:00
Stephane Nicoll d872e7c326 Merge pull request #29198 from izeye
* pr/29198:
  Use actions/checkout@v3 for Gradle Wrapper Validation Action

Closes gh-29198
2022-09-26 08:52:30 +02:00
Johnny Lim 4ca574cde1 Use actions/checkout@v3 for Gradle Wrapper Validation Action
See gh-29198
2022-09-26 08:52:21 +02:00
Stephane Nicoll 98ecf0a412 Merge pull request #29201 from inabajunmr
* pr/29201:
  Fix MockMvc sample setup

Closes gh-29201
2022-09-26 08:50:49 +02:00
inaba jun ddef70935a Fix MockMvc sample setup
See gh-29201
2022-09-26 08:50:01 +02:00
Johnny Lim 1d0dc43e5f Upgrade to Gradle Enterprise Conventions Plugin 0.0.11
Closes gh-29165
2022-09-18 14:50:03 +02:00
Jerome Prinet d8e4dc967f Update Gradle Enterprise plugin to 3.11.1
Closes gh-29164
2022-09-16 17:56:23 +02:00
Sam Brannen f0f633d187 Limit exclude to core Spring Framework committers 2022-09-15 17:15:50 +02:00
Sam Brannen 4b7aa6bb39 Exclude core committers from generated changelog 2022-09-15 16:25:57 +02:00
Spring Builds 13933ee2c6 Next development version (v5.3.24-SNAPSHOT) 2022-09-15 07:59:36 +00:00
151 changed files with 2105 additions and 825 deletions
@@ -9,5 +9,5 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
+1
View File
@@ -24,6 +24,7 @@ buildSrc/build
/spring-core/graalvm/build
/spring-core/kotlin-coroutines/build
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
target/
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=8.0.345-librca
java=8.0.352-librca
+26 -23
View File
@@ -1,17 +1,17 @@
plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.10'
id "io.freefair.aspectj" version '6.2.0' apply false
id 'io.freefair.aspectj' version '6.2.0' apply false
id 'org.jetbrains.dokka' version '1.6.10' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false
id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.32" apply false
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
id "org.unbroken-dome.xjc" version '2.0.0' apply false
id "com.github.ben-manes.versions" version '0.39.0'
id "com.github.johnrengelman.shadow" version '7.0.0' apply false
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.39.0'
id 'com.github.johnrengelman.shadow' version '7.0.0' apply false
id 'de.undercouch.download' version '4.1.2'
id "me.champeau.jmh" version "0.6.6" apply false
id 'me.champeau.jmh' version "0.6.6" apply false
}
ext {
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.82.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.23"
mavenBom "io.netty:netty-bom:4.1.85.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.25"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.48.v20220622"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.49.v20220914"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
mavenBom "org.junit:junit-bom:5.8.2"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.18.0') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.19.0') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
@@ -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.5"
dependency "io.smallrye.reactive:mutiny:1.7.0"
dependency "io.smallrye.reactive:mutiny:1.8.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
dependency "com.h2database:h2:2.1.214"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
dependency "com.github.librepdf:openpdf:1.3.29"
dependency "com.github.librepdf:openpdf:1.3.30"
dependency "com.rometools:rome:1.18.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.4"
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.65') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.68') {
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.65') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.68') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.19.Final') {
dependencySet(group: 'io.undertow', version: '2.2.21.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -150,7 +150,7 @@ configure(allprojects) { project ->
}
}
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.12"
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.13"
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3'
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
@@ -198,7 +198,7 @@ configure(allprojects) { project ->
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '4.6.1') {
dependencySet(group: 'org.mockito', version: '4.8.1') {
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.64.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.66.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.64.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.66.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.3.3"
toolVersion = "10.4"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -413,11 +413,9 @@ configure(rootProject) {
nohttp {
source.exclude "**/test-output/**"
source.exclude "spring-core/graalvm/build/**" // only available on `main`
source.exclude "spring-core-test/build/**" // only available on `main`
allowlistFile = project.file("src/nohttp/allowlist.lines")
def rootPath = file(rootDir).toPath()
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/spring-core-test" + "${rootDir}/buildSrc" + "${rootDir}/framework-docs"
projectDirs.forEach { dir ->
[ 'bin', 'build', 'out', '.settings' ]
.collect { rootPath.relativize(new File(dir, it).toPath()) }
@@ -428,6 +426,10 @@ configure(rootProject) {
}
}
tasks.named("checkstyleNohttp").configure {
maxHeapSize = "1g"
}
publishing {
publications {
mavenJava(MavenPublication) {
@@ -437,4 +439,5 @@ configure(rootProject) {
}
}
}
}
+3
View File
@@ -15,3 +15,6 @@ changelog:
sort: "title"
labels:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220826
FROM ubuntu:focal-20220922
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.23
version=5.3.24
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+2 -2
View File
@@ -7,8 +7,8 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.10.2"
id "io.spring.ge.conventions" version "0.0.10"
id "com.gradle.enterprise" version "3.11.1"
id "io.spring.ge.conventions" version "0.0.11"
}
include "spring-aop"
@@ -97,8 +97,6 @@ public final class CachedIntrospectionResults {
*/
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
@@ -297,7 +295,7 @@ public final class CachedIntrospectionResults {
// Only allow URL attribute introspection, not content resolution
continue;
}
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
// Ignore read-only properties such as ClassLoader - no need to bind to those
continue;
}
@@ -347,7 +345,8 @@ public final class CachedIntrospectionResults {
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
// against a declared read method, so we prefer read method descriptors here.
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
if (pd.getWriteMethod() == null &&
isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
// Ignore read-only properties such as ClassLoader - no need to bind to those
continue;
}
@@ -380,7 +379,7 @@ public final class CachedIntrospectionResults {
if (Modifier.isStatic(method.getModifiers()) ||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
method.getParameterCount() > 0 || method.getReturnType() == void.class ||
isInvalidReadOnlyPropertyType(method.getReturnType())) {
isInvalidReadOnlyPropertyType(method.getReturnType(), method.getDeclaringClass())) {
return false;
}
try {
@@ -393,10 +392,11 @@ public final class CachedIntrospectionResults {
}
}
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
ClassLoader.class.isAssignableFrom(returnType) ||
ProtectionDomain.class.isAssignableFrom(returnType)));
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType, Class<?> beanClass) {
return (returnType != null && (ClassLoader.class.isAssignableFrom(returnType) ||
ProtectionDomain.class.isAssignableFrom(returnType) ||
(AutoCloseable.class.isAssignableFrom(returnType) &&
!AutoCloseable.class.isAssignableFrom(beanClass))));
}
@@ -422,7 +422,7 @@ public final class CachedIntrospectionResults {
}
PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
return this.propertyDescriptors.values().toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
}
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
@@ -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,9 +17,15 @@
package org.springframework.beans;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -32,6 +38,80 @@ import org.springframework.util.ObjectUtils;
*/
abstract class PropertyDescriptorUtils {
public static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
/**
* Simple introspection algorithm for basic set/get/is accessor methods,
* building corresponding JavaBeans property descriptors for them.
* <p>This just supports the basic JavaBeans conventions, without indexed
* properties or any customizers, and without other BeanInfo metadata.
* For standard JavaBeans introspection, use the JavaBeans Introspector.
* @param beanClass the target class to introspect
* @return a collection of property descriptors
* @throws IntrospectionException from introspecting the given bean class
* @since 5.3.24
* @see SimpleBeanInfoFactory
* @see java.beans.Introspector#getBeanInfo(Class)
*/
public static Collection<? extends PropertyDescriptor> determineBasicProperties(Class<?> beanClass)
throws IntrospectionException {
Map<String, BasicPropertyDescriptor> pdMap = new TreeMap<>();
for (Method method : beanClass.getMethods()) {
String methodName = method.getName();
boolean setter;
int nameIndex;
if (methodName.startsWith("set") && method.getParameterCount() == 1) {
setter = true;
nameIndex = 3;
}
else if (methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE) {
setter = false;
nameIndex = 3;
}
else if (methodName.startsWith("is") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
setter = false;
nameIndex = 2;
}
else {
continue;
}
String propertyName = Introspector.decapitalize(methodName.substring(nameIndex));
if (propertyName.isEmpty()) {
continue;
}
BasicPropertyDescriptor pd = pdMap.get(propertyName);
if (pd != null) {
if (setter) {
if (pd.getWriteMethod() == null ||
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
pd.setWriteMethod(method);
}
else {
pd.addWriteMethod(method);
}
}
else {
if (pd.getReadMethod() == null ||
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
pd.setReadMethod(method);
}
}
}
else {
pd = new BasicPropertyDescriptor(propertyName, (!setter ? method : null), (setter ? method : null));
pdMap.put(propertyName, pd);
}
}
return pdMap.values();
}
/**
* See {@link java.beans.FeatureDescriptor}.
*/
@@ -173,4 +253,72 @@ abstract class PropertyDescriptorUtils {
pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());
}
/**
* PropertyDescriptor for {@link #determineBasicProperties(Class)},
* not performing any early type determination for
* {@link #setReadMethod}/{@link #setWriteMethod}.
* @since 5.3.24
*/
private static class BasicPropertyDescriptor extends PropertyDescriptor {
@Nullable
private Method readMethod;
@Nullable
private Method writeMethod;
private final List<Method> alternativeWriteMethods = new ArrayList<>();
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
throws IntrospectionException {
super(propertyName, readMethod, writeMethod);
}
@Override
public void setReadMethod(@Nullable Method readMethod) {
this.readMethod = readMethod;
}
@Override
@Nullable
public Method getReadMethod() {
return this.readMethod;
}
@Override
public void setWriteMethod(@Nullable Method writeMethod) {
this.writeMethod = writeMethod;
}
public void addWriteMethod(Method writeMethod) {
if (this.writeMethod != null) {
this.alternativeWriteMethods.add(this.writeMethod);
this.writeMethod = null;
}
this.alternativeWriteMethods.add(writeMethod);
}
@Override
@Nullable
public Method getWriteMethod() {
if (this.writeMethod == null && !this.alternativeWriteMethods.isEmpty()) {
if (this.readMethod == null) {
return this.alternativeWriteMethods.get(0);
}
else {
for (Method method : this.alternativeWriteMethods) {
if (this.readMethod.getReturnType().isAssignableFrom(method.getParameterTypes()[0])) {
this.writeMethod = method;
break;
}
}
}
}
return this.writeMethod;
}
}
}
@@ -0,0 +1,66 @@
/*
* 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.beans;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
import java.util.Collection;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
/**
* {@link BeanInfoFactory} implementation that bypasses the standard {@link java.beans.Introspector}
* for faster introspection, reduced to basic property determination (as commonly needed in Spring).
*
* <p>To be configured via a {@code META-INF/spring.factories} file with the following content,
* overriding other custom {@code org.springframework.beans.BeanInfoFactory} declarations:
* {@code org.springframework.beans.BeanInfoFactory=org.springframework.beans.SimpleBeanInfoFactory}
*
* <p>Ordered at {@code Ordered.LOWEST_PRECEDENCE - 1} to override {@link ExtendedBeanInfoFactory}
* (registered by default in 5.3) if necessary while still allowing other user-defined
* {@link BeanInfoFactory} types to take precedence.
*
* @author Juergen Hoeller
* @since 5.3.24
* @see ExtendedBeanInfoFactory
* @see CachedIntrospectionResults
*/
public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
@Override
@NonNull
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
Collection<? extends PropertyDescriptor> pds =
PropertyDescriptorUtils.determineBasicProperties(beanClass);
return new SimpleBeanInfo() {
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return pds.toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
}
};
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
}
@@ -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.
@@ -438,7 +438,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
@@ -563,7 +562,9 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
* @param type the type of the bean
* @return the target beans, or an empty Collection if no bean of this type is found
* @throws BeansException if bean retrieval failed
* @deprecated since 5.3.24 since it is unused in the meantime
*/
@Deprecated
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory configured - " +
@@ -161,7 +161,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Cache of pre-filtered post-processors. */
@Nullable
private volatile BeanPostProcessorCache beanPostProcessorCache;
private BeanPostProcessorCache beanPostProcessorCache;
/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
@@ -944,10 +944,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor);
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
synchronized (this.beanPostProcessors) {
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor);
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
}
}
/**
@@ -957,8 +959,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* @see #addBeanPostProcessor
*/
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
this.beanPostProcessors.removeAll(beanPostProcessors);
this.beanPostProcessors.addAll(beanPostProcessors);
synchronized (this.beanPostProcessors) {
// Remove from old position, if any
this.beanPostProcessors.removeAll(beanPostProcessors);
// Add to end of list
this.beanPostProcessors.addAll(beanPostProcessors);
}
}
@Override
@@ -980,26 +986,34 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* @since 5.3
*/
BeanPostProcessorCache getBeanPostProcessorCache() {
BeanPostProcessorCache bpCache = this.beanPostProcessorCache;
if (bpCache == null) {
bpCache = new BeanPostProcessorCache();
for (BeanPostProcessor bp : this.beanPostProcessors) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);
synchronized (this.beanPostProcessors) {
BeanPostProcessorCache bppCache = this.beanPostProcessorCache;
if (bppCache == null) {
bppCache = new BeanPostProcessorCache();
for (BeanPostProcessor bpp : this.beanPostProcessors) {
if (bpp instanceof InstantiationAwareBeanPostProcessor) {
bppCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bpp);
if (bpp instanceof SmartInstantiationAwareBeanPostProcessor) {
bppCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bpp);
}
}
if (bpp instanceof DestructionAwareBeanPostProcessor) {
bppCache.destructionAware.add((DestructionAwareBeanPostProcessor) bpp);
}
if (bpp instanceof MergedBeanDefinitionPostProcessor) {
bppCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bpp);
}
}
if (bp instanceof DestructionAwareBeanPostProcessor) {
bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);
}
if (bp instanceof MergedBeanDefinitionPostProcessor) {
bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);
}
this.beanPostProcessorCache = bppCache;
}
this.beanPostProcessorCache = bpCache;
return bppCache;
}
}
private void resetBeanPostProcessorCache() {
synchronized (this.beanPostProcessors) {
this.beanPostProcessorCache = null;
}
return bpCache;
}
/**
@@ -2014,27 +2028,27 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public BeanPostProcessor set(int index, BeanPostProcessor element) {
BeanPostProcessor result = super.set(index, element);
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
return result;
}
@Override
public boolean add(BeanPostProcessor o) {
boolean success = super.add(o);
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
return success;
}
@Override
public void add(int index, BeanPostProcessor element) {
super.add(index, element);
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
@Override
public BeanPostProcessor remove(int index) {
BeanPostProcessor result = super.remove(index);
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
return result;
}
@@ -2042,7 +2056,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean remove(Object o) {
boolean success = super.remove(o);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2051,7 +2065,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean removeAll(Collection<?> c) {
boolean success = super.removeAll(c);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2060,7 +2074,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean retainAll(Collection<?> c) {
boolean success = super.retainAll(c);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2069,7 +2083,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean addAll(Collection<? extends BeanPostProcessor> c) {
boolean success = super.addAll(c);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2078,7 +2092,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean addAll(int index, Collection<? extends BeanPostProcessor> c) {
boolean success = super.addAll(index, c);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2087,7 +2101,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean removeIf(Predicate<? super BeanPostProcessor> filter) {
boolean success = super.removeIf(filter);
if (success) {
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
return success;
}
@@ -2095,7 +2109,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void replaceAll(UnaryOperator<BeanPostProcessor> operator) {
super.replaceAll(operator);
beanPostProcessorCache = null;
resetBeanPostProcessorCache();
}
}
@@ -153,6 +153,16 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("object")).isEqualTo(8);
}
@Test
void setterOverload() {
SetterOverload target = new SetterOverload();
BeanWrapper accessor = createAccessor(target);
accessor.setPropertyValue("object", "a String");
assertThat(target.value).isEqualTo("a String");
assertThat(target.getObject()).isEqualTo("a String");
assertThat(accessor.getPropertyValue("object")).isEqualTo("a String");
}
@Test
void propertyDescriptors() throws Exception {
TestBean target = new TestBean();
@@ -206,6 +216,10 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.isReadableProperty("inputStream")).isFalse();
assertThat(accessor.isReadableProperty("filename")).isTrue();
assertThat(accessor.isReadableProperty("description")).isTrue();
accessor = createAccessor(new ActiveResource());
assertThat(accessor.isReadableProperty("resource")).isTrue();
}
@Test
@@ -348,6 +362,36 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
}
public static class SetterOverload {
public String value;
public void setObject(Integer length) {
this.value = length.toString();
}
public void setObject(String object) {
this.value = object;
}
public String getObject() {
return this.value;
}
}
public static class ActiveResource implements AutoCloseable {
public ActiveResource getResource() {
return this;
}
@Override
public void close() throws Exception {
}
}
public static class GetterWithOptional {
public TestBean value;
@@ -722,7 +722,7 @@ class CustomEditorTests {
@Test
void testCustomDateEditor() {
CustomDateEditor editor = new CustomDateEditor(null, false);
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), false);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
assertThat(editor.getAsText()).isEmpty();
@@ -730,7 +730,7 @@ class CustomEditorTests {
@Test
void testCustomDateEditorWithEmptyAsNull() {
CustomDateEditor editor = new CustomDateEditor(null, true);
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
assertThat(editor.getAsText()).isEmpty();
@@ -93,7 +93,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
@Override
public void setFrom(String from) {
public void setFrom(@Nullable String from) {
this.from = from;
}
@@ -103,7 +103,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setReplyTo(String replyTo) {
public void setReplyTo(@Nullable String replyTo) {
this.replyTo = replyTo;
}
@@ -113,7 +113,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setTo(String to) {
public void setTo(@Nullable String to) {
this.to = new String[] {to};
}
@@ -128,12 +128,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setCc(String cc) {
public void setCc(@Nullable String cc) {
this.cc = new String[] {cc};
}
@Override
public void setCc(String... cc) {
public void setCc(@Nullable String... cc) {
this.cc = cc;
}
@@ -143,12 +143,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setBcc(String bcc) {
public void setBcc(@Nullable String bcc) {
this.bcc = new String[] {bcc};
}
@Override
public void setBcc(String... bcc) {
public void setBcc(@Nullable String... bcc) {
this.bcc = bcc;
}
@@ -158,7 +158,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setSentDate(Date sentDate) {
public void setSentDate(@Nullable Date sentDate) {
this.sentDate = sentDate;
}
@@ -168,7 +168,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setSubject(String subject) {
public void setSubject(@Nullable String subject) {
this.subject = subject;
}
@@ -178,7 +178,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setText(String text) {
public void setText(@Nullable String text) {
this.text = text;
}
@@ -77,6 +77,23 @@ public class LoggingCacheErrorHandler implements CacheErrorHandler {
this.logStackTraces = logStackTraces;
}
/**
* Create a {@code LoggingCacheErrorHandler} that uses the supplied
* {@code loggerName} and {@code logStackTraces} flag.
* @param loggerName the name of the logger to use. The name will be passed
* to the underlying logger implementation through Commons Logging, getting
* interpreted as log category according to the logger's configuration.
* @param logStackTraces whether to log stack traces
* @since 5.3.24
* @see org.apache.commons.logging.LogFactory#getLog(String)
* @see java.util.logging.Logger#getLogger(String)
*/
public LoggingCacheErrorHandler(String loggerName, boolean logStackTraces) {
Assert.notNull(loggerName, "'loggerName' must not be null");
this.logger = LogFactory.getLog(loggerName);
this.logStackTraces = logStackTraces;
}
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
@@ -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.
@@ -96,6 +96,8 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
@Override
@SuppressWarnings("unchecked")
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
List<String> resolvedFallbackPatterns = new ArrayList<>();
for (String fallbackPattern : annotation.fallbackPatterns()) {
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
@@ -104,7 +106,6 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
}
}
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType,
formatter, resolvedFallbackPatterns.toArray(new String[0]), annotation);
}
@@ -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,7 +71,8 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
}
TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter,
@Nullable String[] fallbackPatterns, @Nullable Object source) {
@Nullable String[] fallbackPatterns, @Nullable Object source) {
this.temporalAccessorType = temporalAccessorType;
this.formatter = formatter;
this.fallbackPatterns = fallbackPatterns;
@@ -100,8 +101,8 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
}
if (this.source != null) {
throw new DateTimeParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
text, ex.getErrorIndex(), ex);
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
text, ex.getErrorIndex(), ex);
}
// else rethrow original exception
throw ex;
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.cache.Cache;
import org.springframework.cache.support.NoOpCache;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -84,4 +85,10 @@ class LoggingCacheErrorHandlerTests {
verify(this.logger).warn("Cache 'NOOP' failed to get entry with key 'enigma'", exception);
}
@Test
void constructorWithLoggerName() {
assertThatCode(() -> new LoggingCacheErrorHandler("org.apache.commons.logging.Log", true))
.doesNotThrowAnyException();
}
}
@@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Stephane Nicoll
*/
public class Gh29105Tests {
class Gh29105Tests {
@Test
void beanProviderWithParentContextReuseOrder() {
@@ -47,6 +47,7 @@ public class Gh29105Tests {
List<Class<?>> orderedTypes = child.getBeanProvider(MyService.class)
.orderedStream().map(Object::getClass).collect(Collectors.toList());
assertThat(orderedTypes).containsExactly(CustomService.class, DefaultService.class);
child.close();
}
@@ -78,4 +79,5 @@ public class Gh29105Tests {
}
}
}
@@ -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.
@@ -433,6 +433,16 @@ public abstract class AnnotatedElementUtils {
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* <p><strong>WARNING</strong>: if the supplied {@code containerType} is not
* {@code null}, the search will be restricted to supporting only repeatable
* annotations whose container is the supplied {@code containerType}. This
* prevents the search from finding repeatable annotations declared as
* meta-annotations on other types of repeatable annotations. If you need to
* support such a use case, favor {@link #getMergedRepeatableAnnotations(AnnotatedElement, Class)}
* over this method or alternatively use the {@link MergedAnnotations} API
* directly in conjunction with {@link RepeatableContainers} that are
* {@linkplain RepeatableContainers#and(Class, Class) composed} to support
* multiple repeatable annotation types.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @param containerType the type of the container that holds the annotations;
@@ -725,6 +735,16 @@ public abstract class AnnotatedElementUtils {
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> as described in the
* {@linkplain AnnotatedElementUtils class-level javadoc}.
* <p><strong>WARNING</strong>: if the supplied {@code containerType} is not
* {@code null}, the search will be restricted to supporting only repeatable
* annotations whose container is the supplied {@code containerType}. This
* prevents the search from finding repeatable annotations declared as
* meta-annotations on other types of repeatable annotations. If you need to
* support such a use case, favor {@link #findMergedRepeatableAnnotations(AnnotatedElement, Class)}
* over this method or alternatively use the {@link MergedAnnotations} API
* directly in conjunction with {@link RepeatableContainers} that are
* {@linkplain RepeatableContainers#and(Class, Class) composed} to support
* multiple repeatable annotation types.
* @param element the annotated element (never {@code null})
* @param annotationType the annotation type to find (never {@code null})
* @param containerType the type of the container that holds the annotations;
@@ -756,7 +776,23 @@ public abstract class AnnotatedElementUtils {
private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element,
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
RepeatableContainers repeatableContainers;
if (containerType == null) {
// Invoke RepeatableContainers.of() in order to adhere to the contract of
// getMergedRepeatableAnnotations() which states that an IllegalArgumentException
// will be thrown if the container cannot be resolved.
//
// In any case, we use standardRepeatables() in order to support repeatable
// annotations on other types of repeatable annotations (i.e., nested repeatable
// annotation types).
//
// See https://github.com/spring-projects/spring-framework/issues/20279
RepeatableContainers.of(annotationType, null);
repeatableContainers = RepeatableContainers.standardRepeatables();
}
else {
repeatableContainers = RepeatableContainers.of(annotationType, containerType);
}
return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers);
}
@@ -767,7 +803,23 @@ public abstract class AnnotatedElementUtils {
private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element,
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
RepeatableContainers repeatableContainers;
if (containerType == null) {
// Invoke RepeatableContainers.of() in order to adhere to the contract of
// findMergedRepeatableAnnotations() which states that an IllegalArgumentException
// will be thrown if the container cannot be resolved.
//
// In any case, we use standardRepeatables() in order to support repeatable
// annotations on other types of repeatable annotations (i.e., nested repeatable
// annotation types).
//
// See https://github.com/spring-projects/spring-framework/issues/20279
RepeatableContainers.of(annotationType, null);
repeatableContainers = RepeatableContainers.standardRepeatables();
}
else {
repeatableContainers = RepeatableContainers.of(annotationType, containerType);
}
return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers);
}
@@ -777,8 +829,7 @@ public abstract class AnnotatedElementUtils {
}
private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
return Comparator.<MergedAnnotation<A>> comparingInt(
MergedAnnotation::getAggregateIndex).reversed();
return Comparator.<MergedAnnotation<A>> comparingInt(MergedAnnotation::getAggregateIndex).reversed();
}
@Nullable
@@ -788,13 +839,12 @@ public abstract class AnnotatedElementUtils {
if (!annotation.isPresent()) {
return null;
}
return annotation.asAnnotationAttributes(
Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
}
/**
* Adapted {@link AnnotatedElement} that hold specific annotations.
* Adapted {@link AnnotatedElement} that holds specific annotations.
*/
private static class AnnotatedElementForAnnotations implements AnnotatedElement {
@@ -32,7 +32,6 @@ import java.util.Set;
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -241,7 +240,7 @@ final class AnnotationTypeMapping {
mapping.claimedAliases.addAll(aliases);
if (mapping.annotation != null) {
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
mapping.annotation, ReflectionUtils::invokeMethod);
mapping.annotation, AnnotationUtils::invokeAnnotationMethod);
for (int i = 0; i < mapping.attributes.size(); i++) {
if (aliases.contains(mapping.attributes.get(i))) {
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
@@ -505,7 +504,7 @@ final class AnnotationTypeMapping {
if (source == this && metaAnnotationsOnly) {
return null;
}
return ReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
return AnnotationUtils.invokeAnnotationMethod(source.attributes.get(mappedIndex), source.annotation);
}
/**
@@ -594,7 +593,7 @@ final class AnnotationTypeMapping {
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
for (int i = 0; i < attributes.size(); i++) {
Method attribute = attributes.get(i);
Object value1 = ReflectionUtils.invokeMethod(attribute, annotation);
Object value1 = AnnotationUtils.invokeAnnotationMethod(attribute, annotation);
Object value2;
if (extractedValue instanceof TypeMappedAnnotation) {
value2 = ((TypeMappedAnnotation<?>) extractedValue).getValue(attribute.getName()).orElse(null);
@@ -19,9 +19,10 @@ package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -1052,23 +1053,42 @@ public abstract class AnnotationUtils {
}
try {
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
ReflectionUtils.makeAccessible(method);
return method.invoke(annotation);
return invokeAnnotationMethod(method, annotation);
}
catch (NoSuchMethodException ex) {
return null;
}
catch (InvocationTargetException ex) {
rethrowAnnotationConfigurationException(ex.getTargetException());
throw new IllegalStateException("Could not obtain value for annotation attribute '" +
attributeName + "' in " + annotation, ex);
}
catch (Throwable ex) {
rethrowAnnotationConfigurationException(ex);
handleIntrospectionFailure(annotation.getClass(), ex);
return null;
}
}
/**
* Invoke the supplied annotation attribute {@link Method} on the supplied
* {@link Annotation}.
* <p>An attempt will first be made to invoke the method via the annotation's
* {@link InvocationHandler} (if the annotation instance is a JDK dynamic proxy).
* If that fails, an attempt will be made to invoke the method via reflection.
* @param method the method to invoke
* @param annotation the annotation on which to invoke the method
* @return the value returned from the method invocation
* @since 5.3.24
*/
static Object invokeAnnotationMethod(Method method, Object annotation) {
if (Proxy.isProxyClass(annotation.getClass())) {
try {
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
return handler.invoke(annotation, method, null);
}
catch (Throwable ex) {
// ignore and fall back to reflection below
}
}
return ReflectionUtils.invokeMethod(method, annotation);
}
/**
* If the supplied throwable is an {@link AnnotationConfigurationException},
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
@@ -109,7 +109,7 @@ final class AttributeMethods {
for (int i = 0; i < size(); i++) {
if (canThrowTypeNotPresentException(i)) {
try {
get(i).invoke(annotation);
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
}
catch (Throwable ex) {
return false;
@@ -134,7 +134,7 @@ final class AttributeMethods {
for (int i = 0; i < size(); i++) {
if (canThrowTypeNotPresentException(i)) {
try {
get(i).invoke(annotation);
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
}
catch (Throwable ex) {
throw new IllegalStateException("Could not obtain annotation attribute value for " +
@@ -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.
@@ -25,7 +25,6 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* Strategy used to determine annotations that act as containers for other
@@ -39,6 +38,7 @@ import org.springframework.util.ReflectionUtils;
* <p>To completely disable repeatable support use {@link #none()}.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2
*/
public abstract class RepeatableContainers {
@@ -101,15 +101,19 @@ public abstract class RepeatableContainers {
}
/**
* Create a {@link RepeatableContainers} instance that uses a defined
* container and repeatable type.
* @param repeatable the contained repeatable annotation
* @param container the container annotation or {@code null}. If specified,
* Create a {@link RepeatableContainers} instance that uses predefined
* repeatable and container types.
* @param repeatable the contained repeatable annotation type
* @param container the container annotation type or {@code null}. If specified,
* this annotation must declare a {@code value} attribute returning an array
* of repeatable annotations. If not specified, the container will be
* deduced by inspecting the {@code @Repeatable} annotation on
* {@code repeatable}.
* @return a {@link RepeatableContainers} instance
* @throws IllegalArgumentException if the supplied container type is
* {@code null} and the annotation type is not a repeatable annotation
* @throws AnnotationConfigurationException if the supplied container type
* is not a properly configured container for a repeatable annotation
*/
public static RepeatableContainers of(
Class<? extends Annotation> repeatable, @Nullable Class<? extends Annotation> container) {
@@ -148,7 +152,7 @@ public abstract class RepeatableContainers {
Annotation[] findRepeatedAnnotations(Annotation annotation) {
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
if (method != null) {
return (Annotation[]) ReflectionUtils.invokeMethod(method, annotation);
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(method, annotation);
}
return super.findRepeatedAnnotations(annotation);
}
@@ -235,7 +239,7 @@ public abstract class RepeatableContainers {
@Nullable
Annotation[] findRepeatedAnnotations(Annotation annotation) {
if (this.container.isAssignableFrom(annotation.annotationType())) {
return (Annotation[]) ReflectionUtils.invokeMethod(this.valueMethod, annotation);
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(this.valueMethod, annotation);
}
return super.findRepeatedAnnotations(annotation);
}
@@ -111,7 +111,7 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
for (int i = 0; i < this.attributes.size(); i++) {
Method attribute = this.attributes.get(i);
Object thisValue = getAttributeValue(attribute);
Object otherValue = ReflectionUtils.invokeMethod(attribute, other);
Object otherValue = AnnotationUtils.invokeAnnotationMethod(attribute, other);
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
return false;
}
@@ -33,7 +33,6 @@ import java.util.function.Predicate;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link MergedAnnotation} that adapts attributes from a root annotation by
@@ -43,9 +42,9 @@ import org.springframework.util.ReflectionUtils;
* {@code BiFunction}. This allows various different annotation models to be
* supported by the same class. For example, the attributes source might be an
* actual {@link Annotation} instance where methods on the annotation instance
* are {@linkplain ReflectionUtils#invokeMethod(Method, Object) invoked} to extract
* values. Equally, the source could be a simple {@link Map} with values
* extracted using {@link Map#get(Object)}.
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
* to extract values. Similarly, the source could be a simple {@link Map} with
* values extracted using {@link Map#get(Object)}.
*
* <p>Extracted root attribute values must be compatible with the attribute
* return type, namely:
@@ -434,7 +433,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
}
if (value == null) {
Method attribute = this.mapping.getAttributes().get(attributeIndex);
value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
value = AnnotationUtils.invokeAnnotationMethod(attribute, this.mapping.getAnnotation());
}
return value;
}
@@ -555,7 +554,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
private ValueExtractor getValueExtractor(Object value) {
if (value instanceof Annotation) {
return ReflectionUtils::invokeMethod;
return AnnotationUtils::invokeAnnotationMethod;
}
if (value instanceof Map) {
return TypeMappedAnnotation::extractFromMap;
@@ -615,7 +614,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
Assert.notNull(annotation, "Annotation must not be null");
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
return new TypeMappedAnnotation<>(
mappings.get(0), null, source, annotation, AnnotationUtils::invokeAnnotationMethod, 0);
}
static <A extends Annotation> MergedAnnotation<A> of(
@@ -649,7 +649,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
int aggregateIndex, IntrospectionFailureLogger logger) {
return createIfPossible(mapping, source, annotation,
ReflectionUtils::invokeMethod, aggregateIndex, logger);
AnnotationUtils::invokeAnnotationMethod, aggregateIndex, logger);
}
@Nullable
@@ -177,7 +177,7 @@ public abstract class DataBufferUtils {
if (options.length > 0) {
for (OpenOption option : options) {
Assert.isTrue(!(option == StandardOpenOption.APPEND || option == StandardOpenOption.WRITE),
"'" + option + "' not allowed");
() -> "'" + option + "' not allowed");
}
}
@@ -695,7 +695,7 @@ public abstract class DataBufferUtils {
@Override
public byte[] delimiter() {
Assert.state(this.longestDelimiter != NO_DELIMITER, "Illegal state!");
Assert.state(this.longestDelimiter != NO_DELIMITER, "'delimiter' not set");
return this.longestDelimiter;
}
@@ -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 @@ public abstract class CollectionUtils {
* @see #newLinkedHashMap(int)
*/
public static <K, V> HashMap<K, V> newHashMap(int expectedSize) {
return new HashMap<>((int) (expectedSize / DEFAULT_LOAD_FACTOR), DEFAULT_LOAD_FACTOR);
return new HashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
/**
@@ -102,7 +102,11 @@ public abstract class CollectionUtils {
* @see #newHashMap(int)
*/
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) {
return new LinkedHashMap<>((int) (expectedSize / DEFAULT_LOAD_FACTOR), DEFAULT_LOAD_FACTOR);
return new LinkedHashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
private static int computeMapInitialCapacity(int expectedSize) {
return (int) Math.ceil(expectedSize / (double) DEFAULT_LOAD_FACTOR);
}
/**
@@ -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.
@@ -34,7 +34,6 @@ import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.util.LinkedMultiValueMap;
@@ -156,7 +155,7 @@ class CollectionFactoryTests {
@Test
void createApproximateCollectionFromEmptyHashSet() {
Collection<String> set = createApproximateCollection(new HashSet<String>(), 2);
Assertions.assertThat(set).isEmpty();
assertThat(set).isEmpty();
}
@Test
@@ -0,0 +1,212 @@
/*
* 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.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Set;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for various ways to search for repeatable annotations that are
* nested (i.e., repeatable annotations used as meta-annotations on other
* repeatable annotations).
*
* @author Sam Brannen
* @since 5.3.24
* @see https://github.com/spring-projects/spring-framework/issues/20279
*/
@SuppressWarnings("unused")
class NestedRepeatableAnnotationsTests {
@Nested
class SingleRepeatableAnnotationTests {
private final Method method = ReflectionUtils.findMethod(getClass(), "annotatedMethod");
@Test
void streamRepeatableAnnotations_MergedAnnotationsApi() {
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY)
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
// Merged, so we expect to find @A once with its value coming from @B(5).
assertThat(annotations).extracting(A::value).containsExactly(5);
}
@Test
void findMergedRepeatableAnnotations_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class);
// Merged, so we expect to find @A once with its value coming from @B(5).
assertThat(annotations).extracting(A::value).containsExactly(5);
}
@Test
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
// Merged, so we expect to find @A once with its value coming from @B(5).
assertThat(annotations).extracting(A::value).containsExactly(5);
}
@Test
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
// Merged, so we expect to find @A once with its value coming from @B(5).
assertThat(annotations).extracting(A::value).containsExactly(5);
}
@Test
@SuppressWarnings("deprecation")
void getRepeatableAnnotations_AnnotationUtils() {
Set<A> annotations = AnnotationUtils.getRepeatableAnnotations(method, A.class);
// Not merged, so we expect to find @A once with the default value of 0.
// @A will actually be found twice, but we have Set semantics here.
assertThat(annotations).extracting(A::value).containsExactly(0);
}
@B(5)
void annotatedMethod() {
}
}
@Nested
class MultipleRepeatableAnnotationsTests {
private final Method method = ReflectionUtils.findMethod(getClass(), "annotatedMethod");
@Test
void streamRepeatableAnnotationsWithStandardRepeatables_MergedAnnotationsApi() {
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables();
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers)
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
}
@Test
void streamRepeatableAnnotationsWithExplicitRepeatables_MergedAnnotationsApi() {
RepeatableContainers repeatableContainers =
RepeatableContainers.of(A.class, A.Container.class).and(B.Container.class, B.class);
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers)
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
}
@Test
void findMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class);
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
}
@Test
void findMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class, A.Container.class);
// When findMergedRepeatableAnnotations(...) is invoked with an explicit container
// type, it uses RepeatableContainers.of(...) which limits the repeatable annotation
// support to a single container type.
//
// In this test case, we are therefore limiting the support to @A.Container, which
// means that @B.Container is unsupported and effectively ignored as a repeatable
// container type.
//
// Long story, short: the search doesn't find anything.
assertThat(annotations).isEmpty();
}
@Test
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
}
@Test
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
// When getMergedRepeatableAnnotations(...) is invoked with an explicit container
// type, it uses RepeatableContainers.of(...) which limits the repeatable annotation
// support to a single container type.
//
// In this test case, we are therefore limiting the support to @A.Container, which
// means that @B.Container is unsupported and effectively ignored as a repeatable
// container type.
//
// Long story, short: the search doesn't find anything.
assertThat(annotations).isEmpty();
}
@Test
@SuppressWarnings("deprecation")
void getRepeatableAnnotations_AnnotationUtils() {
Set<A> annotations = AnnotationUtils.getRepeatableAnnotations(method, A.class);
// Not merged, so we expect to find a single @A with default value of 0.
// @A will actually be found twice, but we have Set semantics here.
assertThat(annotations).extracting(A::value).containsExactly(0);
}
@B(5)
@B(10)
void annotatedMethod() {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Repeatable(A.Container.class)
@interface A {
int value() default 0;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@interface Container {
A[] value();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Repeatable(B.Container.class)
@A
@A
@interface B {
@AliasFor(annotation = A.class)
int value();
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@interface Container {
B[] value();
}
}
}
@@ -63,7 +63,7 @@ class ResourceRegionEncoderTests extends AbstractLeakCheckingTests {
}
@Test
void shouldEncodeResourceRegionFileResource() throws Exception {
void shouldEncodeResourceRegionFileResource() {
ResourceRegion region = new ResourceRegion(
new ClassPathResource("ResourceRegionEncoderTests.txt", getClass()), 0, 6);
Flux<DataBuffer> result = this.encoder.encode(Mono.just(region), this.bufferFactory,
@@ -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.
@@ -35,8 +35,7 @@ class LeakAwareDataBufferFactoryTests {
void leak() {
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer();
try {
assertThatExceptionOfType(AssertionError.class).isThrownBy(
this.bufferFactory::checkForLeaks);
assertThatExceptionOfType(AssertionError.class).isThrownBy(this.bufferFactory::checkForLeaks);
}
finally {
release(dataBuffer);
@@ -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,16 +16,19 @@
package org.springframework.core.io.support;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
@@ -33,8 +36,9 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* If this test case fails, uncomment diagnostics in the
* {@link #assertProtocolAndFilenames} method.
* Tests for {@link PathMatchingResourcePatternResolver}.
*
* <p>If tests fail, uncomment the diagnostics in {@link #assertFilenames(String, boolean, String...)}.
*
* @author Oliver Hutchison
* @author Juergen Hoeller
@@ -44,124 +48,261 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
class PathMatchingResourcePatternResolverTests {
private static final String[] CLASSES_IN_CORE_IO_SUPPORT =
new String[] {"EncodedResource.class", "LocalizedResourceHelper.class",
"PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class",
"ResourcePatternResolver.class", "ResourcePatternUtils.class"};
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT =
new String[] {"PathMatchingResourcePatternResolverTests.class"};
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATIONS =
new String[] {"NonNull.class", "NonNullApi.class", "Nullable.class"};
private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
@Test
void invalidPrefixWithPatternElementInIt() throws IOException {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
resolver.getResources("xx**:**/*.xy"));
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
@Nested
class InvalidPatterns {
@Test
void invalidPrefixWithPatternElementInItThrowsException() {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> resolver.getResources("xx**:**/*.xy"));
}
}
@Test
void singleResourceOnFileSystem() throws IOException {
Resource[] resources =
resolver.getResources("org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class");
assertThat(resources.length).isEqualTo(1);
assertProtocolAndFilenames(resources, "file", "PathMatchingResourcePatternResolverTests.class");
}
@Test
void singleResourceInJar() throws IOException {
Resource[] resources = resolver.getResources("org/reactivestreams/Publisher.class");
assertThat(resources.length).isEqualTo(1);
assertProtocolAndFilenames(resources, "jar", "Publisher.class");
}
@Nested
class FileSystemResources {
@Disabled
@Test
void classpathStarWithPatternOnFileSystem() throws IOException {
Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/sup*/*.class");
// Have to exclude Clover-generated class files here,
// as we might be running as part of a Clover test run.
List<Resource> noCloverResources = new ArrayList<>();
for (Resource resource : resources) {
if (!resource.getFilename().contains("$__CLOVER_")) {
noCloverResources.add(resource);
@Test
void singleResourceOnFileSystem() {
String pattern = "org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class";
assertExactFilenames(pattern, "PathMatchingResourcePatternResolverTests.class");
}
@Test
void classpathStarWithPatternOnFileSystem() {
String pattern = "classpath*:org/springframework/core/io/sup*/*.class";
String[] expectedFilenames = StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT);
assertFilenames(pattern, expectedFilenames);
}
@Nested
class WithHashtagsInTheirFileNames {
@Test
void usingClasspathStarProtocol() {
String pattern = "classpath*:org/springframework/core/io/**/resource#test*.txt";
String pathPrefix = ".+org/springframework/core/io/";
assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt");
assertExactSubPaths(pattern, pathPrefix, "support/resource#test1.txt", "support/resource#test2.txt");
}
@Test
void usingClasspathStarProtocolWithWildcardInPatternAndNotEndingInSlash() throws Exception {
String pattern = "classpath*:org/springframework/core/io/sup*";
String pathPrefix = ".+org/springframework/core/io/";
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
// We DO find "support" if the pattern does NOT end with a slash.
assertThat(actualSubPaths).containsExactly("support");
}
@Test
void usingFileProtocolWithWildcardInPatternAndNotEndingInSlash() throws Exception {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/org/springframework/core/io/sup*", testResourcesDir);
String pathPrefix = ".+org/springframework/core/io/";
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
// We DO find "support" if the pattern does NOT end with a slash.
assertThat(actualSubPaths).containsExactly("support");
}
@Test
void usingClasspathStarProtocolWithWildcardInPatternAndEndingInSlash() throws Exception {
String pattern = "classpath*:org/springframework/core/io/sup*/";
String pathPrefix = ".+org/springframework/core/io/";
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
// We do NOT find "support" if the pattern ENDS with a slash.
assertThat(actualSubPaths).isEmpty();
}
@Test
void usingFileProtocolWithWildcardInPatternAndEndingInSlash() throws Exception {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/org/springframework/core/io/sup*/", testResourcesDir);
String pathPrefix = ".+org/springframework/core/io/";
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
// We do NOT find "support" if the pattern ENDS with a slash.
assertThat(actualSubPaths).isEmpty();
}
@Test
void usingClasspathStarProtocolWithWildcardInPatternAndEndingWithSuffixPattern() throws Exception {
String pattern = "classpath*:org/springframework/core/io/sup*/*.txt";
String pathPrefix = ".+org/springframework/core/io/";
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
assertThat(actualSubPaths)
.containsExactlyInAnyOrder("support/resource#test1.txt", "support/resource#test2.txt");
}
private List<String> getSubPathsIgnoringClassFiles(String pattern, String pathPrefix) throws IOException {
return Arrays.stream(resolver.getResources(pattern))
.map(resource -> getPath(resource).replaceFirst(pathPrefix, ""))
.filter(name -> !name.endsWith(".class"))
.distinct()
.sorted()
.collect(Collectors.toList());
}
@Test
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
String pathPrefix = ".+?resources/";
// We do NOT find "scanned-resources" if the pattern ENDS with "/**" AND does NOT otherwise contain a wildcard.
assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt");
assertExactSubPaths(pattern, pathPrefix, "scanned-resources/resource#test1.txt",
"scanned-resources/resource#test2.txt");
}
@Test
void usingFileProtocolWithWildcardInPatternAndEndingInSlashStarStar() {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/scanned*resources/**", testResourcesDir);
String pathPrefix = ".+?resources/";
// We DO find "scanned-resources" if the pattern ENDS with "/**" AND DOES otherwise contain a wildcard.
assertExactFilenames(pattern, "scanned-resources", "resource#test1.txt", "resource#test2.txt");
assertExactSubPaths(pattern, pathPrefix, "scanned-resources", "scanned-resources/resource#test1.txt",
"scanned-resources/resource#test2.txt");
}
@Test
void usingFileProtocolAndAssertingUrlAndUriSyntax() throws Exception {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/scanned-resources/**/resource#test1.txt", testResourcesDir);
Resource[] resources = resolver.getResources(pattern);
assertThat(resources).hasSize(1);
Resource resource = resources[0];
assertThat(resource.getFilename()).isEqualTo("resource#test1.txt");
// The following assertions serve as regression tests for the lack of the
// "authority component" (//) in the returned URI/URL. For example, we are
// expecting file:/my/path (or file:/C:/My/Path) instead of file:///my/path.
assertThat(resource.getURL().toString()).matches("^file:\\/[^\\/].+test1\\.txt$");
assertThat(resource.getURI().toString()).matches("^file:\\/[^\\/].+test1\\.txt$");
}
}
resources = noCloverResources.toArray(new Resource[0]);
assertProtocolAndFilenames(resources, "file",
StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT));
}
@Test
void getResourcesOnFileSystemContainingHashtagsInTheirFileNames() throws IOException {
Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/**/resource#test*.txt");
assertThat(resources).extracting(Resource::getFile).extracting(File::getName)
.containsExactlyInAnyOrder("resource#test1.txt", "resource#test2.txt");
@Nested
class JarResources {
@Test
void singleResourceInJar() {
String pattern = "org/reactivestreams/Publisher.class";
assertExactFilenames(pattern, "Publisher.class");
}
@Test
void singleResourceInRootOfJar() {
String pattern = "aspectj_1_5_0.dtd";
assertExactFilenames(pattern, "aspectj_1_5_0.dtd");
}
@Test
void classpathWithPatternInJar() {
String pattern = "classpath:reactor/util/annotation/*.class";
assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION);
}
@Test
void classpathStarWithPatternInJar() {
String pattern = "classpath*:reactor/util/annotation/*.class";
assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION);
}
// Fails in a native image -- https://github.com/oracle/graal/issues/5020
@Test
void rootPatternRetrievalInJarFiles() throws IOException {
assertThat(resolver.getResources("classpath*:aspectj*.dtd")).extracting(Resource::getFilename)
.as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar")
.containsExactly("aspectj_1_5_0.dtd");
}
}
@Test
void classpathWithPatternInJar() throws IOException {
Resource[] resources = resolver.getResources("classpath:reactor/util/annotation/*.class");
assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS);
private void assertFilenames(String pattern, String... filenames) {
assertFilenames(pattern, false, filenames);
}
@Test
void classpathStarWithPatternInJar() throws IOException {
Resource[] resources = resolver.getResources("classpath*:reactor/util/annotation/*.class");
assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS);
private void assertExactFilenames(String pattern, String... filenames) {
assertFilenames(pattern, true, filenames);
}
@Test
void rootPatternRetrievalInJarFiles() throws IOException {
Resource[] resources = resolver.getResources("classpath*:*.dtd");
boolean found = false;
for (Resource resource : resources) {
if (resource.getFilename().equals("aspectj_1_5_0.dtd")) {
found = true;
break;
private void assertFilenames(String pattern, boolean exactly, String... filenames) {
try {
Resource[] resources = resolver.getResources(pattern);
List<String> actualNames = Arrays.stream(resources)
.map(Resource::getFilename)
.sorted()
.collect(Collectors.toList());
// Uncomment the following if you encounter problems with matching against the file system.
// List<String> expectedNames = Arrays.stream(filenames).sorted().toList();
// System.out.println("----------------------------------------------------------------------");
// System.out.println("Expected: " + expectedNames);
// System.out.println("Actual: " + actualNames);
// Arrays.stream(resources).forEach(System.out::println);
if (exactly) {
assertThat(actualNames).as("subset of files found").containsExactlyInAnyOrder(filenames);
}
else {
assertThat(actualNames).as("subset of files found").contains(filenames);
}
}
assertThat(found).as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar").isTrue();
}
private void assertProtocolAndFilenames(Resource[] resources, String protocol, String... filenames)
throws IOException {
// Uncomment the following if you encounter problems with matching against the file system
// It shows file locations.
// String[] actualNames = new String[resources.length];
// for (int i = 0; i < resources.length; i++) {
// actualNames[i] = resources[i].getFilename();
// }
// List sortedActualNames = new LinkedList(Arrays.asList(actualNames));
// List expectedNames = new LinkedList(Arrays.asList(fileNames));
// Collections.sort(sortedActualNames);
// Collections.sort(expectedNames);
//
// System.out.println("-----------");
// System.out.println("Expected: " + StringUtils.collectionToCommaDelimitedString(expectedNames));
// System.out.println("Actual: " + StringUtils.collectionToCommaDelimitedString(sortedActualNames));
// for (int i = 0; i < resources.length; i++) {
// System.out.println(resources[i]);
// }
assertThat(resources.length).as("Correct number of files found").isEqualTo(filenames.length);
for (Resource resource : resources) {
String actualProtocol = resource.getURL().getProtocol();
assertThat(actualProtocol).isEqualTo(protocol);
assertFilenameIn(resource, filenames);
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private void assertFilenameIn(Resource resource, String... filenames) {
String filename = resource.getFilename();
assertThat(Arrays.stream(filenames).anyMatch(filename::endsWith)).as(resource + " does not have a filename that matches any of the specified names").isTrue();
private void assertExactSubPaths(String pattern, String pathPrefix, String... subPaths) {
try {
Resource[] resources = resolver.getResources(pattern);
List<String> actualSubPaths = Arrays.stream(resources)
.map(resource -> getPath(resource).replaceFirst(pathPrefix, ""))
.sorted()
.collect(Collectors.toList());
assertThat(actualSubPaths).containsExactlyInAnyOrder(subPaths);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private String getPath(Resource resource) {
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
//
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
// we use FileSystemResource#getPath since this test class is sometimes run within a
// GraalVM native image which cannot support Path#toFile.
//
// See: https://github.com/spring-projects/spring-framework/issues/29243
return ((FileSystemResource) resource).getPath();
}
}
@@ -50,14 +50,14 @@ class SocketUtilsTests {
@Test
void findAvailableTcpPortWithZeroMinPort() {
assertThatIllegalArgumentException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableTcpPort(0));
assertThatIllegalArgumentException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(0));
}
@Test
void findAvailableTcpPortWithNegativeMinPort() {
assertThatIllegalArgumentException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableTcpPort(-500));
assertThatIllegalArgumentException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(-500));
}
@Test
@@ -80,8 +80,8 @@ class SocketUtilsTests {
try (ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"))) {
assertThat(socket).isNotNull();
// will only look for the exact port
assertThatIllegalStateException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableTcpPort(port, port))
assertThatIllegalStateException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(port, port))
.withMessageStartingWith("Could not find an available TCP port")
.withMessageEndingWith("after 1 attempts");
}
@@ -123,8 +123,7 @@ class SocketUtilsTests {
@Test
void findAvailableTcpPortsWithRequestedNumberGreaterThanSizeOfRange() {
assertThatIllegalArgumentException().isThrownBy(() ->
findAvailableTcpPorts(50, 45000, 45010));
assertThatIllegalArgumentException().isThrownBy(() -> findAvailableTcpPorts(50, 45000, 45010));
}
@@ -132,14 +131,14 @@ class SocketUtilsTests {
@Test
void findAvailableUdpPortWithZeroMinPort() {
assertThatIllegalArgumentException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableUdpPort(0));
assertThatIllegalArgumentException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(0));
}
@Test
void findAvailableUdpPortWithNegativeMinPort() {
assertThatIllegalArgumentException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableUdpPort(-500));
assertThatIllegalArgumentException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(-500));
}
@Test
@@ -155,8 +154,8 @@ class SocketUtilsTests {
try (DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost"))) {
assertThat(socket).isNotNull();
// will only look for the exact port
assertThatIllegalStateException().isThrownBy(() ->
org.springframework.util.SocketUtils.findAvailableUdpPort(port, port))
assertThatIllegalStateException().isThrownBy(
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(port, port))
.withMessageStartingWith("Could not find an available UDP port")
.withMessageEndingWith("after 1 attempts");
}
@@ -198,8 +197,7 @@ class SocketUtilsTests {
@Test
void findAvailableUdpPortsWithRequestedNumberGreaterThanSizeOfRange() {
assertThatIllegalArgumentException().isThrownBy(() ->
findAvailableUdpPorts(50, 45000, 45010));
assertThatIllegalArgumentException().isThrownBy(() -> findAvailableUdpPorts(50, 45000, 45010));
}
@@ -226,13 +224,13 @@ class SocketUtilsTests {
SortedSet<Integer> ports = org.springframework.util.SocketUtils.findAvailableUdpPorts(numRequested, minPort, maxPort);
assertAvailablePorts(ports, numRequested, minPort, maxPort);
}
private void assertPortInRange(int port, int minPort, int maxPort) {
assertThat(port >= minPort).as("port [" + port + "] >= " + minPort).isTrue();
assertThat(port <= maxPort).as("port [" + port + "] <= " + maxPort).isTrue();
assertThat(port).as("port").isBetween(minPort, maxPort);
}
private void assertAvailablePorts(SortedSet<Integer> ports, int numRequested, int minPort, int maxPort) {
assertThat(ports.size()).as("number of ports requested").isEqualTo(numRequested);
assertThat(ports).as("number of ports requested").hasSize(numRequested);
for (int port : ports) {
assertPortInRange(port, minPort, maxPort);
}
@@ -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,7 +16,7 @@
package org.springframework.core
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
class KotlinBridgeMethodResolverTests {
@@ -25,22 +25,22 @@ class KotlinBridgeMethodResolverTests {
fun findBridgedMethod() {
val unbridged = GenericRepository::class.java.getDeclaredMethod("delete", Int::class.java)
val bridged = GenericRepository::class.java.getDeclaredMethod("delete", Any::class.java)
Assertions.assertThat(unbridged.isBridge).isFalse
Assertions.assertThat(bridged.isBridge).isTrue
assertThat(unbridged.isBridge).isFalse
assertThat(bridged.isBridge).isTrue
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
}
@Test
fun findBridgedMethodWithArrays() {
val unbridged = GenericRepository::class.java.getDeclaredMethod("delete", Array<Int>::class.java)
val bridged = GenericRepository::class.java.getDeclaredMethod("delete", Array<Any>::class.java)
Assertions.assertThat(unbridged.isBridge).isFalse
Assertions.assertThat(bridged.isBridge).isTrue
assertThat(unbridged.isBridge).isFalse
assertThat(bridged.isBridge).isTrue
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
}
}
@@ -0,0 +1 @@
test 1
@@ -0,0 +1 @@
test 2
@@ -16,6 +16,8 @@
package org.springframework.core.testfixture.io.buffer;
import java.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.springframework.core.io.buffer.DataBufferFactory;
@@ -42,7 +44,7 @@ public abstract class AbstractLeakCheckingTests {
*/
@AfterEach
final void checkForLeaks() {
this.bufferFactory.checkForLeaks();
this.bufferFactory.checkForLeaks(Duration.ofSeconds(1));
}
}
@@ -81,13 +81,21 @@ public class LeakAwareDataBufferFactory implements DataBufferFactory {
* method.
*/
public void checkForLeaks() {
checkForLeaks(Duration.ofSeconds(0));
}
/**
* Variant of {@link #checkForLeaks()} with the option to wait for buffer release.
* @param timeout how long to wait for buffers to be released; 0 for no waiting
*/
public void checkForLeaks(Duration timeout) {
this.trackCreated.set(false);
Instant start = Instant.now();
while (true) {
if (this.created.stream().noneMatch(LeakAwareDataBuffer::isAllocated)) {
return;
}
if (Instant.now().isBefore(start.plus(Duration.ofSeconds(5)))) {
if (Instant.now().isBefore(start.plus(timeout))) {
try {
Thread.sleep(50);
}
@@ -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.
@@ -32,6 +32,7 @@ import org.springframework.util.ObjectUtils;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class Elvis extends SpelNodeImpl {
@@ -64,7 +65,7 @@ public class Elvis extends SpelNodeImpl {
@Override
public String toStringAST() {
return getChild(0).toStringAST() + " ?: " + getChild(1).toStringAST();
return "(" + getChild(0).toStringAST() + " ?: " + getChild(1).toStringAST() + ")";
}
@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.
@@ -32,6 +32,7 @@ import org.springframework.util.ObjectUtils;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class Ternary extends SpelNodeImpl {
@@ -62,7 +63,7 @@ public class Ternary extends SpelNodeImpl {
@Override
public String toStringAST() {
return getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST();
return "(" + getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST() + ")";
}
private void computeExitTypeDescriptor() {
@@ -116,6 +116,10 @@ class EvaluationTests extends AbstractExpressionTests {
void elvisOperator() {
evaluate("'Andy'?:'Dave'", "Andy", String.class);
evaluate("null?:'Dave'", "Dave", String.class);
evaluate("3?:1", 3, Integer.class);
evaluate("(2*3)?:1*10", 6, Integer.class);
evaluate("null?:2*10", 20, Integer.class);
evaluate("(null?:1)*10", 10, Integer.class);
}
@Test
@@ -624,6 +628,24 @@ class EvaluationTests extends AbstractExpressionTests {
evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class);
}
@Test
void ternaryOperator06() {
evaluate("3?:#var=5", 3, Integer.class);
evaluate("null?:#var=5", 5, Integer.class);
evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, Boolean.class);
}
@Test
void ternaryExpressionWithImplicitGrouping() {
evaluate("4 % 2 == 0 ? 2 : 3 * 10", 2, Integer.class);
evaluate("4 % 2 == 1 ? 2 : 3 * 10", 30, Integer.class);
}
@Test
void ternaryExpressionWithExplicitGrouping() {
evaluate("((4 % 2 == 0) ? 2 : 1) * 10", 20, Integer.class);
}
@Test
void ternaryOperatorWithNullValue() {
assertThatExceptionOfType(EvaluationException.class).isThrownBy(
@@ -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.
@@ -24,374 +24,380 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Parse some expressions and check we get the AST we expect. Rather than inspecting each node in the AST, we ask it to
* write itself to a string form and check that is as expected.
* Parse some expressions and check we get the AST we expect.
*
* <p>Rather than inspecting each node in the AST, we ask it to write itself to
* a string form and check that is as expected.
*
* @author Andy Clement
* @author Sam Brannen
*/
public class ParsingTests {
class ParsingTests {
private final SpelExpressionParser parser = new SpelExpressionParser();
private SpelExpressionParser parser = new SpelExpressionParser();
// literals
@Test
public void testLiteralBoolean01() {
void literalBoolean01() {
parseCheck("false");
}
@Test
public void testLiteralLong01() {
void literalLong01() {
parseCheck("37L", "37");
}
@Test
public void testLiteralBoolean02() {
void literalBoolean02() {
parseCheck("true");
}
@Test
public void testLiteralBoolean03() {
void literalBoolean03() {
parseCheck("!true");
}
@Test
public void testLiteralInteger01() {
void literalInteger01() {
parseCheck("1");
}
@Test
public void testLiteralInteger02() {
void literalInteger02() {
parseCheck("1415");
}
@Test
public void testLiteralString01() {
void literalString01() {
parseCheck("'hello'");
}
@Test
public void testLiteralString02() {
void literalString02() {
parseCheck("'joe bloggs'");
}
@Test
public void testLiteralString03() {
void literalString03() {
parseCheck("'Tony''s Pizza'", "'Tony's Pizza'");
}
@Test
public void testLiteralReal01() {
void literalReal01() {
parseCheck("6.0221415E+23", "6.0221415E23");
}
@Test
public void testLiteralHex01() {
void literalHex01() {
parseCheck("0x7FFFFFFF", "2147483647");
}
@Test
public void testLiteralDate01() {
void literalDate01() {
parseCheck("date('1974/08/24')");
}
@Test
public void testLiteralDate02() {
void literalDate02() {
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
}
@Test
public void testLiteralNull01() {
void literalNull01() {
parseCheck("null");
}
// boolean operators
@Test
public void testBooleanOperatorsOr01() {
void booleanOperatorsOr01() {
parseCheck("false or false", "(false or false)");
}
@Test
public void testBooleanOperatorsOr02() {
void booleanOperatorsOr02() {
parseCheck("false or true", "(false or true)");
}
@Test
public void testBooleanOperatorsOr03() {
void booleanOperatorsOr03() {
parseCheck("true or false", "(true or false)");
}
@Test
public void testBooleanOperatorsOr04() {
void booleanOperatorsOr04() {
parseCheck("true or false", "(true or false)");
}
@Test
public void testBooleanOperatorsMix01() {
void booleanOperatorsMix01() {
parseCheck("false or true and false", "(false or (true and false))");
}
// relational operators
@Test
public void testRelOperatorsGT01() {
void relOperatorsGT01() {
parseCheck("3>6", "(3 > 6)");
}
@Test
public void testRelOperatorsLT01() {
void relOperatorsLT01() {
parseCheck("3<6", "(3 < 6)");
}
@Test
public void testRelOperatorsLE01() {
void relOperatorsLE01() {
parseCheck("3<=6", "(3 <= 6)");
}
@Test
public void testRelOperatorsGE01() {
void relOperatorsGE01() {
parseCheck("3>=6", "(3 >= 6)");
}
@Test
public void testRelOperatorsGE02() {
void relOperatorsGE02() {
parseCheck("3>=3", "(3 >= 3)");
}
@Test
public void testElvis() {
parseCheck("3?:1", "3 ?: 1");
void elvis() {
parseCheck("3?:1", "(3 ?: 1)");
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
}
// public void testRelOperatorsIn01() {
// void relOperatorsIn01() {
// parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
// }
//
// public void testRelOperatorsBetween01() {
// void relOperatorsBetween01() {
// parseCheck("1 between {1, 5}", "(1 between {1,5})");
// }
// public void testRelOperatorsBetween02() {
// void relOperatorsBetween02() {
// parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
// }// true
@Test
public void testRelOperatorsIs01() {
void relOperatorsIs01() {
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
}// false
// public void testRelOperatorsIs02() {
// void relOperatorsIs02() {
// parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
// }// true
@Test
public void testRelOperatorsMatches01() {
void relOperatorsMatches01() {
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
}// false
@Test
public void testRelOperatorsMatches02() {
void relOperatorsMatches02() {
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
}// true
// mathematical operators
@Test
public void testMathOperatorsAdd01() {
void mathOperatorsAdd01() {
parseCheck("2+4", "(2 + 4)");
}
@Test
public void testMathOperatorsAdd02() {
void mathOperatorsAdd02() {
parseCheck("'a'+'b'", "('a' + 'b')");
}
@Test
public void testMathOperatorsAdd03() {
void mathOperatorsAdd03() {
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
}
@Test
public void testMathOperatorsSubtract01() {
void mathOperatorsSubtract01() {
parseCheck("5-4", "(5 - 4)");
}
@Test
public void testMathOperatorsMultiply01() {
void mathOperatorsMultiply01() {
parseCheck("7*4", "(7 * 4)");
}
@Test
public void testMathOperatorsDivide01() {
void mathOperatorsDivide01() {
parseCheck("8/4", "(8 / 4)");
}
@Test
public void testMathOperatorModulus01() {
void mathOperatorModulus01() {
parseCheck("7 % 4", "(7 % 4)");
}
// mixed operators
@Test
public void testMixedOperators01() {
void mixedOperators01() {
parseCheck("true and 5>3", "(true and (5 > 3))");
}
// collection processors
// public void testCollectionProcessorsCount01() {
// void collectionProcessorsCount01() {
// parseCheck("new String[] {'abc','def','xyz'}.count()");
// }
// public void testCollectionProcessorsCount02() {
// void collectionProcessorsCount02() {
// parseCheck("new int[] {1,2,3}.count()");
// }
//
// public void testCollectionProcessorsMax01() {
// void collectionProcessorsMax01() {
// parseCheck("new int[] {1,2,3}.max()");
// }
//
// public void testCollectionProcessorsMin01() {
// void collectionProcessorsMin01() {
// parseCheck("new int[] {1,2,3}.min()");
// }
//
// public void testCollectionProcessorsAverage01() {
// void collectionProcessorsAverage01() {
// parseCheck("new int[] {1,2,3}.average()");
// }
//
// public void testCollectionProcessorsSort01() {
// void collectionProcessorsSort01() {
// parseCheck("new int[] {3,2,1}.sort()");
// }
//
// public void testCollectionProcessorsNonNull01() {
// void collectionProcessorsNonNull01() {
// parseCheck("{'a','b',null,'d',null}.nonNull()");
// }
//
// public void testCollectionProcessorsDistinct01() {
// void collectionProcessorsDistinct01() {
// parseCheck("{'a','b','a','d','e'}.distinct()");
// }
// references
@Test
public void testReferences01() {
void references01() {
parseCheck("@foo");
parseCheck("@'foo.bar'");
parseCheck("@\"foo.bar.goo\"","@'foo.bar.goo'");
parseCheck("@\"foo.bar.goo\"" , "@'foo.bar.goo'");
}
@Test
public void testReferences03() {
void references03() {
parseCheck("@$$foo");
}
// properties
@Test
public void testProperties01() {
void properties01() {
parseCheck("name");
}
@Test
public void testProperties02() {
void properties02() {
parseCheck("placeofbirth.CitY");
}
@Test
public void testProperties03() {
void properties03() {
parseCheck("a.b.c.d.e");
}
// inline list creation
@Test
public void testInlineListCreation01() {
void inlineListCreation01() {
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
}
@Test
public void testInlineListCreation02() {
void inlineListCreation02() {
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
}
// inline map creation
@Test
public void testInlineMapCreation01() {
void inlineMapCreation01() {
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
}
@Test
public void testInlineMapCreation02() {
void inlineMapCreation02() {
parseCheck("{1:'January',2:'February',3:'March'}");
}
// methods
@Test
public void testMethods01() {
void methods01() {
parseCheck("echo(12)");
}
@Test
public void testMethods02() {
void methods02() {
parseCheck("echo(name)");
}
@Test
public void testMethods03() {
void methods03() {
parseCheck("age.doubleItAndAdd(12)");
}
// constructors
@Test
public void testConstructors01() {
void constructors01() {
parseCheck("new String('hello')");
}
// public void testConstructors02() {
// void constructors02() {
// parseCheck("new String[3]");
// }
// array construction
// public void testArrayConstruction01() {
// void arrayConstruction01() {
// parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
// }
//
// public void testArrayConstruction02() {
// void arrayConstruction02() {
// parseCheck("new String[] {'abc','xyz'}", "new String[] {'abc','xyz'}");
// }
// variables and functions
@Test
public void testVariables01() {
void variables01() {
parseCheck("#foo");
}
@Test
public void testFunctions01() {
void functions01() {
parseCheck("#fn(1,2,3)");
}
@Test
public void testFunctions02() {
void functions02() {
parseCheck("#fn('hello')");
}
// projections and selections
// public void testProjections01() {
// void projections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
// }
// public void testSelections01() {
// void selections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
// }
// public void testSelectionsFirst01() {
// void selectionsFirst01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
// }
// public void testSelectionsLast01() {
// void selectionsLast01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
// }
// assignment
@Test
public void testAssignmentToVariables01() {
void assignmentToVariables01() {
parseCheck("#var1='value1'");
}
@@ -399,38 +405,43 @@ public class ParsingTests {
// ternary operator
@Test
public void testTernaryOperator01() {
parseCheck("1>2?3:4","(1 > 2) ? 3 : 4");
void ternaryOperator01() {
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
}
// public void testTernaryOperator01() {
// parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
// "({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd'");
// }
@Test
void ternaryOperator02() {
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
}
//
// public void testLambdaMax() {
// void lambdaMax() {
// parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
// }
//
// public void testLambdaFactorial() {
// void lambdaFactorial() {
// parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
// "(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
// } // 120
// Type references
@Test
public void testTypeReferences01() {
void typeReferences01() {
parseCheck("T(java.lang.String)");
}
@Test
public void testTypeReferences02() {
void typeReferences02() {
parseCheck("T(String)");
}
@Test
public void testInlineList1() {
void inlineList1() {
parseCheck("{1,2,3,4}");
}
@@ -440,7 +451,7 @@ public class ParsingTests {
*
* @param expression the expression to parse *and* the expected value of the string form of the resultant AST
*/
public void parseCheck(String expression) {
private void parseCheck(String expression) {
parseCheck(expression, expression);
}
@@ -451,7 +462,7 @@ public class ParsingTests {
* @param expression the expression to parse
* @param expectedStringFormOfAST the expected string form of the AST
*/
public void parseCheck(String expression, String expectedStringFormOfAST) {
private void parseCheck(String expression, String expectedStringFormOfAST) {
SpelExpression e = parser.parseRaw(expression);
assertThat(e).isNotNull();
assertThat(e.toStringAST()).isEqualTo(expectedStringFormOfAST);
@@ -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.
@@ -78,9 +78,9 @@ public abstract class NamedParameterUtils {
* Parse the SQL statement and locate any placeholders or named parameters.
* Named parameters are substituted for a JDBC placeholder.
* @param sql the SQL statement
* @return the parsed statement, represented as ParsedSql instance
* @return the parsed statement, represented as {@link ParsedSql} instance
*/
public static ParsedSql parseSqlStatement(final String sql) {
public static ParsedSql parseSqlStatement(String sql) {
Assert.notNull(sql, "SQL must not be null");
Set<String> namedParameters = new HashSet<>();
@@ -122,17 +122,20 @@ public abstract class NamedParameterUtils {
while (statement[j] != '}') {
j++;
if (j >= statement.length) {
throw new InvalidDataAccessApiUsageException("Non-terminated named parameter declaration " +
"at position " + i + " in statement: " + sql);
throw new InvalidDataAccessApiUsageException(
"Non-terminated named parameter declaration at position " + i +
" in statement: " + sql);
}
if (statement[j] == ':' || statement[j] == '{') {
throw new InvalidDataAccessApiUsageException("Parameter name contains invalid character '" +
statement[j] + "' at position " + i + " in statement: " + sql);
throw new InvalidDataAccessApiUsageException(
"Parameter name contains invalid character '" + statement[j] +
"' at position " + i + " in statement: " + sql);
}
}
if (j - i > 2) {
parameter = sql.substring(i + 2, j);
namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter);
namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
parameterList, totalParameterCount, escapes, i, j + 1, parameter);
}
@@ -144,7 +147,8 @@ public abstract class NamedParameterUtils {
}
if (j - i > 1) {
parameter = sql.substring(i + 1, j);
namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter);
namedParameterCount = addNewNamedParameter(
namedParameters, namedParameterCount, parameter);
totalParameterCount = addNamedParameter(
parameterList, totalParameterCount, escapes, i, j, parameter);
}
@@ -185,8 +189,8 @@ public abstract class NamedParameterUtils {
return parsedSql;
}
private static int addNamedParameter(
List<ParameterHolder> parameterList, int totalParameterCount, int escapes, int i, int j, String parameter) {
private static int addNamedParameter(List<ParameterHolder> parameterList,
int totalParameterCount, int escapes, int i, int j, String parameter) {
parameterList.add(new ParameterHolder(parameter, i - escapes, j - escapes));
totalParameterCount++;
@@ -271,6 +275,7 @@ public abstract class NamedParameterUtils {
if (paramNames.isEmpty()) {
return originalSql;
}
StringBuilder actualSql = new StringBuilder(originalSql.length());
int lastIndex = 0;
for (int i = 0; i < paramNames.size(); i++) {
@@ -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.
@@ -319,4 +319,29 @@ public class NamedParameterUtilsTests {
assertThat(psql2.getParameterNames().get(0)).isEqualTo("xxx");
}
@Test // gh-27925
void namedParamMapReference() {
String sql = "insert into foos (id) values (:headers[id])";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(psql.getNamedParameterCount()).isEqualTo(1);
assertThat(psql.getParameterNames()).containsExactly("headers[id]");
class Foo {
final Map<String, Object> headers = new HashMap<>();
public Foo() {
this.headers.put("id", 1);
}
public Map<String, Object> getHeaders() {
return this.headers;
}
}
Foo foo = new Foo();
Object[] params = NamedParameterUtils.buildValueArray(psql,
new BeanPropertySqlParameterSource(foo), null);
assertThat(params[0]).isInstanceOf(SqlParameterValue.class);
assertThat(((SqlParameterValue) params[0]).getValue()).isEqualTo(foo.getHeaders().get("id"));
}
}
@@ -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,7 +16,7 @@
package org.springframework.jdbc.core
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.jdbc.core.test.ConstructorPerson
import java.math.BigDecimal
@@ -31,7 +31,7 @@ class KotlinDataClassRowMapperTests : AbstractRowMapperTests() {
"select name, age, birth_date, balance from people",
DataClassRowMapper(ConstructorPerson::class.java)
)
Assertions.assertThat(result.size).isEqualTo(1)
assertThat(result.size).isEqualTo(1)
verifyPerson(result[0])
mock.verifyClosed()
}
@@ -43,8 +43,8 @@ class KotlinDataClassRowMapperTests : AbstractRowMapperTests() {
"select name, age, birth_date, balance from people",
DataClassRowMapper(KotlinPerson::class.java)
)
Assertions.assertThat(result.size).isEqualTo(1)
Assertions.assertThat(result[0].name).isEqualTo("Bubba appended by init")
assertThat(result.size).isEqualTo(1)
assertThat(result[0].name).isEqualTo("Bubba appended by init")
}
@@ -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.
@@ -103,7 +103,7 @@ final class DefaultRSocketRequesterBuilder implements RSocketRequester.Builder {
@Override
public RSocketRequester.Builder metadataMimeType(MimeType mimeType) {
Assert.notNull(mimeType, "`metadataMimeType` is required");
Assert.notNull(mimeType, "'metadataMimeType' is required");
this.metadataMimeType = mimeType;
return this;
}
@@ -281,7 +281,7 @@ final class DefaultRSocketRequesterBuilder implements RSocketRequester.Builder {
Mono<DataBuffer> dataMono = Mono.empty();
if (data != null) {
ReactiveAdapter adapter = strategies.reactiveAdapterRegistry().getAdapter(data.getClass());
Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Expected single value: " + data);
Assert.isTrue(adapter == null || !adapter.isMultiValue(), () -> "Expected single value: " + data);
Mono<?> mono = (adapter != null ? Mono.from(adapter.toPublisher(data)) : Mono.just(data));
dataMono = mono.map(value -> {
ResolvableType type = ResolvableType.forClass(value.getClass());
@@ -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.
@@ -141,7 +141,8 @@ final class MetadataEncoder {
}
ReactiveAdapter adapter = this.strategies.reactiveAdapterRegistry().getAdapter(metadata.getClass());
if (adapter != null) {
Assert.isTrue(!adapter.isMultiValue(), "Expected single value: " + metadata);
Object originalMetadata = metadata;
Assert.isTrue(!adapter.isMultiValue(), () -> "Expected single value: " + originalMetadata);
metadata = Mono.from(adapter.toPublisher(metadata)).defaultIfEmpty(NO_VALUE);
this.hasAsyncValues = true;
}
@@ -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.
@@ -421,7 +421,7 @@ public class RSocketMessageHandler extends MessageMappingMessageHandler {
String str = setupPayload.dataMimeType();
MimeType dataMimeType = StringUtils.hasText(str) ? MimeTypeUtils.parseMimeType(str) : this.defaultDataMimeType;
Assert.notNull(dataMimeType, "No `dataMimeType` in ConnectionSetupPayload and no default value");
Assert.isTrue(isDataMimeTypeSupported(dataMimeType), "Data MimeType '" + dataMimeType + "' not supported");
Assert.isTrue(isDataMimeTypeSupported(dataMimeType), () -> "Data MimeType '" + dataMimeType + "' not supported");
str = setupPayload.metadataMimeType();
MimeType metaMimeType = StringUtils.hasText(str) ? MimeTypeUtils.parseMimeType(str) : this.defaultMetadataMimeType;
@@ -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.
@@ -138,7 +138,7 @@ public class SimpMessagingTemplate extends AbstractMessageSendingTemplate<String
*/
@Override
public void send(Message<?> message) {
Assert.notNull(message, "Message is required");
Assert.notNull(message, "Message must not be null");
String destination = SimpMessageHeaderAccessor.getDestination(message.getHeaders());
if (destination != null) {
sendInternal(message);
@@ -224,7 +224,8 @@ public class SimpMessagingTemplate extends AbstractMessageSendingTemplate<String
throws MessagingException {
Assert.notNull(user, "User must not be null");
Assert.isTrue(!user.contains("%2F"), "Invalid sequence \"%2F\" in user name: " + user);
String username = user;
Assert.isTrue(!user.contains("%2F"), () -> "Invalid sequence \"%2F\" in user name: " + username);
user = StringUtils.replace(user, "/", "%2F");
destination = destination.startsWith("/") ? destination : "/" + destination;
super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
@@ -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.
@@ -148,7 +148,7 @@ public class OrderedMessageChannelDecorator implements MessageChannel {
public static void configureInterceptor(MessageChannel channel, boolean preserveOrder) {
if (preserveOrder) {
Assert.isInstanceOf(ExecutorSubscribableChannel.class, channel,
"An ExecutorSubscribableChannel is required for `preservePublishOrder`");
"An ExecutorSubscribableChannel is required for 'preservePublishOrder'");
ExecutorSubscribableChannel execChannel = (ExecutorSubscribableChannel) channel;
if (execChannel.getInterceptors().stream().noneMatch(i -> i instanceof CallbackInterceptor)) {
execChannel.addInterceptor(0, new CallbackInterceptor());
@@ -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.
@@ -264,9 +264,12 @@ public class StompDecoder {
* <a href="https://stomp.github.io/stomp-specification-1.2.html#Value_Encoding">"Value Encoding"</a>.
*/
private String unescape(String inString) {
int index = inString.indexOf('\\');
if (index == -1) {
return inString;
}
StringBuilder sb = new StringBuilder(inString.length());
int pos = 0; // position in the old string
int index = inString.indexOf('\\');
while (index >= 0) {
sb.append(inString, pos, index);
@@ -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.
@@ -211,7 +211,7 @@ public class StompHeaders implements MultiValueMap<String, String>, Serializable
}
Arrays.stream(acceptVersions).forEach(version ->
Assert.isTrue(version != null && (version.equals("1.1") || version.equals("1.2")),
"Invalid version: " + version));
() -> "Invalid version: " + version));
set(ACCEPT_VERSION, StringUtils.arrayToCommaDelimitedString(acceptVersions));
}
@@ -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.
@@ -203,7 +203,7 @@ public class DefaultUserDestinationResolver implements UserDestinationResolver {
}
Principal principal = SimpMessageHeaderAccessor.getUser(headers);
String user = (principal != null ? principal.getName() : null);
Assert.isTrue(user == null || !user.contains("%2F"), "Invalid sequence \"%2F\" in user name: " + user);
Assert.isTrue(user == null || !user.contains("%2F"), () -> "Invalid sequence \"%2F\" in user name: " + user);
Set<String> sessionIds = Collections.singleton(sessionId);
return new ParseResult(sourceDestination, actualDestination, sourceDestination, sessionIds, user);
}
@@ -43,7 +43,7 @@ import org.springframework.util.StringUtils;
* strongly typed accessors for specific headers, the ability to leave headers
* in a {@link Message} mutable, and the option to suppress automatic generation
* of {@link MessageHeaders#ID id} and {@link MessageHeaders#TIMESTAMP
* timesteamp} headers. Subclasses such as {@link NativeMessageHeaderAccessor}
* timestamp} headers. Subclasses such as {@link NativeMessageHeaderAccessor}
* and others provide support for managing processing vs external source headers
* as well as protocol specific headers.
*
@@ -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,7 +17,8 @@
package org.springframework.messaging.converter
import kotlinx.serialization.Serializable
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.jupiter.api.Test
import org.springframework.core.MethodParameter
import org.springframework.messaging.support.MessageBuilder
@@ -50,12 +51,12 @@ class KotlinSerializationJsonMessageConverterTests {
val message = MessageBuilder.withPayload(payload.toByteArray(StandardCharsets.UTF_8)).build()
val result = converter.fromMessage(message, SerializableBean::class.java) as SerializableBean
Assertions.assertThat(result.bytes).containsExactly(0x1, 0x2)
Assertions.assertThat(result.array).containsExactly("Foo", "Bar")
Assertions.assertThat(result.number).isEqualTo(42)
Assertions.assertThat(result.string).isEqualTo("Foo")
Assertions.assertThat(result.bool).isTrue()
Assertions.assertThat(result.fraction).isEqualTo(42.0f)
assertThat(result.bytes).containsExactly(0x1, 0x2)
assertThat(result.array).containsExactly("Foo", "Bar")
assertThat(result.number).isEqualTo(42)
assertThat(result.string).isEqualTo("Foo")
assertThat(result.bool).isTrue()
assertThat(result.fraction).isEqualTo(42.0f)
}
@Test
@@ -82,13 +83,13 @@ class KotlinSerializationJsonMessageConverterTests {
val message = MessageBuilder.withPayload(payload.toByteArray(StandardCharsets.UTF_8)).build()
val result = converter.fromMessage(message, Array<SerializableBean>::class.java) as Array<SerializableBean>
Assertions.assertThat(result).hasSize(1)
Assertions.assertThat(result[0].bytes).containsExactly(0x1, 0x2)
Assertions.assertThat(result[0].array).containsExactly("Foo", "Bar")
Assertions.assertThat(result[0].number).isEqualTo(42)
Assertions.assertThat(result[0].string).isEqualTo("Foo")
Assertions.assertThat(result[0].bool).isTrue()
Assertions.assertThat(result[0].fraction).isEqualTo(42.0f)
assertThat(result).hasSize(1)
assertThat(result[0].bytes).containsExactly(0x1, 0x2)
assertThat(result[0].array).containsExactly("Foo", "Bar")
assertThat(result[0].number).isEqualTo(42)
assertThat(result[0].string).isEqualTo("Foo")
assertThat(result[0].bool).isTrue()
assertThat(result[0].fraction).isEqualTo(42.0f)
}
@Test
@@ -118,13 +119,13 @@ class KotlinSerializationJsonMessageConverterTests {
val message = MessageBuilder.withPayload(payload.toByteArray(StandardCharsets.UTF_8)).build()
val result = converter.fromMessage(message, typeOf<List<SerializableBean>>()::class.java, param) as List<SerializableBean>
Assertions.assertThat(result).hasSize(1)
Assertions.assertThat(result[0].bytes).containsExactly(0x1, 0x2)
Assertions.assertThat(result[0].array).containsExactly("Foo", "Bar")
Assertions.assertThat(result[0].number).isEqualTo(42)
Assertions.assertThat(result[0].string).isEqualTo("Foo")
Assertions.assertThat(result[0].bool).isTrue()
Assertions.assertThat(result[0].fraction).isEqualTo(42.0f)
assertThat(result).hasSize(1)
assertThat(result[0].bytes).containsExactly(0x1, 0x2)
assertThat(result[0].array).containsExactly("Foo", "Bar")
assertThat(result[0].number).isEqualTo(42)
assertThat(result[0].string).isEqualTo("Foo")
assertThat(result[0].bool).isTrue()
assertThat(result[0].fraction).isEqualTo(42.0f)
}
@Test
@@ -134,7 +135,7 @@ class KotlinSerializationJsonMessageConverterTests {
""".trimIndent()
val message = MessageBuilder.withPayload(payload.toByteArray(StandardCharsets.UTF_8)).build()
Assertions.assertThatExceptionOfType(MessageConversionException::class.java).isThrownBy {
assertThatExceptionOfType(MessageConversionException::class.java).isThrownBy {
converter.fromMessage(message, SerializableBean::class.java)
}
}
@@ -145,7 +146,7 @@ class KotlinSerializationJsonMessageConverterTests {
val message = converter.toMessage(serializableBean, null)
val result = String((message!!.payload as ByteArray), StandardCharsets.UTF_8)
Assertions.assertThat(result)
assertThat(result)
.contains("\"bytes\":[1,2]")
.contains("\"array\":[\"Foo\",\"Bar\"]")
.contains("\"number\":42")
@@ -160,7 +161,7 @@ class KotlinSerializationJsonMessageConverterTests {
val message = converter.toMessage(serializableBean, null)
val result = String((message!!.payload as ByteArray), StandardCharsets.UTF_8)
Assertions.assertThat(result)
assertThat(result)
.contains("\"bytes\":[1,2]")
.contains("\"array\":[\"Foo\",\"Bar\"]")
.contains("\"number\":42")
@@ -179,7 +180,7 @@ class KotlinSerializationJsonMessageConverterTests {
val message = converter.toMessage(arrayOf(serializableBean), null)
val result = String((message!!.payload as ByteArray), StandardCharsets.UTF_8)
Assertions.assertThat(result).isEqualTo(expectedJson)
assertThat(result).isEqualTo(expectedJson)
}
@Test
@@ -195,7 +196,7 @@ class KotlinSerializationJsonMessageConverterTests {
val message = converter.toMessage(arrayListOf(serializableBean), null, param)
val result = String((message!!.payload as ByteArray), StandardCharsets.UTF_8)
Assertions.assertThat(result).isEqualTo(expectedJson)
assertThat(result).isEqualTo(expectedJson)
}
@Suppress("UNUSED_PARAMETER")
@@ -144,7 +144,7 @@ public interface DatabaseClient extends ConnectionAccessor {
Builder apply(Consumer<Builder> builderConsumer);
/**
* Builder the {@link DatabaseClient} instance.
* Build the {@link DatabaseClient} instance.
*/
DatabaseClient build();
}
@@ -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.
@@ -273,7 +273,7 @@ class DefaultDatabaseClient implements DatabaseClient {
public DefaultGenericExecuteSpec bind(String name, Object value) {
assertNotPreparedOperation();
Assert.hasText(name, "Parameter name must not be null or empty!");
Assert.hasText(name, "Parameter name must not be null or empty");
Assert.notNull(value, () -> String.format(
"Value for parameter %s must not be null. Use bindNull(…) instead.", name));
@@ -291,7 +291,7 @@ class DefaultDatabaseClient implements DatabaseClient {
@Override
public DefaultGenericExecuteSpec bindNull(String name, Class<?> type) {
assertNotPreparedOperation();
Assert.hasText(name, "Parameter name must not be null or empty!");
Assert.hasText(name, "Parameter name must not be null or empty");
Map<String, Parameter> byName = new LinkedHashMap<>(this.byName);
byName.put(name, Parameter.empty(type));
@@ -301,7 +301,7 @@ class DefaultDatabaseClient implements DatabaseClient {
@Override
public DefaultGenericExecuteSpec filter(StatementFilterFunction filter) {
Assert.notNull(filter, "Statement FilterFunction must not be null");
Assert.notNull(filter, "StatementFilterFunction must not be null");
return new DefaultGenericExecuteSpec(
this.byIndex, this.byName, this.sqlSupplier, this.filterFunction.andThen(filter));
}
@@ -449,7 +449,7 @@ class DefaultDatabaseClient implements DatabaseClient {
private String getRequiredSql(Supplier<String> sqlSupplier) {
String sql = sqlSupplier.get();
Assert.state(StringUtils.hasText(sql), "SQL returned by SQL supplier must not be empty!");
Assert.state(StringUtils.hasText(sql), "SQL returned by supplier must not be empty");
return sql;
}
}
@@ -37,8 +37,7 @@ import org.springframework.util.Assert;
/**
* Helper methods for named parameter parsing.
*
* <p>Only intended for internal use within Spring's R2DBC
* framework.
* <p>Only intended for internal use within Spring's R2DBC framework.
*
* <p>References to the same parameter name are substituted with
* the same bind marker placeholder if a {@link BindMarkersFactory} uses
@@ -293,7 +292,6 @@ abstract class NamedParameterUtils {
if (paramSource.hasValue(paramName)) {
Object value = paramSource.getValue(paramName);
if (value instanceof Collection) {
Iterator<?> entryIter = ((Collection<?>) value).iterator();
int k = 0;
int counter = 0;
@@ -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.
@@ -43,6 +43,7 @@ public class NamedParameterUtilsUnitTests {
private final BindMarkersFactory BIND_MARKERS = BindMarkersFactory.indexed("$", 1);
@Test
public void shouldParseSql() {
String sql = "xxx :a yyyy :b :c :a zzzzz";
@@ -145,7 +146,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "select 'first name' from artists where info->'stat'->'albums' = ?? :album and '[\"1\",\"2\",\"3\"]'::jsonb ?? '4'";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsedSql.getTotalParameterCount()).isEqualTo(1);
assertThat(expand(parsedSql)).isEqualTo(expectedSql);
}
@@ -156,7 +156,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "select '[\"3\", \"11\"]'::jsonb ?| '{1,3,11,12,17}'::text[]";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsedSql.getTotalParameterCount()).isEqualTo(0);
assertThat(expand(parsedSql)).isEqualTo(expectedSql);
}
@@ -177,7 +176,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "select '0\\:0' as a, foo from bar where baz < DATE(:p1 23\\:59\\:59) and baz = :p2";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsedSql.getParameterNames()).containsExactly("p1", "p2");
assertThat(expand(parsedSql)).isEqualTo(expectedSql);
}
@@ -198,7 +196,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "select foo from bar where baz = b:{}z";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsedSql.getParameterNames()).isEmpty();
assertThat(expand(parsedSql)).isEqualTo(expectedSql);
@@ -225,13 +222,11 @@ public class NamedParameterUtilsUnitTests {
String expectedSql = "xxx & yyyy";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(expectedSql);
assertThat(expand(parsedSql)).isEqualTo(expectedSql);
}
@Test
public void substituteNamedParametersWithLogicalAnd() {
String expectedSql = "xxx & yyyy";
assertThat(expand(expectedSql)).isEqualTo(expectedSql);
@@ -249,7 +244,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "SELECT ':foo'':doo', :xxx FROM DUAL";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(psql.getTotalParameterCount()).isEqualTo(1);
assertThat(psql.getParameterNames()).containsExactly("xxx");
}
@@ -259,7 +253,6 @@ public class NamedParameterUtilsUnitTests {
String sql = "SELECT /*:doo*/':foo', :xxx FROM DUAL";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(psql.getTotalParameterCount()).isEqualTo(1);
assertThat(psql.getParameterNames()).containsExactly("xxx");
}
@@ -269,18 +262,23 @@ public class NamedParameterUtilsUnitTests {
String sql2 = "SELECT ':foo'/*:doo*/, :xxx FROM DUAL";
ParsedSql psql2 = NamedParameterUtils.parseSqlStatement(sql2);
assertThat(psql2.getTotalParameterCount()).isEqualTo(1);
assertThat(psql2.getParameterNames()).containsExactly("xxx");
}
@Test // gh-27925
void namedParamMapReference() {
String sql = "insert into foos (id) values (:headers[id])";
ParsedSql psql = NamedParameterUtils.parseSqlStatement(sql);
assertThat(psql.getNamedParameterCount()).isEqualTo(1);
assertThat(psql.getParameterNames()).containsExactly("headers[id]");
}
@Test
public void shouldAllowParsingMultipleUseOfParameter() {
String sql = "SELECT * FROM person where name = :id or lastname = :id";
ParsedSql parsed = NamedParameterUtils.parseSqlStatement(sql);
assertThat(parsed.getTotalParameterCount()).isEqualTo(2);
assertThat(parsed.getNamedParameterCount()).isEqualTo(1);
assertThat(parsed.getParameterNames()).containsExactly("id", "id");
@@ -300,23 +298,19 @@ public class NamedParameterUtilsUnitTests {
"SELECT * FROM person where name = $0 or lastname = $0");
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
assertThat(index).isEqualTo(0);
assertThat(value).isEqualTo("foo");
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
@@ -340,25 +334,20 @@ public class NamedParameterUtilsUnitTests {
"SELECT * FROM person where name IN ($0, $1, $2) or lastname IN ($0, $1, $2)");
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
assertThat(index).isIn(0, 1, 2);
assertThat(value).isIn("foo", "bar", "baz");
bindings.add(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
@@ -386,22 +375,18 @@ public class NamedParameterUtilsUnitTests {
Map<Integer, Object> bindValues = new LinkedHashMap<>();
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
bindValues.put(index, value);
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
throw new UnsupportedOperationException();
@@ -425,22 +410,18 @@ public class NamedParameterUtilsUnitTests {
"SELECT * FROM person where name = $0 or lastname = $0");
operation.bindTo(new BindTarget() {
@Override
public void bind(String identifier, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bind(int index, Object value) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(String identifier, Class<?> type) {
throw new UnsupportedOperationException();
}
@Override
public void bindNull(int index, Class<?> type) {
assertThat(index).isEqualTo(0);
@@ -449,6 +430,7 @@ public class NamedParameterUtilsUnitTests {
});
}
private String expand(ParsedSql sql) {
return NamedParameterUtils.substituteNamedParameters(sql, BIND_MARKERS,
new MapBindParameterSource()).toQuery();
@@ -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.
@@ -77,7 +77,7 @@ import org.springframework.util.StringUtils;
* is {@link Locale#ENGLISH}. This value can be changed via {@link #addPreferredLocale}
* or {@link #setPreferredLocales}.
*
* <p>As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
*
* @author Juergen Hoeller
* @author Rod Johnson
@@ -53,7 +53,7 @@ import org.springframework.web.util.WebUtils;
/**
* Mock implementation of the {@link javax.servlet.http.HttpServletResponse} interface.
*
* <p>As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
*
* @author Juergen Hoeller
* @author Rod Johnson
@@ -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.
@@ -84,7 +84,7 @@ public class PassThroughFilterChain implements FilterChain {
this.filter.doFilter(request, response, this.nextFilterChain);
}
else {
Assert.state(this.servlet != null, "Neither a Filter not a Servlet set");
Assert.state(this.servlet != null, "Neither a Filter nor a Servlet has been set");
this.servlet.service(request, response);
}
}
@@ -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.
@@ -41,8 +41,24 @@ package org.springframework.test.context;
* {@link org.springframework.core.annotation.Order @Order} annotation. See
* {@link TestContextBootstrapper#getTestExecutionListeners()} for details.
*
* <p>Spring provides the following out-of-the-box implementations (all of
* which implement {@code Ordered}):
* <h3>Registering TestExecutionListener Implementations</h3>
*
* <p>A {@code TestExecutionListener} can be registered explicitly for a test class,
* its subclasses, and its nested classes by using the
* {@link TestExecutionListeners @TestExecutionListeners} annotation. Explicit
* registration is suitable for custom listeners that are used in limited testing
* scenarios. However, it can become cumbersome if a custom listener needs to be
* used across an entire test suite. This issue is addressed through support for
* automatic discovery of <em>default</em> {@code TestExecutionListener}
* implementations through the
* {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
* mechanism. Specifically, default {@code TestExecutionListener} implementations
* can be registered under the {@code org.springframework.test.context.TestExecutionListener}
* key in a {@code META-INF/spring.factories} properties file.
*
* <p>Spring provides the following implementations. Each of these implements
* {@code Ordered} and is registered automatically by default.
*
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener
* ServletTestExecutionListener}</li>
@@ -30,16 +30,30 @@ import org.springframework.core.annotation.AliasFor;
* which {@link TestExecutionListener TestExecutionListeners} should be
* registered with a {@link TestContextManager}.
*
* <p>Typically, {@code @TestExecutionListeners} will be used in conjunction
* with {@link ContextConfiguration @ContextConfiguration}.
* <p>{@code @TestExecutionListeners} is used to register listeners for a
* particular test class, its subclasses, and its nested classes. If you wish to
* register a listener globally, you should register it via the automatic discovery
* mechanism described in {@link TestExecutionListener}.
*
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
* <em>composed annotations</em>.
*
* <p>As of Spring Framework 5.3, this annotation will be inherited from an
* enclosing test class by default. See
* <em>composed annotations</em>. As of Spring Framework 5.3, this annotation will
* be inherited from an enclosing test class by default. See
* {@link NestedTestConfiguration @NestedTestConfiguration} for details.
*
* <h3>Switching to default {@code TestExecutionListener} implementations</h3>
*
* <p>If you extend a class that is annotated with {@code @TestExecutionListeners}
* and you need to switch to using the <em>default</em> set of listeners, you
* can annotate your class with the following.
*
* <pre class="code">
* // Switch to default listeners
* &#064;TestExecutionListeners(listeners = {}, inheritListeners = false, mergeMode = MERGE_WITH_DEFAULTS)
* class MyTests extends BaseTests {
* // ...
* }
* </pre>
*
* @author Sam Brannen
* @since 2.5
* @see TestExecutionListener
@@ -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.
@@ -49,8 +49,10 @@ import org.springframework.test.context.web.ServletTestExecutionListener;
* the appropriate {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListeners} manually.</em>
*
* <p>The following {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListeners} are configured by default:
* <p>This class explicitly registers the following {@code TestExecutionListener}
* implementations. If you want to switch to using the <em>default</em> set of
* listeners, see the class-level Javadoc for
* {@link TestExecutionListeners @TestExecutionListeners} for details.
*
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
@@ -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.
@@ -57,8 +57,10 @@ import org.springframework.util.Assert;
* <p>Concrete subclasses must fulfill the same requirements outlined in
* {@link AbstractJUnit4SpringContextTests}.
*
* <p>The following {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListeners} are configured by default:
* <p>This class explicitly registers the following {@code TestExecutionListener}
* implementations. If you want to switch to using the <em>default</em> set of
* listeners, see the class-level Javadoc for
* {@link TestExecutionListeners @TestExecutionListeners} for details.
*
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
@@ -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.
@@ -59,8 +59,10 @@ import org.springframework.test.context.web.ServletTestExecutionListener;
* TestExecutionListeners} manually.</em> Concrete subclasses must also have
* constructors which either implicitly or explicitly delegate to {@code super();}.
*
* <p>The following {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListeners} are configured by default:
* <p>This class explicitly registers the following {@code TestExecutionListener}
* implementations. If you want to switch to using the <em>default</em> set of
* listeners, see the class-level Javadoc for
* {@link TestExecutionListeners @TestExecutionListeners} for details.
*
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
@@ -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.
@@ -56,8 +56,10 @@ import org.springframework.util.Assert;
* <p>Concrete subclasses must fulfill the same requirements outlined in
* {@link AbstractTestNGSpringContextTests}.
*
* <p>The following {@link org.springframework.test.context.TestExecutionListener
* TestExecutionListeners} are configured by default:
* <p>This class explicitly registers the following {@code TestExecutionListener}
* implementations. If you want to switch to using the <em>default</em> set of
* listeners, see the class-level Javadoc for
* {@link TestExecutionListeners @TestExecutionListeners} for details.
*
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
@@ -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.
@@ -43,6 +43,7 @@ public abstract class AopTestUtils {
* {@linkplain AopUtils#isAopProxy proxy}, the target of the proxy will
* be returned; otherwise, the {@code candidate} will be returned
* <em>as is</em>.
* @param <T> the type of the target object
* @param candidate the instance to check (potentially a Spring AOP proxy;
* never {@code null})
* @return the target object or the {@code candidate} (never {@code null})
@@ -75,11 +76,18 @@ public abstract class AopTestUtils {
* {@linkplain AopUtils#isAopProxy proxy}, the ultimate target of all
* nested proxies will be returned; otherwise, the {@code candidate}
* will be returned <em>as is</em>.
* <p>NOTE: If the top-level proxy or a nested proxy is not backed by a
* {@linkplain org.springframework.aop.TargetSource#isStatic() static}
* {@link org.springframework.aop.TargetSource TargetSource}, invocation of
* this utility method may result in undesired behavior such as infinite
* recursion leading to a {@link StackOverflowError}.
* @param <T> the type of the target object
* @param candidate the instance to check (potentially a Spring AOP proxy;
* never {@code null})
* @return the target object or the {@code candidate} (never {@code null})
* @throws IllegalStateException if an error occurs while unwrapping a proxy
* @see Advised#getTargetSource()
* @see org.springframework.aop.TargetSource#isStatic()
* @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass
*/
@SuppressWarnings("unchecked")
@@ -0,0 +1,139 @@
/*
* 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.util;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Random;
import javax.net.ServerSocketFactory;
import org.springframework.util.Assert;
/**
* Simple utility for finding available TCP ports on {@code localhost} for use in
* integration testing scenarios.
*
* <p>This is a limited form of {@link org.springframework.util.SocketUtils} which
* has been deprecated since Spring Framework 5.3.16 and removed in Spring
* Framework 6.0.
*
* <p>{@code TestSocketUtils} can be used in integration tests which start an
* external server on an available random port. However, these utilities make no
* guarantee about the subsequent availability of a given port and are therefore
* unreliable. Instead of using {@code TestSocketUtils} to find an available local
* port for a server, it is recommended that you rely on a server's ability to
* start on a random <em>ephemeral</em> port that it selects or is assigned by the
* operating system. To interact with that server, you should query the server
* for the port it is currently using.
*
* @author Sam Brannen
* @author Ben Hale
* @author Arjen Poutsma
* @author Gunnar Hillert
* @author Gary Russell
* @author Chris Bono
* @since 5.3.24
*/
public class TestSocketUtils {
/**
* The minimum value for port ranges used when finding an available TCP port.
*/
static final int PORT_RANGE_MIN = 1024;
/**
* The maximum value for port ranges used when finding an available TCP port.
*/
static final int PORT_RANGE_MAX = 65535;
private static final int PORT_RANGE_PLUS_ONE = PORT_RANGE_MAX - PORT_RANGE_MIN + 1;
private static final int MAX_ATTEMPTS = 1_000;
private static final Random random = new Random(System.nanoTime());
private static final TestSocketUtils INSTANCE = new TestSocketUtils();
/**
* Although {@code TestSocketUtils} consists solely of static utility methods,
* this constructor is intentionally {@code public}.
* <h5>Rationale</h5>
* <p>Static methods from this class may be invoked from within XML
* configuration files using the Spring Expression Language (SpEL) and the
* following syntax.
* <pre><code>
* &lt;bean id="myBean" ... p:port="#{T(org.springframework.test.util.TestSocketUtils).findAvailableTcpPort()}" /&gt;</code>
* </pre>
* <p>If this constructor were {@code private}, you would be required to supply
* the fully qualified class name to SpEL's {@code T()} function for each usage.
* Thus, the fact that this constructor is {@code public} allows you to reduce
* boilerplate configuration with SpEL as can be seen in the following example.
* <pre><code>
* &lt;bean id="socketUtils" class="org.springframework.test.util.TestSocketUtils" /&gt;
* &lt;bean id="myBean" ... p:port="#{socketUtils.findAvailableTcpPort()}" /&gt;</code>
* </pre>
*/
public TestSocketUtils() {
}
/**
* Find an available TCP port randomly selected from the range [1024, 65535].
* @return an available TCP port number
* @throws IllegalStateException if no available port could be found
*/
public static int findAvailableTcpPort() {
return INSTANCE.findAvailableTcpPortInternal();
}
/**
* Internal implementation of {@link #findAvailableTcpPort()}.
* <p>Package-private solely for testing purposes.
*/
int findAvailableTcpPortInternal() {
int candidatePort;
int searchCounter = 0;
do {
Assert.state(++searchCounter <= MAX_ATTEMPTS, () -> String.format(
"Could not find an available TCP port in the range [%d, %d] after %d attempts",
PORT_RANGE_MIN, PORT_RANGE_MAX, MAX_ATTEMPTS));
candidatePort = PORT_RANGE_MIN + random.nextInt(PORT_RANGE_PLUS_ONE);
}
while (!isPortAvailable(candidatePort));
return candidatePort;
}
/**
* Determine if the specified TCP port is currently available on {@code localhost}.
* <p>Package-private solely for testing purposes.
*/
boolean isPortAvailable(int port) {
try {
ServerSocket serverSocket = ServerSocketFactory.getDefault()
.createServerSocket(port, 1, InetAddress.getByName("localhost"));
serverSocket.close();
return true;
}
catch (Exception ex) {
return false;
}
}
}
@@ -230,7 +230,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
@Override
public WebTestClient.Builder entityExchangeResultConsumer(Consumer<EntityExchangeResult<?>> entityResultConsumer) {
Assert.notNull(entityResultConsumer, "`entityResultConsumer` is required");
Assert.notNull(entityResultConsumer, "'entityResultConsumer' is required");
this.entityResultConsumer = this.entityResultConsumer.andThen(entityResultConsumer);
return this;
}
@@ -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.
@@ -50,6 +50,7 @@ import org.springframework.util.MultiValueMap;
* respectively.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 5.0
* @see EntityExchangeResult
* @see FluxExchangeResult
@@ -171,6 +172,8 @@ public class ExchangeResult {
/**
* Return the HTTP status code as an {@link HttpStatus} enum value.
* @throws IllegalArgumentException in case of an unknown HTTP status code
* @see #getRawStatusCode()
*/
public HttpStatus getStatus() {
return this.response.getStatusCode();
@@ -248,13 +251,22 @@ public class ExchangeResult {
"\n" +
formatBody(getRequestHeaders().getContentType(), this.requestBody) + "\n" +
"\n" +
"< " + getStatus() + " " + getStatus().getReasonPhrase() + "\n" +
"< " + formatStatus() + "\n" +
"< " + formatHeaders(getResponseHeaders(), "\n< ") + "\n" +
"\n" +
formatBody(getResponseHeaders().getContentType(), this.responseBody) +"\n" +
formatMockServerResult();
}
private String formatStatus() {
try {
return getStatus() + " " + getStatus().getReasonPhrase();
}
catch (Exception ex) {
return Integer.toString(getRawStatusCode());
}
}
private String formatHeaders(HttpHeaders headers, String delimiter) {
return headers.entrySet().stream()
.map(entry -> entry.getKey() + ": " + entry.getValue())
@@ -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.
@@ -148,8 +148,8 @@ public class MockHttpServletRequestBuilder
private static URI initUri(String url, Object[] vars) {
Assert.notNull(url, "'url' must not be null");
Assert.isTrue(url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://"), "" +
"'url' should start with a path or be a complete HTTP URL: " + url);
Assert.isTrue(url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://"),
() -> "'url' should start with a path or be a complete HTTP URL: " + url);
return UriComponentsBuilder.fromUriString(url).buildAndExpand(vars).encode().toUri();
}
@@ -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.
@@ -42,8 +42,8 @@ public class HybridContextLoader extends AbstractGenericContextLoader {
@Override
protected void validateMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
Assert.isTrue(mergedConfig.hasClasses() || mergedConfig.hasLocations(), getClass().getSimpleName()
+ " requires either classes or locations");
Assert.isTrue(mergedConfig.hasResources(),
() -> getClass().getSimpleName() + " requires either classes or locations");
}
@Override
@@ -0,0 +1,61 @@
/*
* 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.util;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Unit tests for {@link TestSocketUtils}.
*
* @author Sam Brannen
* @author Gary Russell
* @since 5.3.24
*/
class TestSocketUtilsTests {
@Test
void canBeInstantiated() {
// Just making sure somebody doesn't try to make SocketUtils abstract,
// since that would be a breaking change due to the intentional public
// constructor.
new TestSocketUtils();
}
@RepeatedTest(10)
void findAvailableTcpPort() {
assertThat(TestSocketUtils.findAvailableTcpPort())
.isBetween(TestSocketUtils.PORT_RANGE_MIN, TestSocketUtils.PORT_RANGE_MAX);
}
@Test
void findAvailableTcpPortWhenNoAvailablePortFoundInMaxAttempts() {
TestSocketUtils socketUtils = new TestSocketUtils() {
@Override
boolean isPortAvailable(int port) {
return false;
}
};
assertThatIllegalStateException()
.isThrownBy(socketUtils::findAvailableTcpPortInternal)
.withMessage("Could not find an available TCP port in the range [1024, 65535] after 1000 attempts");
}
}
@@ -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.
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock;
* Unit tests for {@link StatusAssertions}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
*/
class StatusAssertionTests {
@@ -56,9 +57,20 @@ class StatusAssertionTests {
assertions.isEqualTo(408));
}
@Test // gh-23630
@Test // gh-23630, gh-29283
void isEqualToWithCustomStatus() {
statusAssertions(600).isEqualTo(600);
StatusAssertions assertions = statusAssertions(600);
// Success
assertions.isEqualTo(600);
// Wrong status
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
assertions.isEqualTo(HttpStatus.REQUEST_TIMEOUT));
// Wrong status value
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
assertions.isEqualTo(408));
}
@Test
@@ -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.
@@ -15,7 +15,7 @@
*/
package org.springframework.test.context
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
@@ -36,8 +36,8 @@ class KotlinDynamicPropertySourceIntegrationTests {
@Test
fun hasInjectedValues(@Autowired service: Service) {
Assertions.assertThat(service.ip).isEqualTo("127.0.0.1")
Assertions.assertThat(service.port).isEqualTo(4242)
assertThat(service.ip).isEqualTo("127.0.0.1")
assertThat(service.port).isEqualTo(4242)
}
@Configuration
@@ -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.
@@ -57,7 +57,7 @@ final class TransactionalOperatorImpl implements TransactionalOperator {
*/
TransactionalOperatorImpl(ReactiveTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
Assert.notNull(transactionManager, "ReactiveTransactionManager must not be null");
Assert.notNull(transactionManager, "TransactionDefinition must not be null");
Assert.notNull(transactionDefinition, "TransactionDefinition must not be null");
this.transactionManager = transactionManager;
this.transactionDefinition = transactionDefinition;
}
@@ -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,7 +21,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.fail
import org.junit.jupiter.api.Test
import org.springframework.aop.framework.ProxyFactory
import org.springframework.transaction.interceptor.TransactionInterceptor
@@ -60,7 +61,6 @@ class CoroutinesAnnotationTransactionInterceptorTests {
}
catch (ex: IllegalStateException) {
}
}
assertReactiveGetTransactionAndRollbackCount(1)
}
@@ -72,7 +72,7 @@ class CoroutinesAnnotationTransactionInterceptorTests {
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
Assertions.assertThat(proxy.suspendingValueSuccess()).isEqualTo("foo")
assertThat(proxy.suspendingValueSuccess()).isEqualTo("foo")
}
assertReactiveGetTransactionAndCommitCount(1)
}
@@ -86,7 +86,7 @@ class CoroutinesAnnotationTransactionInterceptorTests {
runBlocking {
try {
proxy.suspendingValueFailure()
Assertions.fail("No exception thrown as expected")
fail("No exception thrown as expected")
}
catch (ex: IllegalStateException) {
}
@@ -101,7 +101,7 @@ class CoroutinesAnnotationTransactionInterceptorTests {
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
Assertions.assertThat(proxy.suspendingFlowSuccess().toList()).containsExactly("foo", "foo")
assertThat(proxy.suspendingFlowSuccess().toList()).containsExactly("foo", "foo")
}
assertReactiveGetTransactionAndCommitCount(1)
}
@@ -113,19 +113,19 @@ class CoroutinesAnnotationTransactionInterceptorTests {
proxyFactory.addAdvice(TransactionInterceptor(rtm, source))
val proxy = proxyFactory.proxy as TestWithCoroutines
runBlocking {
Assertions.assertThat(proxy.flowSuccess().toList()).containsExactly("foo", "foo")
assertThat(proxy.flowSuccess().toList()).containsExactly("foo", "foo")
}
assertReactiveGetTransactionAndCommitCount(1)
}
private fun assertReactiveGetTransactionAndCommitCount(expectedCount: Int) {
Assertions.assertThat(rtm.begun).isEqualTo(expectedCount)
Assertions.assertThat(rtm.commits).isEqualTo(expectedCount)
assertThat(rtm.begun).isEqualTo(expectedCount)
assertThat(rtm.commits).isEqualTo(expectedCount)
}
private fun assertReactiveGetTransactionAndRollbackCount(expectedCount: Int) {
Assertions.assertThat(rtm.begun).isEqualTo(expectedCount)
Assertions.assertThat(rtm.rollbacks).isEqualTo(expectedCount)
assertThat(rtm.begun).isEqualTo(expectedCount)
assertThat(rtm.rollbacks).isEqualTo(expectedCount)
}
@Transactional
@@ -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.
@@ -18,7 +18,7 @@ package org.springframework.transaction.interceptor
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Fail
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -188,7 +188,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
ex: Exception, shouldRollback: Boolean, rollbackException: Boolean) {
val txatt: TransactionAttribute = object : DefaultTransactionAttribute() {
override fun rollbackOn(t: Throwable): Boolean {
Assertions.assertThat(t).isSameAs(ex)
assertThat(t).isSameAs(ex)
return shouldRollback
}
}
@@ -218,9 +218,9 @@ abstract class AbstractCoroutinesTransactionAspectTests {
}
catch (actual: Exception) {
if (rollbackException) {
Assertions.assertThat(actual).hasMessage(tex.message).isInstanceOf(tex::class.java)
assertThat(actual).hasMessage(tex.message).isInstanceOf(tex::class.java)
} else {
Assertions.assertThat(actual).hasMessage(ex.message).isInstanceOf(ex::class.java)
assertThat(actual).hasMessage(ex.message).isInstanceOf(ex::class.java)
}
}
}
@@ -259,7 +259,7 @@ abstract class AbstractCoroutinesTransactionAspectTests {
itb.getName()
}
catch (actual: Exception) {
Assertions.assertThat(actual).isInstanceOf(CannotCreateTransactionException::class.java)
assertThat(actual).isInstanceOf(CannotCreateTransactionException::class.java)
}
}
}
@@ -291,11 +291,11 @@ abstract class AbstractCoroutinesTransactionAspectTests {
itb.setName(name)
}
catch (ex: Exception) {
Assertions.assertThat(ex).isInstanceOf(RuntimeException::class.java)
Assertions.assertThat(ex.cause).hasMessage(ex.message).isInstanceOf(ex::class.java)
assertThat(ex).isInstanceOf(RuntimeException::class.java)
assertThat(ex.cause).hasMessage(ex.message).isInstanceOf(ex::class.java)
}
// Should have invoked target and changed name
Assertions.assertThat(itb.getName()).isEqualTo(name)
assertThat(itb.getName()).isEqualTo(name)
}
}
@@ -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.
@@ -16,7 +16,7 @@
package org.springframework.transaction.interceptor
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.springframework.aop.framework.ProxyFactory
import org.springframework.transaction.ReactiveTransactionManager
@@ -44,9 +44,9 @@ class CoroutinesTransactionInterceptorTests : AbstractCoroutinesTransactionAspec
override fun advised(target: Any, rtm: ReactiveTransactionManager, tas: TransactionAttributeSource): Any {
val ti = TransactionInterceptor()
ti.transactionManager = rtm
Assertions.assertThat(ti.transactionManager).isEqualTo(rtm)
assertThat(ti.transactionManager).isEqualTo(rtm)
ti.transactionAttributeSource = tas
Assertions.assertThat(ti.transactionAttributeSource).isEqualTo(tas)
assertThat(ti.transactionAttributeSource).isEqualTo(tas)
val pf = ProxyFactory(target)
pf.addAdvice(0, ti)
return pf.proxy
@@ -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.
@@ -465,8 +465,9 @@ public final class ContentDisposition {
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
*/
private static String decodeFilename(String filename, Charset charset) {
Assert.notNull(filename, "'input' String` should not be null");
Assert.notNull(charset, "'charset' should not be null");
Assert.notNull(filename, "'filename' must not be null");
Assert.notNull(charset, "'charset' must not be null");
byte[] value = filename.getBytes(charset);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int index = 0;
@@ -531,10 +532,10 @@ public final class ContentDisposition {
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
*/
private static String encodeFilename(String input, Charset charset) {
Assert.notNull(input, "`input` is required");
Assert.notNull(charset, "`charset` is required");
Assert.notNull(input, "'input' is required");
Assert.notNull(charset, "'charset' is required");
Assert.isTrue(!StandardCharsets.US_ASCII.equals(charset), "ASCII does not require encoding");
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset), "Only UTF-8 and ISO-8859-1 supported.");
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset), "Only UTF-8 and ISO-8859-1 are supported");
byte[] source = input.getBytes(charset);
int len = source.length;
StringBuilder sb = new StringBuilder(len << 1);
@@ -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.
@@ -65,7 +65,7 @@ public abstract class HttpRange {
long contentLength = getLengthFor(resource);
long start = getRangeStart(contentLength);
long end = getRangeEnd(contentLength);
Assert.isTrue(start < contentLength, "'position' exceeds the resource length " + contentLength);
Assert.isTrue(start < contentLength, () -> "'position' exceeds the resource length " + contentLength);
return new ResourceRegion(resource, start, end - start + 1);
}
@@ -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.
@@ -531,10 +531,10 @@ public class MediaType extends MimeType implements Serializable {
protected void checkParameters(String parameter, String value) {
super.checkParameters(parameter, value);
if (PARAM_QUALITY_FACTOR.equals(parameter)) {
value = unquote(value);
double d = Double.parseDouble(value);
String unquotedValue = unquote(value);
double d = Double.parseDouble(unquotedValue);
Assert.isTrue(d >= 0D && d <= 1D,
"Invalid quality value \"" + value + "\": should be between 0.0 and 1.0");
() -> "Invalid quality value \"" + unquotedValue + "\": should be between 0.0 and 1.0");
}
}
@@ -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.
@@ -54,6 +54,10 @@ public abstract class InterceptingHttpAccessor extends HttpAccessor {
* Set the request interceptors that this accessor should use.
* <p>The interceptors will get immediately sorted according to their
* {@linkplain AnnotationAwareOrderComparator#sort(List) order}.
* <p><strong>Note:</strong> This method does not support concurrent changes,
* and in most cases should not be called after initialization on startup.
* See also related note on {@link org.springframework.web.client.RestTemplate}
* regarding concurrent configuration changes.
* @see #getRequestFactory()
* @see AnnotationAwareOrderComparator
*/
@@ -231,7 +231,7 @@ public abstract class Jackson2CodecSupport {
JsonView annotation = getAnnotation(param, JsonView.class);
if (annotation != null) {
Class<?>[] classes = annotation.value();
Assert.isTrue(classes.length == 1, JSON_VIEW_HINT_ERROR + param);
Assert.isTrue(classes.length == 1, () -> JSON_VIEW_HINT_ERROR + param);
hints = (hints != null ? hints : new HashMap<>(1));
hints.put(JSON_VIEW_HINT, classes[0]);
}
@@ -229,6 +229,7 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
if (upstream() != null &&
!this.sink.isCancelled() &&
this.sink.requestedFromDownstream() > 0 &&
this.state.get().canRequest() &&
this.requestOutstanding.compareAndSet(false, true)) {
request(1);
}
@@ -291,6 +292,13 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
default void error(Throwable throwable) {
}
/**
* Indicates whether the current state is ready to accept a new token.
*/
default boolean canRequest() {
return true;
}
/**
* Cleans up any state.
*/
@@ -814,6 +822,11 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
}
}
@Override
public boolean canRequest() {
return false;
}
@Override
public void dispose() {
this.disposed = true;
@@ -128,7 +128,7 @@ public class BufferedImageHttpMessageConverter implements HttpMessageConverter<B
*/
public void setCacheDir(File cacheDir) {
Assert.notNull(cacheDir, "'cacheDir' must not be null");
Assert.isTrue(cacheDir.isDirectory(), "'cacheDir' is not a directory");
Assert.isTrue(cacheDir.isDirectory(), () -> "'cacheDir' is not a directory: " + cacheDir);
this.cacheDir = cacheDir;
}
@@ -436,7 +436,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
StringBuilder builder = new StringBuilder();
formData.forEach((name, values) -> {
if (name == null) {
Assert.isTrue(CollectionUtils.isEmpty(values), "Null name in form data: " + formData);
Assert.isTrue(CollectionUtils.isEmpty(values), () -> "Null name in form data: " + formData);
return;
}
values.forEach(value -> {
@@ -356,7 +356,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
Charset charset = getCharset(contentType);
ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
Assert.state(objectMapper != null, () -> "No ObjectMapper for " + javaType);
boolean isUnicode = ENCODINGS.containsKey(charset.name()) ||
"UTF-16".equals(charset.name()) ||
@@ -419,7 +419,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
Class<?> clazz = (object instanceof MappingJacksonValue ?
((MappingJacksonValue) object).getValue().getClass() : object.getClass());
ObjectMapper objectMapper = selectObjectMapper(clazz, contentType);
Assert.state(objectMapper != null, "No ObjectMapper for " + clazz.getName());
Assert.state(objectMapper != null, () -> "No ObjectMapper for " + clazz.getName());
OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
try (JsonGenerator generator = objectMapper.getFactory().createGenerator(outputStream, encoding)) {
@@ -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.
@@ -93,7 +93,7 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
@Override
public ServerHttpRequest.Builder path(String path) {
Assert.isTrue(path.startsWith("/"), "The path does not have a leading slash.");
Assert.isTrue(path.startsWith("/"), () -> "The path does not have a leading slash: " + path);
this.uriPath = path;
return this;
}
@@ -90,7 +90,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
super(initUri(request), request.getContextPath() + servletPath, initHeaders(headers, request));
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be higher than 0");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be greater than 0");
this.request = request;
this.bufferFactory = bufferFactory;
@@ -63,12 +63,12 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
HttpServerExchange exchange, DataBufferFactory bufferFactory, UndertowServerHttpRequest request) {
super(bufferFactory, createHeaders(exchange));
Assert.notNull(exchange, "HttpServerExchange must not be null");
this.exchange = exchange;
this.request = request;
}
private static HttpHeaders createHeaders(HttpServerExchange exchange) {
Assert.notNull(exchange, "HttpServerExchange must not be null");
UndertowHeadersAdapter headersMap = new UndertowHeadersAdapter(exchange.getResponseHeaders());
return new HttpHeaders(headersMap);
}
@@ -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.
@@ -68,11 +68,17 @@ import org.springframework.web.util.UriTemplateHandler;
/**
* Synchronous client to perform HTTP requests, exposing a simple, template
* method API over underlying HTTP client libraries such as the JDK
* {@code HttpURLConnection}, Apache HttpComponents, and others.
* {@code HttpURLConnection}, Apache HttpComponents, and others. RestTemplate
* offers templates for common scenarios by HTTP method, in addition to the
* generalized {@code exchange} and {@code execute} methods that support of
* less frequent cases.
*
* <p>The RestTemplate offers templates for common scenarios by HTTP method, in
* addition to the generalized {@code exchange} and {@code execute} methods that
* support of less frequent cases.
* <p>RestTemplate is typically used as a shared component. However, its
* configuration does not support concurrent modification, and as such its
* configuration is typically prepared on startup. If necessary, you can create
* multiple, differently configured RestTemplate instances on startup. Such
* instances may use the same the underlying {@link ClientHttpRequestFactory}
* if they need to share HTTP client resources.
*
* <p><strong>NOTE:</strong> As of 5.0 this class is in maintenance mode, with
* only minor requests for changes and bugs to be accepted going forward. Please,
@@ -220,6 +220,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
if (this.notModified && response != null) {
response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
}
if (SAFE_METHODS.contains(getRequest().getMethod())) {
if (StringUtils.hasLength(etag) && response.getHeader(HttpHeaders.ETAG) == null) {
response.setHeader(HttpHeaders.ETAG, padEtagIfNecessary(etag));
}
response.setDateHeader(HttpHeaders.LAST_MODIFIED, lastModifiedTimestamp);
}
return this.notModified;
}
@@ -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.
@@ -196,7 +196,7 @@ public final class WebAsyncManager {
*/
public void registerCallableInterceptor(Object key, CallableProcessingInterceptor interceptor) {
Assert.notNull(key, "Key is required");
Assert.notNull(interceptor, "CallableProcessingInterceptor is required");
Assert.notNull(interceptor, "CallableProcessingInterceptor is required");
this.callableInterceptors.put(key, interceptor);
}

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