Compare commits

..

89 Commits

Author SHA1 Message Date
Spring Builds 29185a3d28 Release v5.3.14 2021-12-16 08:35:04 +00:00
Juergen Hoeller 31a4c274b7 Upgrade to SnakeYAML 1.30, MockK 1.12.1, Jetty Reactive HttpClient 1.1.10 2021-12-15 21:51:05 +01:00
Juergen Hoeller d665977787 Polishing 2021-12-15 21:50:30 +01:00
Juergen Hoeller 0b6a54dcc7 Upgrade to R2DBC Arabba-SR11, Kotlin 1.5.32, Jackson 2.12.6 2021-12-15 17:58:59 +01:00
Juergen Hoeller 1885ab3e07 Polishing 2021-12-15 17:58:28 +01:00
Stephane Nicoll de10bb69cb Stop resolving AsyncConfigurer instances eagerly
Closes gh-27808
2021-12-15 10:53:09 +01:00
Stephane Nicoll c7642422c3 Stop resolving CachingConfigurer instances eagerly
Closes gh-27751
2021-12-15 10:52:09 +01:00
Stephane Nicoll 4c2e0ee5ff Upgrade to Log4j2 2.16.0 2021-12-15 09:49:57 +01:00
Juergen Hoeller 79804d92c2 Upgrade to Protobuf 3.19.1, Gson 2.8.9, Woodstox 6.2.7, Apache Johnzon 1.2.15, Caffeine 2.9.3 2021-12-14 18:36:05 +01:00
Juergen Hoeller f191cf4eb3 Revised comment on explicit LocalDataSourceJobStore configuration
See gh-27709
2021-12-14 18:32:32 +01:00
Juergen Hoeller 0ebb1c5baa Upgrade to Tomcat 9.0.56, Undertow 2.2.14, Apache HttpClient 5.1.2 & HttpAsyncClient 4.1.5, Mockito 4.1 2021-12-14 16:48:40 +01:00
Juergen Hoeller 4b7d9b1f9c Avoid compilation warning for test class with serializable base class 2021-12-14 16:48:07 +01:00
Juergen Hoeller ca84559588 Provide findAnnotationOnBean variant with allowFactoryBeanInit flag
Closes gh-27796
2021-12-14 16:47:12 +01:00
Juergen Hoeller 1af21bb451 Declare serialVersionUID on DefaultAopProxyFactory
Closes gh-27784
2021-12-14 16:46:59 +01:00
Juergen Hoeller ac581bed92 Avoid NPE against null value from toString call
Closes gh-27782
2021-12-14 16:46:42 +01:00
Juergen Hoeller 0802581aff Unit test for identifying type variable argument
See gh-27748
2021-12-14 16:46:26 +01:00
Juergen Hoeller d7b9270672 Clarify SchedulerFactoryBean's LocalDataSourceJobStore overriding
Includes clarification of interface-level cache annotations for target-class proxies.

Closes gh-27709
See gh-27726
2021-12-14 16:46:13 +01:00
Juergen Hoeller c44447f622 Avoid early initialization of empty interceptor names
Closes gh-12238
2021-12-14 16:46:01 +01:00
Stephane Nicoll 8b80d38c33 Upgrade to Reactor 2020.0.14
Closes gh-27793
2021-12-14 15:33:55 +01:00
Stephane Nicoll b06d267232 Remove references to AsyncConfigurerSupport
Closes gh-27812
2021-12-14 13:59:05 +01:00
Stephane Nicoll 8422d9d22f Add default methods to CachingConfigurer
This commit adds default methods to CachingConfigurer and
JCacheConfigurer and removes the reference to their respective support
classes as they are now irrelevant.

Closes gh-27811
2021-12-14 13:49:57 +01:00
Stephane Nicoll c50a5096a0 Upgrade to Netty 4.1.72.Final 2021-12-13 16:18:25 +01:00
Stephane Nicoll c25c550113 Merge pull request #27804 from aoyvx
* pr/27804:
  Polish "Fix javadoc reference to ThrowsAdvice"
  Update copyright year of changed file
  Fix javadoc reference to ThrowsAdvice

Closes gh-27804
2021-12-12 16:33:45 +01:00
Stephane Nicoll 9e09d6cde4 Polish "Fix javadoc reference to ThrowsAdvice"
See gh-27804
2021-12-12 16:32:35 +01:00
Stephane Nicoll 31b8587ce6 Update copyright year of changed file
See gh-27804
2021-12-12 16:30:06 +01:00
xay 00375df4e8 Fix javadoc reference to ThrowsAdvice
See gh-27804
2021-12-12 16:27:28 +01:00
Stephane Nicoll b1352e1833 Upgrade to Log4j2 2.15.0 2021-12-10 15:05:56 +01:00
Sam Brannen b2e94f611f Convert single null argument to Optional.empty() in SpEL varargs expression
Prior to this commit, if a single null value was passed to a method with
a varargs array of type java.util.Optional, that null value was passed
unmodified. On the contrary, a null passed with additional values to
such a method resulted in the null being converted to Optional.empty().

This commit ensures that a single null value is also converted to
Optional.empty() for such SpEL expressions.

Closes gh-27795
2021-12-10 13:53:28 +01:00
Sam Brannen ad7cdc5ce9 Fix regression for null varargs in SpEL expressions
A regression was introduced in gh-27582. Specifically, when null is
supplied as the single argument for a varargs parameter in a method or
function in a SpEL expression, ReflectionHelper currently throws a
NullPointerException instead of leaving the null value unchanged.

This commit fixes this regression.

Closes gh-27719
2021-12-10 13:12:44 +01:00
Stephane Nicoll 6cc9538ab9 Start building against Reactor 2020.0.14 snapshots
See gh-27793
2021-12-10 07:55:44 +01:00
Rossen Stoyanchev e9083d7d20 Apply LogFormatUtils in more places 2021-12-09 14:53:52 +00:00
Stephane Nicoll 98ce171b30 Revert "Mention explicitly that @EventListener should be put on public methods"
This reverts commit 57a5370eec.

See gh-27777
2021-12-09 15:18:56 +01:00
Stephane Nicoll 57a5370eec Mention explicitly that @EventListener should be put on public methods
Closes gh-27777
2021-12-09 14:43:00 +01:00
Rossen Stoyanchev 99c7608ffe Replace both EOL and control characters 2021-12-08 11:25:38 +00:00
Arjen Poutsma c0f79ee00a Merge pull request #25092 from jkatada:fix-ModelAndView-status-for-redirect
* gh-25092:
  Fix for ModelAndView.status not working with RedirectView
2021-12-07 13:56:19 +01:00
Katada, Junya 9261766677 Fix for ModelAndView.status not working with RedirectView 2021-12-07 13:39:57 +01:00
Juergen Hoeller 14f24f43d7 Polishing 2021-12-03 22:36:31 +01:00
Juergen Hoeller d7e0eed8d2 Clarify getBeanProvider(ResolvableType) semantics for unresolved generics
Closes gh-27727
2021-12-03 22:33:50 +01:00
Juergen Hoeller 7c834d98c2 Upgrade to ASM master (including early support for Java 19 bytecode)
Closes gh-27740
2021-12-03 22:32:26 +01:00
Juergen Hoeller 70974e006e Upgrade to Tomcat 9.0.55, Undertow 2.2.13, RxJava 3.1.3, SmallRye Mutiny 1.2, Joda-Time 2.10.13, JRuby 9.2.20.1, HtmlUnit 2.55, Checkstyle 9.2 2021-12-03 22:32:00 +01:00
Arjen Poutsma 2f557d9583 Merge pull request #25358 from ivd-git:router-function-handler-not-found-to-exception-pr
* gh-25358:
  Polish "Throw 404 ResponseStatusException when no routes found"
  Throw 404 ResponseStatusException when no routes found
2021-12-03 12:51:44 +01:00
Arjen Poutsma b84fe99d07 Polish "Throw 404 ResponseStatusException when no routes found"
See gh-25358
2021-12-03 12:01:49 +01:00
Ingmar van Dijk 69df27a99f Throw 404 ResponseStatusException when no routes found
This commit makes it possible to customize 404 responses generated by
RouterFunctionWebHandler, by throwing an ResponseStatusException
instead of returning a standard 404 response.

See gh-25358
2021-12-03 12:01:49 +01:00
Rossen Stoyanchev 5649a6f8ef Update exchangeToMono Javadoc
This time showing a more representative example.

See gh-27645
2021-12-03 09:21:21 +00:00
Arjen Poutsma be6eeafe78 Revert change createException to createError
createError is not available in 5.3.

See gh-27645
2021-12-02 15:59:51 +01:00
Arjen Poutsma b77b45434f Change createException to createError
See gh-27645
2021-12-02 15:11:15 +01:00
Rossen Stoyanchev 6582787678 Apply resources after application HttpClient mapper
Closes gh-27749
2021-12-02 12:28:36 +00:00
Rossen Stoyanchev 8b89128c7b Update createException example for WebClient
Closes gh-27645
2021-12-02 11:53:43 +00:00
Arjen Poutsma 2a5713f389 Always copy ServerResponse headers
Prior to this commit, ServerResponse headers would only be written if
there were no existing headers with the same name, thus making it
impossible to overwrite existing headers.

With the changes in this commit, headers are always written.

Closes gh-27741
2021-11-30 13:38:16 +01:00
Sam Brannen 40d2058b97 Upgrade to JUnit 5.8.2
Closes gh-27744
2021-11-30 12:22:31 +01:00
Rossen Stoyanchev 0d478ca8dd Add link to WebSocket scope from the Spring Core section
Closes gh-25172
2021-11-29 17:37:37 +00:00
Rossen Stoyanchev 445c0def0c Update docs on heartbeats with simple broker
Closes gh-27746
2021-11-29 17:13:57 +00:00
Stephane Nicoll ef696c787b Merge pull request #22246 from diguage
* pr/22246:
  Polish "Fix reference to CountingBeforeAdvice"
  Fix reference to CountingBeforeAdvice

Closes gh-22246
2021-11-28 10:53:15 +01:00
Stephane Nicoll 27257fb82e Polish "Fix reference to CountingBeforeAdvice"
See gh-22246
2021-11-28 10:52:16 +01:00
diguage e209a460ca Fix reference to CountingBeforeAdvice
See gh-22246
2021-11-28 10:44:48 +01:00
Brian Clozel d019c1f82b Polish
See gh-2030
2021-11-24 21:34:23 +01:00
Stephane Nicoll 82d6e07090 Merge pull request #2030 from matips
* pr/2030:
  Polish "Clarify behaviour of AnnotationBeanNameGenerator with acronyms"
  Clarify behaviour of AnnotationBeanNameGenerator with acronyms

Closes gh-2030
2021-11-24 16:29:45 +01:00
Stephane Nicoll a1b2695c3a Polish "Clarify behaviour of AnnotationBeanNameGenerator with acronyms"
See gh-2030
2021-11-24 16:29:13 +01:00
Mateusz 341f4882ed Clarify behaviour of AnnotationBeanNameGenerator with acronyms
Name transformation is delegated to Introspector#decapitalize, which
states "but in the (unusual) special case when there is more than one
character and both the first and second characters are upper case, we
leave it alone.". This commit clarifies this behavior.

See gh-2030
2021-11-24 16:00:22 +01:00
Rossen Stoyanchev 829bed03af Polishing contribution
Closes gh-27723
2021-11-24 13:05:05 +00:00
Andreas Grub ce0aed216b Add getter for RequestMappingInfo.BuilderConfiguration
This improves support for programmatic registration of mappings to
use the same config as that of the RequestMappingHandlerMapping.

See gh-27723
2021-11-24 13:05:05 +00:00
Rossen Stoyanchev 0d7c562693 Polishing contribution
Closes gh-910
2021-11-24 13:05:05 +00:00
/usr/local/ΕΨΗΕΛΩΝ 913cc079af TagWriter can write empty attribute
See gh-910
2021-11-24 13:05:05 +00:00
Rossen Stoyanchev 3f7dec0b0d Polishing contribution
Closes gh-699
2021-11-24 13:05:05 +00:00
Marco Krikke 830cc3450f Improved DataBinder Javadoc for xxx*yyy pattern matching
The default documentation does not mention xxx*yyy pattern matching,
which is, however, supported by PatternMatchUtils. Such a pattern
can be useful for matching nested properties in all elements of a
collection, e.g. property[*].nestedProperty.

See gh-699
2021-11-24 13:05:05 +00:00
Stephane Nicoll 6f6d27f50f Merge pull request #1246 from fededonna
* pr/1246:
  Polish "Expose prestartAllCoreThreads on ExecutorService"
  Expose prestartAllCoreThreads on ExecutorService

