Compare commits

...

113 Commits

Author SHA1 Message Date
Spring Buildmaster b1280ffeaa Release v5.3.7 2021-05-12 05:38:51 +00:00
Arjen Poutsma 0d0d75e253 Ensure DefaultPartHttpMessageReader temp directories do not collide
This commit makes sure that the DefaultPartHttpMessageReader uses a
random temporary directory to store uploaded files, so that two
instances do not collide.

See gh-26931
2021-05-11 21:12:50 +02:00
Arjen Poutsma cce60c479c Ensure Synchronoss temp directories do not collide
This commit makes sure that Synchronoss uses a random temporary
directory to store uploaded files, so that two instances do not collide.

Closes gh-26931
2021-05-11 21:12:50 +02:00
Juergen Hoeller 1469bdbc70 Nullability refinements and related polishing 2021-05-11 16:50:19 +02:00
Juergen Hoeller be52ec8bd5 DataClassRowMapper exposes generic constructor parameters for type conversion
Closes gh-26881
2021-05-11 16:49:25 +02:00
Sam Brannen cd9cad31f9 Increase the likelihood that timed tests pass on CI server 2021-05-11 16:16:18 +02:00
Juergen Hoeller 60c551a95b Correct clientInboundChannel assertion (includes constructor javadoc)
Closes gh-26896
2021-05-11 15:49:38 +02:00
Juergen Hoeller 4164fc63b1 CandidateComponentsIndexer introspects any kind of class (including records)
Closes gh-26909
2021-05-11 15:49:15 +02:00
Juergen Hoeller 0865abef83 Expose current cached session count
Closes gh-26811
2021-05-11 15:47:15 +02:00
Juergen Hoeller 90af2d5794 Log delegate service initialization at debug level (instead of info)
Closes gh-26810
2021-05-11 15:46:41 +02:00
Juergen Hoeller 5f5cd8a7c4 Log resource path resolution failure at debug level (instead of warn)
Closes gh-26828
2021-05-11 15:46:21 +02:00
Juergen Hoeller 46b7cd54a1 Log missing web application context at warn level (instead of debug)
Closes gh-26925
2021-05-11 15:45:36 +02:00
Juergen Hoeller 7f670bbe68 TransactionSynchronization does not support @Order (just Ordered interface)
See gh-21725
2021-05-11 15:45:05 +02:00
Stephane Nicoll 6aaf7596c0 Upgrade to Reactor 2020.0.7
Closes gh-26890
2021-05-11 15:34:41 +02:00
Juergen Hoeller 4f2c589fe6 Upgrade to Jackson 2.12.3, Jetty 9.4.40, Hibernate ORM 5.4.31, Groovy 3.0.8, Caffeine 2.9.1, Woodstox 6.2.6, Protobuf 3.15.8, XStream 1.4.16, OpenPDF 1.3.26, Checkstyle 8.42, Mockito 3.9, HtmlUnit 2.49.1 2021-05-11 00:19:43 +02:00
Rossen Stoyanchev aa51ed1940 Fix failing tests
This commit ensures that if an Origin is returned as it was provided,
possibly with a trailing slash.

See gh-26892
2021-05-10 14:15:16 +01:00
Rossen Stoyanchev dc4e053d59 CorsConfiguration ignores trailing "/" in pattern
Recent commit dddcc5e9ad ensured a
trailing "/" in the Origin header has no effect. This commit does the
same for a trailing "/" in configured patterns.

See gh-26892
2021-05-10 13:30:02 +01:00
ShindongLee 07ba95739b Fix parameter bug of handler inside the filterFunction DSL
Co-authored-by: hojongs <hojong.jjh@gmail.com>
Co-authored-by: bjh970913 <bjh970913@gmail.com>

Closes gh-26838
2021-05-10 09:35:56 +02:00
Rossen Stoyanchev 0468ef46ac Follow-up fix for recent change to PORT_PATTERN
The pattern was changed in 65797d04f2
to check for characters up until "/", instead of for digits, but now
also checks up until "?" and "#".

Closes gh-26905
2021-05-10 07:19:17 +01:00
Rossen Stoyanchev eb03144e9d RSocketRequester implements Disposable
Closes gh-26886
2021-05-10 07:19:17 +01:00
Rossen Stoyanchev 29790d5bb1 Update docs on how a @ModelAttribute is sourced
Closes gh-26873
2021-05-10 07:19:17 +01:00
Sam Brannen 355d394d7f Polish contribution
See gh-25200
2021-05-08 19:40:13 +02:00
Vlad Kisel 9ddab9e69b Support single-value request param resolution for data class constructors
Prior to this commit, when Web MVC attempted to resolve a constructor
argument for a data class constructor with @ModelAttribute binding,
ModelAttributeMethodProcessor failed to unwrap the array returned by
WebRequest.getParameter(String).

According to the Javadoc for WebRequest.getParameter(String), "a
single-value parameter will be exposed as an array with a single
element."

This commit fixes this issue by extracting the single value from such
an array and using that as the constructor argument (potentially
converted by the WebDataBinder).

Closes gh-25200
2021-05-08 19:25:54 +02:00
Sam Brannen c20a43f72b Polish WebRequestDataBinderIntegrationTests 2021-05-08 18:12:55 +02:00
Rossen Stoyanchev e0fa58aa0f Polishing contribution
Closes gh-26905
2021-05-07 17:30:40 +01:00
Jeon Ilwoong 65797d04f2 UriComponentsBuilder handles invalid port correctly
See gh-26905
2021-05-07 17:15:16 +01:00
Rossen Stoyanchev 5811212a9f Add PreFlightRequestWebFilter
Closes gh-26885
2021-05-07 16:48:15 +01:00
Sam Brannen d79e33b5a0 Handle empty file input in HtmlUnitRequestBuilder
This commit revises the fix submitted in 959e6d1745 by ensuring that
empty file input is converted to a MockPart with the supplied name, an
empty filename, "application/octet-stream" as the content type, and
empty content.

This aligns with the behavior of Servlet containers, as tested with the
interaction between Firefox and a standard Servlet running in a Jetty
Servlet container.

Closes gh-26799
2021-05-07 16:00:09 +02:00
Rossen Stoyanchev dddcc5e9ad Ignore trailing slash in Origin header
See gh-26892
2021-05-07 14:56:56 +01:00
Rossen Stoyanchev 582b94d50e Allow charset input in WebClientResponseException
Closes gh-26866
2021-05-06 16:58:42 +01:00
Rossen Stoyanchev 443c34cc90 CorsRegistry implements combine correctly
Closes gh-26877
2021-05-05 21:02:25 +01:00
Sam Brannen 959e6d1745 Handle empty file input in HtmlUnitRequestBuilder
Prior to this commit, when using HtmlUnit with empty file input,
MockMvc's HtmlUnitRequestBuilder would throw a NullPointerException
when attempting to create a MockPart based on the null File.

This commit ensures that empty file input is converted to a MockPart
with a valid name but with a null filename, a null content type, and
empty content.

Closes gh-26799
2021-05-05 18:11:15 +02:00
Sam Brannen 38b592444d Store resolved toolchain info as custom values in build scan
Prior to this commit, we registered custom values in the build scan for
the main and test toolchains based on input values provided via the
mainToolchain and testToolchain project properties.

Beginning with this commit, the custom values we register are based on
the available metadata for the resolved JDK/JVM for each toolchain.

For example, instead of registering the following custom value...

Test toolchain : JDK11

... we now register the following which includes the vendor, version,
and installation path of the JDK/JVM.

Test toolchain : AdoptOpenJDK 11 (/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home)

Once Gradle's JavaInstallationMetadata includes the exact version, we
will likely use that instead of the installation path.

See gh-25787
2021-05-05 15:19:27 +02:00
Stephane Nicoll 2c2464b1e7 Make script executable 2021-05-05 14:26:12 +02:00
Sviatoslav Hryb 3b247a9c19 Fix MethodMatcher API doc description
Closes gh-26893
2021-05-05 12:21:46 +02:00
Sam Brannen d30432b6c0 Fix syntax in toolchains example
See gh-25787
2021-05-04 18:42:21 +02:00
Rossen Stoyanchev c70aed58bf Switch to Reactor 2020.0.7 snapshots
See gh-26890
2021-05-04 16:25:00 +01:00
Sam Brannen 2de7bf6e2b Exclude empty toolchain versions from Gradle build scan
See gh-25787
2021-05-04 15:20:38 +02:00
Sam Brannen a28511cdbd Polish Jaxb2MarshallerTests 2021-05-04 13:46:31 +02:00
Sam Brannen 668b938832 Remove obsolete order.xsd file
The order.xsd is no longer used by any tests and probably has not been
since commit 89a7e752ef, which removed support for Castor.

See gh-22250
2021-05-04 13:34:42 +02:00
Sam Brannen 091e7bb891 Remove duplicate copy of flight.xsd
See 85eb589c2e
See gh-25787
2021-05-04 13:19:04 +02:00
Rossen Stoyanchev b76e0c4826 HttpMethod#matches compares by String equality
Closes gh-26834
2021-05-04 12:09:35 +01:00
Bryce Yang 55faf6e320 Use HttpMethod enums
See gh-26855
2021-05-04 12:09:35 +01:00
Brian Clozel a522ed0788 Fix task parameters in CI build pipeline
Prior to this commit, the task parameters for the CI project checks were
overridden by a task anchor. This commit splits the anchor declaration
and ensures that the `TEST_TOOLCHAIN` setting is set for the JDK
variant builds.

See gh-25787
2021-05-04 11:48:17 +02:00
Sam Brannen 4152ebc2f3 Remove Eclipse resource filter for 'build' folder
Commit 3eec27a723 introduced a custom resource filter for projects
imported into Eclipse IDE. However, commit 85eb589c2e introduced
generated source files within the Gradle 'build' folder, and the filter
prevents Eclipse from seeing the generated sources, resulting in build
errors within the spring-oxm project in Eclipse.

This commit therefore effectively reverts 3eec27a723 by removing the
resource filter.

See gh-25787
2021-05-04 11:36:21 +02:00
Rossen Stoyanchev 21c90829df Polishing
See gh-26834
2021-05-04 10:34:56 +01:00
Sam Brannen 5b96c9b87e Update @since tag due to backport
See gh-26856
2021-05-04 10:59:49 +02:00
Sébastien Deleuze 08855fee51 Upgrade to Kotlin 1.5.0
This commit retains Kotlin 1.3 compatibility and
upgrades Kotlin Serialization to 1.2.0.

Closes gh-26792
Closes gh-26887
2021-05-03 17:46:25 +02:00
Sam Brannen 66cd979ce4 Support @ModelAttribute(binding=false) with WebFlux
Prior to this commit, @ModelAttribute(binding=false) was honored with
Spring Web MVC but not with WebFlux.

This commit adds support for disabling binding via @ModelAttribute with
WebFlux by adding a check to resolveArgument(...) in
ModelAttributeMethodArgumentResolver.

Closes gh-26856
2021-05-03 15:22:43 +02:00
Sam Brannen 5667d459a7 Polish Javadoc 2021-05-03 15:22:43 +02:00
Brian Clozel 85eb589c2e Fix Gradle Java Toolchain configuration
This commit fixes various issues with the configuration of the Gradle
Java toolchain in the build.

First, the configuration of build properties is fixed in the CI pipeline
because it wasn't properly checked.
The JMH plugin is also upgraded and we now configure its toolchain
support.
This commit also rewrites the XJC tasks in the spring-oxm module,
leveraging a Gradle plugin that creates actual compile tasks we can
configure.

See gh-25787
2021-05-03 15:02:09 +02:00
Arjen Poutsma b18cf3c873 Follow contract of computeIfAbsent LinkedCaseInsensitiveMap
This commit makes sure that LinkedCaseInsensitiveMap::computeIfAbsent
honors the contract of the method, and also replaces the old entry if
that mapped to null.

Closes gh-26868
2021-04-30 15:49:00 +02:00
Arjen Poutsma 399e7ebf22 Follow contract of putIfAbsent LinkedCaseInsensitiveMap
This commit makes sure that LinkedCaseInsensitiveMap::putIfAbsent honors
the contract of the method, and also replaces the old entry if that
mapped to null.

Closes gh-26868
2021-04-30 15:17:46 +02:00
Florian Kirmaier b5d6e53e50 Avoid memory leak when PropertyComparator is reused
This commit fixes a potential memory leak, since PropertyComparator
previously kept an indirect reference to the value it last compared.

Closes gh-26869
2021-04-30 15:08:32 +02:00
Stephane Nicoll 865969400b Polish
See gh-26882
2021-04-30 12:52:36 +02:00
Arjen Poutsma 6f4fb08bf8 Invoke WebMvc.fn error handlers for async errors
This commit makes sure that any error handlers registered on the route
are also applied when an error occurs asynchronously. This commit
applies to asynchronous bodies with both CompletableFuture and Reactive
Streams, as well as completely asynchronous responses.

Closes gh-26831
2021-04-30 11:43:40 +02:00
Stephane Nicoll 4c7cc705de Build PRs on CI
Closes gh-26882
2021-04-30 09:38:30 +02:00
Rossen Stoyanchev f7fc36250e Properly handle completion while in READING state
Closes gh-26834
2021-04-29 22:09:33 +01:00
Sviatoslav Hryb ae7d5200e5 Fix @Scope documentation in ref docs
Closes gh-26773
2021-04-29 18:08:54 +02:00
Sam Brannen ff9b68c1b6 Update copyright date for StaxEventXMLReader
See gh-26875
2021-04-29 18:01:27 +02:00
Tasuku Nakagawa 36c829386b Use an import instead of FQCN in StaxEventXMLReader
Closes gh-26875
2021-04-29 17:59:26 +02:00
Sam Brannen 7316dc726a Polish Javadoc for MessageSourceSupport
See gh-26874
2021-04-29 17:49:29 +02:00
Sviatoslav Hryb 118c7f969b Improve readability of MessageSourceSupport Javadoc
Closes gh-26874
2021-04-29 17:42:41 +02:00
K 26ed6d8bce Fix syntax for RequestEntity headers example in ref docs
Closes gh-26876
2021-04-29 17:34:04 +02:00
Johnny Lim 98770b15e7 Polishing
Closes gh-26878
2021-04-29 16:53:53 +02:00
Sam Brannen 697108cc42 Support safe-updates mode in MySQLMaxValueIncrementer
Prior to this commit, MySQLMaxValueIncrementer could not be used when
the MySQL database was configured to use safe-updates mode.

See https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates

This commit introduces a `limit 1` clause to the generated update
statement to allow MySQLMaxValueIncrementer to be compatible with
MySQL safe-updates mode.

Closes gh-26858
2021-04-29 15:37:40 +02:00
Stephane Nicoll 607d918340 Provide nested cause when a suitable CacheManager cannot be found
Closes gh-25250
2021-04-29 08:48:34 +02:00
Sam Brannen 8456eaca8e Polish SpEL selection/projection in ref docs 2021-04-28 15:54:39 +02:00
Sviatoslav Hryb 59015344a7 Fix SpEL selection description in ref docs
Closes gh-26852
2021-04-28 15:47:16 +02:00
Sam Brannen c03162e616 Polishing
See gh-26862
2021-04-28 15:43:52 +02:00
Sam Brannen 1dde30a7fd Fix grammar in ref docs 2021-04-28 15:39:21 +02:00
Sviatoslav Hryb 35196d3c62 Fix aspect autodetection description
Closes gh-26862
2021-04-28 15:37:59 +02:00
Sébastien Deleuze d7f67b3dc3 Fix a typo in PropertyResolverExtensions documentation 2021-04-28 12:17:27 +02:00
Sviatoslav Hryb 719e7394d6 Fix MessageSource doc description 2021-04-28 11:04:01 +01:00
Stephane Nicoll e4a5d2aaa5 Fix reference to assets on the default branch in ref doc
See gh-26863
2021-04-27 07:18:23 +02:00
Stephane Nicoll ab64607420 Update CI to use main as default branch name
Closes gh-26863
2021-04-27 07:09:41 +02:00
Rossen Stoyanchev b595dc1dfa Add advice on Spring MVC path matching for 5.3+
Closes gh-26750
2021-04-26 17:17:14 +01:00
Rossen Stoyanchev 86123de883 HandlerMappingIntrospector handles attribute changes properly
Closes gh-26833
2021-04-26 17:16:33 +01:00
Sam Brannen 7c3a18490b Polish SpEL SelectionAndProjectionTests 2021-04-26 16:23:21 +02:00
Sam Brannen 01e50fb60a Polish SpEL chapter 2021-04-22 18:55:40 +02:00
Sam Brannen 6c3a0a9026 Polishing 2021-04-22 18:53:31 +02:00
Sviatoslav Hryb 3bded7659a Improve documentation for SpEL constructor support
Closes gh-26846
2021-04-22 18:33:37 +02:00
Arjen Poutsma 7f1062159e Copy HttpStatus::values to prevent allocation
Before this commit, HttpStatus::resolve used the values() method in its
logic. This causes a new array to be allocated for each invocation,
and results in memory overhead.

This commit makes a copy of the HttpStatus values array, and uses that
to resolve status codes.

Closes gh-26842
2021-04-22 15:39:03 +02:00
Stephane Nicoll e4c0ff569b Merge pull request #26787 from dreis2211
* pr/26787:
  Avoid exceptions when evaluating validation hints

Closes gh-26787
2021-04-22 11:26:15 +02:00
Christoph Dreis e7cbe23771 Avoid exceptions when evaluating validation hints
Prior to this commit, evaluating validation hints for
@javax.validation.Valid caused exceptions being raised when getting the
value of this annotation, which does not exist. Bypassing
AnnotationUtils.getValue() in those cases can improve performance by
avoiding the cost incurred by raising exceptions.

