Compare commits

..

85 Commits

Author SHA1 Message Date
Spring Builds 5ea8ba1b96 Release v5.3.23 2022-09-15 07:47:35 +00:00
rstoyanchev ae8b7973b4 Improve Javadoc on RequestEntity#getUrl
Closes gh-28930
2022-09-14 19:25:55 +01:00
Juergen Hoeller a7f430bda8 Upgrade to Checkstyle 10.3.3, RxJava 3.1.5, SmallRye Mutiny 1.7, HtmlUnit 2.64, JsonPath 2.7 2022-09-14 19:28:27 +02:00
Juergen Hoeller 5a781eeb54 Upgrade to RSocket 1.1.3, Netty 4.1.82, Tomcat 9.0.65, Undertow 2.2.19, Hibernate Validator 6.2.5 2022-09-14 18:42:04 +02:00
Sam Brannen a879d18c9c Reintroduce Caucho exclusion for 5.3.x
See gh-28876
2022-09-14 17:10:17 +02:00
Marc Wrobel ce49068ff9 Fix links in Javadoc and reference docs
- Fix broken links (by using a new URL, an alternative URL, or a
  Wayback Machine link)

- Use HTTPS where possible

- Remove https://issuetracker.springsource.com/browse/EBR-349: this
  link is dead and is also mentioned in
  https://jira.spring.io/browse/SPR-8093

- Clean up nohttp allowlist.lines

Closes gh-28876
2022-09-14 17:00:11 +02:00
Sam Brannen 62a0ab3667 Polish contribution 2022-09-14 16:50:32 +02:00
Marc Wrobel 92a231cf91 Fix typos in Javadoc, reference docs, and code
Closes gh-28822
2022-09-14 16:45:34 +02:00
Sam Brannen 50dff77d01 Polish contribution 2022-09-14 16:08:01 +02:00
Marc Wrobel 0f479293b1 Replace use of the <tt> HTML tag in Javadoc
The <tt> HTML tag was used in HTML 4 to define teletype text. It is not
supported in HTML5, and its use is discouraged.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tt

Closes gh-28819
2022-09-14 15:56:16 +02:00
Johnny Lim 5f5d383eeb Fix Javadoc since tag for AbstractGenericWebContextLoader.createContext()
See gh-28983
Closes gh-29154
2022-09-14 15:53:18 +02:00
Sam Brannen 529481c44c Polish contribution
See gh-29150
2022-09-14 14:35:43 +02:00
Jens Dietrich 35d379f9d3 Add missing @Nullable in SettableListenableFuture
Closes gh-29150
2022-09-14 14:32:37 +02:00
Brian Clozel c871758a51 Upgrade to Reactor 2020.0.23
Closes gh-29129
2022-09-13 21:35:11 +02:00
Stephane Nicoll 0d2bfc926f Apply consistent ordering in hierarchical contexts
Previously, if `@Order` is specified on a `@Bean` method, and the
candidate bean is defined in a parent context, its order wasn't taken
into account when retrieving the bean from a child context.

This commit makes sure the metadata of a bean is taken into
consideration in all cases.

Closes gh-29105
2022-09-13 07:23:22 +02:00
rstoyanchev 4e97776969 Polishing contribution
Closes gh-28715
2022-09-12 11:47:55 +01:00
Napster d42f950a36 Pass headers to STOMP receipt callbacks
See gh-28715
2022-09-12 10:55:12 +01:00
rstoyanchev 4eabe29b9a Polishing contribution
Closes gh-28785
2022-09-12 10:52:53 +01:00
npriebe cd10171f98 Make WebSocketConnectionManager#isConnected public
See gh-28785
2022-09-12 09:54:57 +01:00
jbotuck 597c687da8 Fix indentation in ref docs for RestTemplate
Closes gh-29014
2022-09-12 09:51:33 +01:00
Stephane Nicoll 081d70123e Start building against Reactor 2020.0.23 snapshots
See gh-29129
2022-09-10 09:44:11 +02:00
Stephane Nicoll d9c2ccb4b3 Upgrade Ubuntu version in CI image
Closes gh-29106
2022-09-08 10:57:36 +02:00
Stephane Nicoll c503f356c7 Merge pull request #29104 from boahc077
* pr/29104:
  Restrict permissions for GitHub action

Closes gh-29104
2022-09-08 09:04:15 +02:00
Ashish Kurmi 7f9933fdb7 Restrict permissions for GitHub action
See gh-29104
2022-09-08 09:03:47 +02:00
Brian Clozel a425512025 Polish
See gh-29050
2022-09-07 15:02:10 +02:00
Kevin Yue 298c9a6f1b Redirect response wrapper should commit response
This commit ensures that when using `sendRedirect`, the response wrapper
behaves correctly with regards to the Servlet specification:

1. reset the response buffer to clear any partially written response
2. set the expected response HTTP headers
3. flush the buffer to commit the response

Closes gh-29050
2022-09-07 14:59:15 +02:00
Stephane Nicoll 8dcb2a75fa Merge pull request #29048 from 1993heqiang
* pr/29048:
  Fix typo in data-access section

Closes gh-29048
2022-09-05 07:23:56 +02:00
heqiang 111a9902ac Fix typo in data-access section
See gh-29048
2022-09-05 07:21:31 +02:00
Sam Brannen 0389fcca7e Fix sporadically failing SpEL test 2022-09-02 16:41:44 +02:00
junsu bb3ada4a67 Fix Kotlin Serialization converter registration logic
This commit fixes Kotlin Serialization converter
registration logic in RestTemplate,
AbstractMessageBrokerConfiguration and
AllEncompassingFormHttpMessageConverter classes
to be similar to the one in
WebMvcConfigurationSupport.

Closes gh-29008
2022-09-02 11:17:51 +02:00
Sam Brannen 6a68bd58f9 Introduce AnnotationUtils.isSynthesizedAnnotation(Annotation)
Since SynthesizedAnnotation will be deprecated (and potentially
completely removed) in Spring Framework 6.0, this commit introduces
AnnotationUtils.isSynthesizedAnnotation(Annotation) in 5.3.x to allow
people to migrate away from relying on SynthesizedAnnotation.

Closes gh-29054
2022-09-01 17:35:22 +02:00
Sam Brannen b1414bf15b Polishing 2022-09-01 17:23:04 +02:00
Arjen Poutsma f91943977c RenderingResponse must set status code on RedirectView
This commit makes sure that WebFlux's RenderingResponse sets the HTTP
status code when rendering a RedirectView.

Closes: gh-28839
2022-08-30 15:14:03 +02:00
Arjen Poutsma 4c0ece944a Fix race condition in PartGenerator
This commit fixes a race condition in PartGenerator, used by
DefaultPartHttpMessageReader. The condition can occur when a
completion signal comes in, and the state is changed to IdleFileState
at the same time.

Closes gh-28963
2022-08-30 12:11:22 +02:00
Sam Brannen 2c75eb8745 Support @Nested tests in MockServerContainerContextCustomizerFactory
Prior to this commit, MockServerContainerContextCustomizerFactory did
not find @WebAppConfiguration on an enclosing class and therefore
failed to create a MockServerContainerContextCustomizer for a @Nested
test class.

This commit addresses this by using TestContextAnnotationUtils to
determine if the test class is "annotated" with @WebAppConfiguration.

Closes gh-29037
2022-08-28 18:41:27 +02:00
Sam Brannen 711820ec70 Introduce createContext() factory method in AbstractWebGenericContextLoader
Prior to this commit it was possible to configure the
DefaultListableBeanFactory used by the GenericWebApplicationContext
created by AbstractWebGenericContextLoader, but it was not possible to
completely replace the bean factory.

This commit introduces a new createContext() factory method in
AbstractWebGenericContextLoader which indirectly allows subclasses to
supply a custom DefaultListableBeanFactory implementation to the
GenericWebApplicationContext.

See gh-25600
Closes gh-28983
2022-08-21 15:05:23 +02:00
Sam Brannen 69d87f16ee Reorganize internals of DefaultCacheAwareContextLoaderDelegate 2022-08-21 15:02:28 +02:00
Sam Brannen 38f94799f4 Use andExpectAll() in example in MockMvc Javadoc 2022-08-19 16:05:31 +02:00
Sam Brannen 4264d0becd Fix example in MockMvc Javadoc 2022-08-19 16:02:10 +02:00
Arjen Poutsma 7e7d6b9c3a Propagate Context in DataBufferUtils::write(Path)
This commit makes sure that the Reactor context is propagated in
DataBufferUtils::write(Path).

Closes gh-28933
See gh-27517
2022-08-16 11:21:12 +02:00
Stephane Nicoll dfc06eda7a Upgrade Java 8 version in CI image and .sdkmanrc 2022-08-11 13:31:36 +02:00
Stephane Nicoll f0aa6ebed6 Merge pull request #28949 from izeye
* pr/28949:
  Polish "Add missing TreeSet to CollectionFactory.createCollection()"
  Add missing TreeSet to CollectionFactory.createCollection()

Closes gh-28949
2022-08-11 10:43:56 +02:00
Stephane Nicoll 6806aaf162 Polish "Add missing TreeSet to CollectionFactory.createCollection()"
See gh-28949
2022-08-11 08:28:43 +02:00
Johnny Lim ccec75c98d Add missing TreeSet to CollectionFactory.createCollection()
See gh-28949
2022-08-11 08:22:11 +02:00
Stephane Nicoll 22029b4487 Merge pull request #28944 from wilkinsona
* pr/28944:
  Correct description of @RequestParam with WebFlux

Closes gh-28944
2022-08-09 15:32:17 +02:00
Andy Wilkinson d6b60046ce Correct description of @RequestParam with WebFlux
See gh-28944
2022-08-09 15:30:08 +02:00
Stephane Nicoll 6685e78c36 Deprecate NestedIOException
NestedIOException has been removed in Spring Framework 6 and this commit
marks it as deprecated in 5.x. Users that were relying on this exception
should use IOException directly.

Closes gh-28929
2022-08-05 10:04:47 +02:00
Chanhyeong Cho 8685b2f5bf Fix broken kdoc-api links in kotlin.adoc
Fixes gh-28908
2022-08-04 11:02:02 +02:00
Stephane Nicoll 4af225405c Upgrade Java 18 version in CI image 2022-08-04 10:02:49 +02:00
Stephane Nicoll 51e594ae81 Upgrade Java 17 version in CI image 2022-08-04 10:02:15 +02:00
Stephane Nicoll c2554b2b27 Upgrade Java 11 version in CI image 2022-08-04 10:01:49 +02:00
Stephane Nicoll 86a762b5ee Upgrade Java 8 version in CI image 2022-08-04 10:01:23 +02:00
Stephane Nicoll 211241befe Upgrade Ubuntu version in CI image 2022-08-04 10:00:30 +02:00
Brian Clozel 0caa2ac696 Customize connection in UrlResource getInputStream
Prior to this commit, the `AbstractFileResolvingResource` would
provide a default implementation for `customizeConnection` which only
sets the HTTP request method as "HEAD".
While this is consistent with its usage within that class (in
`exists()`, `contentLength()` or `lastModified()`), this is not opened
for general usage by sub-classes.

`UrlResource` is an example of that, where its `getInputStream()` method
does not call this customization method.

This not only prevents implementations from calling
`customizeConnection` in various cases, but it also misleads developers
as they might think that customizations will be applied automatically.

This commit ensures that `customizeConnection` is called in all relevant
places and that the configuration of the HTTP method is instead done in
each method as it is use case specific.

Fixes gh-28909
2022-08-03 23:13:52 +02:00
Stephane Nicoll 4eb8a5c082 Merge pull request #28914 from izeye
* pr/28914:
  Fix Javadoc since for new modules() and modulesToInstall()

Closes gh-28914
2022-08-02 16:11:04 +02:00
Johnny Lim d080b64907 Fix Javadoc since for new modules() and modulesToInstall()
See gh-28914
2022-08-02 16:10:47 +02:00
Stephane Nicoll 5ab55dfb64 Merge pull request #28183 from luvarqpp
* pr/28183:
  Polish "Improve documentation of Cacheable"
  Improve documentation of Cacheable

Closes gh-28183
2022-07-29 18:06:57 +02:00
Stephane Nicoll 04209def46 Polish "Improve documentation of Cacheable"
Apply the same improvements to CacheEvict and CachePut.

See gh-28183
2022-07-29 18:04:23 +02:00
luvarqpp 3a61930bd0 Improve documentation of Cacheable
See gh-28183
2022-07-29 17:57:15 +02:00
Stephane Nicoll 456c7889a3 Merge pull request #28488 from arend-von-reinersdorff
* pr/28488:
  Polish "Clarify docs on JNDI properties in Servlet environment"
  Clarify docs on JNDI properties in Servlet environment

Closes gh-28488
2022-07-29 17:13:42 +02:00
Stephane Nicoll 982d8ea84a Polish "Clarify docs on JNDI properties in Servlet environment"
See gh-28488
2022-07-29 17:09:55 +02:00
Arend v. Reinersdorff 67eb2bbf19 Clarify docs on JNDI properties in Servlet environment
See gh-28488
2022-07-29 17:05:58 +02:00
Stephane Nicoll a6596e2446 Merge pull request #28780 from vpavic
* pr/28780:
  Add missing nullability annotations to ResponseCookie

Closes gh-28780
2022-07-29 17:01:19 +02:00
Vedran Pavic 812b089f1d Add missing nullability annotations to ResponseCookie
See gh-28780
2022-07-29 16:54:37 +02:00
Rossen Stoyanchev 2172b99fee Polishing contribution
Closes gh-28836
2022-07-29 15:24:17 +03:00
floriankirmaier b6144e5682 Improve setting of WebSocket error status
See gh-28836
2022-07-29 15:07:15 +03:00
Rossen Stoyanchev 6e6f8637d1 Polishing contribution
Closes gh-28771
2022-07-29 14:45:42 +03:00
Johnny Lim 00f7f5ff0a Use MediaType.APPLICATION_FORM_URLENCODED_VALUE
See gh-28771
2022-07-29 14:40:45 +03:00
Arjen Poutsma 3d4a778b73 Fix KDoc API link 2022-07-29 11:08:57 +02:00
Stephane Nicoll 446c1add4f Merge pull request #28885 from edfeff
* pr/28885:
  Upgrade copyright year of changed file
  Fix typo in Javadoc of AbstractEncoder