Closes gh-1246
2021-11-24 14:02:48 +01:00
Stephane Nicoll 19a8b94b21 Polish "Expose prestartAllCoreThreads on ExecutorService"
See gh-1246
2021-11-23 17:17:22 +01:00
Federico Donnarumma 2c53e9e308 Expose prestartAllCoreThreads on ExecutorService
See gh-1246
2021-11-23 16:14:39 +01:00
Arjen Poutsma 2271b6078e Polishing contribution
See gh-1607
2021-11-23 10:44:45 +01:00
qxo e8eeb99ac8 Repect StaxDriver in XStreamMarshaller::marshalXmlStreamWriter
This commit makes sure that namespaces configured via the streamDriver
property are respected.
2021-11-23 10:41:04 +01:00
Stephane Nicoll b67e97d388 Polish 2021-11-22 16:13:37 +01:00
Stephane Nicoll 656f0591c6 Merge pull request #1330 from kazuki43zoo
* pr/1330:
  Polish "Add tests for AbstractRoutingDataSource"
  Add tests for AbstractRoutingDataSource

Closes gh-1330
2021-11-22 13:46:29 +01:00
Stephane Nicoll 23babe27bb Polish "Add tests for AbstractRoutingDataSource"
See gh-1330
2021-11-22 13:43:08 +01:00
Kazuki Shimizu bcb9f159ca Add tests for AbstractRoutingDataSource
See gh-1330
2021-11-22 13:43:08 +01:00
Stephane Nicoll 8ca9187938 Merge pull request #1025 from julianladisch
* pr/1025:
  Fix simple data format in appendix

Closes gh-1025
2021-11-22 13:27:56 +01:00
julianladisch 1e0bdf95d0 Fix simple data format in appendix
See gh-1025
2021-11-22 13:26:57 +01:00
Stephane Nicoll 44da9451c4 Merge pull request #1000 from awgtek
* pr/1000:
  Polish "Refine StoredProcedure#declareParameter Javadoc"
  Refine StoredProcedure#declareParameter Javadoc

Closes gh-1000
2021-11-22 13:21:00 +01:00
Stephane Nicoll 544f357661 Polish "Refine StoredProcedure#declareParameter Javadoc"
See gh-1000
2021-11-22 13:20:16 +01:00
awgtek ca4f338d75 Refine StoredProcedure#declareParameter Javadoc
See gh-1000
2021-11-22 13:18:24 +01:00
Stephane Nicoll 8e8c88ea5c Merge pull request #767 from thomasdarimont
* pr/767:
  Document @Bean definitions via default methods

Closes gh-767
2021-11-22 12:55:38 +01:00
Thomas Darimont 767299c6dd Document @Bean definitions via default methods
See gh-767

Co-authored-by: Stephane Nicoll <snicoll@vmware.com>
2021-11-22 12:55:22 +01:00
Stephane Nicoll 3290495222 Merge pull request #27699 from quaff
* pr/27699:
  Fix typo

Closes gh-27699
2021-11-22 10:31:13 +01:00
Yanming Zhou b4b3c2ead6 Fix typo
See gh-27699
2021-11-22 10:30:45 +01:00
Jerome Prinet dfd5374f02 Upgrade Gradle enterprise plugin to 3.7.2
Closes gh-27704
2021-11-19 15:06:27 +01:00
Arjen Poutsma 722ab25f27 Support empty file names in UriUtils::extractFileExtension
Closes gh-27639
2021-11-19 12:37:00 +01:00
Sam Brannen c37d6c30a0 Fix nullability declarations in MergedContextConfiguration 2021-11-18 19:23:28 +01:00
Arjen Poutsma 5fbdd6dcfe Throw exception using capturing patterns in AntPathMatcher
Closes gh-27688
2021-11-17 16:52:17 +01:00
d4ksn 29572600dc Ensure that references > MAX_REFERENCE_SIZE are not processed
This commit ensures that only HTML references of length <
MAX_REFERENCE_SIZE are considered as potential references. This check is
possible because reference longer than 10 digits are out of bounds for
Integers.