See gh-26787
2021-04-22 11:19:30 +02:00
Rossen Stoyanchev d275a4e28d Asciidoc typo from previous commits 2021-04-21 17:37:06 +01:00
Rossen Stoyanchev 42a23098de Update docs on Principal controller method arguments
Closes gh-26791
2021-04-21 17:28:14 +01:00
Rossen Stoyanchev d25ae4b02c Add advice on using exchange from an ExchangeFilterFunction
Closes gh-26819
2021-04-21 17:04:10 +01:00
Sam Brannen 5740eaf33e Polishing 2021-04-21 14:15:27 +02:00
Sviatoslav Hryb 80d46ba80e Update FactoryBean generic description in ref docs
Closes gh-26758
2021-04-21 14:06:43 +02:00
Сергей Цыпанов db9a10bf35 Use String.startsWith() instead of substring() in PatternMatchUtils
Closes gh-26822
2021-04-21 13:56:38 +02:00
Arjen Poutsma 27bfcbbc82 Fix daylight saving issue in CronExpression
Closes gh-26830
2021-04-21 12:04:21 +02:00
Sviatoslav Hryb b153b5e53b Fix Kotlin code formatting for Inline Maps section of ref docs
Closes gh-26836
2021-04-21 11:13:00 +02:00
Sam Brannen 1f477a5c1c Polish Javadoc for ColumnMapRowMapper
See gh-26823
2021-04-20 19:27:56 +02:00
Marten Deinum ceac2a5c3c Remove reference to Commons Collections in ColumnMapRowMapper Javadoc
This commit removes the outdated reference to Commons Collections
in the Javadoc for ColumnMapRowMapper.

Closes gh-26823
2021-04-20 19:22:57 +02:00
Sviatoslav Hryb e03fdeaf8b Improve ref docs regarding @Bean method visibility constraints
Closes gh-26797
2021-04-20 19:08:27 +02:00
Sviatoslav Hryb 0286549466 Fix formatting for PersonValidator Kotlin example in ref docs
Closes gh-26832
2021-04-20 18:59:50 +02:00
Sam Brannen 71158f4d96 Polishing
See gh-26825
2021-04-20 18:56:18 +02:00
Sviatoslav Hryb 7303680369 Remove reference to deprecated Joda Time support in @DateTimeFormat Javadoc
Closes gh-26825
2021-04-20 18:54:34 +02:00
Sam Brannen 906a1f561a Polish CronTriggerTests and assess daylight savings time issue
This test polishes CronTriggerTests and modifies the
daylightSavingMissingHour() test to help analyze why the test fails
for CET if the minute value for the last completion time falls between
0 and 9 minutes.

Associated broken build:

https://ge.spring.io/s/epphj7vruwcn6/tests/:spring-context:test/org.springframework.scheduling.support.CronTriggerTests/testDaylightSavingMissingHour(LocalDateTime,%20TimeZone)%5B2%5D?expanded-stacktrace=WyIwIl0
2021-04-20 12:18:01 +02:00
Sam Brannen e489706f13 Ensure @DateTimeFormat tests pass on Java 9+
Prior to this commit, two tests for exception handling regarding
@DateTimeFormat processing only passed on Java 8. This is due to the
fact that the toString() implementation for annotations changed in Java
9. Specifically, the representation for arrays changed from [] to {},
and strings are enclosed in double quotes beginning with Java 9.

This commit ensures that the affected @DateTimeFormat tests pass on
Java 9+, by making the assertions more lenient regarding toString()
output for annotations.

See gh-26777
See gh-26804
2021-04-18 15:48:00 +02:00
Rossen Stoyanchev 69bbdce826 HandlerMappingIntrospector ensures initialized RequestPath
Closes gh-26814
2021-04-16 19:56:03 +01:00
Sam Brannen 0f31830ae2 Retain root cause for parsing patterns in @DateTimeFormat
The support for fallback parsing patterns in @DateTimeFormat introduced
in gh-20292 introduced a regression in that the original cause of the
parsing exception was no longer retained.

gh-26777 addressed that regression for `java.time` support; whereas,
this commit addresses that regression for legacy Date types.

This commit ensures that the original ParseException is set as the
cause for any newly created ParseException, thereby retaining the
original exception as the root cause.

Closes gh-26804
2021-04-16 19:14:41 +02:00
Sam Brannen 5d297c6387 Polishing 2021-04-16 11:44:48 +02:00
Sviatoslav Hryb 320f5836b4 Fix LocaleEditor description & formatting in reference manual
Closes gh-26816
2021-04-16 11:39:45 +02:00
Sviatoslav Hryb af06b80bdf Update FormatterRegistry interface in reference manual
Closes gh-26812
2021-04-15 18:45:13 +02:00
Sam Brannen 5b1ab31559 Polishing 2021-04-15 11:17:04 +02:00
Sviatoslav Hryb bf05da1f8c Remove unnecessary semicolon in BeanInfo example
Closes gh-26806
2021-04-15 11:10:15 +02:00
Stephane Nicoll 63217e44f1 Merge pull request #26807 from mustafau
* pr/26807:
  Update copyright year of changed file
  Remove leftover Javadoc from WebClient