Closes gh-28885
2022-07-29 09:31:42 +02:00
Stephane Nicoll aef4b0ec5a Upgrade copyright year of changed file
See gh-28885
2022-07-29 09:29:58 +02:00
wpp 675e0b94c1 Fix typo in Javadoc of AbstractEncoder
See gh-28885
2022-07-29 09:29:35 +02:00
Arjen Poutsma b8b54ee524 Ensure WebClient exceptions are serializable
Not all HttpHeaders implementations are serializable. This commit
ensures that WebClientRequestException and WebClientResponseException
are serializable, by copying any non-serializable HttpHeaders into a
new, serializable, instance.

Closes gh-28321
2022-07-28 14:09:11 +02:00
Arjen Poutsma 97ea8a6789 Copy HttpHeaders to ensure serializability
Not all HttpHeaders implementations are serializable. This commit
ensures that WebClientRequestException and WebClientResponseException
are serializable, by copying any non-serializable HttpHeaders into a
new, serializable, instance.

Closes gh-28321
2022-07-28 13:55:43 +02:00
Stephane Nicoll 95a400abfb Merge pull request #28867 from arvyy
* pr/28867:
  Add missing closing parenthesis

Closes gh-28867
2022-07-25 13:32:13 +02:00
arvyy 4383a673bb Add missing closing parenthesis
See gh-28867
2022-07-25 13:30:45 +02:00
Arjen Poutsma f7b94fc3e8 Updated sdkmanrc 2022-07-25 12:21:58 +02:00
Stephane Nicoll 93760d1211 Merge pull request #28853 from marcwrobel
* pr/28853:
  Polish "Deprecate SockJsServiceRegistration#setSupressCors"
  Deprecate SockJsServiceRegistration#setSupressCors

Closes gh-28853
2022-07-22 16:15:05 +02:00
Stephane Nicoll 421b5cb311 Polish "Deprecate SockJsServiceRegistration#setSupressCors"
See gh-28853
2022-07-22 16:07:47 +02:00
Marc Wrobel 1cc36e5be9 Deprecate SockJsServiceRegistration#setSupressCors
SockJsServiceRegistration#setSupressCors name contains a typo. It is
now deprecated in favor of a correctly spelled method, setSuppressCors.

See gh-28853
2022-07-22 16:06:54 +02:00
Stephane Nicoll f7c32d3a0b Merge pull request #28842 from adrianbob
* pr/28842:
  Reintroduce Javadoc links to Apache HttpClient 5.1 APIs