Closes gh-1249
2021-11-16 15:04:08 +01:00
Spring Builds aaf626a537 Next development version (v5.3.14-SNAPSHOT) 2021-11-11 07:42:44 +00:00
112 changed files with 1168 additions and 492 deletions
+44 -44
View File
@@ -3,8 +3,8 @@ plugins {
id 'io.spring.nohttp' version '0.0.10'
id "io.freefair.aspectj" version '6.2.0' apply false
id 'org.jetbrains.dokka' version '1.5.0' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.31' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.31" apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
id "org.unbroken-dome.xjc" version '2.0.0' apply false
@@ -27,19 +27,19 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5"
mavenBom "io.netty:netty-bom:4.1.70.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.13"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10"
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.6"
mavenBom "io.netty:netty-bom:4.1.72.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.14"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR11"
mavenBom "io.rsocket:rsocket-bom:1.1.1"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.44.v20210927"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.31"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
mavenBom "org.junit:junit-bom:5.8.1"
mavenBom "org.junit:junit-bom:5.8.2"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.16.0') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
@@ -67,23 +67,23 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.1"
dependency "io.smallrye.reactive:mutiny:1.1.1"
dependency "io.reactivex.rxjava3:rxjava:3.1.3"
dependency "io.smallrye.reactive:mutiny:1.2.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.3.0"
dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") {
dependency("com.fasterxml.woodstox:woodstox-core:6.2.7") {
exclude group: "stax", name: "stax-api"
}
dependency "com.google.code.gson:gson:2.8.8"
dependency "com.google.protobuf:protobuf-java-util:3.18.0"
dependency "com.google.code.gson:gson:2.8.9"
dependency "com.google.protobuf:protobuf-java-util:3.19.1"
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
dependency("com.thoughtworks.xstream:xstream:1.4.18") {
exclude group: "xpp3", name: "xpp3_min"
exclude group: "xmlpull", name: "xmlpull"
}
dependency "org.apache.johnzon:johnzon-jsonb:1.2.14"
dependency "org.apache.johnzon:johnzon-jsonb:1.2.15"
dependency("org.codehaus.jettison:jettison:1.3.8") {
exclude group: "stax", name: "stax-api"
}
@@ -92,10 +92,10 @@ configure(allprojects) { project ->
entry 'jibx-run'
}
dependency "org.ogce:xpp3:1.1.6"
dependency "org.yaml:snakeyaml:1.29"
dependency "org.yaml:snakeyaml:1.30"
dependency "com.h2database:h2:1.4.200"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.2"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
dependency "com.github.librepdf:openpdf:1.3.26"
dependency "com.rometools:rome:1.16.0"
dependency "commons-io:commons-io:2.5"
@@ -128,43 +128,43 @@ configure(allprojects) { project ->
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.54') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.56') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.54') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.56') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.12.Final') {
dependencySet(group: 'io.undertow', version: '2.2.14.Final') {
entry 'undertow-core'
entry('undertow-websockets-jsr') {
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
}
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
exclude group: "org.jboss.spec.javax.annotation", name: "jboss-annotations-api_1.3_spec"
}
entry('undertow-websockets-jsr') {
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
}
}
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.10"
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.2'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.2'
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.apache.httpcomponents:httpasyncclient:4.1.5") {
exclude group: "commons-logging", name: "commons-logging"
}
dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') {
entry 'okhttp'
entry 'mockwebserver'
}
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9"
dependency "org.jruby:jruby:9.2.19.0"
dependency "org.jruby:jruby:9.2.20.1"
dependency "org.python:jython-standalone:2.7.1"
dependency "org.mozilla:rhino:1.7.11"
@@ -198,18 +198,18 @@ configure(allprojects) { project ->
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '4.0.0') {
dependencySet(group: 'org.mockito', version: '4.1.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
entry 'mockito-junit-jupiter'
}
dependency "io.mockk:mockk:1.12.0"
dependency "io.mockk:mockk:1.12.1"
dependency("net.sourceforge.htmlunit:htmlunit:2.54.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.55.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.54.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.55.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -237,7 +237,7 @@ configure(allprojects) { project ->
dependency "com.ibm.websphere:uow:6.0.2.17"
dependency "com.jamonapi:jamon:2.82"
dependency "joda-time:joda-time:2.10.10"
dependency "joda-time:joda-time:2.10.13"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.9"
dependency "org.javamoney:moneta:1.3"
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "9.0"
toolVersion = "9.2"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -353,12 +353,12 @@ configure([rootProject] + javaProjects) { project ->
testImplementation("io.mockk:mockk")
testImplementation("org.assertj:assertj-core")
// Pull in the latest JUnit 5 Launcher API to ensure proper support in IDEs.
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
@@ -383,9 +383,9 @@ configure([rootProject] + javaProjects) { project ->
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// Disabling linking to JUnit 5.8.1, since the `package-list` file no longer exists due to
// Disabling linking to JUnit 5.8.2, since the `package-list` file no longer exists due to
// https://github.com/junit-team/junit5/commit/67ad4e545518b0ce2b0e7c96df31a669866d5003.
// "https://junit.org/junit5/docs/5.8.1/api/",
// "https://junit.org/junit5/docs/5.8.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/0.8.5.RELEASE/api/"
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.13
version=5.3.14
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
+1 -1
View File
@@ -6,7 +6,7 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.6.1"
id "com.gradle.enterprise" version "3.7.2"
id "io.spring.ge.conventions" version "0.0.7"
}
@@ -45,9 +45,10 @@ import org.springframework.core.NativeDetector;
* @see AdvisedSupport#setProxyTargetClass
* @see AdvisedSupport#setInterfaces
*/
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
private static final long serialVersionUID = 7930414337282325166L;
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -421,11 +421,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
* are unaffected by such changes.
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
@@ -464,9 +460,9 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
addAdvisorOnChainCreation(advice);
}
}
}
this.advisorChainInitialized = true;
this.advisorChainInitialized = true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,8 +25,8 @@ import org.springframework.aop.Advisor;
import org.springframework.aop.ThrowsAdvice;
/**
* Adapter to enable {@link org.springframework.aop.MethodBeforeAdvice}
* to be used in the Spring AOP framework.
* Adapter to enable {@link org.springframework.aop.ThrowsAdvice} to be used
* in the Spring AOP framework.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -16,9 +16,9 @@
</aop:aspect>
</aop:config>
<bean id="getNameCounter" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="getNameCounter" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
<bean id="getAgeCounter" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="getAgeCounter" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
@@ -9,6 +9,6 @@
<aop:advisor advice-ref="countingAdvice" pointcut="within(org.springframework..*)"/>
</aop:config>
<bean id="countingAdvice" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="countingAdvice" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
</beans>
@@ -10,6 +10,6 @@
<aop:advisor advice-ref="countingAdvice" pointcut-ref="pc"/>
</aop:config>
<bean id="countingAdvice" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="countingAdvice" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
</beans>
@@ -12,7 +12,7 @@
</aop:aspect>
</aop:config>
<bean id="getAgeCounter" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="getAgeCounter" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
@@ -12,7 +12,7 @@
</aop:aspect>
</aop:config>
<bean id="getAgeCounter" class="org.springframework.aop.framework.CountingBeforeAdvice"/>
<bean id="getAgeCounter" class="org.springframework.aop.testfixture.advice.CountingBeforeAdvice"/>
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
@@ -107,10 +106,7 @@ public class AspectJEnableCachingIsolatedTests {
try {
load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
}
catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
}
}
@@ -214,6 +214,7 @@ public interface BeanFactory {
/**
* Return a provider for the specified bean, allowing for lazy on-demand retrieval
* of instances, including availability and uniqueness options.
* <p>For matching a generic type, consider {@link #getBeanProvider(ResolvableType)}.
* @param requiredType type the bean must match; can be an interface or superclass
* @return a corresponding provider handle
* @since 5.1
@@ -223,13 +224,20 @@ public interface BeanFactory {
/**
* Return a provider for the specified bean, allowing for lazy on-demand retrieval
* of instances, including availability and uniqueness options.
* @param requiredType type the bean must match; can be a generic type declaration.
* Note that collection types are not supported here, in contrast to reflective
* of instances, including availability and uniqueness options. This variant allows
* for specifying a generic type to match, similar to reflective injection points
* with generic type declarations in method/constructor parameters.
* <p>Note that collections of beans are not supported here, in contrast to reflective
* injection points. For programmatically retrieving a list of beans matching a
* specific type, specify the actual bean type as an argument here and subsequently
* use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options.
* <p>Also, generics matching is strict here, as per the Java assignment rules.
* For lenient fallback matching with unchecked semantics (similar to the ´unchecked´
* Java compiler warning), consider calling {@link #getBeanProvider(Class)} with the
* raw type as a second step if no full generic match is
* {@link ObjectProvider#getIfAvailable() available} with this variant.
* @return a corresponding provider handle
* @param requiredType type the bean must match; can be a generic type declaration
* @since 5.1
* @see ObjectProvider#iterator()
* @see ObjectProvider#stream()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -353,9 +353,31 @@ public interface ListableBeanFactory extends BeanFactory {
* @since 3.0
* @see #getBeanNamesForAnnotation
* @see #getBeansWithAnnotation
* @see #getType(String)
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException;
/**
* Find an {@link Annotation} of {@code annotationType} on the specified bean,
* traversing its interfaces and super classes if no annotation can be found on
* the given class itself, as well as checking the bean's factory method (if any).
* @param beanName the name of the bean to look for annotations on
* @param annotationType the type of annotation to look for
* (at class, interface or factory method level of the specified bean)
* @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized
* just for the purpose of determining its object type
* @return the annotation of the given type if found, or {@code null} otherwise
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 5.3.14
* @see #getBeanNamesForAnnotation
* @see #getBeansWithAnnotation
* @see #getType(String, boolean)
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,9 +34,7 @@ import org.springframework.lang.Nullable;
*
* <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
* internal use within the framework. It is recommended to implement the plain
* {@link BeanPostProcessor} interface as far as possible, or to derive from
* {@link InstantiationAwareBeanPostProcessorAdapter} in order to be shielded
* from extensions to this interface.
* {@link BeanPostProcessor} interface as far as possible.
*
* @author Juergen Hoeller
* @author Rod Johnson
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -730,14 +730,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException {
return findMergedAnnotationOnBean(beanName, annotationType)
return findAnnotationOnBean(beanName, annotationType, true);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
return findMergedAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit)
.synthesize(MergedAnnotation::isPresent).orElse(null);
}
private <A extends Annotation> MergedAnnotation<A> findMergedAnnotationOnBean(
String beanName, Class<A> annotationType) {
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit) {
Class<?> beanType = getType(beanName);
Class<?> beanType = getType(beanName, allowFactoryBeanInit);
if (beanType != null) {
MergedAnnotation<A> annotation =
MergedAnnotations.from(beanType, SearchStrategy.TYPE_HIERARCHY).get(annotationType);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -459,7 +459,16 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException {
Class<?> beanType = getType(beanName);
return findAnnotationOnBean(beanName, annotationType, true);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
Class<?> beanType = getType(beanName, allowFactoryBeanInit);
return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.util.function.Supplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
@@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu
@Override
protected void useCachingConfigurer(CachingConfigurer config) {
super.useCachingConfigurer(config);
if (config instanceof JCacheConfigurer) {
this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver;
}
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
super.useCachingConfigurer(cachingConfigurerSupplier);
this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> {
if (config instanceof JCacheConfigurer) {
return ((JCacheConfigurer) config).exceptionCacheResolver();
}
return null;
});
}
@Bean(name = "jCacheOperationSource")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,7 @@ import org.springframework.lang.Nullable;
* <p>To be implemented by classes annotated with
* {@link org.springframework.cache.annotation.EnableCaching} that wish
* or need to specify explicitly how exception caches are resolved for
* annotation-driven cache management. Consider extending {@link JCacheConfigurerSupport},
* which provides a stub implementation of all interface methods.
* annotation-driven cache management.
*
* <p>See {@link org.springframework.cache.annotation.EnableCaching} for
* general examples and context; see {@link #exceptionCacheResolver()} for
@@ -36,7 +35,6 @@ import org.springframework.lang.Nullable;
* @author Stephane Nicoll
* @since 4.1
* @see CachingConfigurer
* @see JCacheConfigurerSupport
* @see org.springframework.cache.annotation.EnableCaching
*/
public interface JCacheConfigurer extends CachingConfigurer {
@@ -60,6 +58,8 @@ public interface JCacheConfigurer extends CachingConfigurer {
* See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples.
*/
@Nullable
CacheResolver exceptionCacheResolver();
default CacheResolver exceptionCacheResolver() {
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -79,7 +79,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
@Nullable
private JCacheOperation<?> computeCacheOperation(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// Don't allow non-public methods, as configured.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@ import org.springframework.lang.Nullable;
* Subclass of Quartz's {@link JobStoreCMT} class that delegates to a Spring-managed
* {@link DataSource} instead of using a Quartz-managed JDBC connection pool.
* This JobStore will be used if SchedulerFactoryBean's "dataSource" property is set.
* You may also configure it explicitly, possibly as a custom subclass of this
* {@code LocalDataSourceJobStore} or as an equivalent {@code JobStoreCMT} variant.
*
* <p>Supports both transactional and non-transactional DataSource access.
* With a non-XA DataSource and local Spring transactions, a single DataSource
@@ -58,6 +60,8 @@ import org.springframework.lang.Nullable;
* @since 1.1
* @see SchedulerFactoryBean#setDataSource
* @see SchedulerFactoryBean#setNonTransactionalDataSource
* @see SchedulerFactoryBean#getConfigTimeDataSource()
* @see SchedulerFactoryBean#getConfigTimeNonTransactionalDataSource()
* @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
*/
@@ -310,9 +310,11 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
/**
* Set the default {@link DataSource} to be used by the Scheduler.
* If set, this will override corresponding settings in Quartz properties.
* <p>Note: If this is set, the Quartz settings should not define
* a job store "dataSource" to avoid meaningless double configuration.
* Also, do not define a "org.quartz.jobStore.class" property at all.
* (You may explicitly define Spring's {@link LocalDataSourceJobStore}
* but that's the default when using this method anyway.)
* <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used.
* It is therefore strongly recommended to perform all operations on
* the Scheduler within Spring-managed (or plain JTA) transactions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
@@ -104,7 +104,7 @@ public class JCacheEhCacheAnnotationTests extends AbstractCacheAnnotationTests {
@Configuration
@EnableCaching
static class EnableCachingConfig extends CachingConfigurerSupport {
static class EnableCachingConfig implements CachingConfigurer {
@Autowired
CachingProvider cachingProvider;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -180,7 +180,7 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
@Configuration
@EnableCaching
public static class EmptyConfigSupportConfig extends JCacheConfigurerSupport {
public static class EmptyConfigSupportConfig implements JCacheConfigurer {
@Bean
public CacheManager cm() {
return new NoOpCacheManager();
@@ -190,7 +190,7 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
@Configuration
@EnableCaching
static class FullCachingConfigSupport extends JCacheConfigurerSupport {
static class FullCachingConfigSupport implements JCacheConfigurer {
@Override
@Bean
@@ -220,7 +220,7 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
@Configuration
@EnableCaching
static class NoExceptionCacheResolverConfig extends JCacheConfigurerSupport {
static class NoExceptionCacheResolverConfig implements JCacheConfigurer {
@Override
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.cache.jcache.config.JCacheConfigurerSupport;
import org.springframework.cache.jcache.config.JCacheConfigurer;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -141,7 +141,7 @@ public class JCacheErrorHandlerTests {
@Configuration
@EnableCaching
static class Config extends JCacheConfigurerSupport {
static class Config implements JCacheConfigurer {
@Bean
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.cache.jcache.config.JCacheConfigurerSupport;
import org.springframework.cache.jcache.config.JCacheConfigurer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -97,7 +97,7 @@ public class JCacheKeyGeneratorTests {
@Configuration
@EnableCaching
static class Config extends JCacheConfigurerSupport {
static class Config implements JCacheConfigurer {
@Bean
@Override
@@ -151,7 +151,7 @@ public class JCacheKeyGeneratorTests {
@Override
public Object generate(Object target, Method method, Object... params) {
assertThat(Arrays.equals(expectedParams, params)).as("Unexpected parameters: expected: "
+ Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue();
+ Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue();
return new SimpleKey(params);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,12 @@
package org.springframework.cache.annotation;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
@@ -30,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* Abstract base {@code @Configuration} class providing common structure
@@ -63,36 +67,67 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
importMetadata.getAnnotationAttributes(EnableCaching.class.getName()));
if (this.enableCaching == null) {
throw new IllegalArgumentException(
"@EnableCaching is not present on importing class " + importMetadata.getClassName());
}
}
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException(configurers.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
@Autowired
void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
Supplier<CachingConfigurer> configurer = () -> {
List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
if (CollectionUtils.isEmpty(candidates)) {
return null;
}
if (candidates.size() > 1) {
throw new IllegalStateException(candidates.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
return candidates.get(0);
};
useCachingConfigurer(new CachingConfigurerSupplier(configurer));
}
/**
* Extract the configuration from the nominated {@link CachingConfigurer}.
*/
protected void useCachingConfigurer(CachingConfigurer config) {
this.cacheManager = config::cacheManager;
this.cacheResolver = config::cacheResolver;
this.keyGenerator = config::keyGenerator;
this.errorHandler = config::errorHandler;
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler);
}
protected static class CachingConfigurerSupplier {
private final Supplier<CachingConfigurer> supplier;
public CachingConfigurerSupplier(Supplier<CachingConfigurer> supplier) {
this.supplier = SingletonSupplier.of(supplier);
}
/**
* Adapt the {@link CachingConfigurer} supplier to another supplier
* provided by the specified mapping function. If the underlying
* {@link CachingConfigurer} is {@code null}, {@code null} is returned
* and the mapping function is not invoked.
* @param provider the provider to use to adapt the supplier
* @param <T> the type of the supplier
* @return another supplier mapped by the specified function
*/
@Nullable
public <T> Supplier<T> adapt(Function<CachingConfigurer, T> provider) {
return () -> {
CachingConfigurer cachingConfigurer = this.supplier.get();
return (cachingConfigurer != null ? provider.apply(cachingConfigurer) : null);
};
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,7 @@ import org.springframework.lang.Nullable;
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
* Configuration} classes annotated with @{@link EnableCaching} that wish or need to
* specify explicitly how caches are resolved and how keys are generated for annotation-driven
* cache management. Consider extending {@link CachingConfigurerSupport}, which provides a
* stub implementation of all interface methods.
* cache management.
*
* <p>See @{@link EnableCaching} for general examples and context; see
* {@link #cacheManager()}, {@link #cacheResolver()} and {@link #keyGenerator()}
@@ -37,7 +36,6 @@ import org.springframework.lang.Nullable;
* @author Stephane Nicoll
* @since 3.1
* @see EnableCaching
* @see CachingConfigurerSupport
*/
public interface CachingConfigurer {
@@ -64,7 +62,9 @@ public interface CachingConfigurer {
* See @{@link EnableCaching} for more complete examples.
*/
@Nullable
CacheManager cacheManager();
default CacheManager cacheManager() {
return null;
}
/**
* Return the {@link CacheResolver} bean to use to resolve regular caches for
@@ -89,7 +89,9 @@ public interface CachingConfigurer {
* See {@link EnableCaching} for more complete examples.
*/
@Nullable
CacheResolver cacheResolver();
default CacheResolver cacheResolver() {
return null;
}
/**
* Return the key generator bean to use for annotation-driven cache management.
@@ -110,7 +112,9 @@ public interface CachingConfigurer {
* See @{@link EnableCaching} for more complete examples.
*/
@Nullable
KeyGenerator keyGenerator();
default KeyGenerator keyGenerator() {
return null;
}
/**
* Return the {@link CacheErrorHandler} to use to handle cache-related errors.
@@ -133,6 +137,8 @@ public interface CachingConfigurer {
* See @{@link EnableCaching} for more complete examples.
*/
@Nullable
CacheErrorHandler errorHandler();
default CacheErrorHandler errorHandler() {
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
@Nullable
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// Don't allow non-public methods, as configured.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,9 +47,10 @@ import org.springframework.util.StringUtils;
*
* <p>If the annotation's value doesn't indicate a bean name, an appropriate
* name will be built based on the short name of the class (with the first
* letter lower-cased). For example:
* letter lower-cased), unless the two first letters are uppercase. For example:
*
* <pre class="code">com.xyz.FooServiceImpl -&gt; fooServiceImpl</pre>
* <pre class="code">com.xyz.URLFooServiceImpl -&gt; URLFooServiceImpl</pre>
*
* @author Juergen Hoeller
* @author Mark Fisher
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -282,7 +282,7 @@ public abstract class AnnotationConfigUtils {
@Nullable
static AnnotationAttributes attributesFor(AnnotatedTypeMetadata metadata, String annotationClassName) {
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName, false));
return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationClassName));
}
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
@@ -298,10 +298,10 @@ public abstract class AnnotationConfigUtils {
Set<AnnotationAttributes> result = new LinkedHashSet<>();
// Direct annotation present?
addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false));
addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName));
// Container annotation present?
Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false);
Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName);
if (container != null && container.containsKey("value")) {
for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
addAttributesIfNotNull(result, containedAttributes);
@@ -1331,6 +1331,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return getBeanFactory().findAnnotationOnBean(beanName, annotationType);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
assertBeanFactoryActive();
return getBeanFactory().findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,14 @@
package org.springframework.scheduling.annotation;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
@@ -28,6 +31,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* Abstract base {@code Configuration} class providing common structure for enabling
@@ -55,7 +59,7 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
importMetadata.getAnnotationAttributes(EnableAsync.class.getName()));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
@@ -65,17 +69,27 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
/**
* Collect any {@link AsyncConfigurer} beans through autowiring.
*/
@Autowired(required = false)
void setConfigurers(Collection<AsyncConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException("Only one AsyncConfigurer may exist");
}
AsyncConfigurer configurer = configurers.iterator().next();
this.executor = configurer::getAsyncExecutor;
this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
@Autowired
void setConfigurers(ObjectProvider<AsyncConfigurer> configurers) {
Supplier<AsyncConfigurer> configurer = SingletonSupplier.of(() -> {
List<AsyncConfigurer> candidates = configurers.stream().collect(Collectors.toList());
if (CollectionUtils.isEmpty(candidates)) {
return null;
}
if (candidates.size() > 1) {
throw new IllegalStateException("Only one AsyncConfigurer may exist");
}
return candidates.get(0);
});
this.executor = adapt(configurer, AsyncConfigurer::getAsyncExecutor);
this.exceptionHandler = adapt(configurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler);
}
private <T> Supplier<T> adapt(Supplier<AsyncConfigurer> supplier, Function<AsyncConfigurer, T> provider) {
return () -> {
AsyncConfigurer configurer = supplier.get();
return (configurer != null ? provider.apply(configurer) : null);
};
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,11 +28,6 @@ import org.springframework.lang.Nullable;
* {@link AsyncUncaughtExceptionHandler} instance used to process exception thrown from
* async method with {@code void} return type.
*
* <p>Consider using {@link AsyncConfigurerSupport} providing default implementations for
* both methods if only one element needs to be customized. Furthermore, backward compatibility
* of this interface will be insured in case new customization options are introduced
* in the future.
*
* <p>See @{@link EnableAsync} for usage examples.
*
* @author Chris Beams
@@ -40,7 +35,6 @@ import org.springframework.lang.Nullable;
* @since 3.1
* @see AbstractAsyncConfiguration
* @see EnableAsync
* @see AsyncConfigurerSupport
*/
public interface AsyncConfigurer {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,7 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
/**
@@ -65,7 +63,7 @@ import org.springframework.lang.Nullable;
*/
@SuppressWarnings("serial")
public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
implements FactoryBean<ExecutorService>, InitializingBean, DisposableBean {
implements FactoryBean<ExecutorService> {
private int corePoolSize = 1;
@@ -75,6 +73,8 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
private boolean allowCoreThreadTimeOut = false;
private boolean prestartAllCoreThreads = false;
private int queueCapacity = Integer.MAX_VALUE;
private boolean exposeUnconfigurableExecutor = false;
@@ -118,6 +118,16 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
}
/**
* Specify whether to start all core threads, causing them to idly wait for work.
* <p>Default is "false".
* @since 5.3.14
* @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads
*/
public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) {
this.prestartAllCoreThreads = prestartAllCoreThreads;
}
/**
* Set the capacity for the ThreadPoolExecutor's BlockingQueue.
* Default is {@code Integer.MAX_VALUE}.
@@ -153,6 +163,9 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
if (this.prestartAllCoreThreads) {
executor.prestartAllCoreThreads();
}
// Wrap executor with an unconfigurable decorator.
this.exposedExecutor = (this.exposeUnconfigurableExecutor ?
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -94,6 +94,8 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
private boolean allowCoreThreadTimeOut = false;
private boolean prestartAllCoreThreads = false;
@Nullable
private TaskDecorator taskDecorator;
@@ -197,6 +199,16 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
}
/**
* Specify whether to start all core threads, causing them to idly wait for work.
* <p>Default is "false".
* @since 5.3.14
* @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads
*/
public void setPrestartAllCoreThreads(boolean prestartAllCoreThreads) {
this.prestartAllCoreThreads = prestartAllCoreThreads;
}
/**
* Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable}
* about to be executed.
@@ -256,6 +268,9 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
if (this.prestartAllCoreThreads) {
executor.prestartAllCoreThreads();
}
this.threadPoolExecutor = executor;
return executor;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -418,11 +418,13 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Register fields that should be allowed for binding. Default is all
* fields. Restrict this for example to avoid unwanted modifications
* by malicious users when binding HTTP request parameters.
* <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
* can be implemented by overriding the {@code isAllowed} method.
* Register fields that should be allowed for binding. Default is all fields.
* Restrict this for example to avoid unwanted modifications by malicious
* users when binding HTTP request parameters.
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
* arbitrary number of pattern parts), as well as direct equality. More
* sophisticated matching can be implemented by overriding the
* {@code isAllowed} method.
* <p>Alternatively, specify a list of <i>disallowed</i> fields.
* @param allowedFields array of field names
* @see #setDisallowedFields
@@ -442,11 +444,13 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Register fields that should <i>not</i> be allowed for binding. Default is none.
* Mark fields as disallowed for example to avoid unwanted modifications
* by malicious users when binding HTTP request parameters.
* <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
* can be implemented by overriding the {@code isAllowed} method.
* Register fields that should <i>not</i> be allowed for binding. Default
* is none. Mark fields as disallowed for example to avoid unwanted
* modifications by malicious users when binding HTTP request parameters.
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
* arbitrary number of pattern parts), as well as direct equality.
* More sophisticated matching can be implemented by overriding the
* {@code isAllowed} method.
* <p>Alternatively, specify a list of <i>allowed</i> fields.
* @param disallowedFields array of field names
* @see #setAllowedFields
@@ -772,10 +776,11 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
/**
* Return if the given field is allowed for binding.
* Invoked for each passed-in property value.
* <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
* as well as direct equality, in the specified lists of allowed fields and
* disallowed fields. A field matching a disallowed pattern will not be accepted
* even if it also happens to match a pattern in the allowed list.
* <p>The default implementation checks for "xxx*", "*xxx", "*xxx*" and "xxx*yyy"
* matches (with an arbitrary number of pattern parts), as well as direct equality,
* in the specified lists of allowed fields and disallowed fields. A field matching
* a disallowed pattern will not be accepted even if it also happens to match a
* pattern in the allowed list.
* <p>Can be overridden in subclasses.
* @param field the field to check
* @return if the field is allowed
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,10 +65,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException;
/**
* @since 13.03.2003
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 13.03.2003
*/
public class ProxyFactoryBeanTests {
@@ -633,20 +633,50 @@ public class ProxyFactoryBeanTests {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(FROZEN_CONTEXT, CLASS));
Advised advised = (Advised)bf.getBean("frozen");
Advised advised = (Advised) bf.getBean("frozen");
assertThat(advised.isFrozen()).as("The proxy should be frozen").isTrue();
}
@Test
public void testDetectsInterfaces() throws Exception {
public void testDetectsInterfaces() {
ProxyFactoryBean fb = new ProxyFactoryBean();
fb.setTarget(new TestBean());
fb.addAdvice(new DebugInterceptor());
fb.setBeanFactory(new DefaultListableBeanFactory());
ITestBean proxy = (ITestBean) fb.getObject();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue();
}
@Test
public void testWithInterceptorNames() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton("debug", new DebugInterceptor());
ProxyFactoryBean fb = new ProxyFactoryBean();
fb.setTarget(new TestBean());
fb.setInterceptorNames("debug");
fb.setBeanFactory(bf);
Advised proxy = (Advised) fb.getObject();
assertThat(proxy.getAdvisorCount()).isEqualTo(1);
}
@Test
public void testWithLateInterceptorNames() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton("debug", new DebugInterceptor());
ProxyFactoryBean fb = new ProxyFactoryBean();
fb.setTarget(new TestBean());
fb.setBeanFactory(bf);
fb.getObject();
fb.setInterceptorNames("debug");
Advised proxy = (Advised) fb.getObject();
assertThat(proxy.getAdvisorCount()).isEqualTo(1);
}
/**
* Fires only on void methods. Saves list of methods intercepted.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
@@ -306,7 +306,7 @@ public class CacheReproTests {
@Configuration
@EnableCaching
public static class Spr13081Config extends CachingConfigurerSupport {
public static class Spr13081Config implements CachingConfigurer {
@Bean
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -83,6 +83,19 @@ public class EnableCachingIntegrationTests {
assertCacheHit(key, value, cache);
}
@Test
public void barServiceWithCacheableInterfaceCglib() {
this.context = new AnnotationConfigApplicationContext(BarConfigCglib.class);
BarService service = this.context.getBean(BarService.class);
Cache cache = getCache();
Object key = new Object();
assertCacheMiss(key, cache);
Object value = service.getSimple(key);
assertCacheHit(key, value, cache);
}
@Test
public void beanConditionOff() {
this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class);
@@ -124,7 +137,7 @@ public class EnableCachingIntegrationTests {
@Configuration
static class SharedConfig extends CachingConfigurerSupport {
static class SharedConfig implements CachingConfigurer {
@Override
@Bean
@@ -185,6 +198,36 @@ public class EnableCachingIntegrationTests {
}
@Configuration
@Import(SharedConfig.class)
@EnableCaching(proxyTargetClass = true)
static class BarConfigCglib {
@Bean
public BarService barService() {
return new BarServiceImpl();
}
}
interface BarService {
@Cacheable(cacheNames = "testCache")
Object getSimple(Object key);
}
static class BarServiceImpl implements BarService {
private final AtomicLong counter = new AtomicLong();
@Override
public Object getSimple(Object key) {
return this.counter.getAndIncrement();
}
}
@Configuration
@Import(FooConfig.class)
@EnableCaching
@@ -18,11 +18,10 @@ package org.springframework.cache.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheInterceptor;
@@ -107,11 +106,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
try {
ctx.refresh();
}
catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
assertThat(root.getMessage().contains("implementations of CachingConfigurer")).isTrue();
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
}
}
@@ -150,7 +146,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
@Configuration
@EnableCaching
static class EnableCachingConfig extends CachingConfigurerSupport {
static class EnableCachingConfig implements CachingConfigurer {
@Override
@Bean
@@ -227,7 +223,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
@Configuration
@EnableCaching
static class MultiCacheManagerConfigurer extends CachingConfigurerSupport {
static class MultiCacheManagerConfigurer implements CachingConfigurer {
@Bean
public CacheManager cm1() {
@@ -253,7 +249,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
@Configuration
@EnableCaching
static class EmptyConfigSupportConfig extends CachingConfigurerSupport {
static class EmptyConfigSupportConfig implements CachingConfigurer {
@Bean
public CacheManager cm() {
@@ -264,7 +260,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
@Configuration
@EnableCaching
static class FullCachingConfig extends CachingConfigurerSupport {
static class FullCachingConfig implements CachingConfigurer {
@Override
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.ConfigurableApplicationContext;
@@ -122,7 +122,7 @@ public class ExpressionCachingIntegrationTests {
@Configuration
@EnableCaching
static class SharedConfig extends CachingConfigurerSupport {
static class SharedConfig implements CachingConfigurer {
@Override
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
@@ -170,7 +170,7 @@ public class CacheErrorHandlerTests {
@Configuration
@EnableCaching
static class Config extends CachingConfigurerSupport {
static class Config implements CachingConfigurer {
@Bean
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.ConfigurableApplicationContext;
@@ -106,7 +106,7 @@ public class CachePutEvaluationTests {
@Configuration
@EnableCaching
static class Config extends CachingConfigurerSupport {
static class Config implements CachingConfigurer {
@Bean
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -149,7 +149,7 @@ public class CacheResolverCustomizationTests {
@Configuration
@EnableCaching
static class Config extends CachingConfigurerSupport {
static class Config implements CachingConfigurer {
@Override
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -133,7 +133,7 @@ public class CacheSyncFailureTests {
@Configuration
@EnableCaching
static class Config extends CachingConfigurerSupport {
static class Config implements CachingConfigurer {
@Override
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -218,8 +218,8 @@ public class AsyncAnnotationBeanPostProcessorTests {
private void assertFutureWithException(Future<Object> result,
TestableAsyncUncaughtExceptionHandler exceptionHandler) {
assertThatExceptionOfType(ExecutionException.class).isThrownBy(
result::get)
.withCauseExactlyInstanceOf(UnsupportedOperationException.class);
result::get)
.withCauseExactlyInstanceOf(UnsupportedOperationException.class);
assertThat(exceptionHandler.isCalled()).as("handler should never be called with Future return type").isFalse();
}
@@ -343,7 +343,7 @@ public class AsyncAnnotationBeanPostProcessorTests {
@Configuration
@EnableAsync
static class ConfigWithExceptionHandler extends AsyncConfigurerSupport {
static class ConfigWithExceptionHandler implements AsyncConfigurer {
@Bean
public ITestBean target() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,12 @@
package org.springframework.scheduling.concurrent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import org.junit.jupiter.api.Test;
@@ -25,10 +29,16 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link ThreadPoolExecutorFactoryBean}.
*
* @author Juergen Hoeller
*/
class ThreadPoolExecutorFactoryBeanTests {
@@ -44,6 +54,28 @@ class ThreadPoolExecutorFactoryBeanTests {
context.close();
}
@Test
void executorWithDefaultSettingsDoesNotPrestartAllCoreThreads() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, TestThreadPoolExecutorFactoryBean::new);
context.refresh();
ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class);
verify(threadPoolExecutor, never()).prestartAllCoreThreads();
}
@Test
void executorWithPrestartAllCoreThreads() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("taskExecutor", ThreadPoolExecutorFactoryBean.class, () -> {
TestThreadPoolExecutorFactoryBean factoryBean = new TestThreadPoolExecutorFactoryBean();
factoryBean.setPrestartAllCoreThreads(true);
return factoryBean;
});
context.refresh();
ThreadPoolExecutor threadPoolExecutor = context.getBean(ThreadPoolExecutor.class);
verify(threadPoolExecutor).prestartAllCoreThreads();
}
@Configuration
static class ExecutorConfig {
@@ -52,7 +84,19 @@ class ThreadPoolExecutorFactoryBeanTests {
ThreadPoolExecutorFactoryBean executor() {
return new ThreadPoolExecutorFactoryBean();
}
}
@SuppressWarnings("serial")
private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean {
@Override
protected ThreadPoolExecutor createExecutor(
int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue<Runnable> queue,
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
return mock(ThreadPoolExecutor.class);
}
}
}
@@ -194,7 +194,7 @@ public class ClassReader {
this.b = classFileBuffer;
// Check the class' major_version. This field is after the magic and minor_version fields, which
// use 4 and 2 bytes respectively.
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V18) {
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V19) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
@@ -283,6 +283,7 @@ public interface Opcodes {
int V16 = 0 << 16 | 60;
int V17 = 0 << 16 | 61;
int V18 = 0 << 16 | 62;
int V19 = 0 << 16 | 63;
/**
* Version flag indicating that the class is using 'preview' features.
@@ -17,10 +17,12 @@
package org.springframework.core.log;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Utility methods for formatting and logging messages.
@@ -35,10 +37,15 @@ import org.springframework.lang.Nullable;
*/
public abstract class LogFormatUtils {
private static final Pattern NEWLINE_PATTERN = Pattern.compile("[\n\r]");
private static final Pattern CONTROL_CHARACTER_PATTERN = Pattern.compile("\\p{Cc}");
/**
* Convenience variant of {@link #formatValue(Object, int, boolean)} that
* limits the length of a log message to 100 characters and also replaces
* newline characters if {@code limitLength} is set to "true".
* newline and control characters if {@code limitLength} is set to "true".
* @param value the value to format
* @param limitLength whether to truncate the value at a length of 100
* @return the formatted value
@@ -53,25 +60,29 @@ public abstract class LogFormatUtils {
* compacting it into a single line when {@code replaceNewLines} is set.
* @param value the value to be formatted
* @param maxLength the max length, after which to truncate, or -1 for unlimited
* @param replaceNewlines whether to replace newline characters with placeholders
* @param replaceNewlinesAndControlCharacters whether to replace newline and
* control characters with placeholders
* @return the formatted value
*/
public static String formatValue(@Nullable Object value, int maxLength, boolean replaceNewlines) {
public static String formatValue(
@Nullable Object value, int maxLength, boolean replaceNewlinesAndControlCharacters) {
if (value == null) {
return "";
}
String result;
try {
result = value.toString();
result = ObjectUtils.nullSafeToString(value);
}
catch (Throwable ex) {
result = ex.toString();
result = ObjectUtils.nullSafeToString(ex);
}
if (maxLength != -1) {
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
}
if (replaceNewlines) {
result = result.replace("\n", "<LF>").replace("\r", "<CR>");
if (replaceNewlinesAndControlCharacters) {
result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");
result = CONTROL_CHARACTER_PATTERN.matcher(result).replaceAll("?");
}
if (value instanceof CharSequence) {
result = "\"" + result + "\"";
@@ -162,7 +162,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
}
}
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
return (annotatedMethods != null ? annotatedMethods : Collections.emptySet());
}
@@ -127,6 +127,11 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
return this.memberClassNames.clone();
}
@Override
public MergedAnnotations getAnnotations() {
return this.annotations;
}
@Override
public Set<String> getAnnotationTypes() {
Set<String> annotationTypes = this.annotationTypes;
@@ -149,13 +154,9 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
annotatedMethods.add(annotatedMethod);
}
}
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
return (annotatedMethods != null ? annotatedMethods : Collections.emptySet());
}
@Override
public MergedAnnotations getAnnotations() {
return this.annotations;
}
@Override
public boolean equals(@Nullable Object obj) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -99,8 +99,7 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
}
@Override
public void visitInnerClass(String name, @Nullable String outerName, String innerName,
int access) {
public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {
if (outerName != null) {
String className = toClassName(name);
String outerClassName = toClassName(outerName);
@@ -733,6 +733,10 @@ public class AntPathMatcher implements PathMatcher {
}
for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
if (name.startsWith("*")) {
throw new IllegalArgumentException("Capturing patterns (" + name + ") are not " +
"supported by the AntPathMatcher. Use the PathPatternParser instead.");
}
String value = matcher.group(i);
uriTemplateVariables.put(name, value);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1159,6 +1159,20 @@ class ResolvableTypeTests {
assertThatResolvableType(complex4).isNotAssignableFrom(complex3);
}
@Test
void identifyTypeVariable() throws Exception {
Method method = ClassArguments.class.getMethod("typedArgumentFirst", Class.class, Class.class, Class.class);
ResolvableType returnType = ResolvableType.forMethodReturnType(method, ClassArguments.class);
ResolvableType arg0 = ResolvableType.forMethodParameter(method, 0, ClassArguments.class);
ResolvableType arg1 = ResolvableType.forMethodParameter(method, 1, ClassArguments.class);
ResolvableType arg2 = ResolvableType.forMethodParameter(method, 2, ClassArguments.class);
assertThat(returnType.getType().equals(arg0.as(Class.class).getGeneric(0).getType())).isTrue();
assertThat(returnType.getType().equals(arg1.as(Class.class).getGeneric(0).getType())).isFalse();
assertThat(returnType.getType().equals(arg2.as(Class.class).getGeneric(0).getType())).isFalse();
}
@Test
void hashCodeAndEquals() throws Exception {
ResolvableType forClass = ResolvableType.forClass(List.class);
@@ -1427,6 +1441,10 @@ class ResolvableTypeTests {
}
interface TypedMethods extends Methods<String> {
}
static class AssignmentBase<O, C, S> {
public O o;
@@ -1479,7 +1497,9 @@ class ResolvableTypeTests {
}
interface TypedMethods extends Methods<String> {
interface ClassArguments {
<T> T typedArgumentFirst(Class<T> arg0, Class<?> arg1, Class<Object> arg2);
}
@@ -20,6 +20,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
@@ -290,20 +291,29 @@ public abstract class ReflectionHelper {
Object argument = arguments[varargsPosition];
TypeDescriptor targetType = new TypeDescriptor(methodParam);
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
// If the argument type is equal to the varargs element type, there is no need
// to convert it or wrap it in an array. For example, using StringToArrayConverter
// to convert a String containing a comma would result in the String being split
// and repackaged in an array when it should be used as-is.
if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
if (argument == null) {
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) {
arguments[varargsPosition] = Optional.empty();
conversionOccurred = true;
}
}
// If the argument type is equal to the varargs element type, there is no need to
// convert it or wrap it in an array. For example, using StringToArrayConverter to
// convert a String containing a comma would result in the String being split and
// repackaged in an array when it should be used as-is.
else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
}
// Three outcomes of the above if-block:
// 1) the input argument was correct type but not wrapped in an array, and nothing was done.
// 2) the input argument was already compatible (i.e., array of valid type), and nothing was done.
// 3) the input argument was the wrong type and got converted and wrapped in an array.
// Possible outcomes of the above if-else block:
// 1) the input argument was null, and nothing was done.
// 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
// 5) the input argument was the wrong type and got converted and wrapped in an array.
if (argument != arguments[varargsPosition] &&
!isFirstEntryInArray(argument, arguments[varargsPosition])) {
conversionOccurred = true; // case 3
conversionOccurred = true; // case 5
}
}
// Otherwise, convert remaining arguments to the varargs element type.
@@ -234,26 +234,33 @@ public class MethodInvocationTests extends AbstractExpressionTests {
@Test
public void testVarargsInvocation01() {
// Calling 'public int aVarargsMethod(String... strings)' - returns number of arguments
evaluate("aVarargsMethod('a','b','c')", 3, Integer.class);
evaluate("aVarargsMethod('a')", 1, Integer.class);
evaluate("aVarargsMethod()", 0, Integer.class);
evaluate("aVarargsMethod(1,2,3)", 3, Integer.class); // all need converting to strings
evaluate("aVarargsMethod(1)", 1, Integer.class); // needs string conversion
evaluate("aVarargsMethod(1,'a',3.0d)", 3, Integer.class); // first and last need conversion
evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class);
// Calling 'public String aVarargsMethod(String... strings)'
evaluate("aVarargsMethod('a','b','c')", "[a, b, c]", String.class);
evaluate("aVarargsMethod('a')", "[a]", String.class);
evaluate("aVarargsMethod()", "[]", String.class);
evaluate("aVarargsMethod(1,2,3)", "[1, 2, 3]", String.class); // all need converting to strings
evaluate("aVarargsMethod(1)", "[1]", String.class); // needs string conversion
evaluate("aVarargsMethod(1,'a',3.0d)", "[1, a, 3.0]", String.class); // first and last need conversion
evaluate("aVarargsMethod(new String[]{'a','b','c'})", "[a, b, c]", String.class);
evaluate("aVarargsMethod(new String[]{})", "[]", String.class);
evaluate("aVarargsMethod(null)", "[null]", String.class);
evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class);
evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class);
}
@Test
public void testVarargsInvocation02() {
// Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int + length_of_strings
evaluate("aVarargsMethod2(5,'a','b','c')", 8, Integer.class);
evaluate("aVarargsMethod2(2,'a')", 3, Integer.class);
evaluate("aVarargsMethod2(4)", 4, Integer.class);
evaluate("aVarargsMethod2(8,2,3)", 10, Integer.class);
evaluate("aVarargsMethod2(9)", 9, Integer.class);
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
// Calling 'public String aVarargsMethod2(int i, String... strings)'
evaluate("aVarargsMethod2(5,'a','b','c')", "5-[a, b, c]", String.class);
evaluate("aVarargsMethod2(2,'a')", "2-[a]", String.class);
evaluate("aVarargsMethod2(4)", "4-[]", String.class);
evaluate("aVarargsMethod2(8,2,3)", "8-[2, 3]", String.class);
evaluate("aVarargsMethod2(2,'a',3.0d)", "2-[a, 3.0]", String.class);
evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", "8-[a, b, c]", String.class);
evaluate("aVarargsMethod2(8,new String[]{})", "8-[]", String.class);
evaluate("aVarargsMethod2(8,null)", "8-[null]", String.class);
evaluate("aVarargsMethod2(8,null,'a')", "8-[null, a]", String.class);
evaluate("aVarargsMethod2(8,'a',null,'b')", "8-[a, null, b]", String.class);
}
@Test
@@ -284,6 +291,22 @@ public class MethodInvocationTests extends AbstractExpressionTests {
evaluate("aVarargsMethod3('foo', 'bar,baz')", "foo-bar,baz", String.class);
}
@Test
public void testVarargsOptionalInvocation() {
// Calling 'public String optionalVarargsMethod(Optional<String>... values)'
evaluate("optionalVarargsMethod()", "[]", String.class);
evaluate("optionalVarargsMethod(new String[0])", "[]", String.class);
evaluate("optionalVarargsMethod('a')", "[Optional[a]]", String.class);
evaluate("optionalVarargsMethod('a','b','c')", "[Optional[a], Optional[b], Optional[c]]", String.class);
evaluate("optionalVarargsMethod(9)", "[Optional[9]]", String.class);
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class);
evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class);
evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class);
evaluate("optionalVarargsMethod(null)", "[Optional.empty]", String.class);
evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class);
evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class);
}
@Test
public void testInvocationOnNullContextObject() {
evaluateAndCheckError("null.toString()",SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.expression.spel;
import java.util.Arrays;
import java.util.GregorianCalendar;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -51,10 +52,10 @@ public class TestScenarioCreator {
TestScenarioCreator.class.getDeclaredMethod("reverseInt", Integer.TYPE, Integer.TYPE, Integer.TYPE));
testContext.registerFunction("reverseString",
TestScenarioCreator.class.getDeclaredMethod("reverseString", String.class));
testContext.registerFunction("varargsFunctionReverseStringsAndMerge",
TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge", String[].class));
testContext.registerFunction("varargsFunctionReverseStringsAndMerge2",
TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge2", Integer.TYPE, String[].class));
testContext.registerFunction("varargsFunction",
TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class));
testContext.registerFunction("varargsFunction2",
TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", Integer.TYPE, String[].class));
}
catch (Exception ex) {
throw new IllegalStateException(ex);
@@ -108,25 +109,12 @@ public class TestScenarioCreator {
return backwards.toString();
}
public static String varargsFunctionReverseStringsAndMerge(String... strings) {
StringBuilder sb = new StringBuilder();
if (strings != null) {
for (int i = strings.length - 1; i >= 0; i--) {
sb.append(strings[i]);
}
}
return sb.toString();
public static String varargsFunction(String... strings) {
return Arrays.toString(strings);
}
public static String varargsFunctionReverseStringsAndMerge2(int j, String... strings) {
StringBuilder sb = new StringBuilder();
sb.append(j);
if (strings != null) {
for (int i = strings.length - 1; i >= 0; i--) {
sb.append(strings[i]);
}
}
return sb.toString();
public static String varargsFunction2(int i, String... strings) {
return String.valueOf(i) + "-" + Arrays.toString(strings);
}
}
@@ -59,21 +59,33 @@ public class VariableAndFunctionTests extends AbstractExpressionTests {
@Test
public void testCallVarargsFunction() {
evaluate("#varargsFunctionReverseStringsAndMerge('a,b')", "a,b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('a', 'b,c', 'd')", "db,ca", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('a','b','c')", "cba", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('a')", "a", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge()", "", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('b',25)", "25b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge(25)", "25", String.class);
evaluate("#varargsFunction()", "[]", String.class);
evaluate("#varargsFunction(new String[0])", "[]", String.class);
evaluate("#varargsFunction('a')", "[a]", String.class);
evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class);
// Conversion from int to String
evaluate("#varargsFunction(25)", "[25]", String.class);
evaluate("#varargsFunction('b',25)", "[b, 25]", String.class);
// Strings that contain a comma
evaluate("#varargsFunction('a,b')", "[a,b]", String.class);
evaluate("#varargsFunction('a', 'x,y', 'd')", "[a, x,y, d]", String.class);
// null values
evaluate("#varargsFunction(null)", "[null]", String.class);
evaluate("#varargsFunction('a',null,'b')", "[a, null, b]", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(1, 'a,b')", "1a,b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(1, 'a', 'b,c', 'd')", "1db,ca", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(2,'a')", "2a", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(3)", "3", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class);
evaluate("#varargsFunction2(9)", "9-[]", String.class);
evaluate("#varargsFunction2(9, new String[0])", "9-[]", String.class);
evaluate("#varargsFunction2(9,'a')", "9-[a]", String.class);
evaluate("#varargsFunction2(9,'a','b','c')", "9-[a, b, c]", String.class);
// Conversion from int to String
evaluate("#varargsFunction2(9,25)", "9-[25]", String.class);
evaluate("#varargsFunction2(9,'b',25)", "9-[b, 25]", String.class);
// Strings that contain a comma:
evaluate("#varargsFunction2(9, 'a,b')", "9-[a,b]", String.class);
evaluate("#varargsFunction2(9, 'a', 'x,y', 'd')", "9-[a, x,y, d]", String.class);
// null values
evaluate("#varargsFunction2(9,null)", "9-[null]", String.class);
evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class);
}
@Test
@@ -17,11 +17,13 @@
package org.springframework.expression.spel.testresources;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.util.ObjectUtils;
@@ -191,16 +193,17 @@ public class Inventor {
return a + b + c;
}
public int aVarargsMethod(String... strings) {
if (strings == null)
return 0;
return strings.length;
public String aVarargsMethod(String... strings) {
return Arrays.toString(strings);
}
public int aVarargsMethod2(int i, String... strings) {
if (strings == null)
return i;
return strings.length + i;
public String aVarargsMethod2(int i, String... strings) {
return String.valueOf(i) + "-" + Arrays.toString(strings);
}
@SuppressWarnings("unchecked")
public String optionalVarargsMethod(Optional<String>... values) {
return Arrays.toString(values);
}
public String aVarargsMethod3(String str1, String... strings) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -87,6 +87,8 @@ public abstract class StoredProcedure extends SqlCall {
* they appear in the database's stored procedure parameter list.</b>
* <p>Names are purely used to help mapping.
* @param param the parameter object
* @throws InvalidDataAccessApiUsageException if the parameter has no name, or if the
* operation is already compiled, and hence cannot be configured further
*/
@Override
public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
@@ -0,0 +1,161 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.jdbc.datasource.lookup;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link AbstractRoutingDataSource}.
*
* @author Kazuki Shimizu
*/
class AbstractRoutingDataSourceTests {
@Test
void setTargetDataSources() {
final ThreadLocal<String> lookupKey = new ThreadLocal<>();
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return lookupKey.get();
}
};
DataSource ds1 = new StubDataSource();
DataSource ds2 = new StubDataSource();
MapDataSourceLookup dataSourceLookup = new MapDataSourceLookup();
dataSourceLookup.addDataSource("dataSource2", ds2);
routingDataSource.setDataSourceLookup(dataSourceLookup);
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("ds1", ds1);
targetDataSources.put("ds2", "dataSource2");
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.afterPropertiesSet();
lookupKey.set("ds1");
assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds1);
lookupKey.set("ds2");
assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds2);
}
@Test
void targetDataSourcesIsNull() {
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
};
assertThatIllegalArgumentException().isThrownBy(routingDataSource::afterPropertiesSet)
.withMessage("Property 'targetDataSources' is required");
}
@Test
void dataSourceIsUnSupportedType() {
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
};
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("ds1", 1);
routingDataSource.setTargetDataSources(targetDataSources);
assertThatIllegalArgumentException().isThrownBy(routingDataSource::afterPropertiesSet)
.withMessage("Illegal data source value - only [javax.sql.DataSource] and String supported: 1");
}
@Test
void setDefaultTargetDataSource() {
final ThreadLocal<String> lookupKey = new ThreadLocal<>();
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return lookupKey.get();
}
};
DataSource ds = new StubDataSource();
routingDataSource.setTargetDataSources(new HashMap<>());
routingDataSource.setDefaultTargetDataSource(ds);
routingDataSource.afterPropertiesSet();
lookupKey.set("foo");
assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds);
}
@Test
void setDefaultTargetDataSourceFallbackIsFalse() {
final ThreadLocal<String> lookupKey = new ThreadLocal<>();
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return lookupKey.get();
}
};
DataSource ds = new StubDataSource();
routingDataSource.setTargetDataSources(new HashMap<>());
routingDataSource.setDefaultTargetDataSource(ds);
routingDataSource.setLenientFallback(false);
routingDataSource.afterPropertiesSet();
lookupKey.set("foo");
assertThatIllegalStateException().isThrownBy(routingDataSource::determineTargetDataSource)
.withMessage("Cannot determine target DataSource for lookup key [foo]");
}
@Test
void setDefaultTargetDataSourceLookupKeyIsNullWhenFallbackIsFalse() {
final ThreadLocal<String> lookupKey = new ThreadLocal<>();
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return lookupKey.get();
}
};
DataSource ds = new StubDataSource();
routingDataSource.setTargetDataSources(new HashMap<>());
routingDataSource.setDefaultTargetDataSource(ds);
routingDataSource.setLenientFallback(false);
routingDataSource.afterPropertiesSet();
lookupKey.set(null);
assertThat(routingDataSource.determineTargetDataSource()).isSameAs(ds);
}
@Test
public void notInitialized() {
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
};
assertThatIllegalArgumentException().isThrownBy(routingDataSource::determineTargetDataSource)
.withMessage("DataSource router not initialized");
}
}
@@ -56,6 +56,7 @@ import com.thoughtworks.xstream.io.xml.DomReader;
import com.thoughtworks.xstream.io.xml.DomWriter;
import com.thoughtworks.xstream.io.xml.QNameMap;
import com.thoughtworks.xstream.io.xml.SaxWriter;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import com.thoughtworks.xstream.io.xml.StaxReader;
import com.thoughtworks.xstream.io.xml.StaxWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
@@ -696,7 +697,14 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo
@Override
protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
try {
doMarshal(graph, new StaxWriter(new QNameMap(), streamWriter, this.nameCoder), null);
StaxWriter writer;
if (this.streamDriver instanceof StaxDriver) {
writer = ((StaxDriver) this.streamDriver).createStaxWriter(streamWriter);
}
else {
writer = new StaxWriter(new QNameMap(), streamWriter, this.nameCoder);
}
doMarshal(graph, writer, null);
}
catch (XMLStreamException ex) {
throw convertXStreamException(ex, true);
@@ -43,6 +43,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
import com.thoughtworks.xstream.io.xml.QNameMap;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import com.thoughtworks.xstream.security.AnyTypePermission;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -176,6 +178,23 @@ class XStreamMarshallerTests {
assertThat(XmlContent.from(writer)).isSimilarTo(EXPECTED_STRING);
}
@Test
void marshalStaxResultXMLStreamWriterDefaultNamespace() throws Exception {
QNameMap map = new QNameMap();
map.setDefaultNamespace("https://example.com");
map.setDefaultPrefix("spr");
StaxDriver driver = new StaxDriver(map);
marshaller.setStreamDriver(driver);
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
StringWriter writer = new StringWriter();
XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(writer);
Result result = StaxUtils.createStaxResult(streamWriter);
marshaller.marshal(flight, result);
assertThat(XmlContent.from(writer)).isSimilarTo(
"<spr:flight xmlns:spr=\"https://example.com\"><spr:flightNumber>42</spr:flightNumber></spr:flight>");
}
@Test
void marshalStaxResultXMLEventWriter() throws Exception {
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
-3
View File
@@ -60,9 +60,6 @@ dependencies {
testImplementation("org.hibernate:hibernate-core")
testImplementation("org.hibernate:hibernate-validator")
testImplementation("javax.validation:validation-api")
testImplementation("org.junit.platform:junit-platform-runner") {
exclude group: "junit", module: "junit"
}
testImplementation("org.junit.platform:junit-platform-testkit")
testImplementation("com.fasterxml.jackson.core:jackson-databind")
testImplementation("com.thoughtworks.xstream:xstream")
@@ -112,8 +112,8 @@ public class MergedContextConfiguration implements Serializable {
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved {@code ContextLoader}
*/
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
String[] activeProfiles, ContextLoader contextLoader) {
public MergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes,
@Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader) {
this(testClass, locations, classes, null, activeProfiles, contextLoader);
}
@@ -128,9 +128,9 @@ public class MergedContextConfiguration implements Serializable {
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved {@code ContextLoader}
*/
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
public MergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes,
@Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader) {
@Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader) {
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader, null, null);
}
@@ -149,9 +149,9 @@ public class MergedContextConfiguration implements Serializable {
* @param parent the parent configuration or {@code null} if there is no parent
* @since 3.2.2
*/
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
public MergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes,
@Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader,
@Nullable String[] activeProfiles, @Nullable ContextLoader contextLoader,
@Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
@Nullable MergedContextConfiguration parent) {
@@ -197,7 +197,7 @@ public class MergedContextConfiguration implements Serializable {
public MergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes,
@Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses,
@Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations,
@Nullable String[] propertySourceProperties, ContextLoader contextLoader,
@Nullable String[] propertySourceProperties, @Nullable ContextLoader contextLoader,
@Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
@Nullable MergedContextConfiguration parent) {
@@ -235,7 +235,7 @@ public class MergedContextConfiguration implements Serializable {
@Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses,
@Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations,
@Nullable String[] propertySourceProperties, @Nullable Set<ContextCustomizer> contextCustomizers,
ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
@Nullable ContextLoader contextLoader, @Nullable CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
@Nullable MergedContextConfiguration parent) {
this.testClass = testClass;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -193,7 +193,7 @@ public class ContentRequestMatchers {
* <li>{@link Resource} - content from a file
* <li>{@code byte[]} - other raw content
* </ul>
* <p><strong>Note:</strong> This method uses the Apache Commons File Upload
* <p><strong>Note:</strong> This method uses the Apache Commons FileUpload
* library to parse the multipart data and it must be on the test classpath.
* @param expectedMap the expected multipart values
* @since 5.3
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -314,6 +314,15 @@ class StubWebApplicationContext implements WebApplicationContext {
return this.beanFactory.findAnnotationOnBean(beanName, annotationType);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
return this.beanFactory.findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
@@ -73,13 +73,17 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
private static final CountDownLatch countDownLatch = new CountDownLatch(1);
private final TestContextManager testContextManager = new TestContextManager(ExampleTestCase.class);
private final TestContext testContext = testContextManager.getTestContext();
// Note that the following invocation of getApplicationContext() forces eager
// loading of the test's ApplicationContext which consequently results in the
// publication of all test execution events. Otherwise, TestContext#publishEvent
// would never fire any events for ExampleTestCase.
private final TestExecutionListener listener = testContext.getApplicationContext().getBean(TestExecutionListener.class);
private final Object testInstance = new ExampleTestCase();
private final Method traceableTestMethod = ReflectionUtils.findMethod(ExampleTestCase.class, "traceableTest");
@@ -127,8 +131,8 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
public void beforeTestMethodAnnotationWithFailingEventListener() throws Exception {
Method method = ReflectionUtils.findMethod(ExampleTestCase.class, "testWithFailingEventListener");
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() ->
testContextManager.beforeTestMethod(testInstance, method))
.withMessageContaining("Boom!");
testContextManager.beforeTestMethod(testInstance, method))
.withMessageContaining("Boom!");
verify(listener, only()).beforeTestMethod(testContext);
}
@@ -149,7 +153,7 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
verify(listener, only()).beforeTestMethod(testContext);
assertThat(TrackingAsyncUncaughtExceptionHandler.asyncException.getMessage())
.startsWith("Asynchronous exception for test method [" + methodName + "] in thread [" + THREAD_NAME_PREFIX);
.startsWith("Asynchronous exception for test method [" + methodName + "] in thread [" + THREAD_NAME_PREFIX);
}
@Test
@@ -211,7 +215,7 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
@Configuration
@EnableAsync(proxyTargetClass = true)
static class TestEventListenerConfiguration extends AsyncConfigurerSupport {
static class TestEventListenerConfiguration implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
@@ -306,7 +310,7 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
public void beforeTestMethodWithAsyncFailure(BeforeTestMethodEvent event) throws Exception {
this.listener.beforeTestMethod(event.getSource());
throw new RuntimeException(String.format("Asynchronous exception for test method [%s] in thread [%s]",
event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName()));
event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName()));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ public abstract class AbstractTransactionManagementConfiguration implements Impo
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableTx = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName()));
if (this.enableTx == null) {
throw new IllegalArgumentException(
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -163,7 +163,7 @@ public abstract class AbstractFallbackTransactionAttributeSource
*/
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// Don't allow non-public methods, as configured.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,18 +73,21 @@ public class ReactorClientHttpConnector implements ClientHttpConnector {
* @since 5.1
*/
public ReactorClientHttpConnector(ReactorResourceFactory factory, Function<HttpClient, HttpClient> mapper) {
this.httpClient = defaultInitializer.andThen(mapper).apply(initHttpClient(factory));
ConnectionProvider provider = factory.getConnectionProvider();
Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?");
this.httpClient = defaultInitializer.andThen(mapper).andThen(applyLoopResources(factory))
.apply(HttpClient.create(provider));
}
@SuppressWarnings("deprecation")
private static HttpClient initHttpClient(ReactorResourceFactory resourceFactory) {
ConnectionProvider provider = resourceFactory.getConnectionProvider();
LoopResources resources = resourceFactory.getLoopResources();
Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?");
Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?");
return HttpClient.create(provider).tcpConfiguration(tcpClient -> tcpClient.runOn(resources));
private static Function<HttpClient, HttpClient> applyLoopResources(ReactorResourceFactory factory) {
return httpClient -> {
LoopResources resources = factory.getLoopResources();
Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?");
return httpClient.runOn(resources);
};
}
/**
* Constructor with a pre-configured {@code HttpClient} instance.
* @param httpClient the client to use
@@ -254,7 +254,7 @@ public abstract class AbstractListenerWriteProcessor<T> implements Processor<T,
* the next item from the upstream, write Publisher.
* <p>The default implementation is a no-op.
* @deprecated originally introduced for Undertow to stop write notifications
* when no data is available, but deprecated as of as of 5.0.6 since constant
* when no data is available, but deprecated as of 5.0.6 since constant
* switching on every requested item causes a significant slowdown.
*/
@Deprecated
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,9 +54,9 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
* of the {@code Filter.init} and {@code Filter.destroy} lifecycle methods
* on the target bean, letting the servlet container manage the filter lifecycle.
*
* <p>As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally accept
* constructor parameters when using Servlet 3.0's instance-based filter registration
* methods, usually in conjunction with Spring 3.1's
* <p>As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally
* accept constructor parameters when using a Servlet container's instance-based filter
* registration methods, usually in conjunction with Spring's
* {@link org.springframework.web.WebApplicationInitializer} SPI. These constructors allow
* for providing the delegate Filter bean directly, or providing the application context
* and bean name to fetch, avoiding the need to look up the application context from the
@@ -100,8 +100,7 @@ public class DelegatingFilterProxy extends GenericFilterBean {
/**
* Create a new {@code DelegatingFilterProxy}. For traditional (pre-Servlet 3.0) use
* in {@code web.xml}.
* Create a new {@code DelegatingFilterProxy}. For traditional use in {@code web.xml}.
* @see #setTargetBeanName(String)
*/
public DelegatingFilterProxy() {
@@ -111,8 +110,7 @@ public class DelegatingFilterProxy extends GenericFilterBean {
* Create a new {@code DelegatingFilterProxy} with the given {@link Filter} delegate.
* Bypasses entirely the need for interacting with a Spring application context,
* specifying the {@linkplain #setTargetBeanName target bean name}, etc.
* <p>For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* <p>For use with instance-based registration of filters.
* @param delegate the {@code Filter} instance that this proxy will delegate to and
* manage the lifecycle for (must not be {@code null}).
* @see #doFilter(ServletRequest, ServletResponse, FilterChain)
@@ -130,9 +128,8 @@ public class DelegatingFilterProxy extends GenericFilterBean {
* bean from the Spring {@code WebApplicationContext} found in the {@code ServletContext}
* (either the 'root' application context or the context named by
* {@link #setContextAttribute}).
* <p>For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* <p>The target bean must implement the standard Servlet Filter.
* <p>For use with instance-based registration of filters.
* <p>The target bean must implement the standard Servlet Filter interface.
* @param targetBeanName name of the target filter bean to look up in the Spring
* application context (must not be {@code null}).
* @see #findWebApplicationContext()
@@ -145,8 +142,7 @@ public class DelegatingFilterProxy extends GenericFilterBean {
/**
* Create a new {@code DelegatingFilterProxy} that will retrieve the named target
* bean from the given Spring {@code WebApplicationContext}.
* <p>For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* <p>For use with instance-based registration of filters.
* <p>The target bean must implement the standard Servlet Filter interface.
* <p>The given {@code WebApplicationContext} may or may not be refreshed when passed
* in. If it has not, and if the context implements {@link ConfigurableApplicationContext},
@@ -98,12 +98,10 @@ public abstract class OncePerRequestFilter extends GenericFilterBean {
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {
// Proceed without invoking this filter...
filterChain.doFilter(request, response);
}
else if (hasAlreadyFilteredAttribute) {
if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
return;
@@ -197,7 +195,7 @@ public abstract class OncePerRequestFilter extends GenericFilterBean {
* setting up thread locals or to perform final processing at the very end.
* <p>Note that although a filter can be mapped to handle specific dispatcher
* types via {@code web.xml} or in Java through the {@code ServletContext},
* servlet containers may enforce different defaults with regards to
* servlet containers may enforce different defaults with respect to
* dispatcher types. This flag enforces the design intent of the filter.
* <p>The default return value is "true", which means the filter will not be
* invoked during subsequent async dispatches. If "false", the filter will
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.springframework.http.MediaType;
* @author Rossen Stoyanchev
* @since 5.0
* @deprecated in favor of {@link UnsupportedMediaTypeStatusException},
* with this class never thrown by Spring code and to be removed in 5.3
* with this class never thrown by Spring code and to be removed in 6.0
*/
@Deprecated
@SuppressWarnings("serial")
@@ -48,7 +48,6 @@ import org.springframework.lang.Nullable;
* <p>Used e.g. by {@link org.springframework.web.filter.AbstractRequestLoggingFilter}.
* Note: As of Spring Framework 5.0, this wrapper is built on the Servlet 3.1 API.
*
*
* @author Juergen Hoeller
* @author Brian Clozel
* @since 4.1.3
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ class HtmlCharacterEntityDecoder {
boolean isPotentialReference = (this.nextPotentialReferencePosition != -1 &&
this.nextSemicolonPosition != -1 &&
this.nextPotentialReferencePosition - this.nextSemicolonPosition < MAX_REFERENCE_SIZE);
this.nextSemicolonPosition - this.nextPotentialReferencePosition < MAX_REFERENCE_SIZE);
if (isPotentialReference) {
break;
@@ -407,7 +407,7 @@ public abstract class UriUtils {
int paramIndex = path.indexOf(';', begin);
end = (paramIndex != -1 && paramIndex < end ? paramIndex : end);
int extIndex = path.lastIndexOf('.', end);
if (extIndex != -1 && extIndex > begin) {
if (extIndex != -1 && extIndex >= begin) {
return path.substring(extIndex + 1, end);
}
return null;
@@ -578,8 +578,8 @@ public class UrlPathHelper {
return UriUtils.decode(source, enc);
}
catch (UnsupportedCharsetException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
if (logger.isDebugEnabled()) {
logger.debug("Could not decode request string [" + source + "] with encoding '" + enc +
"': falling back to platform default encoding; exception message: " + ex.getMessage());
}
return URLDecoder.decode(source);
@@ -28,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class HtmlUtilsTests {
@Test
public void testHtmlEscape() {
void testHtmlEscape() {
String unescaped = "\"This is a quote'";
String escaped = HtmlUtils.htmlEscape(unescaped);
assertThat(escaped).isEqualTo("&quot;This is a quote&#39;");
@@ -39,14 +39,14 @@ public class HtmlUtilsTests {
}
@Test
public void testHtmlUnescape() {
void testHtmlUnescape() {
String escaped = "&quot;This is a quote&#39;";
String unescaped = HtmlUtils.htmlUnescape(escaped);
assertThat(unescaped).isEqualTo("\"This is a quote'");
}
@Test
public void testEncodeIntoHtmlCharacterSet() {
void testEncodeIntoHtmlCharacterSet() {
assertThat(HtmlUtils.htmlEscape("")).as("An empty string should be converted to an empty string").isEqualTo("");
assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters.");
@@ -62,7 +62,7 @@ public class HtmlUtilsTests {
// SPR-9293
@Test
public void testEncodeIntoHtmlCharacterSetFromUtf8() {
void testEncodeIntoHtmlCharacterSetFromUtf8() {
String utf8 = ("UTF-8");
assertThat(HtmlUtils.htmlEscape("", utf8)).as("An empty string should be converted to an empty string").isEqualTo("");
assertThat(HtmlUtils.htmlEscape("A sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("A sentence containing no special characters.");
@@ -74,7 +74,7 @@ public class HtmlUtilsTests {
}
@Test
public void testDecodeFromHtmlCharacterSet() {
void testDecodeFromHtmlCharacterSet() {
assertThat(HtmlUtils.htmlUnescape("")).as("An empty string should be converted to an empty string").isEqualTo("");
assertThat(HtmlUtils.htmlUnescape("This is a sentence containing no special characters.")).as("A string containing no special characters should not be affected").isEqualTo("This is a sentence containing no special characters.");
@@ -133,6 +133,7 @@ public class UriUtilsTests {
assertThat(UriUtils.extractFileExtension("/products;q=11/view.html?param=/path/a.do")).isEqualTo("html");
assertThat(UriUtils.extractFileExtension("/products;q=11/view.html;r=22?param=/path/a.do")).isEqualTo("html");
assertThat(UriUtils.extractFileExtension("/products;q=11/view.html;r=22;s=33?param=/path/a.do")).isEqualTo("html");
assertThat(UriUtils.extractFileExtension("/products/.html")).isEqualTo("html");
}
}
@@ -519,18 +519,15 @@ public interface WebClient {
* scenarios, for example to decode the response differently depending
* on the response status:
* <p><pre>
* Mono&lt;Object&gt; entityMono = client.get()
* Mono&lt;Person&gt; entityMono = client.get()
* .uri("/persons/1")
* .accept(MediaType.APPLICATION_JSON)
* .exchangeToMono(response -&gt; {
* if (response.statusCode().equals(HttpStatus.OK)) {
* return response.bodyToMono(Person.class);
* }
* else if (response.statusCode().is4xxClientError()) {
* return response.bodyToMono(ErrorContainer.class);
* }
* else {
* return Mono.error(response.createException());
* return response.createException().flatMap(Mono::error);
* }
* });
* </pre>
@@ -551,18 +548,15 @@ public interface WebClient {
* scenarios, for example to decode the response differently depending
* on the response status:
* <p><pre>
* Mono&lt;Object&gt; entityMono = client.get()
* Flux&lt;Person&gt; entityMono = client.get()
* .uri("/persons")
* .accept(MediaType.APPLICATION_JSON)
* .exchangeToFlux(response -&gt; {
* if (response.statusCode().equals(HttpStatus.OK)) {
* return response.bodyToFlux(Person.class);
* }
* else if (response.statusCode().is4xxClientError()) {
* return response.bodyToMono(ErrorContainer.class).flux();
* }
* else {
* return Flux.error(response.createException());
* return response.createException().flatMapMany(Mono::error);
* }
* });
* </pre>
@@ -362,9 +362,7 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
private static <K,V> void copy(MultiValueMap<K,V> src, MultiValueMap<K,V> dst) {
if (!src.isEmpty()) {
src.entrySet().stream()
.filter(entry -> !dst.containsKey(entry.getKey()))
.forEach(entry -> dst.put(entry.getKey(), entry.getValue()));
dst.putAll(src);
}
}
}
@@ -32,10 +32,12 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.Assert;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
@@ -1231,9 +1233,6 @@ public abstract class RouterFunctions {
private static class RouterFunctionWebHandler implements WebHandler {
private static final HandlerFunction<ServerResponse> NOT_FOUND_HANDLER =
request -> ServerResponse.notFound().build();
private final HandlerStrategies strategies;
private final RouterFunction<?> routerFunction;
@@ -1249,7 +1248,7 @@ public abstract class RouterFunctions {
ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders());
addAttributes(exchange, request);
return this.routerFunction.route(request)
.defaultIfEmpty(notFound())
.switchIfEmpty(createNotFoundError())
.flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request)))
.flatMap(response -> wrapException(() -> response.writeTo(exchange,
new HandlerStrategiesResponseContext(this.strategies))));
@@ -1261,9 +1260,9 @@ public abstract class RouterFunctions {
attributes.put(REQUEST_ATTRIBUTE, request);
}
@SuppressWarnings("unchecked")
private static <T extends ServerResponse> HandlerFunction<T> notFound() {
return (HandlerFunction<T>) NOT_FOUND_HANDLER;
private <R> Mono<R> createNotFoundError() {
return Mono.defer(() -> Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND,
"No matching router function")));
}
private static <T> Mono<T> wrapException(Supplier<Mono<T>> supplier) {
@@ -29,6 +29,7 @@ import reactor.core.publisher.Mono;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
@@ -119,11 +120,12 @@ public class PathResourceResolver extends AbstractResourceResolver {
return Mono.just(resource);
}
else if (logger.isWarnEnabled()) {
Resource[] allowedLocations = getAllowedLocations();
logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
"but resource \"" + resource.getURL() + "\" is neither under the " +
"current location \"" + location.getURL() + "\" nor under any of the " +
"allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
Resource[] allowed = getAllowedLocations();
logger.warn(LogFormatUtils.formatValue(
"Resource path \"" + resourcePath + "\" was successfully resolved " +
"but resource \"" + resource.getURL() + "\" is neither under the " +
"current location \"" + location.getURL() + "\" nor under any of the " +
"allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true));
}
}
return Mono.empty();
@@ -199,7 +201,8 @@ public class PathResourceResolver extends AbstractResourceResolver {
try {
String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
if (decodedPath.contains("../") || decodedPath.contains("..\\")) {
logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
logger.warn(LogFormatUtils.formatValue(
"Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true));
return true;
}
}
@@ -39,6 +39,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Hints;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -572,7 +573,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
protected boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
if (logger.isWarnEnabled()) {
logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
}
return true;
}
@@ -580,14 +582,16 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
if (logger.isWarnEnabled()) {
logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true));
}
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
if (logger.isWarnEnabled()) {
logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
}
return true;
}
@@ -123,6 +123,7 @@ public class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy {
}
@SuppressWarnings("deprecation") // for old doUpgrade variant in Tomcat 9.0.55
@Override
public Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler handler,
@Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory){
@@ -320,6 +320,23 @@ public class DefaultServerResponseBuilderTests {
assertThat(serverResponse.block().cookies().isEmpty()).isFalse();
}
@Test
public void overwriteHeaders() {
ServerResponse serverResponse =
ServerResponse.ok().headers(headers -> headers.set("Foo", "Bar")).build().block();
assertThat(serverResponse).isNotNull();
MockServerWebExchange mockExchange = MockServerWebExchange
.builder(MockServerHttpRequest.get("https://example.org"))
.build();
MockServerHttpResponse response = mockExchange.getResponse();
response.getHeaders().set("Foo", "Baz");
serverResponse.writeTo(mockExchange, EMPTY_CONTEXT).block();
assertThat(response.getHeaders().getFirst("Foo")).isEqualTo("Bar");
}
@Test
public void build() {
@@ -16,14 +16,17 @@
package org.springframework.web.reactive.function.server;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
@@ -191,6 +194,27 @@ public class RouterFunctionsTests {
assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Test
public void toHttpHandlerRouteNotFoundReturnsResponseStatusException() {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.accepted().build();
RouterFunction<ServerResponse> routerFunction =
RouterFunctions.route(RequestPredicates.GET("/path"), handlerFunction);
HandlerStrategies handlerStrategies = HandlerStrategies.empty().exceptionHandler((exchange, ex) -> {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap("Custom response".getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().writeWith(Flux.just(buffer));
}).build();
HttpHandler result = RouterFunctions.toHttpHandler(routerFunction, handlerStrategies);
assertThat(result).isNotNull();
MockServerHttpRequest httpRequest = MockServerHttpRequest.get("https://localhost").build();
MockServerHttpResponse httpResponse = new MockServerHttpResponse();
result.handle(httpRequest, httpResponse).block();
assertThat(httpResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(httpResponse.getBodyAsString().block()).isEqualTo("Custom response");
}
@Test
public void toHttpHandlerHandlerReturnResponseStatusExceptionInResponseWriteTo() {
HandlerFunction<ServerResponse> handlerFunction =
@@ -1395,6 +1395,7 @@ public class DispatcherServlet extends FrameworkServlet {
}
try {
if (mv.getStatus() != null) {
request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
@@ -103,14 +103,6 @@ public class HandlerMappingIntrospector
}
/**
* Return the configured or detected {@code HandlerMapping}s.
*/
public List<HandlerMapping> getHandlerMappings() {
return (this.handlerMappings != null ? this.handlerMappings : Collections.emptyList());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
@@ -125,6 +117,13 @@ public class HandlerMappingIntrospector
}
}
/**
* Return the configured or detected {@code HandlerMapping}s.
*/
public List<HandlerMapping> getHandlerMappings() {
return (this.handlerMappings != null ? this.handlerMappings : Collections.emptyList());
}
/**
* Find the {@link HandlerMapping} that would handle the given request and
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,9 +69,8 @@ public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
if (uriVars != null) {
uriVars.forEach((name, value) -> {
if (mpvs.contains(name)) {
if (logger.isWarnEnabled()) {
logger.warn("Skipping URI variable '" + name +
"' because request contains bind value with same name.");
if (logger.isDebugEnabled()) {
logger.debug("URI variable '" + name + "' overridden by request bind value.");
}
}
else {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -202,7 +202,7 @@ class ReactiveTypeHandler {
"-------------------------------\n" +
"Controller:\t" + returnType.getContainingClass().getName() + "\n" +
"Method:\t\t" + returnType.getMethod().getName() + "\n" +
"Returning:\t" + ResolvableType.forMethodParameter(returnType).toString() + "\n" +
"Returning:\t" + ResolvableType.forMethodParameter(returnType) + "\n" +
"!!!");
this.taskExecutorWarning = false;
}
@@ -187,7 +187,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
@@ -246,6 +245,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return this.config.getFileExtensions();
}
/**
* Obtain a {@link RequestMappingInfo.BuilderConfiguration} that can reflects
* the internal configuration of this {@code HandlerMapping} and can be used
* to set {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)}.
* <p>This is useful for programmatic registration of request mappings via
* {@link #registerHandlerMethod(Object, Method, RequestMappingInfo)}.
* @return the builder configuration that reflects the internal state
* @since 5.3.14
*/
public RequestMappingInfo.BuilderConfiguration getBuilderConfiguration() {
return this.config;
}
/**
* {@inheritDoc}
@@ -390,6 +402,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
updateConsumesCondition(mapping, method);
}
/**
* {@inheritDoc}
* <p><strong>Note:</strong> To create the {@link RequestMappingInfo},
* please use {@link #getBuilderConfiguration()} and set the options on
* {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)}
* to match how this {@code HandlerMapping} is configured. This
* is important for example to ensure use of
* {@link org.springframework.web.util.pattern.PathPattern} or
* {@link org.springframework.util.PathMatcher} based matching.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
*/
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.server.PathContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
@@ -190,11 +191,12 @@ public class PathResourceResolver extends AbstractResourceResolver {
return resource;
}
else if (logger.isWarnEnabled()) {
Resource[] allowedLocations = getAllowedLocations();
logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
"but resource \"" + resource.getURL() + "\" is neither under the " +
"current location \"" + location.getURL() + "\" nor under any of the " +
"allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
Resource[] allowed = getAllowedLocations();
logger.warn(LogFormatUtils.formatValue(
"Resource path \"" + resourcePath + "\" was successfully resolved " +
"but resource \"" + resource.getURL() + "\" is neither under " +
"the current location \"" + location.getURL() + "\" nor under any of " +
"the allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true));
}
}
return null;
@@ -297,7 +299,8 @@ public class PathResourceResolver extends AbstractResourceResolver {
try {
String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
if (decodedPath.contains("../") || decodedPath.contains("..\\")) {
logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
logger.warn(LogFormatUtils.formatValue(
"Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true));
return true;
}
}
@@ -39,6 +39,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRange;
@@ -734,7 +735,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
protected boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
if (logger.isWarnEnabled()) {
logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
}
return true;
}
@@ -742,14 +744,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
if (logger.isWarnEnabled()) {
logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true));
}
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
if (logger.isWarnEnabled()) {
logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
logger.warn(LogFormatUtils.formatValue(
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
}
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,6 +98,18 @@ public class TagWriter {
.append(attributeValue).append("\"");
}
/**
* Variant of {@link #writeAttribute(String, String)} for writing empty HTML
* attributes without a value such as {@code required}.
* @since 5.3.14
*/
public void writeAttribute(String attributeName) throws JspException {
if (currentState().isBlockTag()) {
throw new IllegalStateException("Cannot write attributes after opening tag is closed.");
}
this.writer.append(" ").append(attributeName);
}
/**
* Write an HTML attribute if the supplied value is not {@code null}
* or zero length.
@@ -155,13 +155,15 @@ public class PathMatchingUrlHandlerMappingTests {
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
if (mapping.getPatternParser() != null) {
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
}
request = new MockHttpServletRequest("GET", "/administrator/testing/longer2/notmatching/notmatching");
chain = getHandler(mapping, wac, request);

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