Closes gh-26807
2021-04-15 09:56:16 +02:00
Stephane Nicoll 5226a67161 Update copyright year of changed file
See gh-26807
2021-04-15 09:50:54 +02:00
Mustafa Ulu 1694274e32 Remove leftover Javadoc from WebClient
See gh-26807
2021-04-15 09:50:39 +02:00
Rossen Stoyanchev 27c4e74e24 Update CORS Javadoc in spring-websocket
Closes gh-26753
2021-04-13 17:43:40 +01:00
Spring Buildmaster b73eb51cb1 Next development version (v5.3.7-SNAPSHOT) 2021-04-13 11:15:32 +00:00
140 changed files with 2503 additions and 1192 deletions
+21 -23
View File
@@ -1,7 +1,7 @@
plugins {
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.5.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.4.32' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.0' apply false
id 'org.jetbrains.dokka' version '0.10.1' apply false
id 'org.asciidoctor.jvm.convert' version '3.1.0'
id 'org.asciidoctor.jvm.pdf' version '3.1.0'
@@ -9,8 +9,9 @@ plugins {
id "io.freefair.aspectj" version '5.1.1' apply false
id "com.github.ben-manes.versions" version '0.28.0'
id "com.github.johnrengelman.shadow" version "6.1.0" apply false
id "me.champeau.gradle.jmh" version "0.5.2" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.4.32" apply false
id "me.champeau.jmh" version "0.6.4" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.0" apply false
id "org.unbroken-dome.xjc" version '2.0.0' apply false
}
ext {
@@ -26,14 +27,15 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.2"
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.3"
mavenBom "io.netty:netty-bom:4.1.63.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.6"
mavenBom "io.projectreactor:reactor-bom:2020.0.7"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR9"
mavenBom "io.rsocket:rsocket-bom:1.1.0"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.39.v20210325"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.32"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.40.v20210413"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.0"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.3"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.0"
mavenBom "org.junit:junit-bom:5.7.1"
}
dependencies {
@@ -54,7 +56,7 @@ configure(allprojects) { project ->
entry 'aspectjtools'
entry 'aspectjweaver'
}
dependencySet(group: 'org.codehaus.groovy', version: '3.0.7') {
dependencySet(group: 'org.codehaus.groovy', version: '3.0.8') {
entry 'groovy'
entry 'groovy-jsr223'
entry 'groovy-templates' // requires findbugs for warning-free compilation
@@ -70,13 +72,13 @@ configure(allprojects) { project ->
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.2.2"
dependency("com.fasterxml.woodstox:woodstox-core:6.2.4") {
dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") {
exclude group: "stax", name: "stax-api"
}
dependency "com.google.code.gson:gson:2.8.6"
dependency "com.google.protobuf:protobuf-java-util:3.15.5"
dependency "com.google.protobuf:protobuf-java-util:3.15.8"
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
dependency("com.thoughtworks.xstream:xstream:1.4.15") {
dependency("com.thoughtworks.xstream:xstream:1.4.16") {
exclude group: "xpp3", name: "xpp3_min"
exclude group: "xmlpull", name: "xmlpull"
}
@@ -90,14 +92,10 @@ configure(allprojects) { project ->
}
dependency "org.ogce:xpp3:1.1.6"
dependency "org.yaml:snakeyaml:1.28"
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.1') {
entry 'kotlinx-serialization-core'
entry 'kotlinx-serialization-json'
}
dependency "com.h2database:h2:1.4.200"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.0"
dependency "com.github.librepdf:openpdf:1.3.25"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.1"
dependency "com.github.librepdf:openpdf:1.3.26"
dependency "com.rometools:rome:1.15.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.3"
@@ -124,7 +122,7 @@ configure(allprojects) { project ->
dependency "net.sf.ehcache:ehcache:2.10.6"
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.30.Final"
dependency "org.hibernate:hibernate-core:5.4.31.Final"
dependency "org.hibernate:hibernate-validator:6.2.0.Final"
dependency "org.webjars:webjars-locator-core:0.46"
dependency "org.webjars:underscorejs:1.8.3"
@@ -198,7 +196,7 @@ configure(allprojects) { project ->
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '3.8.0') {
dependencySet(group: 'org.mockito', version: '3.9.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
@@ -206,10 +204,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.10.2"
dependency("net.sourceforge.htmlunit:htmlunit:2.48.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.49.1") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.48.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.49.1") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -318,7 +316,7 @@ configure([rootProject] + javaProjects) { project ->
kotlinOptions {
languageVersion = "1.3"
apiVersion = "1.3"
freeCompilerArgs = ["-Xjsr305=strict"]
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
allWarningsAsErrors = true
}
}
@@ -338,7 +336,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "8.41"
toolVersion = "8.42"
configDirectory.set(rootProject.file("src/checkstyle"))
}
+1 -1
View File
@@ -5,7 +5,7 @@ github-repo: "https://github.com/spring-projects/spring-framework.git"
github-repo-name: "spring-projects/spring-framework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "master"
branch: "main"
milestone: "5.3.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
+56 -8
View File
@@ -18,9 +18,6 @@ anchors:
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
build-project-task-params: &build-project-task-params
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
docker-resource-source: &docker-resource-source
@@ -54,6 +51,11 @@ resource_types:
source:
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
@@ -93,6 +95,14 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
@@ -162,7 +172,10 @@ jobs:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
<<: *build-project-task-params
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
@@ -217,10 +230,11 @@ jobs:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
MAIN_TOOLCHAIN: 8
TEST_TOOLCHAIN: 11
<<: *build-project-task-params
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk11-build
@@ -244,10 +258,11 @@ jobs:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
MAIN_TOOLCHAIN: 8
TEST_TOOLCHAIN: 15
<<: *build-project-task-params
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk15-build
@@ -257,6 +272,37 @@ jobs:
<<: *slack-fail-params
- put: repo-status-jdk15-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
@@ -412,3 +458,5 @@ groups:
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
+2 -2
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 \
-PmainToolchain=$MAIN_TOOLCHAIN -PtestToolchain=$TEST_TOOLCHAIN --no-daemon --max-workers=4 check
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+19
View File
@@ -0,0 +1,19 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.6-SNAPSHOT
version=5.3.7
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
-18
View File
@@ -108,21 +108,3 @@ task eclipseBuildship(type: Copy) {
tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare)
tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi)
tasks["eclipse"].dependsOn(eclipseSettings, eclipseWstComponent)
// Filter 'build' folder
eclipse.project.file.withXml {
def node = it.asNode()
def filteredResources = node.get("filteredResources")
if(filteredResources) {
node.remove(filteredResources)
}
def filterNode = node.appendNode("filteredResources").appendNode("filter")
filterNode.appendNode("id", "1359048889071")
filterNode.appendNode("name", "")
filterNode.appendNode("type", "30")
def matcherNode = filterNode.appendNode("matcher")
matcherNode.appendNode("id", "org.eclipse.ui.ide.multiFilter")
matcherNode.appendNode("arguments", "1.0-projectRelativePath-matches-false-false-build")
}
+3 -3
View File
@@ -4,12 +4,12 @@ apply plugin: 'org.springframework.build.optional-dependencies'
// Uncomment the following for Shadow support in the jmhJar block.
// Currently commented out due to ZipException: archive is not a ZIP archive
// apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'me.champeau.gradle.jmh'
apply plugin: 'me.champeau.jmh'
apply from: "$rootDir/gradle/publications.gradle"
dependencies {
jmh 'org.openjdk.jmh:jmh-core:1.25'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.25'
jmh 'org.openjdk.jmh:jmh-core:1.28'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.28'
jmh 'net.sf.jopt-simple:jopt-simple:4.6'
}
+73 -15
View File
@@ -11,6 +11,8 @@
* <li>a JDK11 toolchain for compiling and running the test SourceSet
* </ul>
*
* By default, the build will fall back to using the current JDK and 1.8 language level for all sourceSets.
*
* Gradle will automatically detect JDK distributions in well-known locations.
* The following command will list the detected JDKs on the host.
* {@code
@@ -23,19 +25,41 @@
* /opt/openjdk/java11
* $ echo JDK15
* /opt/openjdk/java15
* $ ./gradlew -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 check
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 check
* }
*
* @author Brian Clozel
* @author Sam Brannen
*/
def mainToolchainConfigured() {
return project.hasProperty('mainToolchain') && project.mainToolchain
}
def testToolchainConfigured() {
return project.hasProperty('testToolchain') && project.testToolchain
}
def mainToolchainLanguageVersion() {
if (mainToolchainConfigured()) {
return JavaLanguageVersion.of(project.mainToolchain.toString())
}
return JavaLanguageVersion.of(8)
}
def testToolchainLanguageVersion() {
if (testToolchainConfigured()) {
return JavaLanguageVersion.of(project.testToolchain.toString())
}
return mainToolchainLanguageVersion()
}
plugins.withType(JavaPlugin) {
// Configure the Java Toolchain if the 'mainToolchain' property is defined
if (project.hasProperty('mainToolchain') && project.mainToolchain) {
def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString())
// Configure the Java Toolchain if the 'mainToolchain' is configured
if (mainToolchainConfigured()) {
java {
toolchain {
languageVersion = mainLanguageVersion
languageVersion = mainToolchainLanguageVersion()
}
}
}
@@ -46,8 +70,8 @@ plugins.withType(JavaPlugin) {
}
}
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
if (project.hasProperty('testToolchain') && project.testToolchain) {
def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString());
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
@@ -63,17 +87,17 @@ plugins.withType(JavaPlugin) {
plugins.withType(GroovyPlugin) {
// Fallback to JDK8
if (!project.hasProperty('mainToolchain')) {
if (!mainToolchainConfigured()) {
compileGroovy {
sourceCompatibility = JavaVersion.VERSION_1_8
}
}
}
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
pluginManager.withPlugin("kotlin") {
if (project.hasProperty('mainToolchain') && project.mainToolchain) {
def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString());
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
if (mainToolchainConfigured()) {
def mainLanguageVersion = mainToolchainLanguageVersion()
def compiler = javaToolchains.compilerFor {
languageVersion = mainLanguageVersion
}
@@ -106,9 +130,9 @@ pluginManager.withPlugin("kotlin") {
}
}
}
if (project.hasProperty('testToolchain') && project.testToolchain) {
def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString());
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
def compiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
@@ -121,4 +145,38 @@ pluginManager.withPlugin("kotlin") {
}
}
}
}
}
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
pluginManager.withPlugin("me.champeau.jmh") {
if (mainToolchainConfigured() || testToolchainConfigured()) {
tasks.matching { it.name.contains('jmh') && it.hasProperty('javaLauncher') }.configureEach {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(testToolchainLanguageVersion())
})
}
tasks.withType(JavaCompile).matching { it.name.contains("Jmh") }.configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testToolchainLanguageVersion()
}
}
}
}
// Store resolved Toolchain JVM information as custom values in the build scan.
rootProject.ext {
resolvedMainToolchain = false
resolvedTestToolchain = false
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (mainToolchainConfigured() && !resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) {
def metadata = task.javaCompiler.get().metadata
task.project.buildScan.value('Main toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
resolvedMainToolchain = true
}
if (testToolchainConfigured() && !resolvedTestToolchain && task instanceof Test && task.javaLauncher.isPresent()) {
def metadata = task.javaLauncher.get().metadata
task.project.buildScan.value('Test toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
resolvedTestToolchain = true
}
}
-6
View File
@@ -45,12 +45,6 @@ rootProject.children.each {project ->
settings.gradle.projectsLoaded {
gradleEnterprise {
buildScan {
if (settings.gradle.rootProject.hasProperty('mainToolchain')) {
value("Main toolchain", 'JDK' + settings.gradle.rootProject.getProperty('mainToolchain'))
}
if (settings.gradle.rootProject.hasProperty('testToolchain')) {
value("Test toolchain", 'JDK' + settings.gradle.rootProject.getProperty('testToolchain'))
}
File buildDir = settings.gradle.rootProject.getBuildDir()
buildDir.mkdirs()
new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)"
@@ -510,7 +510,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
if (specificInterceptors.length > 0) {
// specificInterceptors may equals PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
allInterceptors.addAll(Arrays.asList(specificInterceptors));
}
if (commonInterceptors.length > 0) {
@@ -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.
@@ -23,9 +23,9 @@ import org.springframework.util.StringUtils;
/**
* Editor for {@code java.util.Locale}, to directly populate a Locale property.
*
* <p>Expects the same syntax as Locale's {@code toString}, i.e. language +
* <p>Expects the same syntax as Locale's {@code toString()}, i.e. language +
* optionally country + optionally variant, separated by "_" (e.g. "en", "en_US").
* Also accepts spaces as separators, as alternative to underscores.
* Also accepts spaces as separators, as an alternative to underscores.
*
* @author Juergen Hoeller
* @since 26.05.2003
@@ -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.
@@ -44,8 +44,6 @@ public class PropertyComparator<T> implements Comparator<T> {
private final SortDefinition sortDefinition;
private final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(false);
/**
* Create a new PropertyComparator for the given SortDefinition.
@@ -115,8 +113,9 @@ public class PropertyComparator<T> implements Comparator<T> {
// (similar to JSTL EL). If the property doesn't exist in the
// first place, let the exception through.
try {
this.beanWrapper.setWrappedInstance(obj);
return this.beanWrapper.getPropertyValue(this.sortDefinition.getProperty());
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(false);
beanWrapper.setWrappedInstance(obj);
return beanWrapper.getPropertyValue(this.sortDefinition.getProperty());
}
catch (BeansException ex) {
logger.debug("PropertyComparator could not access property - treating as null for sorting", ex);
@@ -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.
@@ -19,7 +19,6 @@ package org.springframework.context.index.processor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -46,9 +45,6 @@ import javax.lang.model.element.TypeElement;
*/
public class CandidateComponentsIndexer implements Processor {
private static final Set<ElementKind> TYPE_KINDS =
Collections.unmodifiableSet(EnumSet.of(ElementKind.CLASS, ElementKind.INTERFACE));
private MetadataStore metadataStore;
private MetadataCollector metadataCollector;
@@ -136,7 +132,8 @@ public class CandidateComponentsIndexer implements Processor {
private static List<TypeElement> staticTypesIn(Iterable<? extends Element> elements) {
List<TypeElement> list = new ArrayList<>();
for (Element element : elements) {
if (TYPE_KINDS.contains(element.getKind()) && element.getModifiers().contains(Modifier.STATIC)) {
if ((element.getKind().isClass() || element.getKind() == ElementKind.INTERFACE) &&
element.getModifiers().contains(Modifier.STATIC) && element instanceof TypeElement) {
list.add((TypeElement) element);
}
}
@@ -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.
@@ -48,7 +48,7 @@ class IndexedStereotypesProvider implements StereotypesProvider {
public Set<String> getStereotypes(Element element) {
Set<String> stereotypes = new LinkedHashSet<>();
ElementKind kind = element.getKind();
if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
if (!kind.isClass() && kind != ElementKind.INTERFACE) {
return stereotypes;
}
Set<Element> seen = new HashSet<>();
@@ -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.
@@ -130,8 +130,8 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
@Override
public void afterPropertiesSet() throws CacheException {
if (logger.isInfoEnabled()) {
logger.info("Initializing EhCache CacheManager" +
if (logger.isDebugEnabled()) {
logger.debug("Initializing EhCache CacheManager" +
(this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : ""));
}
@@ -188,8 +188,8 @@ public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, Ini
@Override
public void destroy() {
if (this.cacheManager != null && this.locallyManaged) {
if (logger.isInfoEnabled()) {
logger.info("Shutting down EhCache CacheManager" +
if (logger.isDebugEnabled()) {
logger.debug("Shutting down EhCache CacheManager" +
(this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : ""));
}
this.cacheManager.shutdown();
@@ -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.
@@ -221,11 +221,11 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.", ex);
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.", ex);
}
}
this.initialized = 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.
@@ -57,17 +57,18 @@ public abstract class MessageSourceSupport {
/**
* Set whether to always apply the {@code MessageFormat} rules,
* parsing even messages without arguments.
* <p>Default is "false": Messages without arguments are by default
* returned as-is, without parsing them through MessageFormat.
* Set this to "true" to enforce MessageFormat for all messages,
* expecting all message texts to be written with MessageFormat escaping.
* <p>For example, MessageFormat expects a single quote to be escaped
* as "''". If your message texts are all written with such escaping,
* even when not defining argument placeholders, you need to set this
* flag to "true". Else, only message texts with actual arguments
* are supposed to be written with MessageFormat escaping.
* Set whether to always apply the {@code MessageFormat} rules, parsing even
* messages without arguments.
* <p>Default is {@code false}: Messages without arguments are by default
* returned as-is, without parsing them through {@code MessageFormat}.
* Set this to {@code true} to enforce {@code MessageFormat} for all messages,
* expecting all message texts to be written with {@code MessageFormat} escaping.
* <p>For example, {@code MessageFormat} expects a single quote to be escaped
* as two adjacent single quotes ({@code "''"}). If your message texts are all
* written with such escaping, even when not defining argument placeholders,
* you need to set this flag to {@code true}. Otherwise, only message texts
* with actual arguments are supposed to be written with {@code MessageFormat}
* escaping.
* @see java.text.MessageFormat
*/
public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
@@ -75,7 +76,7 @@ public abstract class MessageSourceSupport {
}
/**
* Return whether to always apply the MessageFormat rules, parsing even
* Return whether to always apply the {@code MessageFormat} rules, parsing even
* messages without arguments.
*/
protected boolean isAlwaysUseMessageFormat() {
@@ -150,10 +151,10 @@ public abstract class MessageSourceSupport {
}
/**
* Create a MessageFormat for the given message and Locale.
* @param msg the message to create a MessageFormat for
* @param locale the Locale to create a MessageFormat for
* @return the MessageFormat instance
* Create a {@code MessageFormat} for the given message and Locale.
* @param msg the message to create a {@code MessageFormat} for
* @param locale the Locale to create a {@code MessageFormat} for
* @return the {@code MessageFormat} instance
*/
protected MessageFormat createMessageFormat(String msg, Locale locale) {
return new MessageFormat(msg, locale);
@@ -27,7 +27,7 @@ import java.lang.annotation.Target;
*
* <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
* Can be applied to {@link java.util.Date}, {@link java.util.Calendar}, {@link Long} (for
* millisecond timestamps) as well as JSR-310 {@code java.time} and Joda-Time value types.
* millisecond timestamps) as well as JSR-310 {@code java.time} value types.
*
* <p>For style-based formatting, set the {@link #style} attribute to the desired style pattern code.
* The first character of the code is the date style, and the second character is the time style.
@@ -219,9 +219,11 @@ public class DateFormatter implements Formatter<Date> {
}
}
if (this.source != null) {
throw new ParseException(
ParseException parseException = new ParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
parseException.initCause(ex);
throw parseException;
}
// else rethrow original exception
throw ex;
@@ -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.
@@ -177,8 +177,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
* Set up the ExecutorService.
*/
public void initialize() {
if (logger.isInfoEnabled()) {
logger.info("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
if (logger.isDebugEnabled()) {
logger.debug("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
}
if (!this.threadNamePrefixSet && this.beanName != null) {
setThreadNamePrefix(this.beanName + "-");
@@ -214,8 +214,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
* @see java.util.concurrent.ExecutorService#shutdownNow()
*/
public void shutdown() {
if (logger.isInfoEnabled()) {
logger.info("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
if (logger.isDebugEnabled()) {
logger.debug("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
}
if (this.executor != null) {
if (this.waitForTasksToCompleteOnShutdown) {
@@ -157,6 +157,11 @@ abstract class CronField {
return this.type;
}
@SuppressWarnings("unchecked")
protected static <T extends Temporal & Comparable<? super T>> T cast(Temporal temporal) {
return (T) temporal;
}
/**
* Represents the type of cron field, i.e. seconds, minutes, hours,
@@ -236,16 +241,17 @@ abstract class CronField {
*/
public <T extends Temporal & Comparable<? super T>> T elapseUntil(T temporal, int goal) {
int current = get(temporal);
ValueRange range = temporal.range(this.field);
if (current < goal) {
T result = this.field.getBaseUnit().addTo(temporal, goal - current);
current = get(result);
if (current > goal) { // can occur due to daylight saving, see gh-26744
result = this.field.getBaseUnit().addTo(result, goal - current);
if (range.isValidIntValue(goal)) {
return cast(temporal.with(this.field, goal));
}
else {
// goal is invalid, eg. 29th Feb, lets try to get as close as possible
return this.field.getBaseUnit().addTo(temporal, goal - current);
}
return result;
}
else {
ValueRange range = temporal.range(this.field);
long amount = goal + range.getMaximum() - current + 1 - range.getMinimum();
return this.field.getBaseUnit().addTo(temporal, amount);
}
@@ -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.
@@ -326,12 +326,6 @@ final class QuartzCronField extends CronField {
}
}
@SuppressWarnings("unchecked")
private static <T extends Temporal & Comparable<? super T>> T cast(Temporal temporal) {
return (T) temporal;
}
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal);
@@ -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.
@@ -66,7 +66,8 @@ public class ConcurrentModel extends ConcurrentHashMap<String, Object> implement
@Override
public Object put(String key, Object value) {
@Nullable
public Object put(String key, @Nullable Object value) {
if (value != null) {
return super.put(key, value);
}
@@ -0,0 +1,70 @@
/*
* 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.validation.annotation;
import java.lang.annotation.Annotation;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
/**
* Utility class for handling validation annotations.
* Mainly for internal use within the framework.
*
* @author Christoph Dreis
* @since 5.3.7
*/
public abstract class ValidationAnnotationUtils {
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/**
* Determine any validation hints by the given annotation.
* <p>This implementation checks for {@code @javax.validation.Valid},
* Spring's {@link org.springframework.validation.annotation.Validated},
* and custom annotations whose name starts with "Valid".
* @param ann the annotation (potentially a validation annotation)
* @return the validation hints to apply (possibly an empty array),
* or {@code null} if this annotation does not trigger any validation
*/
@Nullable
public static Object[] determineValidationHints(Annotation ann) {
Class<? extends Annotation> annotationType = ann.annotationType();
String annotationName = annotationType.getName();
if ("javax.validation.Valid".equals(annotationName)) {
return EMPTY_OBJECT_ARRAY;
}
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null) {
Object hints = validatedAnn.value();
return convertValidationHints(hints);
}
if (annotationType.getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
return convertValidationHints(hints);
}
return null;
}
private static Object[] convertValidationHints(@Nullable Object hints) {
if (hints == null) {
return EMPTY_OBJECT_ARRAY;
}
return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
}
}
@@ -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.
@@ -19,6 +19,8 @@ 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.EnableCaching;
@@ -87,6 +89,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
}
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("no unique bean of type CacheManager")).isTrue();
assertThat(ex).hasCauseInstanceOf(NoUniqueBeanDefinitionException.class);
}
}
@@ -121,6 +124,7 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
}
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("no bean of type CacheManager")).isTrue();
assertThat(ex).hasCauseInstanceOf(NoSuchBeanDefinitionException.class);
}
}
@@ -119,6 +119,39 @@ public class DateFormattingTests {
assertThat(binder.getBindingResult().getFieldValue("styleDate")).isEqualTo("10/31/09");
}
@Test
void styleDateWithInvalidFormat() {
String propertyName = "styleDate";
String propertyValue = "99/01/01";
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add(propertyName, propertyValue);
binder.bind(propertyValues);
BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getErrorCount()).isEqualTo(1);
FieldError fieldError = bindingResult.getFieldError(propertyName);
TypeMismatchException exception = fieldError.unwrap(TypeMismatchException.class);
assertThat(exception)
.hasMessageContaining("for property 'styleDate'")
.hasCauseInstanceOf(ConversionFailedException.class).getCause()
.hasMessageContaining("for value '99/01/01'")
.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
.hasMessageContaining("Parse attempt failed for value [99/01/01]")
.hasCauseInstanceOf(ParseException.class).getCause()
// Unable to parse date time value "99/01/01" using configuration from
// @org.springframework.format.annotation.DateTimeFormat(pattern=, style=S-, iso=NONE, fallbackPatterns=[])
// We do not check "fallbackPatterns=[]", since the array representation in the toString()
// implementation for annotations changed from [] to {} in Java 9. In addition, strings
// are enclosed in double quotes beginning with Java 9. Thus, we cannot check directly
// for the presence of "style=S-".
.hasMessageContainingAll(
"Unable to parse date time value \"99/01/01\" using configuration from",
"@org.springframework.format.annotation.DateTimeFormat",
"style=", "S-", "iso=NONE")
.hasCauseInstanceOf(ParseException.class).getCause()
.hasMessageStartingWith("Unparseable date: \"99/01/01\"")
.hasNoCause();
}
@Test
void testBindDateArray() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -330,7 +363,10 @@ public class DateFormattingTests {
.hasMessageContainingAll(
"Unable to parse date time value \"210302\" using configuration from",
"@org.springframework.format.annotation.DateTimeFormat",
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd");
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd")
.hasCauseInstanceOf(ParseException.class).getCause()
.hasMessageStartingWith("Unparseable date: \"210302\"")
.hasNoCause();
}
}
@@ -339,10 +339,11 @@ class DateTimeFormattingTests {
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
// Unable to parse date time value "2009-31-10" using configuration from
// @org.springframework.format.annotation.DateTimeFormat(pattern=, style=SS, iso=DATE, fallbackPatterns=[])
// We do not check "fallbackPatterns=[]", since the array representation in the toString()
// implementation for annotations changed from [] to {} in Java 9.
.hasMessageContainingAll(
"Unable to parse date time value \"2009-31-10\" using configuration from",
"@org.springframework.format.annotation.DateTimeFormat",
"iso=DATE", "fallbackPatterns=[]")
"@org.springframework.format.annotation.DateTimeFormat", "iso=DATE")
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
.hasMessageStartingWith("Text '2009-31-10'")
.hasCauseInstanceOf(DateTimeException.class).getCause()
@@ -1276,6 +1276,14 @@ class CronExpressionTests {
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
cronExpression = CronExpression.parse("0 10 2 * * *");
last = ZonedDateTime.parse("2013-03-31T01:09:00+01:00[Europe/Amsterdam]");
expected = ZonedDateTime.parse("2013-04-01T02:10:00+02:00[Europe/Amsterdam]");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
@@ -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 @@ class CronTriggerTests {
@ParameterizedCronTriggerTest
void testMatchAll(LocalDateTime localDateTime, TimeZone timeZone) {
void matchAll(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * * * *", timeZone);
@@ -66,7 +66,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMatchLastSecond(LocalDateTime localDateTime, TimeZone timeZone) {
void matchLastSecond(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * * * *", timeZone);
@@ -76,7 +76,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMatchSpecificSecond(LocalDateTime localDateTime, TimeZone timeZone) {
void matchSpecificSecond(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("10 * * * * *", timeZone);
@@ -86,7 +86,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementSecondByOne(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementSecondByOne(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("11 * * * * *", timeZone);
@@ -98,7 +98,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementSecondWithPreviousExecutionTooEarly(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementSecondWithPreviousExecutionTooEarly(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("11 * * * * *", timeZone);
@@ -111,7 +111,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementSecondAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementSecondAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("10 * * * * *", timeZone);
@@ -123,7 +123,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSecondRange(LocalDateTime localDateTime, TimeZone timeZone) {
void secondRange(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("10-15 * * * * *", timeZone);
@@ -134,7 +134,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementMinute(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementMinute(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 * * * * *", timeZone);
@@ -152,7 +152,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementMinuteByOne(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementMinuteByOne(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 11 * * * *", timeZone);
@@ -164,7 +164,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementMinuteAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementMinuteAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 10 * * * *", timeZone);
@@ -177,7 +177,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementHour(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementHour(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 * * * *", timeZone);
@@ -198,7 +198,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementHourAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementHourAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 * * * *", timeZone);
@@ -220,7 +220,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementDayOfMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementDayOfMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 * * *", timeZone);
@@ -236,13 +236,13 @@ class CronTriggerTests {
assertThat(this.calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(2);
this.calendar.add(Calendar.DAY_OF_MONTH, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
assertThat(this.calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(3);
}
@ParameterizedCronTriggerTest
void testIncrementDayOfMonthByOne(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementDayOfMonthByOne(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * 10 * *", timeZone);
@@ -257,7 +257,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementDayOfMonthAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementDayOfMonthAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * 10 * *", timeZone);
@@ -273,7 +273,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testDailyTriggerInShortMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void dailyTriggerInShortMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 * * *", timeZone);
@@ -294,7 +294,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testDailyTriggerInLongMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void dailyTriggerInLongMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 * * *", timeZone);
@@ -315,7 +315,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testDailyTriggerOnDaylightSavingBoundary(LocalDateTime localDateTime, TimeZone timeZone) {
void dailyTriggerOnDaylightSavingBoundary(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 * * *", timeZone);
@@ -336,7 +336,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 1 * *", timeZone);
@@ -357,7 +357,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementMonthAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementMonthAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 1 * *", timeZone);
@@ -380,7 +380,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMonthlyTriggerInLongMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void monthlyTriggerInLongMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 31 * *", timeZone);
@@ -396,7 +396,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMonthlyTriggerInShortMonth(LocalDateTime localDateTime, TimeZone timeZone) {
void monthlyTriggerInShortMonth(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 1 * *", timeZone);
@@ -413,7 +413,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementDayOfWeekByOne(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementDayOfWeekByOne(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * * * 2", timeZone);
@@ -429,7 +429,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testIncrementDayOfWeekAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
void incrementDayOfWeekAndRollover(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * * * 2", timeZone);
@@ -445,7 +445,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSpecificMinuteSecond(LocalDateTime localDateTime, TimeZone timeZone) {
void specificMinuteSecond(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("55 5 * * * *", timeZone);
@@ -459,12 +459,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.HOUR, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testSpecificHourSecond(LocalDateTime localDateTime, TimeZone timeZone) {
void specificHourSecond(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("55 * 10 * * *", timeZone);
@@ -479,12 +479,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.MINUTE, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testSpecificMinuteHour(LocalDateTime localDateTime, TimeZone timeZone) {
void specificMinuteHour(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* 5 10 * * *", timeZone);
@@ -500,12 +500,12 @@ class CronTriggerTests {
// next trigger is in one second because second is wildcard
this.calendar.add(Calendar.SECOND, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testSpecificDayOfMonthSecond(LocalDateTime localDateTime, TimeZone timeZone) {
void specificDayOfMonthSecond(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("55 * * 3 * *", timeZone);
@@ -521,12 +521,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.MINUTE, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testSpecificDate(LocalDateTime localDateTime, TimeZone timeZone) {
void specificDate(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("* * * 3 11 *", timeZone);
@@ -543,12 +543,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.SECOND, 1);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testNonExistentSpecificDate(LocalDateTime localDateTime, TimeZone timeZone) {
void nonExistentSpecificDate(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
// TODO: maybe try and detect this as a special case in parser?
@@ -561,7 +561,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testLeapYearSpecificDate(LocalDateTime localDateTime, TimeZone timeZone) {
void leapYearSpecificDate(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 0 29 2 *", timeZone);
@@ -579,12 +579,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.YEAR, 4);
TriggerContext context2 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context2);
Object actual = trigger.nextExecutionTime(context2);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testWeekDaySequence(LocalDateTime localDateTime, TimeZone timeZone) {
void weekDaySequence(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 0 7 ? * MON-FRI", timeZone);
@@ -607,12 +607,12 @@ class CronTriggerTests {
assertThat(actual1).isEqualTo(this.calendar.getTime());
this.calendar.add(Calendar.DAY_OF_MONTH, 1);
TriggerContext context3 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context3);
Object actual = trigger.nextExecutionTime(context3);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testDayOfWeekIndifferent(LocalDateTime localDateTime, TimeZone timeZone) {
void dayOfWeekIndifferent(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * 2 * *", timeZone);
@@ -621,7 +621,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSecondIncrementer(LocalDateTime localDateTime, TimeZone timeZone) {
void secondIncrementer(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("57,59 * * * * *", timeZone);
@@ -630,7 +630,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSecondIncrementerWithRange(LocalDateTime localDateTime, TimeZone timeZone) {
void secondIncrementerWithRange(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("1,3,5 * * * * *", timeZone);
@@ -639,7 +639,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testHourIncrementer(LocalDateTime localDateTime, TimeZone timeZone) {
void hourIncrementer(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * 4,8,12,16,20 * * *", timeZone);
@@ -648,7 +648,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testDayNames(LocalDateTime localDateTime, TimeZone timeZone) {
void dayNames(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * * 0-6", timeZone);
@@ -657,7 +657,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSundayIsZero(LocalDateTime localDateTime, TimeZone timeZone) {
void sundayIsZero(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * * 0", timeZone);
@@ -666,7 +666,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSundaySynonym(LocalDateTime localDateTime, TimeZone timeZone) {
void sundaySynonym(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * * 0", timeZone);
@@ -675,7 +675,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMonthNames(LocalDateTime localDateTime, TimeZone timeZone) {
void monthNames(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * 1-12 *", timeZone);
@@ -684,7 +684,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMonthNamesMixedCase(LocalDateTime localDateTime, TimeZone timeZone) {
void monthNamesMixedCase(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * 2 *", timeZone);
@@ -693,91 +693,91 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testSecondInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void secondInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("77 * * * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testSecondRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void secondRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("44-77 * * * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testMinuteInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void minuteInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* 77 * * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testMinuteRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void minuteRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* 44-77 * * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testHourInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void hourInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* * 27 * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testHourRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void hourRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* * 23-28 * * *", timeZone));
}
@ParameterizedCronTriggerTest
void testDayInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void dayInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* * * 45 * *", timeZone));
}
@ParameterizedCronTriggerTest
void testDayRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void dayRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* * * 28-45 * *", timeZone));
}
@ParameterizedCronTriggerTest
void testMonthInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void monthInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("0 0 0 25 13 ?", timeZone));
}
@ParameterizedCronTriggerTest
void testMonthInvalidTooSmall(LocalDateTime localDateTime, TimeZone timeZone) {
void monthInvalidTooSmall(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("0 0 0 25 0 ?", timeZone));
}
@ParameterizedCronTriggerTest
void testDayOfMonthInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void dayOfMonthInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("0 0 0 32 12 ?", timeZone));
}
@ParameterizedCronTriggerTest
void testMonthRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
void monthRangeInvalid(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
assertThatIllegalArgumentException().isThrownBy(() -> new CronTrigger("* * * * 11-13 *", timeZone));
}
@ParameterizedCronTriggerTest
void testWhitespace(LocalDateTime localDateTime, TimeZone timeZone) {
void whitespace(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger1 = new CronTrigger("* * * * 1 *", timeZone);
@@ -786,7 +786,7 @@ class CronTriggerTests {
}
@ParameterizedCronTriggerTest
void testMonthSequence(LocalDateTime localDateTime, TimeZone timeZone) {
void monthSequence(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
CronTrigger trigger = new CronTrigger("0 30 23 30 1/3 ?", timeZone);
@@ -808,23 +808,33 @@ class CronTriggerTests {
// Next trigger is 3 months latter
this.calendar.add(Calendar.MONTH, 3);
TriggerContext context3 = getTriggerContext(localDate);
Object actual = localDate = trigger.nextExecutionTime(context3);
Object actual = trigger.nextExecutionTime(context3);
assertThat(actual).isEqualTo(this.calendar.getTime());
}
@ParameterizedCronTriggerTest
void testDaylightSavingMissingHour(LocalDateTime localDateTime, TimeZone timeZone) {
void daylightSavingMissingHour(LocalDateTime localDateTime, TimeZone timeZone) {
setUp(localDateTime, timeZone);
// This trigger has to be somewhere in between 2am and 3am
// This trigger has to be somewhere between 2:00 AM and 3:00 AM, so we
// use a cron expression for 2:10 AM every day.
CronTrigger trigger = new CronTrigger("0 10 2 * * *", timeZone);
// 2:00 AM on March 31, 2013: start of Daylight Saving Time for CET in 2013.
// Setting up last completion:
// - PST: Sun Mar 31 10:09:54 CEST 2013
// - CET: Sun Mar 31 01:09:54 CET 2013
this.calendar.set(Calendar.DAY_OF_MONTH, 31);
this.calendar.set(Calendar.MONTH, Calendar.MARCH);
this.calendar.set(Calendar.YEAR, 2013);
this.calendar.set(Calendar.HOUR_OF_DAY, 1);
this.calendar.set(Calendar.MINUTE, 9);
this.calendar.set(Calendar.SECOND, 54);
Date localDate = this.calendar.getTime();
TriggerContext context1 = getTriggerContext(localDate);
Date lastCompletionTime = this.calendar.getTime();
// Setting up expected next execution time:
// - PST: Sun Mar 31 11:10:00 CEST 2013
// - CET: Mon Apr 01 02:10:00 CEST 2013
if (timeZone.equals(TimeZone.getTimeZone("CET"))) {
// Clocks go forward an hour so 2am doesn't exist in CET for this localDateTime
this.calendar.add(Calendar.DAY_OF_MONTH, 1);
@@ -832,8 +842,10 @@ class CronTriggerTests {
this.calendar.add(Calendar.HOUR_OF_DAY, 1);
this.calendar.set(Calendar.MINUTE, 10);
this.calendar.set(Calendar.SECOND, 0);
Object actual = localDate = trigger.nextExecutionTime(context1);
assertThat(actual).isEqualTo(this.calendar.getTime());
TriggerContext context = getTriggerContext(lastCompletionTime);
Object nextExecutionTime = trigger.nextExecutionTime(context);
assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime());
}
private static void roundup(Calendar calendar) {
@@ -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.
@@ -299,7 +299,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
@Override
@Nullable
public V remove(Object key) {
public V remove(@Nullable Object key) {
return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_AFTER, TaskOption.SKIP_IF_EMPTY) {
@Override
@Nullable
@@ -316,7 +316,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
}
@Override
public boolean remove(Object key, final Object value) {
public boolean remove(@Nullable Object key, final @Nullable Object value) {
Boolean result = doTask(key, new Task<Boolean>(TaskOption.RESTRUCTURE_AFTER, TaskOption.SKIP_IF_EMPTY) {
@Override
protected Boolean execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
@@ -333,7 +333,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
}
@Override
public boolean replace(K key, final V oldValue, final V newValue) {
public boolean replace(@Nullable K key, final @Nullable V oldValue, final @Nullable V newValue) {
Boolean result = doTask(key, new Task<Boolean>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.SKIP_IF_EMPTY) {
@Override
protected Boolean execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
@@ -349,7 +349,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
@Override
@Nullable
public V replace(K key, final V value) {
public V replace(@Nullable K key, final @Nullable V value) {
return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.SKIP_IF_EMPTY) {
@Override
@Nullable
@@ -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.
@@ -211,7 +211,13 @@ public class LinkedCaseInsensitiveMap<V> implements Map<String, V>, Serializable
public V putIfAbsent(String key, @Nullable V value) {
String oldKey = this.caseInsensitiveKeys.putIfAbsent(convertKey(key), key);
if (oldKey != null) {
return this.targetMap.get(oldKey);
V oldKeyValue = this.targetMap.get(oldKey);
if (oldKeyValue != null) {
return oldKeyValue;
}
else {
key = oldKey;
}
}
return this.targetMap.putIfAbsent(key, value);
}
@@ -221,7 +227,13 @@ public class LinkedCaseInsensitiveMap<V> implements Map<String, V>, Serializable
public V computeIfAbsent(String key, Function<? super String, ? extends V> mappingFunction) {
String oldKey = this.caseInsensitiveKeys.putIfAbsent(convertKey(key), key);
if (oldKey != null) {
return this.targetMap.get(oldKey);
V oldKeyValue = this.targetMap.get(oldKey);
if (oldKeyValue != null) {
return oldKeyValue;
}
else {
key = oldKey;
}
}
return this.targetMap.computeIfAbsent(key, mappingFunction);
}
@@ -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.
@@ -68,7 +68,7 @@ public abstract class PatternMatchUtils {
}
return (str.length() >= firstIndex &&
pattern.substring(0, firstIndex).equals(str.substring(0, firstIndex)) &&
pattern.startsWith(str.substring(0, firstIndex)) &&
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
}
@@ -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,9 +28,11 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
/**
* Utility class for working with Strings that have placeholder values in them. A placeholder takes the form
* {@code ${name}}. Using {@code PropertyPlaceholderHelper} these placeholders can be substituted for
* user-supplied values. <p> Values for substitution can be supplied using a {@link Properties} instance or
* Utility class for working with Strings that have placeholder values in them.
* A placeholder takes the form {@code ${name}}. Using {@code PropertyPlaceholderHelper}
* these placeholders can be substituted for user-supplied values.
*
* <p>Values for substitution can be supplied using a {@link Properties} instance or
* using a {@link PlaceholderResolver}.
*
* @author Juergen Hoeller
@@ -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.
@@ -292,7 +292,7 @@ class StaxEventXMLReader extends AbstractStaxXMLReader {
private void handleDtd(DTD dtd) throws SAXException {
if (getLexicalHandler() != null) {
javax.xml.stream.Location location = dtd.getLocation();
Location location = dtd.getLocation();
getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
}
if (getLexicalHandler() != null) {
@@ -34,7 +34,7 @@ operator fun PropertyResolver.get(key: String) : String? = getProperty(key)
/**
* Extension for [PropertyResolver.getProperty] providing a `getProperty<Foo>(...)`
* variant returning a nullable [String].
* variant returning a nullable `Foo`.
*
* @author Sebastien Deleuze
* @since 5.1
@@ -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,6 +99,12 @@ class LinkedCaseInsensitiveMapTests {
assertThat(map.computeIfAbsent("key", key2 -> "value1")).isEqualTo("value3");
assertThat(map.computeIfAbsent("KEY", key1 -> "value2")).isEqualTo("value3");
assertThat(map.computeIfAbsent("Key", key -> "value3")).isEqualTo("value3");
assertThat(map.put("null", null)).isNull();
assertThat(map.putIfAbsent("NULL", "value")).isNull();
assertThat(map.put("null", null)).isEqualTo("value");
assertThat(map.computeIfAbsent("NULL", s -> "value")).isEqualTo("value");
assertThat(map.get("null")).isEqualTo("value");
}
@Test
@@ -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.
@@ -49,7 +49,7 @@ public abstract class AbstractExpressionTests {
/**
* Evaluate an expression and check that the actual result matches the
* expectedValue and the class of the result matches the expectedClassOfResult.
* expectedValue and the class of the result matches the expectedResultType.
* @param expression the expression to evaluate
* @param expectedValue the expected result for evaluating the expression
* @param expectedResultType the expected class of the evaluation result
@@ -106,15 +106,15 @@ public abstract class AbstractExpressionTests {
/**
* Evaluate an expression and check that the actual result matches the
* expectedValue and the class of the result matches the expectedClassOfResult.
* expectedValue and the class of the result matches the expectedResultType.
* This method can also check if the expression is writable (for example,
* it is a variable or property reference).
* @param expression the expression to evaluate
* @param expectedValue the expected result for evaluating the expression
* @param expectedClassOfResult the expected class of the evaluation result
* @param expectedResultType the expected class of the evaluation result
* @param shouldBeWritable should the parsed expression be writable?
*/
public void evaluate(String expression, Object expectedValue, Class<?> expectedClassOfResult, boolean shouldBeWritable) {
public void evaluate(String expression, Object expectedValue, Class<?> expectedResultType, boolean shouldBeWritable) {
Expression expr = parser.parseExpression(expression);
assertThat(expr).as("expression").isNotNull();
if (DEBUG) {
@@ -134,7 +134,7 @@ public abstract class AbstractExpressionTests {
else {
assertThat(value).as("Did not get expected value for expression '" + expression + "'.").isEqualTo(expectedValue);
}
assertThat(expectedClassOfResult.equals(resultType)).as("Type of the result was not as expected. Expected '" + expectedClassOfResult +
assertThat(expectedResultType.equals(resultType)).as("Type of the result was not as expected. Expected '" + expectedResultType +
"' but result was of type '" + resultType + "'").isTrue();
assertThat(expr.isWritable(context)).as("isWritable").isEqualTo(shouldBeWritable);
@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.expression.spel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -40,98 +39,79 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Sam Brannen
* @author Juergen Hoeller
*/
public class SelectionAndProjectionTests {
class SelectionAndProjectionTests {
@Test
public void selectionWithList() throws Exception {
@SuppressWarnings("unchecked")
void selectionWithList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(5);
assertThat(list.get(0)).isEqualTo(0);
assertThat(list.get(1)).isEqualTo(1);
assertThat(list.get(2)).isEqualTo(2);
assertThat(list.get(3)).isEqualTo(3);
assertThat(list.get(4)).isEqualTo(4);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(0, 1, 2, 3, 4);
}
@Test
public void selectFirstItemInList() throws Exception {
void selectFirstItemInList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(0);
}
@Test
public void selectLastItemInList() throws Exception {
void selectLastItemInList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(4);
}
@Test
public void selectionWithSet() throws Exception {
@SuppressWarnings("unchecked")
void selectionWithSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(5);
assertThat(list.get(0)).isEqualTo(0);
assertThat(list.get(1)).isEqualTo(1);
assertThat(list.get(2)).isEqualTo(2);
assertThat(list.get(3)).isEqualTo(3);
assertThat(list.get(4)).isEqualTo(4);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(0, 1, 2, 3, 4);
}
@Test
public void selectFirstItemInSet() throws Exception {
void selectFirstItemInSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(0);
}
@Test
public void selectLastItemInSet() throws Exception {
void selectLastItemInSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(4);
}
@Test
public void selectionWithIterable() throws Exception {
@SuppressWarnings("unchecked")
void selectionWithIterable() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new IterableTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(5);
assertThat(list.get(0)).isEqualTo(0);
assertThat(list.get(1)).isEqualTo(1);
assertThat(list.get(2)).isEqualTo(2);
assertThat(list.get(3)).isEqualTo(3);
assertThat(list.get(4)).isEqualTo(4);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(0, 1, 2, 3, 4);
}
@Test
public void selectionWithArray() throws Exception {
void selectionWithArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
@@ -139,36 +119,29 @@ public class SelectionAndProjectionTests {
TypedValue typedValue = new TypedValue(value);
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
Integer[] array = (Integer[]) value;
assertThat(array.length).isEqualTo(5);
assertThat(array[0]).isEqualTo(0);
assertThat(array[1]).isEqualTo(1);
assertThat(array[2]).isEqualTo(2);
assertThat(array[3]).isEqualTo(3);
assertThat(array[4]).isEqualTo(4);
assertThat(array).containsExactly(0, 1, 2, 3, 4);
}
@Test
public void selectFirstItemInArray() throws Exception {
void selectFirstItemInArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(0);
}
@Test
public void selectLastItemInArray() throws Exception {
void selectLastItemInArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(4);
}
@Test
public void selectionWithPrimitiveArray() throws Exception {
void selectionWithPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
@@ -176,51 +149,41 @@ public class SelectionAndProjectionTests {
TypedValue typedValue = new TypedValue(value);
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
Integer[] array = (Integer[]) value;
assertThat(array.length).isEqualTo(5);
assertThat(array[0]).isEqualTo(0);
assertThat(array[1]).isEqualTo(1);
assertThat(array[2]).isEqualTo(2);
assertThat(array[3]).isEqualTo(3);
assertThat(array[4]).isEqualTo(4);
assertThat(array).containsExactly(0, 1, 2, 3, 4);
}
@Test
public void selectFirstItemInPrimitiveArray() throws Exception {
void selectFirstItemInPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(0);
}
@Test
public void selectLastItemInPrimitiveArray() throws Exception {
void selectLastItemInPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
boolean condition = value instanceof Integer;
assertThat(condition).isTrue();
assertThat(value).isInstanceOf(Integer.class);
assertThat(value).isEqualTo(4);
}
@Test
@SuppressWarnings("unchecked")
public void selectionWithMap() {
void selectionWithMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("colors.?[key.startsWith('b')]");
Map<String, String> colorsMap = (Map<String, String>) exp.getValue(context);
assertThat(colorsMap.size()).isEqualTo(3);
assertThat(colorsMap.containsKey("beige")).isTrue();
assertThat(colorsMap.containsKey("blue")).isTrue();
assertThat(colorsMap.containsKey("brown")).isTrue();
assertThat(colorsMap).containsOnlyKeys("beige", "blue", "brown");
}
@Test
@SuppressWarnings("unchecked")
public void selectFirstItemInMap() {
void selectFirstItemInMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
@@ -232,7 +195,7 @@ public class SelectionAndProjectionTests {
@Test
@SuppressWarnings("unchecked")
public void selectLastItemInMap() {
void selectLastItemInMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
@@ -243,52 +206,43 @@ public class SelectionAndProjectionTests {
}
@Test
public void projectionWithList() throws Exception {
@SuppressWarnings("unchecked")
void projectionWithList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testList", IntegerTestBean.createList());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(3);
assertThat(list.get(0)).isEqualTo(5);
assertThat(list.get(1)).isEqualTo(6);
assertThat(list.get(2)).isEqualTo(7);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(5, 6, 7);
}
@Test
public void projectionWithSet() throws Exception {
@SuppressWarnings("unchecked")
void projectionWithSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testList", IntegerTestBean.createSet());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(3);
assertThat(list.get(0)).isEqualTo(5);
assertThat(list.get(1)).isEqualTo(6);
assertThat(list.get(2)).isEqualTo(7);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(5, 6, 7);
}
@Test
public void projectionWithIterable() throws Exception {
@SuppressWarnings("unchecked")
void projectionWithIterable() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testList", IntegerTestBean.createIterable());
Object value = expression.getValue(context);
boolean condition = value instanceof List;
assertThat(condition).isTrue();
List<?> list = (List<?>) value;
assertThat(list.size()).isEqualTo(3);
assertThat(list.get(0)).isEqualTo(5);
assertThat(list.get(1)).isEqualTo(6);
assertThat(list.get(2)).isEqualTo(7);
assertThat(value).isInstanceOf(List.class);
List<Integer> list = (List<Integer>) value;
assertThat(list).containsExactly(5, 6, 7);
}
@Test
public void projectionWithArray() throws Exception {
void projectionWithArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testArray", IntegerTestBean.createArray());
@@ -297,10 +251,7 @@ public class SelectionAndProjectionTests {
TypedValue typedValue = new TypedValue(value);
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Number.class);
Number[] array = (Number[]) value;
assertThat(array.length).isEqualTo(3);
assertThat(array[0]).isEqualTo(5);
assertThat(array[1]).isEqualTo(5.9f);
assertThat(array[2]).isEqualTo(7);
assertThat(array).containsExactly(5, 5.9f, 7);
}
@@ -347,12 +298,7 @@ public class SelectionAndProjectionTests {
}
public Iterable<Integer> getIntegers() {
return new Iterable<Integer>() {
@Override
public Iterator<Integer> iterator() {
return integers.iterator();
}
};
return integers::iterator;
}
}
@@ -429,12 +375,7 @@ public class SelectionAndProjectionTests {
static Iterable<IntegerTestBean> createIterable() {
final Set<IntegerTestBean> set = createSet();
return new Iterable<IntegerTestBean>() {
@Override
public Iterator<IntegerTestBean> iterator() {
return set.iterator();
}
};
return set::iterator;
}
static IntegerTestBean[] createArray() {
@@ -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.
@@ -31,15 +31,12 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
* entry for each column, with the column name as key.
*
* <p>The Map implementation to use and the key to use for each column
* in the column Map can be customized through overriding
* {@link #createColumnMap} and {@link #getColumnKey}, respectively.
* in the column Map can be customized by overriding {@link #createColumnMap}
* and {@link #getColumnKey}, respectively.
*
* <p><b>Note:</b> By default, ColumnMapRowMapper will try to build a linked Map
* <p><b>Note:</b> By default, {@code ColumnMapRowMapper} will try to build a linked Map
* with case-insensitive keys, to preserve column order as well as allow any
* casing to be used for column names. This requires Commons Collections on the
* classpath (which will be autodetected). Else, the fallback is a standard linked
* HashMap, which will still preserve column order but requires the application
* to specify the column names in the same casing as exposed by the driver.
* casing to be used for column names.
*
* @author Juergen Hoeller
* @since 1.2
@@ -74,6 +71,7 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
/**
* Determine the key to use for the given column in the column Map.
* <p>By default, the supplied column name will be returned unmodified.
* @param columnName the column name as returned by the ResultSet
* @return the column key to use
* @see java.sql.ResultSetMetaData#getColumnName
@@ -86,9 +84,9 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation uses the {@code getObject} method.
* Additionally, this implementation includes a "hack" to get around Oracle
* returning a non standard object for their TIMESTAMP datatype.
* @param rs is the ResultSet holding the data
* @param index is the column index
* returning a non standard object for their TIMESTAMP data type.
* @param rs the ResultSet holding the data
* @param index the column index
* @return the Object returned
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue
*/
@@ -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.
@@ -22,7 +22,9 @@ import java.sql.SQLException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -50,7 +52,7 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
private String[] constructorParameterNames;
@Nullable
private Class<?>[] constructorParameterTypes;
private TypeDescriptor[] constructorParameterTypes;
/**
@@ -75,9 +77,13 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
super.initialize(mappedClass);
this.mappedConstructor = BeanUtils.getResolvableConstructor(mappedClass);
if (this.mappedConstructor.getParameterCount() > 0) {
int paramCount = this.mappedConstructor.getParameterCount();
if (paramCount > 0) {
this.constructorParameterNames = BeanUtils.getParameterNames(this.mappedConstructor);
this.constructorParameterTypes = this.mappedConstructor.getParameterTypes();
this.constructorParameterTypes = new TypeDescriptor[paramCount];
for (int i = 0; i < paramCount; i++) {
this.constructorParameterTypes[i] = new TypeDescriptor(new MethodParameter(this.mappedConstructor, i));
}
}
}
@@ -90,8 +96,9 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
args = new Object[this.constructorParameterNames.length];
for (int i = 0; i < args.length; i++) {
String name = underscoreName(this.constructorParameterNames[i]);
Class<?> type = this.constructorParameterTypes[i];
args[i] = tc.convertIfNecessary(getColumnValue(rs, rs.findColumn(name), type), type);
TypeDescriptor td = this.constructorParameterTypes[i];
Object value = getColumnValue(rs, rs.findColumn(name), td.getType());
args[i] = tc.convertIfNecessary(value, td.getType(), td);
}
}
else {
@@ -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.
@@ -40,22 +40,27 @@ import org.springframework.jdbc.support.JdbcUtils;
*
* <p>Example:
*
* <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
* <pre class="code">
* create table tab (id int unsigned not null primary key, text varchar(100));
* create table tab_sequence (value int not null);
* insert into tab_sequence values(0);</pre>
*
* If "cacheSize" is set, the intermediate values are served without querying the
* <p>If {@code cacheSize} is set, the intermediate values are served without querying the
* database. If the server or your application is stopped or crashes or a transaction
* is rolled back, the unused values will never be served. The maximum hole size in
* numbering is consequently the value of cacheSize.
* numbering is consequently the value of {@code cacheSize}.
*
* <p>It is possible to avoid acquiring a new connection for the incrementer by setting the
* "useNewConnection" property to false. In this case you <i>MUST</i> use a non-transactional
* storage engine like MYISAM when defining the incrementer table.
*
* <p>As of Spring Framework 5.3.7, {@code MySQLMaxValueIncrementer} is compatible with
* <a href="https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates">MySQL safe updates mode</a>.
*
* @author Jean-Pierre Pawlak
* @author Thomas Risberg
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer {
@@ -141,7 +146,7 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
String columnName = getColumnName();
try {
stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
" = last_insert_id(" + columnName + " + " + getCacheSize() + ") limit 1");
}
catch (SQLException ex) {
throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +
@@ -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.
@@ -135,6 +135,7 @@ public abstract class AbstractRowMapperTests {
given(resultSet.getObject(anyInt(), any(Class.class))).willThrow(new SQLFeatureNotSupportedException());
given(resultSet.getDate(3)).willReturn(new java.sql.Date(1221222L));
given(resultSet.getBigDecimal(4)).willReturn(new BigDecimal("1234.56"));
given(resultSet.getObject(4)).willReturn(new BigDecimal("1234.56"));
given(resultSet.wasNull()).willReturn(type == MockType.TWO);
given(resultSetMetaData.getColumnCount()).willReturn(4);
@@ -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,15 @@
package org.springframework.jdbc.core;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.test.ConstructorPerson;
import org.springframework.jdbc.core.test.ConstructorPersonWithGenerics;
import static org.assertj.core.api.Assertions.assertThat;
@@ -42,4 +46,20 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests {
mock.verifyClosed();
}
@Test
public void testStaticQueryWithDataClassAndGenerics() throws Exception {
Mock mock = new Mock();
List<ConstructorPersonWithGenerics> result = mock.getJdbcTemplate().query(
"select name, age, birth_date, balance from people",
new DataClassRowMapper<>(ConstructorPersonWithGenerics.class));
assertThat(result.size()).isEqualTo(1);
ConstructorPersonWithGenerics person = result.get(0);
assertThat(person.name()).isEqualTo("Bubba");
assertThat(person.age()).isEqualTo(22L);
assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.balance()).isEqualTo(Collections.singletonList(new BigDecimal("1234.56")));
mock.verifyClosed();
}
}
@@ -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.
@@ -24,13 +24,13 @@ import java.util.Date;
*/
public class ConstructorPerson {
private String name;
private final String name;
private long age;
private final long age;
private java.util.Date birth_date;
private final Date birth_date;
private BigDecimal balance;
private final BigDecimal balance;
public ConstructorPerson(String name, long age, Date birth_date, BigDecimal balance) {
@@ -42,19 +42,19 @@ public class ConstructorPerson {
public String name() {
return name;
return this.name;
}
public long age() {
return age;
return this.age;
}
public Date birth_date() {
return birth_date;
return this.birth_date;
}
public BigDecimal balance() {
return balance;
return this.balance;
}
}
@@ -0,0 +1,61 @@
/*
* 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.core.test;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* @author Juergen Hoeller
*/
public class ConstructorPersonWithGenerics {
private final String name;
private final long age;
private final Date birth_date;
private final List<BigDecimal> balance;
public ConstructorPersonWithGenerics(String name, long age, Date birth_date, List<BigDecimal> balance) {
this.name = name;
this.age = age;
this.birth_date = birth_date;
this.balance = balance;
}
public String name() {
return this.name;
}
public long age() {
return this.age;
}
public Date birth_date() {
return this.birth_date;
}
public List<BigDecimal> balance() {
return this.balance;
}
}
@@ -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.
@@ -25,6 +25,7 @@ import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
@@ -38,10 +39,13 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Unit tests for {@link DataFieldMaxValueIncrementer} implementations.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 27.02.2004
*/
public class DataFieldMaxValueIncrementerTests {
class DataFieldMaxValueIncrementerTests {
private final DataSource dataSource = mock(DataSource.class);
@@ -53,7 +57,7 @@ public class DataFieldMaxValueIncrementerTests {
@Test
public void testHanaSequenceMaxValueIncrementer() throws SQLException {
void hanaSequenceMaxValueIncrementer() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select myseq.nextval from dummy")).willReturn(resultSet);
@@ -75,7 +79,7 @@ public class DataFieldMaxValueIncrementerTests {
}
@Test
public void testHsqlMaxValueIncrementer() throws SQLException {
void hsqlMaxValueIncrementer() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
@@ -105,7 +109,7 @@ public class DataFieldMaxValueIncrementerTests {
}
@Test
public void testHsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
void hsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
@@ -136,7 +140,7 @@ public class DataFieldMaxValueIncrementerTests {
}
@Test
public void testMySQLMaxValueIncrementer() throws SQLException {
void mySQLMaxValueIncrementer() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select last_insert_id()")).willReturn(resultSet);
@@ -156,14 +160,14 @@ public class DataFieldMaxValueIncrementerTests {
assertThat(incrementer.nextStringValue()).isEqualTo("3");
assertThat(incrementer.nextLongValue()).isEqualTo(4);
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2)");
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2) limit 1");
verify(resultSet, times(2)).close();
verify(statement, times(2)).close();
verify(connection, times(2)).close();
}
@Test
public void testOracleSequenceMaxValueIncrementer() throws SQLException {
void oracleSequenceMaxValueIncrementer() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select myseq.nextval from dual")).willReturn(resultSet);
@@ -185,7 +189,7 @@ public class DataFieldMaxValueIncrementerTests {
}
@Test
public void testPostgresSequenceMaxValueIncrementer() throws SQLException {
void postgresSequenceMaxValueIncrementer() throws SQLException {
given(dataSource.getConnection()).willReturn(connection);
given(connection.createStatement()).willReturn(statement);
given(statement.executeQuery("select nextval('myseq')")).willReturn(resultSet);
@@ -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.
@@ -179,6 +179,23 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
}
/**
* Return a current session count, indicating the number of sessions currently
* cached by this connection factory.
* @since 5.3.7
*/
public int getCachedSessionCount() {
int count = 0;
synchronized (this.cachedSessions) {
for (Deque<Session> sessionList : this.cachedSessions.values()) {
synchronized (sessionList) {
count += sessionList.size();
}
}
}
return count;
}
/**
* Resets the Session cache as well.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import io.rsocket.transport.ClientTransport;
import io.rsocket.transport.netty.client.TcpClientTransport;
import io.rsocket.transport.netty.client.WebsocketClientTransport;
import org.reactivestreams.Publisher;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -49,7 +50,7 @@ import org.springframework.util.MimeType;
* @author Brian Clozel
* @since 5.2
*/
public interface RSocketRequester {
public interface RSocketRequester extends Disposable {
/**
* Return the underlying {@link RSocketClient} used to make requests with.
@@ -110,6 +111,27 @@ public interface RSocketRequester {
*/
RequestSpec metadata(Object metadata, @Nullable MimeType mimeType);
/**
* Shortcut method that delegates to the same on the underlying
* {@link #rsocketClient()} in order to close the connection from the
* underlying transport and notify subscribers.
* @since 5.3.7
*/
@Override
default void dispose() {
rsocketClient().dispose();
}
/**
* Shortcut method that delegates to the same on the underlying
* {@link #rsocketClient()}.
* @since 5.3.7
*/
@Override
default boolean isDisposed() {
return rsocketClient().isDisposed();
}
/**
* Obtain a builder to create a client {@link RSocketRequester} by connecting
* to an RSocket server.
@@ -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.
@@ -42,10 +42,16 @@ public abstract class AbstractBrokerRegistration {
private final List<String> destinationPrefixes;
/**
* Create a new broker registration.
* @param clientInboundChannel the inbound channel
* @param clientOutboundChannel the outbound channel
* @param destinationPrefixes the destination prefixes
*/
public AbstractBrokerRegistration(SubscribableChannel clientInboundChannel,
MessageChannel clientOutboundChannel, @Nullable String[] destinationPrefixes) {
Assert.notNull(clientOutboundChannel, "'clientInboundChannel' must not be null");
Assert.notNull(clientInboundChannel, "'clientInboundChannel' must not be null");
Assert.notNull(clientOutboundChannel, "'clientOutboundChannel' must not be null");
this.clientInboundChannel = clientInboundChannel;
@@ -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.
@@ -40,8 +40,16 @@ public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
private String selectorHeaderName = "selector";
public SimpleBrokerRegistration(SubscribableChannel inChannel, MessageChannel outChannel, String[] prefixes) {
super(inChannel, outChannel, prefixes);
/**
* Create a new {@code SimpleBrokerRegistration}.
* @param clientInboundChannel the inbound channel
* @param clientOutboundChannel the outbound channel
* @param destinationPrefixes the destination prefixes
*/
public SimpleBrokerRegistration(SubscribableChannel clientInboundChannel,
MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
super(clientInboundChannel, clientOutboundChannel, destinationPrefixes);
}
@@ -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.
@@ -68,6 +68,12 @@ public class StompBrokerRelayRegistration extends AbstractBrokerRegistration {
private String userRegistryBroadcast;
/**
* Create a new {@code StompBrokerRelayRegistration}.
* @param clientInboundChannel the inbound channel
* @param clientOutboundChannel the outbound channel
* @param destinationPrefixes the destination prefixes
*/
public StompBrokerRelayRegistration(SubscribableChannel clientInboundChannel,
MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
@@ -166,7 +166,10 @@ public class InvocableHandlerMethodTests {
@SuppressWarnings("unused")
private static class Handler {
static class Handler {
public Handler() {
}
public String handle(Integer intArg, String stringArg) {
return intArg + "-" + stringArg;
@@ -181,7 +184,7 @@ public class InvocableHandlerMethodTests {
}
private static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver {
static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
@@ -183,6 +183,8 @@ public class InvocableHandlerMethodTests {
private AtomicReference<String> result = new AtomicReference<>();
public Handler() {
}
public String getResult() {
return this.result.get();
+12 -44
View File
@@ -1,56 +1,24 @@
plugins {
id "org.unbroken-dome.xjc"
}
description = "Spring Object/XML Marshalling"
configurations {
jibx
xjc
}
dependencies {
jibx "org.jibx:jibx-bind:1.3.3"
jibx "org.apache.bcel:bcel:6.0"
xjc "javax.xml.bind:jaxb-api:2.3.1"
xjc "com.sun.xml.bind:jaxb-core:2.3.0.1"
xjc "com.sun.xml.bind:jaxb-impl:2.3.0.1"
xjc "com.sun.xml.bind:jaxb-xjc:2.3.1"
xjc "com.sun.activation:javax.activation:1.2.0"
}
ext.genSourcesDir = "${buildDir}/generated-sources"
ext.flightSchema = "${projectDir}/src/test/resources/org/springframework/oxm/flight.xsd"
task genJaxb {
ext.sourcesDir = "${genSourcesDir}/jaxb"
ext.classesDir = "${buildDir}/classes/jaxb"
inputs.files(flightSchema).withPathSensitivity(PathSensitivity.RELATIVE)
outputs.dir classesDir
doLast() {
project.ant {
taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
classpath: configurations.xjc.asPath
mkdir(dir: sourcesDir)
mkdir(dir: classesDir)
xjc(destdir: sourcesDir, schema: flightSchema,
package: "org.springframework.oxm.jaxb.test") {
produces(dir: sourcesDir, includes: "**/*.java")
}
javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true,
debugLevel: "lines,vars,source",
classpath: configurations.xjc.asPath) {
src(path: sourcesDir)
include(name: "**/*.java")
include(name: "*.java")
}
copy(todir: classesDir) {
fileset(dir: sourcesDir, erroronmissingdir: false) {
exclude(name: "**/*.java")
}
}
}
xjc {
xjcVersion = '2.2'
}
sourceSets {
test {
xjcTargetPackage = 'org.springframework.oxm.jaxb.test'
}
}
@@ -67,7 +35,7 @@ dependencies {
testCompile("org.codehaus.jettison:jettison") {
exclude group: "stax", module: "stax-api"
}
testCompile(files(genJaxb.classesDir).builtBy(genJaxb))
//testCompile(files(genJaxb.classesDir).builtBy(genJaxb))
testCompile("org.xmlunit:xmlunit-assertj")
testCompile("org.xmlunit:xmlunit-matchers")
testRuntime("com.sun.xml.bind:jaxb-core")
@@ -76,7 +44,7 @@ dependencies {
// JiBX compiler is currently not compatible with JDK 9+.
// If customJavaHome has been set, we assume the custom JDK version is 9+.
if ((JavaVersion.current() == JavaVersion.VERSION_1_8) && !System.getProperty("customJavaSourceVersion")) {
if ((JavaVersion.current() == JavaVersion.VERSION_1_8) && !project.hasProperty("testToolchain")) {
compileTestJava {
def bindingXml = "${projectDir}/src/test/resources/org/springframework/oxm/jibx/binding.xml"
@@ -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.
@@ -78,7 +78,7 @@ import static org.xmlunit.diff.DifferenceEvaluators.downgradeDifferencesToEqual;
* @author Biju Kunjummen
* @author Sam Brannen
*/
public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshaller> {
class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshaller> {
private static final String CONTEXT_PATH = "org.springframework.oxm.jaxb.test";
@@ -104,7 +104,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
@Test
public void marshalSAXResult() throws Exception {
void marshalSAXResult() throws Exception {
ContentHandler contentHandler = mock(ContentHandler.class);
SAXResult result = new SAXResult(contentHandler);
marshaller.marshal(flights, result);
@@ -124,7 +124,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test
public void lazyInit() throws Exception {
void lazyInit() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(CONTEXT_PATH);
marshaller.setLazyInit(true);
@@ -137,48 +137,44 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test
public void properties() throws Exception {
void properties() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(CONTEXT_PATH);
marshaller.setMarshallerProperties(
Collections.<String, Object>singletonMap(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE));
Collections.singletonMap(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE));
marshaller.afterPropertiesSet();
}
@Test
public void noContextPathOrClassesToBeBound() throws Exception {
void noContextPathOrClassesToBeBound() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
assertThatIllegalArgumentException().isThrownBy(
marshaller::afterPropertiesSet);
assertThatIllegalArgumentException().isThrownBy(marshaller::afterPropertiesSet);
}
@Test
public void testInvalidContextPath() throws Exception {
void testInvalidContextPath() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("ab");
assertThatExceptionOfType(UncategorizedMappingException.class).isThrownBy(
marshaller::afterPropertiesSet);
assertThatExceptionOfType(UncategorizedMappingException.class).isThrownBy(marshaller::afterPropertiesSet);
}
@Test
public void marshalInvalidClass() throws Exception {
void marshalInvalidClass() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(FlightType.class);
marshaller.afterPropertiesSet();
Result result = new StreamResult(new StringWriter());
Flights flights = new Flights();
assertThatExceptionOfType(XmlMappingException.class).isThrownBy(() ->
marshaller.marshal(flights, result));
assertThatExceptionOfType(XmlMappingException.class).isThrownBy(() -> marshaller.marshal(flights, result));
}
@Test
public void supportsContextPath() throws Exception {
void supportsContextPath() throws Exception {
testSupports();
}
@Test
public void supportsClassesToBeBound() throws Exception {
void supportsClassesToBeBound() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Flights.class, FlightType.class);
marshaller.afterPropertiesSet();
@@ -186,7 +182,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test
public void supportsPackagesToScan() throws Exception {
void supportsPackagesToScan() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(CONTEXT_PATH);
marshaller.afterPropertiesSet();
@@ -224,11 +220,11 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
private void testSupportsPrimitives() {
final Primitives primitives = new Primitives();
ReflectionUtils.doWithMethods(Primitives.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.doWithMethods(Primitives.class, method -> {
Type returnType = method.getGenericReturnType();
assertThat(marshaller.supports(returnType)).as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(9) + ">").isTrue();
assertThat(marshaller.supports(returnType))
.as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(9) + ">")
.isTrue();
try {
// make sure the marshalling does not result in errors
Object returnValue = method.invoke(primitives);
@@ -237,22 +233,18 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
catch (InvocationTargetException e) {
throw new AssertionError(e.getMessage(), e);
}
}
}, new ReflectionUtils.MethodFilter() {
@Override
public boolean matches(Method method) {
return method.getName().startsWith("primitive");
}
});
},
method -> method.getName().startsWith("primitive")
);
}
private void testSupportsStandardClasses() throws Exception {
final StandardClasses standardClasses = new StandardClasses();
ReflectionUtils.doWithMethods(StandardClasses.class, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.doWithMethods(StandardClasses.class, method -> {
Type returnType = method.getGenericReturnType();
assertThat(marshaller.supports(returnType)).as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(13) + ">").isTrue();
assertThat(marshaller.supports(returnType))
.as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(13) + ">")
.isTrue();
try {
// make sure the marshalling does not result in errors
Object returnValue = method.invoke(standardClasses);
@@ -261,17 +253,13 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
catch (InvocationTargetException e) {
throw new AssertionError(e.getMessage(), e);
}
}
}, new ReflectionUtils.MethodFilter() {
@Override
public boolean matches(Method method) {
return method.getName().startsWith("standardClass");
}
});
},
method -> method.getName().startsWith("standardClass")
);
}
@Test
public void supportsXmlRootElement() throws Exception {
void supportsXmlRootElement() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(DummyRootElement.class, DummyType.class);
marshaller.afterPropertiesSet();
@@ -284,7 +272,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
@Test
public void marshalAttachments() throws Exception {
void marshalAttachments() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(BinaryObject.class);
marshaller.setMtomEnabled(true);
@@ -304,7 +292,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test // SPR-10714
public void marshalAWrappedObjectHoldingAnXmlElementDeclElement() throws Exception {
void marshalAWrappedObjectHoldingAnXmlElementDeclElement() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("org.springframework.oxm.jaxb");
marshaller.afterPropertiesSet();
@@ -318,7 +306,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test // SPR-10806
public void unmarshalStreamSourceWithXmlOptions() throws Exception {
void unmarshalStreamSourceWithXmlOptions() throws Exception {
final javax.xml.bind.Unmarshaller unmarshaller = mock(javax.xml.bind.Unmarshaller.class);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
@Override
@@ -352,7 +340,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
}
@Test // SPR-10806
public void unmarshalSaxSourceWithXmlOptions() throws Exception {
void unmarshalSaxSourceWithXmlOptions() throws Exception {
final javax.xml.bind.Unmarshaller unmarshaller = mock(javax.xml.bind.Unmarshaller.class);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
@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.
@@ -31,6 +31,7 @@ import javax.xml.transform.stream.StreamSource;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.oxm.AbstractUnmarshallerTests;
import org.springframework.oxm.jaxb.test.FlightType;
@@ -56,7 +57,7 @@ public class Jaxb2UnmarshallerTests extends AbstractUnmarshallerTests<Jaxb2Marsh
protected Jaxb2Marshaller createUnmarshaller() throws Exception {
Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller();
unmarshaller.setContextPath("org.springframework.oxm.jaxb.test");
unmarshaller.setSchema(new ClassPathResource("org/springframework/oxm/flight.xsd"));
unmarshaller.setSchema(new FileSystemResource("src/test/schema/flight.xsd"));
unmarshaller.afterPropertiesSet();
return unmarshaller;
}
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="https://samples.springframework.org/order"
xmlns:tns="https://samples.springframework.org/order">
<element name="order">
<complexType>
<sequence>
<element name="order-item" type="tns:orderItemType"
maxOccurs="unbounded">
</element>
</sequence>
</complexType>
</element>
<complexType name="orderItemType">
<attribute name="id" type="string" />
<attribute name="quantity" type="int" />
</complexType>
</schema>
@@ -1,4 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://samples.springframework.org/flight"
xmlns:tns="http://samples.springframework.org/flight">
@@ -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.
@@ -315,8 +315,8 @@ public class MockServletContext implements ServletContext {
return resourcePaths;
}
catch (InvalidPathException | IOException ex ) {
if (logger.isWarnEnabled()) {
logger.warn("Could not get resource paths for " +
if (logger.isDebugEnabled()) {
logger.debug("Could not get resource paths for " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -339,8 +339,8 @@ public class MockServletContext implements ServletContext {
throw ex;
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not get URL for resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not get URL for resource " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -360,8 +360,8 @@ public class MockServletContext implements ServletContext {
return resource.getInputStream();
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not open InputStream for resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not open InputStream for resource " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -476,8 +476,8 @@ public class MockServletContext implements ServletContext {
return resource.getFile().getAbsolutePath();
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not determine real path of resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not determine real path of resource " +
(resource != null ? resource : resourceLocation), ex);
}
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.
@@ -373,8 +373,16 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable {
for (NameValuePair param : this.webRequest.getRequestParameters()) {
if (param instanceof KeyDataPair) {
KeyDataPair pair = (KeyDataPair) param;
MockPart part = new MockPart(pair.getName(), pair.getFile().getName(), readAllBytes(pair.getFile()));
part.getHeaders().setContentType(MediaType.valueOf(pair.getMimeType()));
File file = pair.getFile();
MockPart part;
if (file != null) {
part = new MockPart(pair.getName(), file.getName(), readAllBytes(file));
part.getHeaders().setContentType(MediaType.valueOf(pair.getMimeType()));
}
else { // mimic empty file upload
part = new MockPart(pair.getName(), "", null);
part.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
}
request.addPart(part);
}
else {
@@ -496,7 +496,6 @@ class MockHttpServletResponseTests {
String expiryDate = "Tue, 8 Oct 2019 19:50:00 GMT";
String cookieValue = "SESSION=123; Path=/; Expires=" + expiryDate;
response.addHeader(SET_COOKIE, cookieValue);
System.err.println(response.getCookie("SESSION"));
assertThat(response.getHeader(SET_COOKIE)).isEqualTo(cookieValue);
assertNumCookies(1);
@@ -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.
@@ -67,7 +67,7 @@ class TimedTransactionalSpringExtensionTests {
event(test("WithExceededJUnitJupiterTimeout"),
finishedWithFailure(
instanceOf(TimeoutException.class),
message(msg -> msg.endsWith("timed out after 50 milliseconds")))));
message(msg -> msg.endsWith("timed out after 10 milliseconds")))));
}
@@ -83,10 +83,10 @@ class TimedTransactionalSpringExtensionTests {
}
@Test
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
void transactionalWithExceededJUnitJupiterTimeout() throws Exception {
assertThatTransaction().isActive();
Thread.sleep(100);
Thread.sleep(200);
}
@Test
@@ -97,11 +97,11 @@ class TimedTransactionalSpringExtensionTests {
}
@Test
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
void notTransactionalWithExceededJUnitJupiterTimeout() throws Exception {
assertThatTransaction().isNotActive();
Thread.sleep(100);
Thread.sleep(200);
}
@@ -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.
@@ -76,14 +76,14 @@ public class TimedSpringRunnerTests {
}
// Should Fail due to timeout.
@Test(timeout = 100)
@Test(timeout = 10)
public void jUnitTimeoutWithSleep() throws Exception {
Thread.sleep(200);
}
// Should Fail due to timeout.
@Test
@Timed(millis = 100)
@Timed(millis = 10)
public void springTimeoutWithSleep() throws Exception {
Thread.sleep(200);
}
@@ -97,7 +97,7 @@ public class TimedSpringRunnerTests {
// Should Fail due to timeout.
@Test
@MetaTimedWithOverride(millis = 100)
@MetaTimedWithOverride(millis = 10)
public void springTimeoutWithSleepAndMetaAnnotationAndOverride() throws Exception {
Thread.sleep(200);
}
@@ -110,7 +110,7 @@ public class TimedSpringRunnerTests {
}
}
@Timed(millis = 100)
@Timed(millis = 10)
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTimed {
}
@@ -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,10 @@
package org.springframework.test.web.servlet.htmlunit;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
@@ -52,6 +54,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
@@ -423,8 +426,7 @@ public class HtmlUnitRequestBuilderTests {
}
@Test // gh-24926
public void buildRequestParameterMapViaWebRequestDotSetFileToUploadAsParameter() throws Exception {
public void buildRequestParameterMapViaWebRequestDotSetRequestParametersWithFileToUploadAsParameter() throws Exception {
webRequest.setRequestParameters(Collections.singletonList(
new KeyDataPair("key",
new ClassPathResource("org/springframework/test/web/htmlunit/test.txt").getFile(),
@@ -432,7 +434,7 @@ public class HtmlUnitRequestBuilderTests {
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
assertThat(actualRequest.getParts().size()).isEqualTo(1);
assertThat(actualRequest.getParts()).hasSize(1);
Part part = actualRequest.getPart("key");
assertThat(part).isNotNull();
assertThat(part.getName()).isEqualTo("key");
@@ -441,6 +443,30 @@ public class HtmlUnitRequestBuilderTests {
assertThat(part.getContentType()).isEqualTo(MimeType.TEXT_PLAIN);
}
@Test // gh-26799
public void buildRequestParameterMapViaWebRequestDotSetRequestParametersWithNullFileToUploadAsParameter() throws Exception {
webRequest.setRequestParameters(Collections.singletonList(new KeyDataPair("key", null, null, null, (Charset) null)));
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
assertThat(actualRequest.getParts()).hasSize(1);
Part part = actualRequest.getPart("key");
assertSoftly(softly -> {
softly.assertThat(part).isNotNull();
softly.assertThat(part.getName()).as("name").isEqualTo("key");
softly.assertThat(part.getSize()).as("size").isEqualTo(0);
try {
softly.assertThat(part.getInputStream()).isEmpty();
}
catch (IOException ex) {
softly.fail("failed to get InputStream", ex);
}
softly.assertThat(part.getSubmittedFileName()).as("filename").isEqualTo("");
softly.assertThat(part.getContentType()).as("content-type").isEqualTo("application/octet-stream");
});
}
@Test
public void buildRequestParameterMapFromSingleQueryParam() throws Exception {
webRequest.setUrl(new URL("https://example.com/example/?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.
@@ -28,7 +28,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.OrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -320,7 +320,7 @@ public abstract class TransactionSynchronizationManager {
else {
// Sort lazily here, not in registerSynchronization.
List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);
AnnotationAwareOrderComparator.sort(sortedSynchs);
OrderComparator.sort(sortedSynchs);
return Collections.unmodifiableList(sortedSynchs);
}
}
@@ -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.
@@ -57,14 +57,13 @@ public enum HttpMethod {
/**
* Determine whether this {@code HttpMethod} matches the given
* method value.
* @param method the method value as a String
* Determine whether this {@code HttpMethod} matches the given method value.
* @param method the HTTP method as a String
* @return {@code true} if it matches, {@code false} otherwise
* @since 4.2.4
*/
public boolean matches(String method) {
return (this == resolve(method));
return name().equals(method);
}
}
@@ -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.
@@ -416,6 +416,13 @@ public enum HttpStatus {
NETWORK_AUTHENTICATION_REQUIRED(511, Series.SERVER_ERROR, "Network Authentication Required");
private static final HttpStatus[] VALUES;
static {
VALUES = values();
}
private final int value;
private final Series series;
@@ -550,7 +557,8 @@ public enum HttpStatus {
*/
@Nullable
public static HttpStatus resolve(int statusCode) {
for (HttpStatus status : values()) {
// used cached VALUES instead of values() to prevent array allocation
for (HttpStatus status : VALUES) {
if (status.value == statusCode) {
return status;
}
@@ -19,9 +19,7 @@ package org.springframework.http.codec.multipart;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -63,8 +61,6 @@ import org.springframework.util.Assert;
*/
public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> {
private static final String IDENTIFIER = "spring-multipart";
private int maxInMemorySize = 256 * 1024;
private int maxHeadersSize = 8 * 1024;
@@ -77,7 +73,7 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
private Scheduler blockingOperationScheduler = Schedulers.boundedElastic();
private Mono<Path> fileStorageDirectory = Mono.defer(this::defaultFileStorageDirectory).cache();
private FileStorage fileStorage = FileStorage.tempDirectory(this::getBlockingOperationScheduler);
private Charset headersCharset = StandardCharsets.UTF_8;
@@ -147,10 +143,7 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
*/
public void setFileStorageDirectory(Path fileStorageDirectory) throws IOException {
Assert.notNull(fileStorageDirectory, "FileStorageDirectory must not be null");
if (!Files.exists(fileStorageDirectory)) {
Files.createDirectory(fileStorageDirectory);
}
this.fileStorageDirectory = Mono.just(fileStorageDirectory);
this.fileStorage = FileStorage.fromPath(fileStorageDirectory);
}
/**
@@ -168,6 +161,10 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
this.blockingOperationScheduler = blockingOperationScheduler;
}
private Scheduler getBlockingOperationScheduler() {
return this.blockingOperationScheduler;
}
/**
* When set to {@code true}, the {@linkplain Part#content() part content}
* is streamed directly from the parsed input buffer stream, and not stored
@@ -230,7 +227,7 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
this.maxHeadersSize, this.headersCharset);
return PartGenerator.createParts(tokens, this.maxParts, this.maxInMemorySize, this.maxDiskUsagePerPart,
this.streaming, this.fileStorageDirectory, this.blockingOperationScheduler);
this.streaming, this.fileStorage.directory(), this.blockingOperationScheduler);
});
}
@@ -250,16 +247,4 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
return null;
}
@SuppressWarnings("BlockingMethodInNonBlockingContext")
private Mono<Path> defaultFileStorageDirectory() {
return Mono.fromCallable(() -> {
Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir"), IDENTIFIER);
if (!Files.exists(tempDirectory)) {
Files.createDirectory(tempDirectory);
}
return tempDirectory;
}).subscribeOn(this.blockingOperationScheduler);
}
}
@@ -0,0 +1,128 @@
/*
* 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.http.codec.multipart;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
/**
* Represents a directory used to store parts larger than
* {@link DefaultPartHttpMessageReader#setMaxInMemorySize(int)}.
*
* @author Arjen Poutsma
* @since 5.3.7
*/
abstract class FileStorage {
private static final Log logger = LogFactory.getLog(FileStorage.class);
protected FileStorage() {
}
/**
* Get the mono of the directory to store files in.
*/
public abstract Mono<Path> directory();
/**
* Create a new {@code FileStorage} from a user-specified path. Creates the
* path if it does not exist.
*/
public static FileStorage fromPath(Path path) throws IOException {
if (!Files.exists(path)) {
Files.createDirectory(path);
}
return new PathFileStorage(path);
}
/**
* Create a new {@code FileStorage} based a on a temporary directory.
* @param scheduler scheduler to use for blocking operations
*/
public static FileStorage tempDirectory(Supplier<Scheduler> scheduler) {
return new TempFileStorage(scheduler);
}
private static final class PathFileStorage extends FileStorage {
private final Mono<Path> directory;
public PathFileStorage(Path directory) {
this.directory = Mono.just(directory);
}
@Override
public Mono<Path> directory() {
return this.directory;
}
}
private static final class TempFileStorage extends FileStorage {
private static final String IDENTIFIER = "spring-multipart-";
private final Supplier<Scheduler> scheduler;
private volatile Mono<Path> directory = tempDirectory();
public TempFileStorage(Supplier<Scheduler> scheduler) {
this.scheduler = scheduler;
}
@Override
public Mono<Path> directory() {
return this.directory
.flatMap(this::createNewDirectoryIfDeleted)
.subscribeOn(this.scheduler.get());
}
private Mono<Path> createNewDirectoryIfDeleted(Path directory) {
if (!Files.exists(directory)) {
// Some daemons remove temp directories. Let's create a new one.
Mono<Path> newDirectory = tempDirectory();
this.directory = newDirectory;
return newDirectory;
}
else {
return Mono.just(directory);
}
}
private static Mono<Path> tempDirectory() {
return Mono.fromCallable(() -> {
Path directory = Files.createTempDirectory(IDENTIFIER);
if (logger.isDebugEnabled()) {
logger.debug("Created temporary storage directory: " + directory);
}
return directory;
}).cache();
}
}
}
@@ -578,9 +578,6 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
private WritingFileState createFileState(Path directory) {
try {
if (!Files.exists(directory)) {
Files.createDirectory(directory);
}
Path tempFile = Files.createTempFile(directory, null, ".multipart");
if (logger.isTraceEnabled()) {
logger.trace("Storing multipart data in file " + tempFile);
@@ -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.
@@ -17,11 +17,13 @@
package org.springframework.http.codec.multipart;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
@@ -78,12 +80,16 @@ import org.springframework.util.Assert;
*/
public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> {
private static final String FILE_STORAGE_DIRECTORY_PREFIX = "synchronoss-file-upload-";
private int maxInMemorySize = 256 * 1024;
private long maxDiskUsagePerPart = -1;
private int maxParts = -1;
private Path fileStorageDirectory = createTempDirectory();
/**
* Configure the maximum amount of memory that is allowed to use per part.
@@ -144,6 +150,22 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
return this.maxParts;
}
/**
* Set the directory used to store parts larger than
* {@link #setMaxInMemorySize(int) maxInMemorySize}. By default, a new
* temporary directory is created.
* @throws IOException if an I/O error occurs, or the parent directory
* does not exist
* @since 5.3.7
*/
public void setFileStorageDirectory(Path fileStorageDirectory) throws IOException {
Assert.notNull(fileStorageDirectory, "FileStorageDirectory must not be null");
if (!Files.exists(fileStorageDirectory)) {
Files.createDirectory(fileStorageDirectory);
}
this.fileStorageDirectory = fileStorageDirectory;
}
@Override
public List<MediaType> getReadableMediaTypes() {
@@ -167,7 +189,7 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
@Override
public Flux<Part> read(ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) {
return Flux.create(new SynchronossPartGenerator(message))
return Flux.create(new SynchronossPartGenerator(message, this.fileStorageDirectory))
.doOnNext(part -> {
if (!Hints.isLoggingSuppressed(hints)) {
LogFormatUtils.traceDebug(logger, traceOn -> Hints.getLogPrefix(hints) + "Parsed " +
@@ -183,6 +205,15 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
return Mono.error(new UnsupportedOperationException("Cannot read multipart request body into single Part"));
}
private static Path createTempDirectory() {
try {
return Files.createTempDirectory(FILE_STORAGE_DIRECTORY_PREFIX);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
/**
* Subscribe to the input stream and feed the Synchronoss parser. Then listen
@@ -194,14 +225,17 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
private final LimitedPartBodyStreamStorageFactory storageFactory = new LimitedPartBodyStreamStorageFactory();
private final Path fileStorageDirectory;
@Nullable
private NioMultipartParserListener listener;
@Nullable
private NioMultipartParser parser;
public SynchronossPartGenerator(ReactiveHttpInputMessage inputMessage) {
public SynchronossPartGenerator(ReactiveHttpInputMessage inputMessage, Path fileStorageDirectory) {
this.inputMessage = inputMessage;
this.fileStorageDirectory = fileStorageDirectory;
}
@Override
@@ -218,6 +252,7 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
this.parser = Multipart
.multipart(context)
.saveTemporaryFilesTo(this.fileStorageDirectory.toString())
.usePartBodyStreamStorageFactory(this.storageFactory)
.forNIO(this.listener);
@@ -68,10 +68,10 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
@Nullable
private volatile Subscriber<? super T> subscriber;
private volatile boolean completionBeforeDemand;
private volatile boolean completionPending;
@Nullable
private volatile Throwable errorBeforeDemand;
private volatile Throwable errorPending;
private final String logPrefix;
@@ -186,7 +186,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
*/
private boolean readAndPublish() throws IOException {
long r;
while ((r = this.demand) > 0 && !this.state.get().equals(State.COMPLETED)) {
while ((r = this.demand) > 0 && (this.state.get() != State.COMPLETED)) {
T data = read();
if (data != null) {
if (r != Long.MAX_VALUE) {
@@ -222,27 +222,30 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
// Protect from infinite recursion in Undertow, where we can't check if data
// is available, so all we can do is to try to read.
// Generally, no need to check if we just came out of readAndPublish()...
if (!oldState.equals(State.READING)) {
if (oldState != State.READING) {
checkOnDataAvailable();
}
}
}
private void handleCompletionOrErrorBeforeDemand() {
private boolean handlePendingCompletionOrError() {
State state = this.state.get();
if (!state.equals(State.UNSUBSCRIBED) && !state.equals(State.SUBSCRIBING)) {
if (this.completionBeforeDemand) {
rsReadLogger.trace(getLogPrefix() + "Completed before demand");
if (state == State.DEMAND || state == State.NO_DEMAND) {
if (this.completionPending) {
rsReadLogger.trace(getLogPrefix() + "Processing pending completion");
this.state.get().onAllDataRead(this);
return true;
}
Throwable ex = this.errorBeforeDemand;
Throwable ex = this.errorPending;
if (ex != null) {
if (rsReadLogger.isTraceEnabled()) {
rsReadLogger.trace(getLogPrefix() + "Completed with error before demand: " + ex);
rsReadLogger.trace(getLogPrefix() + "Processing pending completion with error: " + ex);
}
this.state.get().onError(this, ex);
return true;
}
}
return false;
}
private Subscription createSubscription() {
@@ -305,7 +308,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
publisher.subscriber = subscriber;
subscriber.onSubscribe(subscription);
publisher.changeState(SUBSCRIBING, NO_DEMAND);
publisher.handleCompletionOrErrorBeforeDemand();
publisher.handlePendingCompletionOrError();
}
else {
throw new IllegalStateException("Failed to transition to SUBSCRIBING, " +
@@ -315,14 +318,14 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
@Override
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
publisher.completionBeforeDemand = true;
publisher.handleCompletionOrErrorBeforeDemand();
publisher.completionPending = true;
publisher.handlePendingCompletionOrError();
}
@Override
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
publisher.errorBeforeDemand = ex;
publisher.handleCompletionOrErrorBeforeDemand();
publisher.errorPending = ex;
publisher.handlePendingCompletionOrError();
}
},
@@ -341,14 +344,14 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
@Override
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
publisher.completionBeforeDemand = true;
publisher.handleCompletionOrErrorBeforeDemand();
publisher.completionPending = true;
publisher.handlePendingCompletionOrError();
}
@Override
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
publisher.errorBeforeDemand = ex;
publisher.handleCompletionOrErrorBeforeDemand();
publisher.errorPending = ex;
publisher.handlePendingCompletionOrError();
}
},
@@ -379,14 +382,17 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
boolean demandAvailable = publisher.readAndPublish();
if (demandAvailable) {
publisher.changeToDemandState(READING);
publisher.handlePendingCompletionOrError();
}
else {
publisher.readingPaused();
if (publisher.changeState(READING, NO_DEMAND)) {
// Demand may have arrived since readAndPublish returned
long r = publisher.demand;
if (r > 0) {
publisher.changeToDemandState(NO_DEMAND);
if (!publisher.handlePendingCompletionOrError()) {
// Demand may have arrived since readAndPublish returned
long r = publisher.demand;
if (r > 0) {
publisher.changeToDemandState(NO_DEMAND);
}
}
}
}
@@ -408,6 +414,18 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
publisher.changeToDemandState(NO_DEMAND);
}
}
@Override
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
publisher.completionPending = true;
publisher.handlePendingCompletionOrError();
}
@Override
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
publisher.errorPending = ex;
publisher.handlePendingCompletionOrError();
}
},
COMPLETED {
@@ -329,7 +329,7 @@ public abstract class AbstractListenerWriteFlushProcessor<T> implements Processo
public <T> void onComplete(AbstractListenerWriteFlushProcessor<T> processor) {
processor.sourceCompleted = true;
// A competing write might have completed very quickly
if (processor.state.get().equals(State.REQUESTED)) {
if (processor.state.get() == State.REQUESTED) {
handleSourceCompleted(processor);
}
}
@@ -151,10 +151,11 @@ public abstract class AbstractListenerWriteProcessor<T> implements Processor<T,
* container.
*/
public final void onWritePossible() {
State state = this.state.get();
if (rsWriteLogger.isTraceEnabled()) {
rsWriteLogger.trace(getLogPrefix() + "onWritePossible");
rsWriteLogger.trace(getLogPrefix() + "onWritePossible [" + state + "]");
}
this.state.get().onWritePossible(this);
state.onWritePossible(this);
}
/**
@@ -182,14 +183,14 @@ public abstract class AbstractListenerWriteProcessor<T> implements Processor<T,
cancel();
for (;;) {
State prev = this.state.get();
if (prev.equals(State.COMPLETED)) {
if (prev == State.COMPLETED) {
break;
}
if (this.state.compareAndSet(prev, State.COMPLETED)) {
if (rsWriteLogger.isTraceEnabled()) {
rsWriteLogger.trace(getLogPrefix() + prev + " -> " + this.state);
}
if (!prev.equals(State.WRITING)) {
if (prev != State.WRITING) {
discardCurrentData();
}
break;
@@ -429,7 +430,7 @@ public abstract class AbstractListenerWriteProcessor<T> implements Processor<T,
public <T> void onComplete(AbstractListenerWriteProcessor<T> processor) {
processor.sourceCompleted = true;
// A competing write might have completed very quickly
if (processor.state.get().equals(State.REQUESTED)) {
if (processor.state.get() == State.REQUESTED) {
processor.changeStateToComplete(State.REQUESTED);
}
}
@@ -440,7 +441,7 @@ public abstract class AbstractListenerWriteProcessor<T> implements Processor<T,
public <T> void onComplete(AbstractListenerWriteProcessor<T> processor) {
processor.sourceCompleted = true;
// A competing write might have completed very quickly
if (processor.state.get().equals(State.REQUESTED)) {
if (processor.state.get() == State.REQUESTED) {
processor.changeStateToComplete(State.REQUESTED);
}
}
@@ -157,7 +157,7 @@ public class ServletHttpHandlerAdapter implements Servlet {
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
// Check for existing error attribute first
if (DispatcherType.ASYNC.equals(request.getDispatcherType())) {
if (DispatcherType.ASYNC == request.getDispatcherType()) {
Throwable ex = (Throwable) request.getAttribute(WRITE_ERROR_ATTRIBUTE_NAME);
throw new ServletException("Failed to create response content", ex);
}
@@ -182,14 +182,14 @@ class WriteResultPublisher implements Publisher<Void> {
@Override
void publishComplete(WriteResultPublisher publisher) {
publisher.completedBeforeSubscribed = true;
if(State.SUBSCRIBED.equals(publisher.state.get())) {
if(State.SUBSCRIBED == publisher.state.get()) {
publisher.state.get().publishComplete(publisher);
}
}
@Override
void publishError(WriteResultPublisher publisher, Throwable ex) {
publisher.errorBeforeSubscribed = ex;
if(State.SUBSCRIBED.equals(publisher.state.get())) {
if(State.SUBSCRIBED == publisher.state.get()) {
publisher.state.get().publishError(publisher, ex);
}
}
@@ -203,14 +203,14 @@ class WriteResultPublisher implements Publisher<Void> {
@Override
void publishComplete(WriteResultPublisher publisher) {
publisher.completedBeforeSubscribed = true;
if(State.SUBSCRIBED.equals(publisher.state.get())) {
if(State.SUBSCRIBED == publisher.state.get()) {
publisher.state.get().publishComplete(publisher);
}
}
@Override
void publishError(WriteResultPublisher publisher, Throwable ex) {
publisher.errorBeforeSubscribed = ex;
if(State.SUBSCRIBED.equals(publisher.state.get())) {
if(State.SUBSCRIBED == publisher.state.get()) {
publisher.state.get().publishError(publisher, ex);
}
}
@@ -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.
@@ -34,7 +34,7 @@ import org.springframework.web.server.ServerWebExchange;
/**
* Specialized {@link org.springframework.validation.DataBinder} to perform data
* binding from URL query params or form data in the request data to Java objects.
* binding from URL query parameters or form data in the request data to Java objects.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
@@ -64,7 +64,7 @@ public class WebExchangeDataBinder extends WebDataBinder {
/**
* Bind query params, form data, and or multipart form data to the binder target.
* Bind query parameters, form data, or multipart form data to the binder target.
* @param exchange the current exchange
* @return a {@code Mono<Void>} when binding is complete
*/
@@ -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.
@@ -85,10 +85,11 @@ public abstract class SpringBeanAutowiringSupport {
bpp.processInjection(target);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Current WebApplicationContext is not available for processing of " +
if (logger.isWarnEnabled()) {
logger.warn("Current WebApplicationContext is not available for processing of " +
ClassUtils.getShortName(target.getClass()) + ": " +
"Make sure this class gets constructed in a Spring web application. Proceeding without injection.");
"Make sure this class gets constructed in a Spring web application after the" +
"Spring WebApplicationContext has been initialized. Proceeding without injection.");
}
}
}
@@ -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.
@@ -138,7 +138,12 @@ public class CorsConfiguration {
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
*/
public void setAllowedOrigins(@Nullable List<String> allowedOrigins) {
this.allowedOrigins = (allowedOrigins != null ? new ArrayList<>(allowedOrigins) : null);
this.allowedOrigins = (allowedOrigins != null ?
allowedOrigins.stream().map(this::trimTrailingSlash).collect(Collectors.toList()) : null);
}
private String trimTrailingSlash(String origin) {
return origin.endsWith("/") ? origin.substring(0, origin.length() - 1) : origin;
}
/**
@@ -159,6 +164,7 @@ public class CorsConfiguration {
else if (this.allowedOrigins == DEFAULT_PERMIT_ALL && CollectionUtils.isEmpty(this.allowedOriginPatterns)) {
setAllowedOrigins(DEFAULT_PERMIT_ALL);
}
origin = trimTrailingSlash(origin);
this.allowedOrigins.add(origin);
}
@@ -209,6 +215,7 @@ public class CorsConfiguration {
if (this.allowedOriginPatterns == null) {
this.allowedOriginPatterns = new ArrayList<>(4);
}
originPattern = trimTrailingSlash(originPattern);
this.allowedOriginPatterns.add(new OriginPattern(originPattern));
if (this.allowedOrigins == DEFAULT_PERMIT_ALL) {
this.allowedOrigins = null;
@@ -475,7 +482,6 @@ public class CorsConfiguration {
* @return the combined {@code CorsConfiguration}, or {@code this}
* configuration if the supplied configuration is {@code null}
*/
@Nullable
public CorsConfiguration combine(@Nullable CorsConfiguration other) {
if (other == null) {
return this;
@@ -543,30 +549,31 @@ public class CorsConfiguration {
/**
* Check the origin of the request against the configured allowed origins.
* @param requestOrigin the origin to check
* @param origin the origin to check
* @return the origin to use for the response, or {@code null} which
* means the request origin is not allowed
*/
@Nullable
public String checkOrigin(@Nullable String requestOrigin) {
if (!StringUtils.hasText(requestOrigin)) {
public String checkOrigin(@Nullable String origin) {
if (!StringUtils.hasText(origin)) {
return null;
}
String originToCheck = trimTrailingSlash(origin);
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
if (this.allowedOrigins.contains(ALL)) {
validateAllowCredentials();
return ALL;
}
for (String allowedOrigin : this.allowedOrigins) {
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
return requestOrigin;
if (originToCheck.equalsIgnoreCase(allowedOrigin)) {
return origin;
}
}
}
if (!ObjectUtils.isEmpty(this.allowedOriginPatterns)) {
for (OriginPattern p : this.allowedOriginPatterns) {
if (p.getDeclaredPattern().equals(ALL) || p.getPattern().matcher(requestOrigin).matches()) {
return requestOrigin;
if (p.getDeclaredPattern().equals(ALL) || p.getPattern().matcher(originToCheck).matches()) {
return origin;
}
}
}
@@ -25,6 +25,7 @@ import org.springframework.web.server.ServerWebExchange;
*
* @author Rossen Stoyanchev
* @since 5.3.4
* @see PreFlightRequestWebFilter
*/
public interface PreFlightRequestHandler {
@@ -0,0 +1,57 @@
/*
* 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.web.cors.reactive;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
/**
* WebFilter that handles pre-flight requests through a
* {@link PreFlightRequestHandler} and bypasses the rest of the chain.
*
* <p>A WebFlux application can simply inject PreFlightRequestHandler and use
* it to create an instance of this WebFilter since {@code @EnableWebFlux}
* declares {@code DispatcherHandler} as a bean and that is a
* PreFlightRequestHandler.
*
* @author Rossen Stoyanchev
* @since 5.3.7
*/
public class PreFlightRequestWebFilter implements WebFilter {
private final PreFlightRequestHandler handler;
/**
* Create an instance that will delegate to the given handler.
*/
public PreFlightRequestWebFilter(PreFlightRequestHandler handler) {
Assert.notNull(handler, "PreFlightRequestHandler is required");
this.handler = handler;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return (CorsUtils.isPreFlightRequest(exchange.getRequest()) ?
this.handler.handlePreFlight(exchange) : chain.filter(exchange));
}
}
@@ -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.
@@ -17,6 +17,7 @@
package org.springframework.web.method.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -37,16 +38,16 @@ import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.annotation.ValidationAnnotationUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -76,6 +77,7 @@ import org.springframework.web.multipart.support.StandardServletPartUtils;
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @author Vladislav Kisel
* @since 3.1
*/
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@@ -256,6 +258,14 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
String paramName = paramNames[i];
Class<?> paramType = paramTypes[i];
Object value = webRequest.getParameterValues(paramName);
// Since WebRequest#getParameter exposes a single-value parameter as an array
// with a single element, we unwrap the single value in such cases, analogous
// to WebExchangeDataBinder.addBindValue(Map<String, Object>, String, List<?>).
if (ObjectUtils.isArray(value) && Array.getLength(value) == 1) {
value = Array.get(value, 0);
}
if (value == null) {
if (fieldDefaultPrefix != null) {
value = webRequest.getParameter(fieldDefaultPrefix + paramName);
@@ -269,6 +279,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
}
}
}
try {
MethodParameter methodParam = new FieldAwareConstructorParameter(ctor, i, paramName);
if (value == null && methodParam.isOptional()) {
@@ -362,7 +373,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
*/
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
for (Annotation ann : parameter.getParameterAnnotations()) {
Object[] validationHints = determineValidationHints(ann);
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
@@ -388,7 +399,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
Class<?> targetType, String fieldName, @Nullable Object value) {
for (Annotation ann : parameter.getParameterAnnotations()) {
Object[] validationHints = determineValidationHints(ann);
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
for (Validator validator : binder.getValidators()) {
if (validator instanceof SmartValidator) {
@@ -406,26 +417,6 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
}
}
/**
* Determine any validation triggered by the given annotation.
* @param ann the annotation (potentially a validation annotation)
* @return the validation hints to apply (possibly an empty array),
* or {@code null} if this annotation does not trigger any validation
* @since 5.1
*/
@Nullable
private Object[] determineValidationHints(Annotation ann) {
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
if (hints == null) {
return new Object[0];
}
return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
}
return null;
}
/**
* Whether to raise a fatal bind exception on validation errors.
* <p>The default implementation delegates to {@link #isBindExceptionRequired(MethodParameter)}.
@@ -85,7 +85,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?})?)";
private static final String PORT_PATTERN = "(.[^/?#]*(?:\\{[^/]+?})?)";
private static final String PATH_PATTERN = "([^?#]*)";
@@ -0,0 +1,82 @@
/*
* 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.http.codec.multipart;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
*/
class FileStorageTests {
@Test
void fromPath() throws IOException {
Path path = Files.createTempFile("spring", "test");
FileStorage storage = FileStorage.fromPath(path);
Mono<Path> directory = storage.directory();
StepVerifier.create(directory)
.expectNext(path)
.verifyComplete();
}
@Test
void tempDirectory() {
FileStorage storage = FileStorage.tempDirectory(Schedulers::boundedElastic);
Mono<Path> directory = storage.directory();
StepVerifier.create(directory)
.consumeNextWith(path -> {
assertThat(path).exists();
StepVerifier.create(directory)
.expectNext(path)
.verifyComplete();
})
.verifyComplete();
}
@Test
void tempDirectoryDeleted() {
FileStorage storage = FileStorage.tempDirectory(Schedulers::boundedElastic);
Mono<Path> directory = storage.directory();
StepVerifier.create(directory)
.consumeNextWith(path1 -> {
try {
Files.delete(path1);
StepVerifier.create(directory)
.consumeNextWith(path2 -> assertThat(path2).isNotEqualTo(path1))
.verifyComplete();
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
})
.verifyComplete();
}
}
@@ -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 @@ public class JsonbHttpMessageConverterTests {
public void readTyped() throws IOException {
String body = "{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
MyBean result = (MyBean) this.converter.read(MyBean.class, inputMessage);
@@ -90,7 +90,7 @@ public class JsonbHttpMessageConverterTests {
public void readUntyped() throws IOException {
String body = "{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
HashMap<String, Object> result = (HashMap<String, Object>) this.converter.read(HashMap.class, inputMessage);
assertThat(result.get("string")).isEqualTo("Foo");
@@ -167,9 +167,9 @@ public class JsonbHttpMessageConverterTests {
}
@Test
public void readInvalidJson() throws IOException {
public void readInvalidJson() {
String body = "FooBar";
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
assertThatExceptionOfType(HttpMessageNotReadableException.class).isThrownBy(() ->
this.converter.read(MyBean.class, inputMessage));
@@ -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.
@@ -32,10 +32,11 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -48,23 +49,22 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Brian Clozel
* @author Sam Brannen
*/
public class WebRequestDataBinderIntegrationTests {
@TestInstance(Lifecycle.PER_CLASS)
class WebRequestDataBinderIntegrationTests {
private static Server jettyServer;
private final PartsServlet partsServlet = new PartsServlet();
private static final PartsServlet partsServlet = new PartsServlet();
private static final PartListServlet partListServlet = new PartListServlet();
private final PartListServlet partListServlet = new PartListServlet();
private final RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
protected static String baseUrl;
private Server jettyServer;
protected static MediaType contentType;
private String baseUrl;
@BeforeAll
public static void startJettyServer() throws Exception {
void startJettyServer() throws Exception {
// Let server pick its own random, available port.
jettyServer = new Server(0);
@@ -89,7 +89,7 @@ public class WebRequestDataBinderIntegrationTests {
}
@AfterAll
public static void stopJettyServer() throws Exception {
void stopJettyServer() throws Exception {
if (jettyServer != null) {
jettyServer.stop();
}
@@ -97,7 +97,7 @@ public class WebRequestDataBinderIntegrationTests {
@Test
public void partsBinding() {
void partsBinding() {
PartsBean bean = new PartsBean();
partsServlet.setBean(bean);
@@ -113,7 +113,7 @@ public class WebRequestDataBinderIntegrationTests {
}
@Test
public void partListBinding() {
void partListBinding() {
PartListBean bean = new PartListBean();
partListServlet.setBean(bean);
@@ -143,7 +143,7 @@ public class WebRequestDataBinderIntegrationTests {
response.setStatus(HttpServletResponse.SC_OK);
}
public void setBean(T bean) {
void setBean(T bean) {
this.bean = bean;
}
}
@@ -151,9 +151,9 @@ public class WebRequestDataBinderIntegrationTests {
private static class PartsBean {
public Part firstPart;
private Part firstPart;
public Part secondPart;
private Part secondPart;
public Part getFirstPart() {
return firstPart;
@@ -182,7 +182,7 @@ public class WebRequestDataBinderIntegrationTests {
private static class PartListBean {
public List<Part> partList;
private List<Part> partList;
public List<Part> getPartList() {
return partList;
@@ -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.
@@ -282,15 +282,24 @@ public class CorsConfigurationTests {
@Test
public void checkOriginAllowed() {
// "*" matches
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
assertThat(config.checkOrigin("https://domain.com")).isEqualTo("*");
// "*" does not match together with allowCredentials
config.setAllowCredentials(true);
assertThatIllegalArgumentException().isThrownBy(() -> config.checkOrigin("https://domain.com"));
// specific origin matches Origin header with or without trailing "/"
config.setAllowedOrigins(Collections.singletonList("https://domain.com"));
assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com");
assertThat(config.checkOrigin("https://domain.com/")).isEqualTo("https://domain.com/");
// specific origin with trailing "/" matches Origin header with or without trailing "/"
config.setAllowedOrigins(Collections.singletonList("https://domain.com/"));
assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com");
assertThat(config.checkOrigin("https://domain.com/")).isEqualTo("https://domain.com/");
config.setAllowCredentials(false);
assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com");
@@ -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.
@@ -170,10 +170,19 @@ public class DefaultCorsProcessorTests {
this.conf.addAllowedOrigin("https://DOMAIN2.com");
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(this.response.getHeaders(HttpHeaders.VARY)).contains(HttpHeaders.ORIGIN,
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
}
@Test // gh-26892
public void actualRequestTrailingSlashOriginMatch() throws Exception {
this.request.setMethod(HttpMethod.GET.name());
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com/");
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
}
@Test
@@ -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.
@@ -172,10 +172,22 @@ public class DefaultCorsProcessorTests {
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
assertThat((Object) response.getStatusCode()).isNull();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
}
@Test // gh-26892
public void actualRequestTrailingSlashOriginMatch() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest
.method(HttpMethod.GET, "http://localhost/test.html")
.header(HttpHeaders.ORIGIN, "https://domain2.com/"));
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat((Object) response.getStatusCode()).isNull();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.web.method.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -26,6 +27,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
@@ -58,6 +60,7 @@ import static org.mockito.Mockito.verify;
* Test fixture with {@link ModelAttributeMethodProcessor}.
*
* @author Rossen Stoyanchev
* @author Vladislav Kisel
*/
public class ModelAttributeMethodProcessorTests {
@@ -73,6 +76,7 @@ public class ModelAttributeMethodProcessorTests {
private MethodParameter paramModelAttr;
private MethodParameter paramBindingDisabledAttr;
private MethodParameter paramNonSimpleType;
private MethodParameter beanWithConstructorArgs;
private MethodParameter returnParamNamedModelAttr;
private MethodParameter returnParamNonSimpleType;
@@ -86,7 +90,7 @@ public class ModelAttributeMethodProcessorTests {
Method method = ModelAttributeHandler.class.getDeclaredMethod("modelAttribute",
TestBean.class, Errors.class, int.class, TestBean.class,
TestBean.class, TestBean.class);
TestBean.class, TestBean.class, TestBeanWithConstructorArgs.class);
this.paramNamedValidModelAttr = new SynthesizingMethodParameter(method, 0);
this.paramErrors = new SynthesizingMethodParameter(method, 1);
@@ -94,6 +98,7 @@ public class ModelAttributeMethodProcessorTests {
this.paramModelAttr = new SynthesizingMethodParameter(method, 3);
this.paramBindingDisabledAttr = new SynthesizingMethodParameter(method, 4);
this.paramNonSimpleType = new SynthesizingMethodParameter(method, 5);
this.beanWithConstructorArgs = new SynthesizingMethodParameter(method, 6);
method = getClass().getDeclaredMethod("annotatedReturnValue");
this.returnParamNamedModelAttr = new MethodParameter(method, -1);
@@ -264,6 +269,26 @@ public class ModelAttributeMethodProcessorTests {
assertThat(this.container.getModel().get("testBean")).isSameAs(testBean);
}
@Test // gh-25182
public void resolveConstructorListArgumentFromCommaSeparatedRequestParameter() throws Exception {
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addParameter("listOfStrings", "1,2");
ServletWebRequest requestWithParam = new ServletWebRequest(mockRequest);
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
given(factory.createBinder(any(), any(), eq("testBeanWithConstructorArgs")))
.willAnswer(invocation -> {
WebRequestDataBinder binder = new WebRequestDataBinder(invocation.getArgument(1));
// Add conversion service which will convert "1,2" to a list
binder.setConversionService(new DefaultFormattingConversionService());
return binder;
});
Object resolved = this.processor.resolveArgument(this.beanWithConstructorArgs, this.container, requestWithParam, factory);
assertThat(resolved).isInstanceOf(TestBeanWithConstructorArgs.class);
assertThat(((TestBeanWithConstructorArgs) resolved).listOfStrings).containsExactly("1", "2");
}
private void testGetAttributeFromModel(String expectedAttrName, MethodParameter param) throws Exception {
Object target = new TestBean();
@@ -330,10 +355,20 @@ public class ModelAttributeMethodProcessorTests {
int intArg,
@ModelAttribute TestBean defaultNameAttr,
@ModelAttribute(name="noBindAttr", binding=false) @Valid TestBean noBindAttr,
TestBean notAnnotatedAttr) {
TestBean notAnnotatedAttr,
TestBeanWithConstructorArgs beanWithConstructorArgs) {
}
}
static class TestBeanWithConstructorArgs {
final List<String> listOfStrings;
public TestBeanWithConstructorArgs(List<String> listOfStrings) {
this.listOfStrings = listOfStrings;
}
}
@ModelAttribute("modelAttrName") @SuppressWarnings("unused")
private String annotatedReturnValue() {
@@ -38,6 +38,7 @@ import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Unit tests for {@link UriComponentsBuilder}.
@@ -1272,4 +1273,28 @@ class UriComponentsBuilderTests {
assertThat(path).isEqualTo("/home/path");
}
@Test
void validPort() {
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://localhost:52567/path").build();
assertThat(uriComponents.getPort()).isEqualTo(52567);
assertThat(uriComponents.getPath()).isEqualTo("/path");
uriComponents = UriComponentsBuilder.fromUriString("http://localhost:52567?trace=false").build();
assertThat(uriComponents.getPort()).isEqualTo(52567);
assertThat(uriComponents.getQuery()).isEqualTo("trace=false");
uriComponents = UriComponentsBuilder.fromUriString("http://localhost:52567#fragment").build();
assertThat(uriComponents.getPort()).isEqualTo(52567);
assertThat(uriComponents.getFragment()).isEqualTo("fragment");
}
@Test
void verifyInvalidPort() {
String url = "http://localhost:port/path";
assertThatThrownBy(() -> UriComponentsBuilder.fromUriString(url).build().toUri())
.isInstanceOf(NumberFormatException.class);
assertThatThrownBy(() -> UriComponentsBuilder.fromHttpUrl(url).build().toUri())
.isInstanceOf(NumberFormatException.class);
}
}
@@ -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.
@@ -315,8 +315,8 @@ public class MockServletContext implements ServletContext {
return resourcePaths;
}
catch (InvalidPathException | IOException ex ) {
if (logger.isWarnEnabled()) {
logger.warn("Could not get resource paths for " +
if (logger.isDebugEnabled()) {
logger.debug("Could not get resource paths for " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -339,8 +339,8 @@ public class MockServletContext implements ServletContext {
throw ex;
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not get URL for resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not get URL for resource " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -360,8 +360,8 @@ public class MockServletContext implements ServletContext {
return resource.getInputStream();
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not open InputStream for resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not open InputStream for resource " +
(resource != null ? resource : resourceLocation), ex);
}
return null;
@@ -476,8 +476,8 @@ public class MockServletContext implements ServletContext {
return resource.getFile().getAbsolutePath();
}
catch (InvalidPathException | IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not determine real path of resource " +
if (logger.isDebugEnabled()) {
logger.debug("Could not determine real path of resource " +
(resource != null ? resource : resourceLocation), ex);
}
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.
@@ -35,7 +35,7 @@ public class CorsRegistration {
private final String pathPattern;
private final CorsConfiguration config;
private CorsConfiguration config;
public CorsRegistration(String pathPattern) {
@@ -46,10 +46,14 @@ public class CorsRegistration {
/**
* A list of origins for which cross-origin requests are allowed. Please,
* see {@link CorsConfiguration#setAllowedOrigins(List)} for details.
* <p>By default all origins are allowed unless {@code originPatterns} is
* also set in which case {@code originPatterns} is used instead.
* Set the origins for which cross-origin requests are allowed from a browser.
* Please, refer to {@link CorsConfiguration#setAllowedOrigins(List)} for
* format details and other considerations.
*
* <p>By default, all origins are allowed, but if
* {@link #allowedOriginPatterns(String...) allowedOriginPatterns} is also
* set, then that takes precedence.
* @see #allowedOriginPatterns(String...)
*/
public CorsRegistration allowedOrigins(String... origins) {
this.config.setAllowedOrigins(Arrays.asList(origins));
@@ -57,9 +61,11 @@ public class CorsRegistration {
}
/**
* Alternative to {@link #allowCredentials} that supports origins declared
* via wildcard patterns. Please, see
* @link CorsConfiguration#setAllowedOriginPatterns(List)} for details.
* Alternative to {@link #allowedOrigins(String...)} that supports more
* flexible patterns for specifying the origins for which cross-origin
* requests are allowed from a browser. Please, refer to
* {@link CorsConfiguration#setAllowedOriginPatterns(List)} for format
* details and other considerations.
* <p>By default this is not set.
* @since 5.3
*/
@@ -143,7 +149,7 @@ public class CorsRegistration {
* @since 5.3
*/
public CorsRegistration combine(CorsConfiguration other) {
this.config.combine(other);
this.config = this.config.combine(other);
return this;
}
@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.web.reactive.function.client;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -207,9 +206,7 @@ class DefaultClientResponse implements ClientResponse {
.onErrorReturn(IllegalStateException.class::isInstance, EMPTY)
.map(bodyBytes -> {
HttpRequest request = this.requestSupplier.get();
Charset charset = headers().contentType()
.map(MimeType::getCharset)
.orElse(StandardCharsets.ISO_8859_1);
Charset charset = headers().contentType().map(MimeType::getCharset).orElse(null);
int statusCode = rawStatusCode();
HttpStatus httpStatus = HttpStatus.resolve(statusCode);
if (httpStatus != null) {
@@ -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.
@@ -39,6 +39,13 @@ public interface ExchangeFilterFunction {
* in the chain, to be invoked via
* {@linkplain ExchangeFunction#exchange(ClientRequest) invoked} in order to
* proceed with the exchange, or not invoked to shortcut the chain.
*
* <p><strong>Note:</strong> When a filter handles the response after the
* call to {@link ExchangeFunction#exchange}, extra care must be taken to
* always consume its content or otherwise propagate it downstream for
* further handling, for example by the {@link WebClient}. Please, see the
* reference documentation for more details on this.
*
* @param request the current request
* @param next the next exchange function in the chain
* @return the filtered response
@@ -43,6 +43,14 @@ public interface ExchangeFunction {
/**
* Exchange the given request for a {@link ClientResponse} promise.
*
* <p><strong>Note:</strong> When calling this method from an
* {@link ExchangeFilterFunction} that handles the response in some way,
* extra care must be taken to always consume its content or otherwise
* propagate it downstream for further handling, for example by the
* {@link WebClient}. Please, see the reference documentation for more
* details on this.
*
* @param request the request to exchange
* @return the delayed response
*/
@@ -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.
@@ -50,7 +50,7 @@ public class UnknownHttpStatusCodeException extends WebClientResponseException {
* @since 5.1.4
*/
public UnknownHttpStatusCodeException(
int statusCode, HttpHeaders headers, byte[] responseBody, Charset responseCharset,
int statusCode, HttpHeaders headers, byte[] responseBody, @Nullable Charset responseCharset,
@Nullable HttpRequest request) {
super("Unknown status code [" + statusCode + "]", statusCode, "",
@@ -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.
@@ -186,13 +186,6 @@ public interface WebClient {
*/
Builder baseUrl(String baseUrl);
/**
* Configure default URI variable values that will be used when expanding
* URI templates using a {@link Map}.
* @param defaultUriVariables the default values to use
* @see #baseUrl(String)
* @see #uriBuilderFactory(UriBuilderFactory)
*/
/**
* Configure default URL variable values to use when expanding URI
* templates with a {@link Map}. Effectively a shortcut for:

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