Closes gh-28842
2022-07-19 15:39:36 +02:00
Adrian Bob 9c0a2eeeb2 Reintroduce Javadoc links to Apache HttpClient 5.1 APIs
See gh-28842
2022-07-19 15:39:18 +02:00
Jupiter 7fa9f1c9cf Fix broken link to rsocket protocol page
Closes gh-28817
2022-07-19 10:14:46 +02:00
Arjen Poutsma 1e03b30d33 Use existing context path in DefaultServerRequestBuilder
Closes gh-28820
2022-07-14 15:10:31 +02:00
Spring Builds 7df149c8b8 Next development version (v5.3.23-SNAPSHOT) 2022-07-14 08:50:40 +00:00
135 changed files with 1181 additions and 680 deletions
@@ -1,6 +1,9 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
permissions:
contents: read
jobs:
validation:
name: "Validation"
+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.333-librca
java=8.0.345-librca
+14 -15
View File
@@ -28,10 +28,10 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.79.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.21"
mavenBom "io.netty:netty-bom:4.1.82.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.23"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.2"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.48.v20220622"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
@@ -67,8 +67,8 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.4"
dependency "io.smallrye.reactive:mutiny:1.4.0"
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
dependency "io.smallrye.reactive:mutiny:1.7.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
@@ -124,22 +124,22 @@ configure(allprojects) { project ->
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.33.Final"
dependency "org.hibernate:hibernate-validator:6.2.3.Final"
dependency "org.hibernate:hibernate-validator:6.2.5.Final"
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.64') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.65') {
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.64') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.65') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.18.Final') {
dependencySet(group: 'io.undertow', version: '2.2.19.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.12.1"
dependency("net.sourceforge.htmlunit:htmlunit:2.63.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.64.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.63.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.64.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -217,7 +217,7 @@ configure(allprojects) { project ->
exclude group: "io.netty", name: "netty"
}
dependency "org.skyscreamer:jsonassert:1.5.0"
dependency "com.jayway.jsonpath:json-path:2.6.0"
dependency "com.jayway.jsonpath:json-path:2.7.0"
dependency "org.bouncycastle:bcpkix-jdk18on:1.71"
dependencySet(group: 'org.apache.tiles', version: '3.0.8') {
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.3.1"
toolVersion = "10.3.3"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -380,8 +380,7 @@ configure([rootProject] + javaProjects) { project ->
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
// TODO Uncomment once httpclient5 API docs are again online.
// "https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// Disabling linking to JUnit 5.8.2, since the `package-list` file no longer exists due to
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220531
FROM ubuntu:focal-20220826
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+4 -4
View File
@@ -3,16 +3,16 @@ set -e
case "$1" in
java8)
echo "https://github.com/bell-sw/Liberica/releases/download/8u333+2/bellsoft-jdk8u333+2-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
;;
java11)
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.15.1+2/bellsoft-jdk11.0.15.1+2-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.16+8/bellsoft-jdk11.0.16+8-linux-amd64.tar.gz"
;;
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.3.1+2/bellsoft-jdk17.0.3.1+2-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.4+8/bellsoft-jdk17.0.4+8-linux-amd64.tar.gz"
;;
java18)
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.1.1+2/bellsoft-jdk18.0.1.1+2-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2+10/bellsoft-jdk18.0.2+10-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.22
version=5.3.23
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+1 -1
View File
@@ -153,7 +153,7 @@ asciidoctorPdf {
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', 'dokkaHtmlMultiModule']) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs."
"for deployment at https://docs.spring.io/spring-framework/docs/."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
+1 -1
View File
@@ -31,6 +31,6 @@ You'll notice these files are already intentionally in .gitignore. The same poli
## FAQ
Q. What about IntelliJ IDEA's own [Gradle support](https://confluence.jetbrains.net/display/IDEADEV/Gradle+integration)?
Q. What about IntelliJ IDEA's own [Gradle support](https://www.jetbrains.com/help/idea/gradle.html)?
A. Keep an eye on https://youtrack.jetbrains.com/issue/IDEA-53476
@@ -147,7 +147,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
/**
* Set whether the proxy should be frozen, preventing advice
* from being added to it once it is created.
* <p>Overridden from the super class to prevent the proxy configuration
* <p>Overridden from the superclass to prevent the proxy configuration
* from being frozen before the proxy is created.
*/
@Override
@@ -35,7 +35,7 @@ public abstract aspect AbstractDependencyInjectionAspect {
mostSpecificSubTypeConstruction() && !preConstructionConfiguration();
/**
* Select least specific super type that is marked for DI
* Select least specific supertype that is marked for DI
* (so that injection occurs only once with pre-construction injection).
*/
public abstract pointcut leastSpecificSuperTypeConstruction();
@@ -2167,20 +2167,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Nullable
public Object getOrderSource(Object obj) {
String beanName = this.instancesToBeanNames.get(obj);
if (beanName == null || !containsBeanDefinition(beanName)) {
if (beanName == null) {
return null;
}
RootBeanDefinition beanDefinition = getMergedLocalBeanDefinition(beanName);
List<Object> sources = new ArrayList<>(2);
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
if (factoryMethod != null) {
sources.add(factoryMethod);
try {
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
List<Object> sources = new ArrayList<>(2);
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
if (factoryMethod != null) {
sources.add(factoryMethod);
}
Class<?> targetType = beanDefinition.getTargetType();
if (targetType != null && targetType != obj.getClass()) {
sources.add(targetType);
}
return sources.toArray();
}
Class<?> targetType = beanDefinition.getTargetType();
if (targetType != null && targetType != obj.getClass()) {
sources.add(targetType);
catch (NoSuchBeanDefinitionException ex) {
return null;
}
return sources.toArray();
}
}
@@ -1982,6 +1982,21 @@ class DefaultListableBeanFactoryTests {
assertThat(bean.getSpouse()).isEqualTo(lbf.getBean("spouse"));
}
@Test
void beanProviderWithParentBeanFactoryReuseOrder() {
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
parentBf.registerBeanDefinition("regular", new RootBeanDefinition(TestBean.class));
parentBf.registerBeanDefinition("test", new RootBeanDefinition(HighPriorityTestBean.class));
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
lbf.setParentBeanFactory(parentBf);
lbf.registerBeanDefinition("low", new RootBeanDefinition(LowPriorityTestBean.class));
List<Class<?>> orderedTypes = lbf.getBeanProvider(TestBean.class).orderedStream()
.map(Object::getClass).collect(Collectors.toList());
assertThat(orderedTypes).containsExactly(
HighPriorityTestBean.class, LowPriorityTestBean.class, TestBean.class);
}
@Test
void autowireExistingBeanByName() {
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
@@ -81,7 +81,7 @@ class TypeHelper {
}
/**
* Return the super class of the specified {@link Element} or null if this
* Return the superclass of the specified {@link Element} or null if this
* {@code element} represents {@link Object}.
*/
public Element getSuperClass(Element element) {
@@ -100,7 +100,7 @@ class TypeHelper {
public List<Element> getDirectInterfaces(Element element) {
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
List<Element> directInterfaces = new ArrayList<>();
if (superTypes.size() > 1) { // index 0 is the super class
if (superTypes.size() > 1) { // index 0 is the superclass
for (int i = 1; i < superTypes.size(); i++) {
Element e = this.types.asElement(superTypes.get(i));
if (e != null) {
@@ -50,7 +50,7 @@ video/x-msvideo avi
################################################################################
#
# Additional file types adapted from
# http://sites.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
# https://web.archive.org/web/20220119153325/http%3A//sites.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
# kindly re-licensed to Apache Software License 2.0 by Ian Graham.
#
################################################################################
@@ -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,7 +24,7 @@ import org.springframework.lang.Nullable;
* Interface that defines common cache operations.
*
* <b>Note:</b> Due to the generic use of caching, it is recommended that
* implementations allow storage of <tt>null</tt> values (for example to
* implementations allow storage of {@code null} values (for example to
* cache methods that return {@code null}).
*
* @author Costin Leau
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 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.
@@ -111,7 +111,8 @@ public @interface CacheEvict {
/**
* Spring Expression Language (SpEL) expression used for making the cache
* eviction operation conditional.
* eviction operation conditional. Evict that cache if the condition evaluates
* to {@code true}.
* <p>Default is {@code ""}, meaning the cache eviction is always performed.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -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.
@@ -118,7 +118,8 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used for making the cache
* put operation conditional.
* put operation conditional. Update the cache if the condition evaluates to
* {@code true}.
* <p>This expression is evaluated after the method has been called due to the
* nature of the put operation and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning the method result is always cached.
@@ -142,6 +143,7 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used to veto the cache put operation.
* Veto updating the cache if the condition evaluates to {@code true}.
* <p>Default is {@code ""}, meaning that caching is never vetoed.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -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.
@@ -123,7 +123,8 @@ public @interface Cacheable {
/**
* Spring Expression Language (SpEL) expression used for making the method
* caching conditional.
* caching conditional. Cache the result if the condition evaluates to
* {@code true}.
* <p>Default is {@code ""}, meaning the method result is always cached.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -142,6 +143,7 @@ public @interface Cacheable {
/**
* Spring Expression Language (SpEL) expression used to veto method caching.
* Veto caching the result if the condition evaluates to {@code true}.
* <p>Unlike {@link #condition}, this expression is evaluated after the method
* has been called and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning that caching is never vetoed.
@@ -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,7 +54,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.context.annotation.DeferredImportSelector.Group;
import org.springframework.core.NestedIOException;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
@@ -680,6 +679,7 @@ class ConfigurationClassParser {
/**
* Factory method to obtain a {@link SourceClass} from a class name.
*/
@SuppressWarnings("deprecation")
SourceClass asSourceClass(@Nullable String className, Predicate<String> filter) throws IOException {
if (className == null || filter.test(className)) {
return this.objectSourceClass;
@@ -690,7 +690,7 @@ class ConfigurationClassParser {
return new SourceClass(ClassUtils.forName(className, this.resourceLoader.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to load class [" + className + "]", ex);
throw new org.springframework.core.NestedIOException("Failed to load class [" + className + "]", ex);
}
}
return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
@@ -1073,6 +1073,7 @@ class ConfigurationClassParser {
return result;
}
@SuppressWarnings("deprecation")
private SourceClass getRelated(String className) throws IOException {
if (this.source instanceof Class) {
try {
@@ -1082,7 +1083,7 @@ class ConfigurationClassParser {
catch (ClassNotFoundException ex) {
// Ignore -> fall back to ASM next, except for core java types.
if (className.startsWith("java")) {
throw new NestedIOException("Failed to load class [" + className + "]", ex);
throw new org.springframework.core.NestedIOException("Failed to load class [" + className + "]", ex);
}
return new SourceClass(metadataReaderFactory.getMetadataReader(className));
}
@@ -1,6 +1,6 @@
/**
* Package providing integration of
* <a href="http://www.beanshell.org">BeanShell</a>
* <a href="https://beanshell.github.io/">BeanShell</a>
* (and <a href="https://code.google.com/p/beanshell2/">BeanShell2</a>)
* into Spring's scripting infrastructure.
*/
@@ -1,6 +1,6 @@
/**
* Package providing integration of
* <a href="http://www.groovy-lang.org/">Groovy</a>
* <a href="https://www.groovy-lang.org/">Groovy</a>
* into Spring's scripting infrastructure.
*/
@NonNullApi
@@ -0,0 +1,81 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for gh-29105.
*
* @author Stephane Nicoll
*/
public class Gh29105Tests {
@Test
void beanProviderWithParentContextReuseOrder() {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.register(DefaultConfiguration.class);
parent.register(CustomConfiguration.class);
parent.refresh();
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
child.setParent(parent);
child.register(DefaultConfiguration.class);
child.refresh();
List<Class<?>> orderedTypes = child.getBeanProvider(MyService.class)
.orderedStream().map(Object::getClass).collect(Collectors.toList());
assertThat(orderedTypes).containsExactly(CustomService.class, DefaultService.class);
}
interface MyService {}
static class CustomService implements MyService {}
static class DefaultService implements MyService {}
@Configuration
static class CustomConfiguration {
@Bean
@Order(-1)
CustomService customService() {
return new CustomService();
}
}
@Configuration
static class DefaultConfiguration {
@Bean
@Order(0)
DefaultService defaultService() {
return new DefaultService();
}
}
}
@@ -117,7 +117,7 @@ class ApplicationContextExpressionTests {
ac.registerBeanDefinition("tb1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setBeanClassName("#{tb1.class.name}");
bd2.setBeanClassName("#{tb1.class}");
bd2.setScope("myScope");
bd2.getPropertyValues().add("name", "{ XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ }");
bd2.getPropertyValues().add("age", "#{mySpecialAttr}");
@@ -46,11 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@code jmxremote_optional.jar} into your classpath, for example in the {@code lib/ext}
* folder of your JVM.
*
* <p>See also:
* <ul>
* <li><a href="https://jira.spring.io/browse/SPR-8093">SPR-8093</a></li>
* <li><a href="https://issuetracker.springsource.com/browse/EBR-349">EBR-349</a></li>
* </ul>
* <p>See also: <a href="https://jira.spring.io/browse/SPR-8093">SPR-8093</a>
*
* @author Rob Harrop
* @author Juergen Hoeller
+1
View File
@@ -58,6 +58,7 @@ dependencies {
testImplementation("org.xmlunit:xmlunit-matchers")
testImplementation("io.projectreactor:reactor-test")
testImplementation("io.projectreactor.tools:blockhound")
testImplementation("com.squareup.okhttp3:mockwebserver")
testFixturesImplementation("com.google.code.findbugs:jsr305")
testFixturesImplementation("org.junit.platform:junit-platform-launcher")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
@@ -708,8 +708,8 @@ public final class Type {
*
* @return the size of the arguments of the method (plus one for the implicit this argument),
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
* {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
* i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}).
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
* i >> 2}, and returnSize to {@code i & 0x03}).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
@@ -721,8 +721,8 @@ public final class Type {
* @param methodDescriptor a method descriptor.
* @return the size of the arguments of the method (plus one for the implicit this argument),
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
* {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
* i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}).
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
* i >> 2}, and returnSize to {@code i & 0x03}).
*/
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
int argumentsSize = 1;
@@ -226,7 +226,7 @@ public final class BridgeMethodResolver {
/**
* Compare the signatures of the bridge method and the method which it bridges. If
* the parameter and return types are the same, it is a 'visibility' bridge method
* introduced in Java 6 to fix https://bugs.java.com/view_bug.do?bug_id=6342411.
* introduced in Java 6 to fix https://bugs.openjdk.org/browse/JDK-6342411.
* See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
* @return whether signatures match as described
*/
@@ -191,7 +191,8 @@ public final class CollectionFactory {
else if (LinkedList.class == collectionType) {
return new LinkedList<>();
}
else if (SortedSet.class == collectionType || NavigableSet.class == collectionType) {
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType
|| SortedSet.class == collectionType) {
return new TreeSet<>();
}
else if (EnumSet.class.isAssignableFrom(collectionType)) {
@@ -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.
@@ -29,7 +29,6 @@ import org.springframework.lang.Nullable;
* @since 2.0
* @see NestedRuntimeException
* @see NestedCheckedException
* @see NestedIOException
* @see org.springframework.web.util.NestedServletException
*/
public abstract class NestedExceptionUtils {
@@ -37,7 +37,9 @@ import org.springframework.lang.Nullable;
* @see #printStackTrace
* @see org.springframework.core.NestedCheckedException
* @see org.springframework.core.NestedRuntimeException
* @deprecated as of 5.3.23, in favor of using {@link IOException} directly
*/
@Deprecated
@SuppressWarnings("serial")
public class NestedIOException extends IOException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 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.
@@ -173,7 +173,7 @@ import java.lang.annotation.Target;
* @author Sam Brannen
* @since 4.2
* @see MergedAnnotations
* @see SynthesizedAnnotation
* @see AnnotationUtils#isSynthesizedAnnotation(Annotation)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 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,7 @@ import org.springframework.core.NestedRuntimeException;
* @author Sam Brannen
* @since 4.2
* @see AnnotationUtils
* @see SynthesizedAnnotation
* @see AnnotationUtils#isSynthesizedAnnotation(java.lang.annotation.Annotation)
*/
@SuppressWarnings("serial")
public class AnnotationConfigurationException extends NestedRuntimeException {
@@ -160,7 +160,7 @@ final class AnnotationTypeMappings {
* @param index the index to return
* @return the {@link AnnotationTypeMapping}
* @throws IndexOutOfBoundsException if the index is out of range
* (<tt>index &lt; 0 || index &gt;= size()</tt>)
* ({@code index < 0 || index >= size()})
*/
AnnotationTypeMapping get(int index) {
return this.mappings.get(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.
@@ -1188,7 +1188,7 @@ public abstract class AnnotationUtils {
public static <A extends Annotation> A synthesizeAnnotation(
A annotation, @Nullable AnnotatedElement annotatedElement) {
if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) {
if (isSynthesizedAnnotation(annotation) || AnnotationFilter.PLAIN.matches(annotation)) {
return annotation;
}
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
@@ -1282,6 +1282,18 @@ public abstract class AnnotationUtils {
return synthesized;
}
/**
* Determine if the supplied {@link Annotation} has been <em>synthesized</em>
* by Spring (i.e. wrapped in a dynamic proxy) with additional functionality
* such as attribute alias handling.
* @param annotation the annotation to check
* @return {@code true} if the supplied annotation is a synthesized annotation
* @since 5.3.23
*/
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
return (annotation instanceof SynthesizedAnnotation);
}
/**
* Clear the internal annotation metadata cache.
* @since 4.3.15
@@ -168,7 +168,7 @@ final class AttributeMethods {
* @param index the index of the attribute to return
* @return the attribute method
* @throws IndexOutOfBoundsException if the index is out of range
* (<tt>index &lt; 0 || index &gt;= size()</tt>)
* ({@code index < 0 || index >= size()})
*/
Method get(int index) {
return this.attributeMethods[index];
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.MimeType;
/**
* Abstract base class for {@link Decoder} implementations.
* Abstract base class for {@link Encoder} implementations.
*
* @author Sebastien Deleuze
* @author Arjen Poutsma
@@ -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.
@@ -238,7 +238,7 @@ public class TypeDescriptor implements Serializable {
* <p>As of Spring Framework 4.2, this method supports arbitrary levels
* of meta-annotations.
* @param annotationType the annotation type
* @return <tt>true</tt> if the annotation is present
* @return {@code true} if the annotation is present
*/
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
if (this.annotatedElement.isEmpty()) {
@@ -273,8 +273,8 @@ public class TypeDescriptor implements Serializable {
* On the other hand, {@code valueOf(Number.class).isAssignableTo(valueOf(Integer.class))}
* returns {@code false} because, while all Integers are Numbers, not all Numbers are Integers.
* <p>For arrays, collections, and maps, element and key/value types are checked if declared.
* For example, a List&lt;String&gt; field value is assignable to a Collection&lt;CharSequence&gt;
* field, but List&lt;Number&gt; is not assignable to List&lt;Integer&gt;.
* For example, a {@code List<String>} field value is assignable to a {@code Collection<CharSequence>}
* field, but {@code List<Number>} is not assignable to {@code List<Integer>}.
* @return {@code true} if this type is assignable to the type represented by the provided
* type descriptor
* @see #getObjectType()
@@ -576,7 +576,7 @@ public class TypeDescriptor implements Serializable {
/**
* Create a new type descriptor from a {@link java.util.Map} type.
* <p>Useful for converting to typed Maps.
* <p>For example, a Map&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt;
* <p>For example, a {@code Map<String, String>} could be converted to a {@code Map<Id, EmailAddress>}
* by converting to a targetType built with this method:
* The method call to construct such a TypeDescriptor would look something like:
* <pre class="code">
@@ -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.
@@ -104,7 +104,7 @@ public class ConvertingComparator<S, T> implements Comparator<S> {
/**
* Adapts a {@link ConversionService} and <tt>targetType</tt> to a {@link Converter}.
* Adapts a {@link ConversionService} and {@code targetType} to a {@link Converter}.
*/
private static class ConversionServiceConverter<S, T> implements Converter<S, T> {
@@ -57,6 +57,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
HttpURLConnection httpCon =
(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
if (httpCon != null) {
httpCon.setRequestMethod("HEAD");
int code = httpCon.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return true;
@@ -108,6 +109,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
customizeConnection(con);
if (con instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) con;
httpCon.setRequestMethod("HEAD");
int code = httpCon.getResponseCode();
if (code != HttpURLConnection.HTTP_OK) {
httpCon.disconnect();
@@ -245,6 +247,10 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
// Try a URL connection content-length header
URLConnection con = url.openConnection();
customizeConnection(con);
if (con instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) con;
httpCon.setRequestMethod("HEAD");
}
return con.getContentLengthLong();
}
}
@@ -270,6 +276,10 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
// Try a URL connection last-modified header
URLConnection con = url.openConnection();
customizeConnection(con);
if (con instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) con;
httpCon.setRequestMethod("HEAD");
}
long lastModified = con.getLastModified();
if (fileCheck && lastModified == 0 && con.getContentLengthLong() <= 0) {
throw new FileNotFoundException(getDescription() +
@@ -279,8 +289,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
}
/**
* Customize the given {@link URLConnection}, obtained in the course of an
* {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call.
* Customize the given {@link URLConnection} before fetching the resource.
* <p>Calls {@link ResourceUtils#useCachesIfNecessary(URLConnection)} and
* delegates to {@link #customizeConnection(HttpURLConnection)} if possible.
* Can be overridden in subclasses.
@@ -295,14 +304,12 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
}
/**
* Customize the given {@link HttpURLConnection}, obtained in the course of an
* {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call.
* <p>Sets request method "HEAD" by default. Can be overridden in subclasses.
* Customize the given {@link HttpURLConnection} before fetching the resource.
* <p>Can be overridden in subclasses for configuring request headers and timeouts.
* @param con the HttpURLConnection to customize
* @throws IOException if thrown from HttpURLConnection methods
*/
protected void customizeConnection(HttpURLConnection con) throws IOException {
con.setRequestMethod("HEAD");
}
@@ -29,7 +29,6 @@ import java.nio.channels.ReadableByteChannel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.NestedIOException;
import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;
@@ -119,13 +118,14 @@ public abstract class AbstractResource implements Resource {
* by {@link #getURL()}.
*/
@Override
@SuppressWarnings("deprecation")
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
throw new org.springframework.core.NestedIOException("Invalid URI [" + url + "]", ex);
}
}
@@ -28,7 +28,6 @@ import java.net.URLConnection;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
@@ -183,7 +182,7 @@ public class UrlResource extends AbstractFileResolvingResource {
@Override
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
customizeConnection(con);
try {
return con.getInputStream();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import org.springframework.core.NestedIOException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -72,22 +71,24 @@ public class VfsResource extends AbstractResource {
}
@Override
@SuppressWarnings("deprecation")
public URL getURL() throws IOException {
try {
return VfsUtils.getURL(this.resource);
}
catch (Exception ex) {
throw new NestedIOException("Failed to obtain URL for file " + this.resource, ex);
throw new org.springframework.core.NestedIOException("Failed to obtain URL for file " + this.resource, ex);
}
}
@Override
@SuppressWarnings("deprecation")
public URI getURI() throws IOException {
try {
return VfsUtils.getURI(this.resource);
}
catch (Exception ex) {
throw new NestedIOException("Failed to obtain URI for " + this.resource, ex);
throw new org.springframework.core.NestedIOException("Failed to obtain URI for " + this.resource, ex);
}
}
@@ -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.
@@ -38,9 +38,9 @@ import org.springframework.util.Assert;
* the following invariant holds for the read and write positions, and the capacity:
*
* <blockquote>
* <tt>0</tt> <tt>&lt;=</tt>
* <i>readPosition</i> <tt>&lt;=</tt>
* <i>writePosition</i> <tt>&lt;=</tt>
* {@code 0} {@code <=}
* <i>readPosition</i> {@code <=}
* <i>writePosition</i> {@code <=}
* <i>capacity</i>
* </blockquote>
*
@@ -366,7 +366,8 @@ public abstract class DataBufferUtils {
sink.onDispose(() -> closeChannel(channel));
write(source, channel).subscribe(DataBufferUtils::release,
sink::error,
sink::success);
sink::success,
Context.of(sink.contextView()));
}
catch (IOException ex) {
sink.error(ex);
@@ -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.
@@ -21,7 +21,6 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import org.springframework.core.ConfigurableObjectInputStream;
import org.springframework.core.NestedIOException;
import org.springframework.lang.Nullable;
/**
@@ -65,14 +64,14 @@ public class DefaultDeserializer implements Deserializer<Object> {
* @see ObjectInputStream#readObject()
*/
@Override
@SuppressWarnings("resource")
@SuppressWarnings("deprecation")
public Object deserialize(InputStream inputStream) throws IOException {
ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, this.classLoader);
try {
return objectInputStream.readObject();
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to deserialize object type", ex);
throw new org.springframework.core.NestedIOException("Failed to deserialize object type", ex);
}
}
@@ -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.
@@ -20,7 +20,6 @@ import java.io.IOException;
import java.io.InputStream;
import org.springframework.asm.ClassReader;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
@@ -51,13 +50,14 @@ final class SimpleMetadataReader implements MetadataReader {
this.annotationMetadata = visitor.getMetadata();
}
@SuppressWarnings("deprecation")
private static ClassReader getClassReader(Resource resource) throws IOException {
try (InputStream is = resource.getInputStream()) {
try {
return new ClassReader(is);
}
catch (IllegalArgumentException ex) {
throw new NestedIOException("ASM ClassReader failed to parse class file - " +
throw new org.springframework.core.NestedIOException("ASM ClassReader failed to parse class file - " +
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
}
}
@@ -86,7 +86,7 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not read super class [" + metadata.getSuperClassName() +
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
"] of type-filtered class [" + metadata.getClassName() + "]");
}
}
@@ -638,7 +638,7 @@ public class AntPathMatcher implements PathMatcher {
/**
* Tests whether a string matches against a pattern via a {@link Pattern}.
* <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and
* only one character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
* only one character; '{' and '}' indicate a URI template pattern. For example {@code /users/{user}}.
*/
protected static class AntPathStringMatcher {
@@ -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.
@@ -150,7 +150,7 @@ public class FastByteArrayOutputStream extends OutputStream {
/**
* Convert the buffer's contents into a string decoding bytes using the
* platform's default character set. The length of the new <tt>String</tt>
* platform's default character set. The length of the new {@code String}
* is a function of the character set, and hence may not be equal to the
* size of the buffer.
* <p>This method always replaces malformed-input and unmappable-character
@@ -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.
@@ -114,6 +114,7 @@ public class SettableListenableFuture<T> implements ListenableFuture<T> {
* {@link java.util.concurrent.CancellationException} if the future has been cancelled.
* @return the value associated with this future
*/
@Nullable
@Override
public T get() throws InterruptedException, ExecutionException {
return this.settableTask.get();
@@ -129,6 +130,7 @@ public class SettableListenableFuture<T> implements ListenableFuture<T> {
* @param unit the unit of the timeout argument
* @return the value associated with this future
*/
@Nullable
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return this.settableTask.get(timeout, unit);
@@ -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.
@@ -33,7 +33,7 @@ class AnnotationBackCompatibilityTests {
@Test
void multiplRoutesToMetaAnnotation() {
Class<WithMetaMetaTestAnnotation1AndMetaTestAnnotation2> source = WithMetaMetaTestAnnotation1AndMetaTestAnnotation2.class;
Class<?> source = WithMetaMetaTestAnnotation1AndMetaTestAnnotation2.class;
// Merged annotation chooses lowest depth
MergedAnnotation<TestAnnotation> mergedAnnotation = MergedAnnotations.from(source).get(TestAnnotation.class);
assertThat(mergedAnnotation.getString("value")).isEqualTo("testAndMetaTest");
@@ -45,7 +45,7 @@ class AnnotationBackCompatibilityTests {
@Test
void defaultValue() {
DefaultValueAnnotation synthesized = MergedAnnotations.from(WithDefaultValue.class).get(DefaultValueAnnotation.class).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertThat(AnnotationUtils.isSynthesizedAnnotation(synthesized)).as("synthesized annotation").isTrue();
Object defaultValue = AnnotationUtils.getDefaultValue(synthesized, "enumValue");
assertThat(defaultValue).isEqualTo(TestEnum.ONE);
}
@@ -60,14 +60,12 @@ class AnnotationBackCompatibilityTests {
@Retention(RetentionPolicy.RUNTIME)
@TestAnnotation("metaTest")
@interface MetaTestAnnotation {
}
@Retention(RetentionPolicy.RUNTIME)
@TestAnnotation("testAndMetaTest")
@MetaTestAnnotation
@interface TestAndMetaTestAnnotation {
}
@Retention(RetentionPolicy.RUNTIME)
@@ -78,7 +76,6 @@ class AnnotationBackCompatibilityTests {
@MetaMetaTestAnnotation
@TestAndMetaTestAnnotation
static class WithMetaMetaTestAnnotation1AndMetaTestAnnotation2 {
}
@Retention(RetentionPolicy.RUNTIME)
@@ -94,7 +91,6 @@ class AnnotationBackCompatibilityTests {
@DefaultValueAnnotation
static class WithDefaultValue {
}
enum TestEnum {
@@ -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.
@@ -45,7 +45,7 @@ class MergedAnnotationCollectorsTests {
MergedAnnotationCollectors.toAnnotationSet());
assertThat(set).isInstanceOf(LinkedHashSet.class).flatExtracting(
TestAnnotation::value).containsExactly("a", "b", "c");
assertThat(set).allMatch(SynthesizedAnnotation.class::isInstance);
assertThat(set).allMatch(AnnotationUtils::isSynthesizedAnnotation);
}
@Test
@@ -55,7 +55,7 @@ class MergedAnnotationCollectorsTests {
assertThat(Arrays.stream(array).map(
annotation -> ((TestAnnotation) annotation).value())).containsExactly("a",
"b", "c");
assertThat(array).allMatch(SynthesizedAnnotation.class::isInstance);
assertThat(array).allMatch(AnnotationUtils::isSynthesizedAnnotation);
}
@Test
@@ -64,7 +64,7 @@ class MergedAnnotationCollectorsTests {
MergedAnnotationCollectors.toAnnotationArray(TestAnnotation[]::new));
assertThat(Arrays.stream(array).map(TestAnnotation::value)).containsExactly("a",
"b", "c");
assertThat(array).allMatch(SynthesizedAnnotation.class::isInstance);
assertThat(array).allMatch(AnnotationUtils::isSynthesizedAnnotation);
}
@Test
@@ -1395,9 +1395,10 @@ class MergedAnnotationsTests {
RequestMapping synthesizedWebMapping = MergedAnnotation.from(webMapping).synthesize();
RequestMapping synthesizedAgainWebMapping = MergedAnnotation.from(synthesizedWebMapping).synthesize();
assertThat(synthesizedWebMapping).isInstanceOf(SynthesizedAnnotation.class);
assertThat(synthesizedAgainWebMapping).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedWebMapping);
assertSynthesized(synthesizedAgainWebMapping);
assertThat(synthesizedWebMapping).isEqualTo(synthesizedAgainWebMapping);
assertThat(synthesizedWebMapping).isSameAs(synthesizedAgainWebMapping);
assertThat(synthesizedWebMapping.name()).isEqualTo("foo");
assertThat(synthesizedWebMapping.path()).containsExactly("/test");
assertThat(synthesizedWebMapping.value()).containsExactly("/test");
@@ -1412,7 +1413,7 @@ class MergedAnnotationsTests {
Id synthesizedId = MergedAnnotation.from(id).synthesize();
assertThat(id).isEqualTo(synthesizedId);
// It doesn't make sense to synthesize @Id since it declares zero attributes.
assertThat(synthesizedId).isNotInstanceOf(SynthesizedAnnotation.class);
assertNotSynthesized(synthesizedId);
assertThat(id).isSameAs(synthesizedId);
GeneratedValue generatedValue = method.getAnnotation(GeneratedValue.class);
@@ -1420,7 +1421,7 @@ class MergedAnnotationsTests {
GeneratedValue synthesizedGeneratedValue = MergedAnnotation.from(generatedValue).synthesize();
assertThat(generatedValue).isEqualTo(synthesizedGeneratedValue);
// It doesn't make sense to synthesize @GeneratedValue since it declares zero attributes with aliases.
assertThat(synthesizedGeneratedValue).isNotInstanceOf(SynthesizedAnnotation.class);
assertNotSynthesized(synthesizedGeneratedValue);
assertThat(generatedValue).isSameAs(synthesizedGeneratedValue);
}
@@ -1430,19 +1431,19 @@ class MergedAnnotationsTests {
MergedAnnotations mergedAnnotations = MergedAnnotations.from(directlyAnnotatedField);
RootAnnotation rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
assertThat(rootAnnotation.flag()).isFalse();
assertThat(rootAnnotation).isNotInstanceOf(SynthesizedAnnotation.class);
assertNotSynthesized(rootAnnotation);
Field metaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaAnnotated");
mergedAnnotations = MergedAnnotations.from(metaAnnotatedField);
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
assertThat(rootAnnotation.flag()).isTrue();
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(rootAnnotation);
Field metaMetaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaMetaAnnotated");
mergedAnnotations = MergedAnnotations.from(metaMetaAnnotatedField);
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
assertThat(rootAnnotation.flag()).isTrue();
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(rootAnnotation);
}
@Test // gh-28704
@@ -1450,10 +1451,10 @@ class MergedAnnotationsTests {
MergedAnnotations mergedAnnotations = MergedAnnotations.from(SecurityConfig.class);
EnableWebSecurity enableWebSecurity = mergedAnnotations.get(EnableWebSecurity.class).synthesize();
assertThat(enableWebSecurity).isNotInstanceOf(SynthesizedAnnotation.class);
assertNotSynthesized(enableWebSecurity);
EnableGlobalAuthentication enableGlobalAuthentication = mergedAnnotations.get(EnableGlobalAuthentication.class).synthesize();
assertThat(enableGlobalAuthentication).isNotInstanceOf(SynthesizedAnnotation.class);
assertNotSynthesized(enableGlobalAuthentication);
}
/**
@@ -1472,8 +1473,8 @@ class MergedAnnotationsTests {
RequestMapping synthesizedWebMapping1 = mergedAnnotation1.synthesize();
RequestMapping synthesizedWebMapping2 = MergedAnnotation.from(webMapping).synthesize();
assertThat(synthesizedWebMapping1).isInstanceOf(SynthesizedAnnotation.class);
assertThat(synthesizedWebMapping2).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedWebMapping1);
assertSynthesized(synthesizedWebMapping2);
assertThat(synthesizedWebMapping1).isEqualTo(synthesizedWebMapping2);
// Synthesizing an annotation from a different MergedAnnotation results in a different synthesized annotation instance.
@@ -1595,14 +1596,11 @@ class MergedAnnotationsTests {
testSynthesisWithImplicitAliases(GroovyImplicitAliasesSimpleTestConfigurationClass.class, "groovyScript");
}
private void testSynthesisWithImplicitAliases(Class<?> clazz, String expected)
throws Exception {
ImplicitAliasesTestConfiguration config = clazz.getAnnotation(
ImplicitAliasesTestConfiguration.class);
private void testSynthesisWithImplicitAliases(Class<?> clazz, String expected) throws Exception {
ImplicitAliasesTestConfiguration config = clazz.getAnnotation(ImplicitAliasesTestConfiguration.class);
assertThat(config).isNotNull();
ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(
config).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
assertSynthesized(synthesized);
assertThat(synthesized.value()).isEqualTo(expected);
assertThat(synthesized.location1()).isEqualTo(expected);
assertThat(synthesized.xmlFile()).isEqualTo(expected);
@@ -1630,7 +1628,7 @@ class MergedAnnotationsTests {
assertThat(config).isNotNull();
ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration synthesized =
MergedAnnotation.from(config).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesized);
assertThat(synthesized.value()).isEqualTo(expected);
assertThat(synthesized.location()).isEqualTo(expected);
assertThat(synthesized.xmlFile()).isEqualTo(expected);
@@ -1642,7 +1640,7 @@ class MergedAnnotationsTests {
ImplicitAliasesForAliasPairTestConfigurationClass.class.getAnnotation(
ImplicitAliasesForAliasPairTestConfiguration.class);
ImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesized);
assertThat(synthesized.xmlFile()).isEqualTo("test.xml");
assertThat(synthesized.groovyScript()).isEqualTo("test.xml");
}
@@ -1653,7 +1651,7 @@ class MergedAnnotationsTests {
TransitiveImplicitAliasesTestConfigurationClass.class.getAnnotation(
TransitiveImplicitAliasesTestConfiguration.class);
TransitiveImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesized);
assertThat(synthesized.xml()).isEqualTo("test.xml");
assertThat(synthesized.groovy()).isEqualTo("test.xml");
}
@@ -1665,7 +1663,7 @@ class MergedAnnotationsTests {
TransitiveImplicitAliasesForAliasPairTestConfiguration.class);
TransitiveImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from(
config).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesized);
assertThat(synthesized.xml()).isEqualTo("test.xml");
assertThat(synthesized.groovy()).isEqualTo("test.xml");
}
@@ -1724,7 +1722,7 @@ class MergedAnnotationsTests {
Map<String, Object> map = Collections.singletonMap("value", "webController");
MergedAnnotation<Component> annotation = MergedAnnotation.of(Component.class, map);
Component synthesizedComponent = annotation.synthesize();
assertThat(synthesizedComponent).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedComponent);
assertThat(synthesizedComponent.value()).isEqualTo("webController");
}
@@ -1745,7 +1743,7 @@ class MergedAnnotationsTests {
MergedAnnotation<ComponentScanSingleFilter> annotation = MergedAnnotation.of(
ComponentScanSingleFilter.class, map);
ComponentScanSingleFilter synthesizedComponentScan = annotation.synthesize();
assertThat(synthesizedComponentScan).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedComponentScan);
assertThat(synthesizedComponentScan.value().pattern()).isEqualTo("newFoo");
}
@@ -1769,7 +1767,7 @@ class MergedAnnotationsTests {
MergedAnnotation<ComponentScan> annotation = MergedAnnotation.of(
ComponentScan.class, map);
ComponentScan synthesizedComponentScan = annotation.synthesize();
assertThat(synthesizedComponentScan).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedComponentScan);
assertThat(Arrays.stream(synthesizedComponentScan.excludeFilters()).map(
Filter::pattern)).containsExactly("newFoo", "newBar");
}
@@ -1888,7 +1886,7 @@ class MergedAnnotationsTests {
assertThat(component).isNotNull();
Map<String, Object> attributes = MergedAnnotation.from(component).asMap();
Component synthesized = MergedAnnotation.of(Component.class, attributes).synthesize();
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesized);
assertThat(synthesized).isEqualTo(component);
}
@@ -2047,7 +2045,7 @@ class MergedAnnotationsTests {
assertThat(annotation).isNotNull();
MergedAnnotation<Annotation> mergedAnnotation = MergedAnnotation.from(annotation);
Annotation synthesizedAnnotation = mergedAnnotation.synthesize();
assertThat(synthesizedAnnotation).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedAnnotation);
assertThat(mergedAnnotation.getString("name")).isEqualTo("test");
assertThat(mergedAnnotation.getString("path")).isEqualTo("/test");
assertThat(mergedAnnotation.getString("value")).isEqualTo("/test");
@@ -2058,10 +2056,10 @@ class MergedAnnotationsTests {
Hierarchy hierarchy = HierarchyClass.class.getAnnotation(Hierarchy.class);
assertThat(hierarchy).isNotNull();
Hierarchy synthesizedHierarchy = MergedAnnotation.from(hierarchy).synthesize();
assertThat(synthesizedHierarchy).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedHierarchy);
TestConfiguration[] configs = synthesizedHierarchy.value();
assertThat(configs).isNotNull();
assertThat(configs).allMatch(SynthesizedAnnotation.class::isInstance);
assertThat(configs).allMatch(AnnotationUtils::isSynthesizedAnnotation);
assertThat(configs).extracting(TestConfiguration::value).containsExactly("A", "B");
assertThat(configs).extracting(TestConfiguration::location).containsExactly("A", "B");
@@ -2082,7 +2080,7 @@ class MergedAnnotationsTests {
assertThat(charsContainer).isNotNull();
CharsContainer synthesizedCharsContainer = MergedAnnotation.from(
charsContainer).synthesize();
assertThat(synthesizedCharsContainer).isInstanceOf(SynthesizedAnnotation.class);
assertSynthesized(synthesizedCharsContainer);
char[] chars = synthesizedCharsContainer.chars();
assertThat(chars).containsExactly('x', 'y', 'z');
// Alter array returned from synthesized annotation
@@ -3682,4 +3680,12 @@ class MergedAnnotationsTests {
}
// @formatter:on
static void assertSynthesized(Annotation annotation) {
assertThat(AnnotationUtils.isSynthesizedAnnotation(annotation)).as("synthesized annotation").isTrue();
}
static void assertNotSynthesized(Annotation annotation) {
assertThat(AnnotationUtils.isSynthesizedAnnotation(annotation)).as("synthesized annotation").isFalse();
}
}
@@ -23,20 +23,32 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.stream.Stream;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
* Unit tests for various {@link Resource} implementations.
@@ -44,137 +56,25 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
* @since 09.09.2004
* @author Brian Clozel
*/
class ResourceTests {
@Test
void byteArrayResource() throws IOException {
Resource resource = new ByteArrayResource("testString".getBytes());
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isFalse();
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(new ByteArrayResource("testString".getBytes())).isEqualTo(resource);
}
@Test
void byteArrayResourceWithDescription() throws IOException {
Resource resource = new ByteArrayResource("testString".getBytes(), "my description");
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isFalse();
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(resource.getDescription().contains("my description")).isTrue();
assertThat(new ByteArrayResource("testString".getBytes())).isEqualTo(resource);
}
@Test
void inputStreamResource() throws IOException {
InputStream is = new ByteArrayInputStream("testString".getBytes());
Resource resource = new InputStreamResource(is);
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isTrue();
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(new InputStreamResource(is)).isEqualTo(resource);
}
@Test
void inputStreamResourceWithDescription() throws IOException {
InputStream is = new ByteArrayInputStream("testString".getBytes());
Resource resource = new InputStreamResource(is, "my description");
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isTrue();
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(resource.getDescription().contains("my description")).isTrue();
assertThat(new InputStreamResource(is)).isEqualTo(resource);
}
@Test
void classPathResource() throws IOException {
Resource resource = new ClassPathResource("org/springframework/core/io/Resource.class");
doTestResource(resource);
Resource resource2 = new ClassPathResource("org/springframework/core/../core/io/./Resource.class");
assertThat(resource2).isEqualTo(resource);
Resource resource3 = new ClassPathResource("org/springframework/core/").createRelative("../core/io/./Resource.class");
assertThat(resource3).isEqualTo(resource);
// Check whether equal/hashCode works in a HashSet.
HashSet<Resource> resources = new HashSet<>();
resources.add(resource);
resources.add(resource2);
assertThat(resources.size()).isEqualTo(1);
}
@Test
void classPathResourceWithClassLoader() throws IOException {
Resource resource =
new ClassPathResource("org/springframework/core/io/Resource.class", getClass().getClassLoader());
doTestResource(resource);
assertThat(new ClassPathResource("org/springframework/core/../core/io/./Resource.class", getClass().getClassLoader())).isEqualTo(resource);
}
@Test
void classPathResourceWithClass() throws IOException {
Resource resource = new ClassPathResource("Resource.class", getClass());
doTestResource(resource);
assertThat(new ClassPathResource("Resource.class", getClass())).isEqualTo(resource);
}
@Test
void fileSystemResource() throws IOException {
String file = getClass().getResource("Resource.class").getFile();
Resource resource = new FileSystemResource(file);
doTestResource(resource);
assertThat(resource).isEqualTo(new FileSystemResource(file));
}
@Test
void fileSystemResourceWithFile() throws IOException {
File file = new File(getClass().getResource("Resource.class").getFile());
Resource resource = new FileSystemResource(file);
doTestResource(resource);
assertThat(resource).isEqualTo(new FileSystemResource(file));
}
@Test
void fileSystemResourceWithFilePath() throws Exception {
Path filePath = Paths.get(getClass().getResource("Resource.class").toURI());
Resource resource = new FileSystemResource(filePath);
doTestResource(resource);
assertThat(resource).isEqualTo(new FileSystemResource(filePath));
}
@Test
void fileSystemResourceWithPlainPath() {
Resource resource = new FileSystemResource("core/io/Resource.class");
assertThat(new FileSystemResource("core/../core/io/./Resource.class")).isEqualTo(resource);
}
@Test
void urlResource() throws IOException {
Resource resource = new UrlResource(getClass().getResource("Resource.class"));
doTestResource(resource);
assertThat(resource).isEqualTo(new UrlResource(getClass().getResource("Resource.class")));
Resource resource2 = new UrlResource("file:core/io/Resource.class");
assertThat(new UrlResource("file:core/../core/io/./Resource.class")).isEqualTo(resource2);
assertThat(new UrlResource("file:/dir/test.txt?argh").getFilename()).isEqualTo("test.txt");
assertThat(new UrlResource("file:\\dir\\test.txt?argh").getFilename()).isEqualTo("test.txt");
assertThat(new UrlResource("file:\\dir/test.txt?argh").getFilename()).isEqualTo("test.txt");
}
private void doTestResource(Resource resource) throws IOException {
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void resourceIsValid(Resource resource) throws Exception {
assertThat(resource.getFilename()).isEqualTo("Resource.class");
assertThat(resource.getURL().getFile().endsWith("Resource.class")).isTrue();
assertThat(resource.exists()).isTrue();
assertThat(resource.isReadable()).isTrue();
assertThat(resource.contentLength() > 0).isTrue();
assertThat(resource.lastModified() > 0).isTrue();
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void resourceCreateRelative(Resource resource) throws Exception {
Resource relative1 = resource.createRelative("ClassPathResource.class");
assertThat(relative1.getFilename()).isEqualTo("ClassPathResource.class");
assertThat(relative1.getURL().getFile().endsWith("ClassPathResource.class")).isTrue();
@@ -182,7 +82,11 @@ class ResourceTests {
assertThat(relative1.isReadable()).isTrue();
assertThat(relative1.contentLength() > 0).isTrue();
assertThat(relative1.lastModified() > 0).isTrue();
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void resourceCreateRelativeWithFolder(Resource resource) throws Exception {
Resource relative2 = resource.createRelative("support/ResourcePatternResolver.class");
assertThat(relative2.getFilename()).isEqualTo("ResourcePatternResolver.class");
assertThat(relative2.getURL().getFile().endsWith("ResourcePatternResolver.class")).isTrue();
@@ -190,7 +94,11 @@ class ResourceTests {
assertThat(relative2.isReadable()).isTrue();
assertThat(relative2.contentLength() > 0).isTrue();
assertThat(relative2.lastModified() > 0).isTrue();
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void resourceCreateRelativeWithDotPath(Resource resource) throws Exception {
Resource relative3 = resource.createRelative("../SpringVersion.class");
assertThat(relative3.getFilename()).isEqualTo("SpringVersion.class");
assertThat(relative3.getURL().getFile().endsWith("SpringVersion.class")).isTrue();
@@ -198,7 +106,11 @@ class ResourceTests {
assertThat(relative3.isReadable()).isTrue();
assertThat(relative3.contentLength() > 0).isTrue();
assertThat(relative3.lastModified() > 0).isTrue();
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void resourceCreateRelativeUnknown(Resource resource) throws Exception {
Resource relative4 = resource.createRelative("X.class");
assertThat(relative4.exists()).isFalse();
assertThat(relative4.isReadable()).isFalse();
@@ -208,126 +120,323 @@ class ResourceTests {
relative4::lastModified);
}
@Test
void classPathResourceWithRelativePath() throws IOException {
Resource resource = new ClassPathResource("dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new ClassPathResource("dir/subdir"));
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void loadingMissingResourceFails(Resource resource) {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
resource.createRelative("X").getInputStream());
}
@Test
void fileSystemResourceWithRelativePath() throws IOException {
Resource resource = new FileSystemResource("dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new FileSystemResource("dir/subdir"));
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("resource")
void readingMissingResourceFails(Resource resource) {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
resource.createRelative("X").readableChannel());
}
@Test
void urlResourceWithRelativePath() throws IOException {
Resource resource = new UrlResource("file:dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new UrlResource("file:dir/subdir"));
private static Stream<Arguments> resource() throws URISyntaxException {
URL resourceClass = ResourceTests.class.getResource("Resource.class");
Path resourceClassFilePath = Paths.get(resourceClass.toURI());
return Stream.of(
Arguments.of(Named.of("ClassPathResource", new ClassPathResource("org/springframework/core/io/Resource.class"))),
Arguments.of(Named.of("ClassPathResource with ClassLoader", new ClassPathResource("org/springframework/core/io/Resource.class", ResourceTests.class.getClassLoader()))),
Arguments.of(Named.of("ClassPathResource with Class", new ClassPathResource("Resource.class", ResourceTests.class))),
Arguments.of(Named.of("FileSystemResource", new FileSystemResource(resourceClass.getFile()))),
Arguments.of(Named.of("FileSystemResource with File", new FileSystemResource(new File(resourceClass.getFile())))),
Arguments.of(Named.of("FileSystemResource with File path", new FileSystemResource(resourceClassFilePath))),
Arguments.of(Named.of("UrlResource", new UrlResource(resourceClass)))
);
}
@Test
void nonFileResourceExists() throws Exception {
URL url = new URL("https://spring.io/");
// Abort if spring.io is not reachable.
assumeTrue(urlIsReachable(url));
@Nested
class ByteArrayResourceTests {
Resource resource = new UrlResource(url);
assertThat(resource.exists()).isTrue();
}
private boolean urlIsReachable(URL url) {
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
connection.setReadTimeout(5_000);
return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
@Test
void hasContent() throws Exception {
Resource resource = new ByteArrayResource("testString".getBytes());
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isFalse();
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(new ByteArrayResource("testString".getBytes())).isEqualTo(resource);
}
catch (Exception ex) {
return false;
@Test
void isNotOpen() {
Resource resource = new ByteArrayResource("testString".getBytes());
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isFalse();
}
@Test
void hasDescription() {
Resource resource = new ByteArrayResource("testString".getBytes(), "my description");
assertThat(resource.getDescription().contains("my description")).isTrue();
}
}
@Nested
class InputStreamResourceTests {
@Test
void hasContent() throws Exception {
InputStream is = new ByteArrayInputStream("testString".getBytes());
Resource resource = new InputStreamResource(is);
String content = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
assertThat(content).isEqualTo("testString");
assertThat(new InputStreamResource(is)).isEqualTo(resource);
}
@Test
void isOpen() {
InputStream is = new ByteArrayInputStream("testString".getBytes());
Resource resource = new InputStreamResource(is);
assertThat(resource.exists()).isTrue();
assertThat(resource.isOpen()).isTrue();
}
@Test
void hasDescription() {
InputStream is = new ByteArrayInputStream("testString".getBytes());
Resource resource = new InputStreamResource(is, "my description");
assertThat(resource.getDescription().contains("my description")).isTrue();
}
}
@Test
void abstractResourceExceptions() throws Exception {
final String name = "test-resource";
Resource resource = new AbstractResource() {
@Override
public String getDescription() {
return name;
}
@Override
public InputStream getInputStream() throws IOException {
throw new FileNotFoundException();
}
};
@Nested
class ClassPathResourceTests {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(
resource::getURL)
.withMessageContaining(name);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(
resource::getFile)
.withMessageContaining(name);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
resource.createRelative("/testing"))
.withMessageContaining(name);
@Test
void equalsAndHashCode() {
Resource resource = new ClassPathResource("org/springframework/core/io/Resource.class");
Resource resource2 = new ClassPathResource("org/springframework/core/../core/io/./Resource.class");
Resource resource3 = new ClassPathResource("org/springframework/core/").createRelative("../core/io/./Resource.class");
assertThat(resource2).isEqualTo(resource);
assertThat(resource3).isEqualTo(resource);
// Check whether equal/hashCode works in a HashSet.
HashSet<Resource> resources = new HashSet<>();
resources.add(resource);
resources.add(resource2);
assertThat(resources.size()).isEqualTo(1);
}
@Test
void resourcesWithDifferentPathsAreEqual() {
Resource resource = new ClassPathResource("org/springframework/core/io/Resource.class", getClass().getClassLoader());
ClassPathResource sameResource = new ClassPathResource("org/springframework/core/../core/io/./Resource.class", getClass().getClassLoader());
assertThat(sameResource).isEqualTo(resource);
}
@Test
void relativeResourcesAreEqual() throws Exception {
Resource resource = new ClassPathResource("dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new ClassPathResource("dir/subdir"));
}
assertThat(resource.getFilename()).isNull();
}
@Test
void contentLength() throws IOException {
AbstractResource resource = new AbstractResource() {
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(new byte[] { 'a', 'b', 'c' });
@Nested
class FileSystemResourceTests {
@Test
void sameResourceIsEqual() {
String file = getClass().getResource("Resource.class").getFile();
Resource resource = new FileSystemResource(file);
assertThat(resource).isEqualTo(new FileSystemResource(file));
}
@Test
void sameResourceFromFileIsEqual() {
File file = new File(getClass().getResource("Resource.class").getFile());
Resource resource = new FileSystemResource(file);
assertThat(resource).isEqualTo(new FileSystemResource(file));
}
@Test
void sameResourceFromFilePathIsEqual() throws Exception {
Path filePath = Paths.get(getClass().getResource("Resource.class").toURI());
Resource resource = new FileSystemResource(filePath);
assertThat(resource).isEqualTo(new FileSystemResource(filePath));
}
@Test
void sameResourceFromDotPathIsEqual() {
Resource resource = new FileSystemResource("core/io/Resource.class");
assertThat(new FileSystemResource("core/../core/io/./Resource.class")).isEqualTo(resource);
}
@Test
void relativeResourcesAreEqual() throws Exception {
Resource resource = new FileSystemResource("dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new FileSystemResource("dir/subdir"));
}
@Test
void readableChannelProvidesContent() throws Exception {
Resource resource = new FileSystemResource(getClass().getResource("Resource.class").getFile());
try (ReadableByteChannel channel = resource.readableChannel()) {
ByteBuffer buffer = ByteBuffer.allocate((int) resource.contentLength());
channel.read(buffer);
buffer.rewind();
assertThat(buffer.limit() > 0).isTrue();
}
@Override
public String getDescription() {
return "";
}
};
assertThat(resource.contentLength()).isEqualTo(3L);
}
}
@Test
void readableChannel() throws IOException {
Resource resource = new FileSystemResource(getClass().getResource("Resource.class").getFile());
try (ReadableByteChannel channel = resource.readableChannel()) {
ByteBuffer buffer = ByteBuffer.allocate((int) resource.contentLength());
channel.read(buffer);
buffer.rewind();
assertThat(buffer.limit() > 0).isTrue();
@Nested
class UrlResourceTests {
private MockWebServer server = new MockWebServer();
@Test
void sameResourceWithRelativePathIsEqual() throws Exception {
Resource resource = new UrlResource("file:core/io/Resource.class");
assertThat(new UrlResource("file:core/../core/io/./Resource.class")).isEqualTo(resource);
}
@Test
void filenameIsExtractedFromFilePath() throws Exception {
assertThat(new UrlResource("file:/dir/test.txt?argh").getFilename()).isEqualTo("test.txt");
assertThat(new UrlResource("file:\\dir\\test.txt?argh").getFilename()).isEqualTo("test.txt");
assertThat(new UrlResource("file:\\dir/test.txt?argh").getFilename()).isEqualTo("test.txt");
}
@Test
void relativeResourcesAreEqual() throws Exception {
Resource resource = new UrlResource("file:dir/");
Resource relative = resource.createRelative("subdir");
assertThat(relative).isEqualTo(new UrlResource("file:dir/subdir"));
}
@Test
void missingRemoteResourceDoesNotExist() throws Exception {
String baseUrl = startServer();
UrlResource resource = new UrlResource(baseUrl + "/missing");
assertThat(resource.exists()).isFalse();
}
@Test
void remoteResourceExists() throws Exception {
String baseUrl = startServer();
UrlResource resource = new UrlResource(baseUrl + "/resource");
assertThat(resource.exists()).isTrue();
assertThat(resource.contentLength()).isEqualTo(6);
}
@Test
void canCustomizeHttpUrlConnectionForExists() throws Exception {
String baseUrl = startServer();
CustomResource resource = new CustomResource(baseUrl + "/resource");
assertThat(resource.exists()).isTrue();
RecordedRequest request = this.server.takeRequest();
assertThat(request.getMethod()).isEqualTo("HEAD");
assertThat(request.getHeader("Framework-Name")).isEqualTo("Spring");
}
@Test
void canCustomizeHttpUrlConnectionForRead() throws Exception {
String baseUrl = startServer();
CustomResource resource = new CustomResource(baseUrl + "/resource");
assertThat(resource.getInputStream()).hasContent("Spring");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getHeader("Framework-Name")).isEqualTo("Spring");
}
@AfterEach
void shutdown() throws Exception {
this.server.shutdown();
}
private String startServer() throws Exception {
this.server.setDispatcher(new ResourceDispatcher());
this.server.start();
return "http://localhost:" + this.server.getPort();
}
class CustomResource extends UrlResource {
public CustomResource(String path) throws MalformedURLException {
super(path);
}
@Override
protected void customizeConnection(HttpURLConnection con) throws IOException {
con.setRequestProperty("Framework-Name", "Spring");
}
}
class ResourceDispatcher extends Dispatcher {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
if (request.getPath().equals("/resource")) {
switch (request.getMethod()) {
case "HEAD":
return new MockResponse()
.addHeader("Content-Length", "6");
case "GET":
return new MockResponse()
.addHeader("Content-Length", "6")
.addHeader("Content-Type", "text/plain")
.setBody("Spring");
}
}
return new MockResponse().setResponseCode(404);
}
}
}
@Test
void inputStreamNotFoundOnFileSystemResource() throws IOException {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
new FileSystemResource(getClass().getResource("Resource.class").getFile()).createRelative("X").getInputStream());
}
@Nested
class AbstractResourceTests {
@Test
void readableChannelNotFoundOnFileSystemResource() throws IOException {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
new FileSystemResource(getClass().getResource("Resource.class").getFile()).createRelative("X").readableChannel());
}
@Test
void missingResourceIsNotReadable() {
final String name = "test-resource";
@Test
void inputStreamNotFoundOnClassPathResource() throws IOException {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
new ClassPathResource("Resource.class", getClass()).createRelative("X").getInputStream());
}
Resource resource = new AbstractResource() {
@Override
public String getDescription() {
return name;
}
@Override
public InputStream getInputStream() throws IOException {
throw new FileNotFoundException();
}
};
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(resource::getURL)
.withMessageContaining(name);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(resource::getFile)
.withMessageContaining(name);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
resource.createRelative("/testing")).withMessageContaining(name);
assertThat(resource.getFilename()).isNull();
}
@Test
void hasContentLength() throws Exception {
AbstractResource resource = new AbstractResource() {
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(new byte[] {'a', 'b', 'c'});
}
@Override
public String getDescription() {
return "";
}
};
assertThat(resource.contentLength()).isEqualTo(3L);
}
@Test
void readableChannelNotFoundOnClassPathResource() throws IOException {
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
new ClassPathResource("Resource.class", getClass()).createRelative("X").readableChannel());
}
}
@@ -990,6 +990,27 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
}
}
@ParameterizedDataBufferAllocatingTest
void propagateContextPath(DataBufferFactory bufferFactory) throws IOException {
Path path = Paths.get(this.resource.getURI());
Path out = Files.createTempFile("data-buffer-utils-tests", ".tmp");
Flux<Void> result = DataBufferUtils.read(path, bufferFactory, 1024, StandardOpenOption.READ)
.transformDeferredContextual((f, ctx) -> {
assertThat(ctx.getOrDefault("key", "EMPTY")).isEqualTo("TEST");
return f;
})
.transform(f -> DataBufferUtils.write(f, out))
.transformDeferredContextual((f, ctx) -> {
assertThat(ctx.getOrDefault("key", "EMPTY")).isEqualTo("TEST");
return f;
})
.contextWrite(Context.of("key", "TEST"));
StepVerifier.create(result)
.verifyComplete();
}
private static class ZeroDemandSubscriber extends BaseSubscriber<DataBuffer> {
@Override
+2 -2
View File
@@ -31,10 +31,10 @@ Syntax
- Need to agree on a standard date format for 'default' processing of dates. Currently it is:
formatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.UK);
// this is something of this format: "Wed, 4 Jul 2001 12:08:56 GMT"
// https://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html
// https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
- See LiteralTests for Date (4,5,6) - should date take an expression rather than be hardcoded in the grammar
to take 2 strings only?
- when doing arithmetic, eg. 8.4 / 4 and the user asks for an Integer return type - do we silently coerce or
say we cannot as it won't fit into an int? (see OperatorTests.testMathOperatorDivide04)
- Is $index within projection/selection useful or just cute?
- All reals are represented as Doubles (so 1.25f is held internally as a double, can be converted to float when required though) - is that ok?
- All reals are represented as Doubles (so 1.25f is held internally as a double, can be converted to float when required though) - is that ok?
@@ -19,7 +19,7 @@ package org.springframework.expression;
import org.springframework.lang.Nullable;
/**
* Super class for exceptions that can occur whilst processing expressions.
* Superclass for exceptions that can occur whilst processing expressions.
*
* @author Andy Clement
* @author Phillip Webb
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
///CLOVER:OFF
/**
* Spring Security scenarios from https://wiki.springsource.com/display/SECURITY/Spring+Security+Expression-based+Authorization
* Spring Security scenarios from https://docs.spring.io/spring-security/reference/servlet/authorization/expression-based.html
*
* @author Andy Clement
*/
@@ -29,7 +29,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Convenient super class for JDBC-based data access objects.
* Convenient superclass for JDBC-based data access objects.
*
* <p>Requires a {@link javax.sql.DataSource} to be set, providing a
* {@link org.springframework.jdbc.core.JdbcTemplate} based on it to
@@ -25,7 +25,7 @@ package org.springframework.jdbc.datasource.embedded;
*/
public enum EmbeddedDatabaseType {
/** The <a href="http://hsqldb.org">Hypersonic</a> Embedded Java SQL Database. */
/** The <a href="https://hsqldb.org">Hypersonic</a> Embedded Java SQL Database. */
HSQL,
/** The <a href="https://h2database.com">H2</a> Embedded Java SQL Database Engine. */
@@ -27,7 +27,7 @@ import org.springframework.jms.core.JmsTemplate;
import org.springframework.lang.Nullable;
/**
* Convenient super class for application classes that need JMS access.
* Convenient superclass for application classes that need JMS access.
*
* <p>Requires a ConnectionFactory or a JmsTemplate instance to be set.
* It will create its own JmsTemplate if a ConnectionFactory is passed in.
@@ -29,7 +29,7 @@ import org.springframework.util.Assert;
/**
* Implementation of {@link MessageConverter} that can read and write JSON
* using the <a href="http://json-b.net/">JSON Binding API</a>.
* using the <a href="https://javaee.github.io/jsonb-spec/">JSON Binding API</a>.
*
* @author Juergen Hoeller
* @since 5.3
@@ -262,7 +262,7 @@ public abstract class AbstractBrokerMessageHandler
* may still independently alternate between being on and off depending on the
* concrete subclass implementation.
* <p>Application components may implement
* {@code org.springframework.context.ApplicationListener&lt;BrokerAvailabilityEvent&gt;}
* {@code org.springframework.context.ApplicationListener<BrokerAvailabilityEvent>}
* to receive notifications when broker becomes available and unavailable.
*/
public boolean isBrokerAvailable() {
@@ -461,6 +461,9 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
if (registerDefaults) {
converters.add(new StringMessageConverter());
converters.add(new ByteArrayMessageConverter());
if (kotlinSerializationJsonPresent) {
converters.add(new KotlinSerializationJsonMessageConverter());
}
if (jackson2Present) {
converters.add(createJacksonConverter());
}
@@ -470,9 +473,6 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
else if (jsonbPresent) {
converters.add(new JsonbMessageConverter());
}
else if (kotlinSerializationJsonPresent) {
converters.add(new KotlinSerializationJsonMessageConverter());
}
}
return new CompositeMessageConverter(converters);
}
@@ -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.
@@ -18,7 +18,6 @@ package org.springframework.messaging.simp.stomp;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -27,6 +26,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.apache.commons.logging.Log;
@@ -441,7 +441,7 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
String receiptId = headers.getReceiptId();
ReceiptHandler handler = this.receiptHandlers.get(receiptId);
if (handler != null) {
handler.handleReceiptReceived();
handler.handleReceiptReceived(headers);
}
else if (logger.isDebugEnabled()) {
logger.debug("No matching receipt: " + accessor.getDetailedLogMessage(message.getPayload()));
@@ -546,7 +546,7 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
@Nullable
private final String receiptId;
private final List<Runnable> receiptCallbacks = new ArrayList<>(2);
private final List<Consumer<StompHeaders>> receiptCallbacks = new ArrayList<>(2);
private final List<Runnable> receiptLostCallbacks = new ArrayList<>(2);
@@ -556,6 +556,9 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
@Nullable
private Boolean result;
@Nullable
private StompHeaders receiptHeaders;
public ReceiptHandler(@Nullable String receiptId) {
this.receiptId = receiptId;
if (receiptId != null) {
@@ -578,64 +581,80 @@ public class DefaultStompSession implements ConnectionHandlingStompSession {
@Override
public void addReceiptTask(Runnable task) {
addTask(task, true);
addReceiptTask(headers -> task.run());
}
@Override
public void addReceiptTask(Consumer<StompHeaders> task) {
Assert.notNull(this.receiptId, "Set autoReceiptEnabled to track receipts or add a 'receiptId' header");
synchronized (this) {
if (this.result != null) {
if (this.result) {
task.accept(this.receiptHeaders);
}
}
else {
this.receiptCallbacks.add(task);
}
}
}
@Override
public void addReceiptLostTask(Runnable task) {
addTask(task, false);
}
private void addTask(Runnable task, boolean successTask) {
Assert.notNull(this.receiptId,
"To track receipts, set autoReceiptEnabled=true or add 'receiptId' header");
synchronized (this) {
if (this.result != null && this.result == successTask) {
invoke(Collections.singletonList(task));
if (this.result != null) {
if (!this.result) {
task.run();
}
}
else {
if (successTask) {
this.receiptCallbacks.add(task);
}
else {
this.receiptLostCallbacks.add(task);
}
this.receiptLostCallbacks.add(task);
}
}
}
private void invoke(List<Runnable> callbacks) {
for (Runnable runnable : callbacks) {
try {
runnable.run();
}
catch (Throwable ex) {
// ignore
}
}
}
public void handleReceiptReceived() {
handleInternal(true);
public void handleReceiptReceived(StompHeaders receiptHeaders) {
handleInternal(true, receiptHeaders);
}
public void handleReceiptNotReceived() {
handleInternal(false);
handleInternal(false, null);
}
private void handleInternal(boolean result) {
private void handleInternal(boolean result, @Nullable StompHeaders receiptHeaders) {
synchronized (this) {
if (this.result != null) {
return;
}
this.result = result;
invoke(result ? this.receiptCallbacks : this.receiptLostCallbacks);
this.receiptHeaders = receiptHeaders;
if (result) {
this.receiptCallbacks.forEach(consumer -> {
try {
consumer.accept(this.receiptHeaders);
}
catch (Throwable ex) {
// ignore
}
});
}
else {
this.receiptLostCallbacks.forEach(task -> {
try {
task.run();
}
catch (Throwable ex) {
// ignore
}
});
}
DefaultStompSession.this.receiptHandlers.remove(this.receiptId);
if (this.future != null) {
this.future.cancel(true);
}
}
}
}
@@ -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,6 +16,8 @@
package org.springframework.messaging.simp.stomp;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
/**
@@ -139,16 +141,27 @@ public interface StompSession {
/**
* Task to invoke when a receipt is received.
* @param task the task to invoke
* @throws java.lang.IllegalArgumentException if the receiptId is {@code null}
*/
void addReceiptTask(Runnable runnable);
void addReceiptTask(Runnable task);
/**
* Variant of {@link #addReceiptTask(Runnable)} with a {@link Consumer}
* of the headers from the {@code RECEIPT} frame.
* @param task the consumer to invoke
* @throws java.lang.IllegalArgumentException if the receiptId is {@code null}
* @since 5.3.23
*/
void addReceiptTask(Consumer<StompHeaders> task);
/**
* Task to invoke when a receipt is not received in the configured time.
* @param task the task to invoke
* @throws java.lang.IllegalArgumentException if the receiptId is {@code null}
* @see org.springframework.messaging.simp.stomp.StompClientSupport#setReceiptTimeLimit(long)
*/
void addReceiptLostTask(Runnable runnable);
void addReceiptLostTask(Runnable task);
}
@@ -39,6 +39,7 @@ import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.ContentTypeResolver;
import org.springframework.messaging.converter.DefaultContentTypeResolver;
import org.springframework.messaging.converter.KotlinSerializationJsonMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
@@ -282,12 +283,13 @@ public class MessageBrokerConfigurationTests {
CompositeMessageConverter compositeConverter = config.brokerMessageConverter();
List<MessageConverter> converters = compositeConverter.getConverters();
assertThat(converters).hasSize(3);
assertThat(converters).hasSize(4);
assertThat(converters.get(0)).isInstanceOf(StringMessageConverter.class);
assertThat(converters.get(1)).isInstanceOf(ByteArrayMessageConverter.class);
assertThat(converters.get(2)).isInstanceOf(MappingJackson2MessageConverter.class);
assertThat(converters.get(2)).isInstanceOf(KotlinSerializationJsonMessageConverter.class);
assertThat(converters.get(3)).isInstanceOf(MappingJackson2MessageConverter.class);
ContentTypeResolver resolver = ((MappingJackson2MessageConverter) converters.get(2)).getContentTypeResolver();
ContentTypeResolver resolver = ((MappingJackson2MessageConverter) converters.get(3)).getContentTypeResolver();
assertThat(((DefaultContentTypeResolver) resolver).getDefaultMimeType()).isEqualTo(MimeTypeUtils.APPLICATION_JSON);
}
@@ -340,11 +342,12 @@ public class MessageBrokerConfigurationTests {
};
CompositeMessageConverter compositeConverter = config.brokerMessageConverter();
assertThat(compositeConverter.getConverters()).hasSize(4);
assertThat(compositeConverter.getConverters()).hasSize(5);
Iterator<MessageConverter> iterator = compositeConverter.getConverters().iterator();
assertThat(iterator.next()).isEqualTo(testConverter);
assertThat(iterator.next()).isInstanceOf(StringMessageConverter.class);
assertThat(iterator.next()).isInstanceOf(ByteArrayMessageConverter.class);
assertThat(iterator.next()).isInstanceOf(KotlinSerializationJsonMessageConverter.class);
assertThat(iterator.next()).isInstanceOf(MappingJackson2MessageConverter.class);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -576,22 +576,30 @@ public class DefaultStompSessionTests {
this.session.setTaskScheduler(mock(TaskScheduler.class));
AtomicReference<Boolean> received = new AtomicReference<>();
AtomicReference<StompHeaders> receivedHeaders = new AtomicReference<>();
StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/foo");
headers.setReceipt("my-receipt");
Subscription subscription = this.session.subscribe(headers, mock(StompFrameHandler.class));
subscription.addReceiptTask(() -> received.set(true));
subscription.addReceiptTask(receiptHeaders -> {
received.set(true);
receivedHeaders.set(receiptHeaders);
});
assertThat((Object) received.get()).isNull();
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.RECEIPT);
accessor.setReceiptId("my-receipt");
accessor.setNativeHeader("foo", "bar");
accessor.setLeaveMutable(true);
this.session.handleMessage(MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders()));
assertThat(received.get()).isNotNull();
assertThat(received.get()).isTrue();
assertThat(receivedHeaders.get()).isNotNull();
assertThat(receivedHeaders.get().get("foo").size()).isEqualTo(1);
assertThat(receivedHeaders.get().get("foo").get(0)).isEqualTo("bar");
}
@Test
@@ -600,6 +608,7 @@ public class DefaultStompSessionTests {
this.session.setTaskScheduler(mock(TaskScheduler.class));
AtomicReference<Boolean> received = new AtomicReference<>();
AtomicReference<StompHeaders> receivedHeaders = new AtomicReference<>();
StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/foo");
@@ -608,13 +617,20 @@ public class DefaultStompSessionTests {
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.RECEIPT);
accessor.setReceiptId("my-receipt");
accessor.setNativeHeader("foo", "bar");
accessor.setLeaveMutable(true);
this.session.handleMessage(MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders()));
subscription.addReceiptTask(() -> received.set(true));
subscription.addReceiptTask(receiptHeaders -> {
received.set(true);
receivedHeaders.set(receiptHeaders);
});
assertThat(received.get()).isNotNull();
assertThat(received.get()).isTrue();
assertThat(receivedHeaders.get()).isNotNull();
assertThat(receivedHeaders.get().get("foo").size()).isEqualTo(1);
assertThat(receivedHeaders.get().get("foo").get(0)).isEqualTo("bar");
}
@Test
@@ -26,7 +26,7 @@ import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.util.Assert;
/**
* Convenient super class for Hibernate-based data access objects.
* Convenient superclass for Hibernate-based data access objects.
*
* <p>Requires a {@link SessionFactory} to be set, providing a
* {@link org.springframework.orm.hibernate5.HibernateTemplate} based on it to
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,40 +73,6 @@ public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContext
this.contextCache = contextCache;
}
/**
* Get the {@link ContextCache} used by this context loader delegate.
*/
protected ContextCache getContextCache() {
return this.contextCache;
}
/**
* Load the {@code ApplicationContext} for the supplied merged context configuration.
* <p>Supports both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
* @throws Exception if an error occurs while loading the application context
*/
protected ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. " +
"Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
ApplicationContext applicationContext;
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
}
else {
String[] locations = mergedContextConfiguration.getLocations();
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. " +
"Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
applicationContext = contextLoader.loadContext(locations);
}
return applicationContext;
}
@Override
public boolean isContextLoaded(MergedContextConfiguration mergedContextConfiguration) {
@@ -152,4 +118,34 @@ public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContext
}
}
/**
* Get the {@link ContextCache} used by this context loader delegate.
*/
protected ContextCache getContextCache() {
return this.contextCache;
}
/**
* Load the {@code ApplicationContext} for the supplied merged context configuration.
* <p>Supports both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
* @throws Exception if an error occurs while loading the application context
*/
protected ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. " +
"Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
if (contextLoader instanceof SmartContextLoader) {
return ((SmartContextLoader) contextLoader).loadContext(mergedContextConfiguration);
}
else {
String[] locations = mergedContextConfiguration.getLocations();
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. " +
"Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
return contextLoader.loadContext(locations);
}
}
}
@@ -200,9 +200,10 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* Factory method for creating the {@link GenericApplicationContext} used by
* this {@code ContextLoader}.
* <p>The default implementation creates a {@code GenericApplicationContext}
* using the default constructor. This method may get overridden e.g. to use
* a custom context subclass or to create a {@code GenericApplicationContext}
* with a custom {@link DefaultListableBeanFactory} implementation.
* using the default constructor. This method may be overridden &mdash; for
* example, to use a custom context subclass or to create a
* {@code GenericApplicationContext} with a custom
* {@link DefaultListableBeanFactory} implementation.
* @return a newly instantiated {@code GenericApplicationContext}
* @since 5.2.9
*/
@@ -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.
@@ -114,7 +114,7 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
validateMergedContextConfiguration(webMergedConfig);
GenericWebApplicationContext context = new GenericWebApplicationContext();
GenericWebApplicationContext context = createContext();
ApplicationContext parent = mergedConfig.getParentApplicationContext();
if (parent != null) {
@@ -145,6 +145,21 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
// no-op
}
/**
* Factory method for creating the {@link GenericWebApplicationContext} used
* by this {@code ContextLoader}.
* <p>The default implementation creates a {@code GenericWebApplicationContext}
* using the default constructor. This method may be overridden &mdash; for
* example, to use a custom context subclass or to create a
* {@code GenericWebApplicationContext} with a custom
* {@link DefaultListableBeanFactory} implementation.
* @return a newly instantiated {@code GenericWebApplicationContext}
* @since 5.3.23
*/
protected GenericWebApplicationContext createContext() {
return new GenericWebApplicationContext();
}
/**
* Configures web resources for the supplied web application context (WAC).
* <h4>Implementation Details</h4>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,26 +19,25 @@ package org.springframework.test.context.web.socket;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.ClassUtils;
/**
* {@link ContextCustomizerFactory} which creates a {@link MockServerContainerContextCustomizer}
* if WebSocket support is present in the classpath and the test class is annotated
* with {@code @WebAppConfiguration}.
* if WebSocket support is present in the classpath and the test class or one of
* its enclosing classes is annotated or meta-annotated with
* {@link WebAppConfiguration @WebAppConfiguration}.
*
* @author Sam Brannen
* @since 4.3.1
*/
class MockServerContainerContextCustomizerFactory implements ContextCustomizerFactory {
private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME =
"org.springframework.test.context.web.WebAppConfiguration";
private static final String MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME =
"org.springframework.test.context.web.socket.MockServerContainerContextCustomizer";
@@ -68,8 +67,7 @@ class MockServerContainerContextCustomizerFactory implements ContextCustomizerFa
}
private static boolean isAnnotatedWithWebAppConfiguration(Class<?> testClass) {
return (AnnotatedElementUtils.findMergedAnnotationAttributes(testClass,
WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, false, false) != null);
return TestContextAnnotationUtils.hasAnnotation(testClass, WebAppConfiguration.class);
}
}
@@ -255,7 +255,7 @@ public class ContentRequestMatchers {
* two are "similar" - i.e. they contain the same elements and attributes
* regardless of order.
* <p>Use of this matcher assumes the
* <a href="http://xmlunit.sourceforge.net/">XMLUnit</a> library is available.
* <a href="https://www.xmlunit.org/">XMLUnit</a> library is available.
* @param expectedXmlContent the expected XML content
*/
public RequestMatcher xml(String expectedXmlContent) {
@@ -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.
@@ -56,9 +56,11 @@ import org.springframework.web.servlet.DispatcherServlet;
* MockMvc mockMvc = webAppContextSetup(wac).build();
*
* mockMvc.perform(get("/form"))
* .andExpect(status().isOk())
* .andExpect(content().mimeType("text/html"))
* .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
* .andExpectAll(
* status().isOk(),
* content().contentType("text/html"),
* forwardedUrl("/WEB-INF/layouts/main.jsp")
* );
* </pre>
*
* @author Rossen Stoyanchev
@@ -40,7 +40,7 @@ import org.springframework.util.Assert;
/**
* {@code MockMvcWebConnection} enables {@link MockMvc} to transform a
* {@link WebRequest} into a {@link WebResponse}.
* <p>This is the core integration with <a href="http://htmlunit.sourceforge.net/">HtmlUnit</a>.
* <p>This is the core integration with <a href="https://htmlunit.sourceforge.io/">HtmlUnit</a>.
* <p>Example usage can be seen below.
*
* <pre class="code">
@@ -160,7 +160,7 @@ public class ContentResultMatchers {
* are "similar" - i.e. they contain the same elements and attributes
* regardless of order.
* <p>Use of this matcher requires the <a
* href="http://xmlunit.sourceforge.net/">XMLUnit</a> library.
* href="https://www.xmlunit.org/">XMLUnit</a> library.
* @param xmlContent the expected XML content
* @see MockMvcResultMatchers#xpath(String, Object...)
* @see MockMvcResultMatchers#xpath(String, Map, Object...)
@@ -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.
@@ -18,6 +18,7 @@ package org.springframework.test.context.web.socket;
import javax.websocket.server.ServerContainer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,6 +47,20 @@ class WebSocketServletServerContainerFactoryBeanTests {
assertThat(serverContainer.getDefaultMaxTextMessageBufferSize()).isEqualTo(42);
}
/*
* @Nested test class to verify that the MockServerContainerContextCustomizerFactory
* properly supports finding @WebAppConfiguration on an enclosing class.
*/
@Nested
class NestedTests {
@Test // gh-29037
void servletServerContainerFactoryBeanSupport(@Autowired ServerContainer serverContainer) {
assertThat(serverContainer.getDefaultMaxTextMessageBufferSize()).isEqualTo(42);
}
}
@Configuration
@EnableWebSocket
@@ -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.
@@ -25,7 +25,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Convenient super class for CCI-based data access objects.
* Convenient superclass for CCI-based data access objects.
*
* <p>Requires a {@link javax.resource.cci.ConnectionFactory} to be set,
* providing a {@link org.springframework.jca.cci.core.CciTemplate} based
@@ -29,7 +29,7 @@ import org.springframework.util.StringUtils;
* {@link TransactionAttributeEditor} in this package.
*
* <p>Strings are in property syntax, with the form:<br>
* {@code FQCN.methodName=&lt;transaction attribute string&gt;}
* {@code FQCN.methodName=<transaction attribute string>}
*
* <p>For example:<br>
* {@code com.mycompany.mycode.MyClass.myMethod=PROPAGATION_MANDATORY,ISOLATION_DEFAULT}
@@ -28,6 +28,7 @@ import java.util.function.Consumer;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.util.UriTemplateHandler;
/**
* Extension of {@link HttpEntity} that also exposes the HTTP method and the
@@ -158,11 +159,23 @@ public class RequestEntity<T> extends HttpEntity<T> {
}
/**
* Return the URL of the request.
* Return the {@link URI} for the target HTTP endpoint.
* <p><strong>Note:</strong> This method raises
* {@link UnsupportedOperationException} if the {@code RequestEntity} was
* created with a URI template and variables rather than with a {@link URI}
* instance. This is because a URI cannot be created without further input
* on how to expand template and encode the URI. In such cases, the
* {@code URI} is prepared by the
* {@link org.springframework.web.client.RestTemplate} with the help of the
* {@link UriTemplateHandler} it is configured with.
*/
public URI getUrl() {
if (this.url == null) {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException(
"The RequestEntity was created with a URI template and variables, " +
"and there is not enough information on how to correctly expand and " +
"encode the URI template. This will be done by the RestTemplate instead " +
"with help from the UriTemplateHandler it is configured with.");
}
return this.url;
}
@@ -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.
@@ -238,13 +238,13 @@ public final class ResponseCookie extends HttpCookie {
}
@Override
public ResponseCookieBuilder domain(String domain) {
public ResponseCookieBuilder domain(@Nullable String domain) {
this.domain = initDomain(domain);
return this;
}
@Nullable
private String initDomain(String domain) {
private String initDomain(@Nullable String domain) {
if (lenient && StringUtils.hasLength(domain)) {
String str = domain.trim();
if (str.startsWith("\"") && str.endsWith("\"")) {
@@ -257,7 +257,7 @@ public final class ResponseCookie extends HttpCookie {
}
@Override
public ResponseCookieBuilder path(String path) {
public ResponseCookieBuilder path(@Nullable String path) {
this.path = path;
return this;
}
@@ -312,12 +312,12 @@ public final class ResponseCookie extends HttpCookie {
/**
* Set the cookie "Path" attribute.
*/
ResponseCookieBuilder path(String path);
ResponseCookieBuilder path(@Nullable String path);
/**
* Set the cookie "Domain" attribute.
*/
ResponseCookieBuilder domain(String domain);
ResponseCookieBuilder domain(@Nullable String domain);
/**
* Add the "Secure" attribute to the cookie.
@@ -746,8 +746,15 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
@Override
public void partComplete(boolean finalPart) {
this.completed = true;
this.finalPart = finalPart;
State state = PartGenerator.this.state.get();
// writeComplete might have changed our state to IdleFileState
if (state != this) {
state.partComplete(finalPart);
}
else {
this.completed = true;
this.finalPart = finalPart;
}
}
public void writeBuffer(DataBuffer dataBuffer) {
@@ -771,14 +778,16 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
private void writeComplete() {
IdleFileState newState = new IdleFileState(this);
if (this.completed) {
newState.partComplete(this.finalPart);
}
else if (this.disposed) {
if (this.disposed) {
newState.dispose();
}
else if (changeState(this, newState)) {
requestToken();
if (this.completed) {
newState.partComplete(this.finalPart);
}
else {
requestToken();
}
}
else {
MultipartUtils.closeChannel(this.channel);
@@ -553,7 +553,7 @@ public class Jackson2ObjectMapperBuilder {
/**
* Variant of {@link #modules(Module...)} with a {@link Consumer} for full
* control over the underlying list of modules.
* @since 6.0
* @since 5.3.22
* @see #modules(Module...)
* @see #modules(List)
* @see com.fasterxml.jackson.databind.Module
@@ -589,7 +589,7 @@ public class Jackson2ObjectMapperBuilder {
/**
* Variant of {@link #modulesToInstall(Module...)} with a {@link Consumer}
* for full control over the underlying list of modules.
* @since 6.0
* @since 5.3.22
* @see #modulesToInstall(Module...)
* @see #modulesToInstall(Class...)
* @see com.fasterxml.jackson.databind.Module
@@ -31,7 +31,7 @@ import org.springframework.util.Assert;
/**
* Implementation of {@link org.springframework.http.converter.HttpMessageConverter}
* that can read and write JSON using the
* <a href="http://json-b.net/">JSON Binding API</a>.
* <a href="https://javaee.github.io/jsonb-spec/">JSON Binding API</a>.
*
* <p>This converter can be used to bind to typed beans or untyped {@code HashMap}s.
* By default, it supports {@code application/json} and {@code application/*+json} with
@@ -87,6 +87,9 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv
}
}
if (kotlinSerializationJsonPresent) {
addPartConverter(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
@@ -96,9 +99,6 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
else if (kotlinSerializationJsonPresent) {
addPartConverter(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2XmlPresent && !shouldIgnoreXml) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
@@ -56,8 +56,6 @@ import org.springframework.util.StringUtils;
*/
public class ServletServerHttpRequest implements ServerHttpRequest {
protected static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
protected static final Charset FORM_CHARSET = StandardCharsets.UTF_8;
@@ -231,7 +229,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
private static boolean isFormPost(HttpServletRequest request) {
String contentType = request.getContentType();
return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&
return (contentType != null && contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE) &&
HttpMethod.POST.matches(request.getMethod()));
}
@@ -174,6 +174,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
}
if (kotlinSerializationJsonPresent) {
this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
@@ -183,9 +186,6 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
}
else if (kotlinSerializationJsonPresent) {
this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
@@ -24,7 +24,7 @@ import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
/**
* Convenient super class for application classes that need REST access.
* Convenient superclass for application classes that need REST access.
*
* <p>Requires a {@link ClientHttpRequestFactory} or a {@link RestTemplate} instance to be set.
*
@@ -28,7 +28,7 @@ import org.springframework.lang.Nullable;
* this class which {@link RequestAttributes} scope to read attributes from.
*
* <p>Subclasses may wish to override the {@link #get} and {@link #remove}
* methods to add synchronization around the call back into this super class.
* methods to add synchronization around the call back into this superclass.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,6 +95,8 @@ public class StandardServletEnvironment extends StandardEnvironment implements C
* {@link StubPropertySource stubs} at this stage, and will be
* {@linkplain #initPropertySources(ServletContext, ServletConfig) fully initialized}
* once the actual {@link ServletContext} object becomes available.
* <p>Addition of {@value #JNDI_PROPERTY_SOURCE_NAME} can be disabled with
* {@link JndiLocatorDelegate#IGNORE_JNDI_PROPERTY_NAME}.
* @see StandardEnvironment#customizePropertySources
* @see org.springframework.core.env.AbstractEnvironment#customizePropertySources
* @see ServletConfigPropertySource
@@ -16,6 +16,8 @@
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
@@ -44,9 +46,11 @@ final class RelativeRedirectResponseWrapper extends HttpServletResponseWrapper {
@Override
public void sendRedirect(String location) {
public void sendRedirect(String location) throws IOException {
resetBuffer();
setStatus(this.redirectStatus.value());
setHeader(HttpHeaders.LOCATION, location);
flushBuffer();
}
@@ -128,7 +128,7 @@ public final class HandlerTypePredicate implements Predicate<Class<?>> {
/**
* Match handlers that are assignable to a given type.
* @param types one or more handler super types
* @param types one or more handler supertypes
*/
public static HandlerTypePredicate forAssignableType(Class<?>... types) {
return new Builder().assignableType(types).build();
@@ -187,7 +187,7 @@ public final class HandlerTypePredicate implements Predicate<Class<?>> {
/**
* Match handlers that are assignable to a given type.
* @param types one or more handler super types
* @param types one or more handler supertypes
*/
public Builder assignableType(Class<?>... types) {
this.assignableTypes.addAll(Arrays.asList(types));
@@ -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.
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
/**
@@ -55,9 +56,6 @@ import org.springframework.lang.Nullable;
*/
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
private final ByteArrayOutputStream cachedContent;
@Nullable
@@ -152,7 +150,7 @@ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
private boolean isFormPost() {
String contentType = getContentType();
return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&
return (contentType != null && contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE) &&
HttpMethod.POST.matches(getMethod()));
}
@@ -21,7 +21,7 @@ package org.springframework.web.util;
* Escapes based on the JavaScript 1.5 recommendation.
*
* <p>Reference:
* <a href="https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Values,_variables,_and_literals#String_literals">
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#string_literals">
* JavaScript Guide</a> on Mozilla Developer Network.
*
* @author Juergen Hoeller
@@ -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.
@@ -628,17 +628,17 @@ public abstract class WebUtils {
* following algorithm:
* <ol>
* <li>Try to get the parameter value using just the given <i>logical</i> name.
* This handles parameters of the form <tt>logicalName = value</tt>. For normal
* This handles parameters of the form {@code logicalName = value}. For normal
* parameters, e.g. submitted using a hidden HTML form field, this will return
* the requested value.</li>
* <li>Try to obtain the parameter value from the parameter name, where the
* parameter name in the request is of the form <tt>logicalName_value = xyz</tt>
* parameter name in the request is of the form {@code logicalName_value = xyz}
* with "_" being the configured delimiter. This deals with parameter values
* submitted using an HTML form submit button.</li>
* <li>If the value obtained in the previous step has a ".x" or ".y" suffix,
* remove that. This handles cases where the value was submitted using an
* HTML form image button. In this case the parameter in the request would
* actually be of the form <tt>logicalName_value.x = 123</tt>. </li>
* actually be of the form {@code logicalName_value.x = 123}.</li>
* </ol>
* @param parameters the available parameter map
* @param name the <i>logical</i> name of the request parameter
@@ -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.
@@ -192,8 +192,8 @@ class InternalPathPatternParser {
* Just hit a ':' and want to jump over the regex specification for this
* variable. pos will be pointing at the ':', we want to skip until the }.
* <p>
* Nested {...} pairs don't have to be escaped: <tt>/abc/{var:x{1,2}}/def</tt>
* <p>An escaped } will not be treated as the end of the regex: <tt>/abc/{var:x\\{y:}/def</tt>
* Nested {...} pairs don't have to be escaped: <code>/abc/{var:x{1,2}}/def</code>
* <p>An escaped } will not be treated as the end of the regex: <code>/abc/{var:x\\{y:}/def</code>
* <p>A separator that should not indicate the end of the regex can be escaped:
*/
private void skipCaptureRegex() {
@@ -26,9 +26,11 @@ import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A regex path element. Used to represent any complicated element of the path.
* For example in '<tt>/foo/&ast;_&ast;/&ast;_{foobar}</tt>' both <tt>*_*</tt> and <tt>*_{foobar}</tt>
* are {@link RegexPathElement} path elements. Derived from the general
* {@link org.springframework.util.AntPathMatcher} approach.
*
* <p>For example in '<code>/foo/&#42;_&#42;/&#42;_{foobar}</code>' both {@code *_*}
* and <code>*_{foobar}</code> are {@link RegexPathElement regex path elements}.
*
* <p>Derived from the general {@link org.springframework.util.AntPathMatcher} approach.
*
* @author Andy Clement
* @since 5.0
@@ -51,6 +51,8 @@ import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.DefaultUriBuilderFactory;
@@ -103,6 +105,16 @@ class RestTemplateTests {
template.setErrorHandler(errorHandler);
}
@Test // gh-29008
void defaultMessageConvertersWithKotlinSerialization() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> httpMessageConverters = restTemplate.getMessageConverters();
assertThat(httpMessageConverters).extracting("class").containsOnlyOnce(
KotlinSerializationJsonHttpMessageConverter.class,
MappingJackson2HttpMessageConverter.class
);
}
@Test
void constructorPreconditions() {
assertThatIllegalArgumentException()

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