mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
294 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1280ffeaa | |||
| 0d0d75e253 | |||
| cce60c479c | |||
| 1469bdbc70 | |||
| be52ec8bd5 | |||
| cd9cad31f9 | |||
| 60c551a95b | |||
| 4164fc63b1 | |||
| 0865abef83 | |||
| 90af2d5794 | |||
| 5f5cd8a7c4 | |||
| 46b7cd54a1 | |||
| 7f670bbe68 | |||
| 6aaf7596c0 | |||
| 4f2c589fe6 | |||
| aa51ed1940 | |||
| dc4e053d59 | |||
| 07ba95739b | |||
| 0468ef46ac | |||
| eb03144e9d | |||
| 29790d5bb1 | |||
| 355d394d7f | |||
| 9ddab9e69b | |||
| c20a43f72b | |||
| e0fa58aa0f | |||
| 65797d04f2 | |||
| 5811212a9f | |||
| d79e33b5a0 | |||
| dddcc5e9ad | |||
| 582b94d50e | |||
| 443c34cc90 | |||
| 959e6d1745 | |||
| 38b592444d | |||
| 2c2464b1e7 | |||
| 3b247a9c19 | |||
| d30432b6c0 | |||
| c70aed58bf | |||
| 2de7bf6e2b | |||
| a28511cdbd | |||
| 668b938832 | |||
| 091e7bb891 | |||
| b76e0c4826 | |||
| 55faf6e320 | |||
| a522ed0788 | |||
| 4152ebc2f3 | |||
| 21c90829df | |||
| 5b96c9b87e | |||
| 08855fee51 | |||
| 66cd979ce4 | |||
| 5667d459a7 | |||
| 85eb589c2e | |||
| b18cf3c873 | |||
| 399e7ebf22 | |||
| b5d6e53e50 | |||
| 865969400b | |||
| 6f4fb08bf8 | |||
| 4c7cc705de | |||
| f7fc36250e | |||
| ae7d5200e5 | |||
| ff9b68c1b6 | |||
| 36c829386b | |||
| 7316dc726a | |||
| 118c7f969b | |||
| 26ed6d8bce | |||
| 98770b15e7 | |||
| 697108cc42 | |||
| 607d918340 | |||
| 8456eaca8e | |||
| 59015344a7 | |||
| c03162e616 | |||
| 1dde30a7fd | |||
| 35196d3c62 | |||
| d7f67b3dc3 | |||
| 719e7394d6 | |||
| e4a5d2aaa5 | |||
| ab64607420 | |||
| b595dc1dfa | |||
| 86123de883 | |||
| 7c3a18490b | |||
| 01e50fb60a | |||
| 6c3a0a9026 | |||
| 3bded7659a | |||
| 7f1062159e | |||
| e4c0ff569b | |||
| e7cbe23771 | |||
| d275a4e28d | |||
| 42a23098de | |||
| d25ae4b02c | |||
| 5740eaf33e | |||
| 80d46ba80e | |||
| db9a10bf35 | |||
| 27bfcbbc82 | |||
| b153b5e53b | |||
| 1f477a5c1c | |||
| ceac2a5c3c | |||
| e03fdeaf8b | |||
| 0286549466 | |||
| 71158f4d96 | |||
| 7303680369 | |||
| 906a1f561a | |||
| e489706f13 | |||
| 69bbdce826 | |||
| 0f31830ae2 | |||
| 5d297c6387 | |||
| 320f5836b4 | |||
| af06b80bdf | |||
| 5b1ab31559 | |||
| bf05da1f8c | |||
| 63217e44f1 | |||
| 5226a67161 | |||
| 1694274e32 | |||
| 27c4e74e24 | |||
| b73eb51cb1 | |||
| f31933e67e | |||
| c7989c78c8 | |||
| f03ccd5cc9 | |||
| bcc6171a72 | |||
| a80c5fcfaf | |||
| 04ce8e0ac4 | |||
| 74f7eb11be | |||
| e05b584623 | |||
| 29955a2898 | |||
| 4b6b12bf2f | |||
| ab0e8f0617 | |||
| 0a6a6d48e2 | |||
| a08593b44b | |||
| 7954dc7a00 | |||
| 22d9012081 | |||
| 327e761536 | |||
| 4e42b5dfbc | |||
| 432c720dd0 | |||
| f7606ba51b | |||
| 0f54585f05 | |||
| d3d6e100da | |||
| a48c5f6a0d | |||
| 35ea33e13a | |||
| a2d91a562d | |||
| 97f3846971 | |||
| 44e1d6d1bf | |||
| 7adac25e7e | |||
| 7dca6a36a6 | |||
| c5ac8e8ab6 | |||
| fa4c62cab2 | |||
| 8a1182a678 | |||
| 01bea34569 | |||
| aec151e014 | |||
| 58e40d1aa2 | |||
| 85dbd2a4c3 | |||
| eca87b87de | |||
| 5e1e29a2dd | |||
| 7dc3a55648 | |||
| f982fd99d7 | |||
| ab18ab6025 | |||
| d83fb09914 | |||
| b651c10e83 | |||
| 5651695fd1 | |||
| 1a9556925d | |||
| 2dc203ffdc | |||
| 610de3ae78 | |||
| 5b2ace5742 | |||
| 5b227e5677 | |||
| 41d5048280 | |||
| a931ff12f6 | |||
| e4f753e3e3 | |||
| 4982b5fcb9 | |||
| f7678cdcdd | |||
| 3ab39eda08 | |||
| 14a6909c4a | |||
| fd17738f76 | |||
| f674652dd0 | |||
| 5476b2edff | |||
| c0fee67c6d | |||
| f229fa4b72 | |||
| 684939d5f8 | |||
| b8d75c3139 | |||
| d92c74d923 | |||
| 4f14291e2f | |||
| 7d3f42b27b | |||
| a8d553218c | |||
| 7f422f206c | |||
| 70f0895f96 | |||
| 2728a9b931 | |||
| bc261fd995 | |||
| 0eaf6d12eb | |||
| 6e264f9bdd | |||
| 6214ff153f | |||
| c6b271f1b6 | |||
| 8bf16ee1f4 | |||
| a3451f9908 | |||
| 5c38b89f4e | |||
| 9c030bacb8 | |||
| 12f8cdd715 | |||
| 2daefedf5e | |||
| 6670db9b58 | |||
| 7b6cac2c93 | |||
| 6ffeee3e6f | |||
| 1b458aeafc | |||
| 0503cf2937 | |||
| 14c802f979 | |||
| c1b1940dd2 | |||
| 97b3aa4b13 | |||
| 79b5710386 | |||
| b2bcb0f93a | |||
| 5593e95e89 | |||
| 6d4c0091b5 | |||
| 55aa8e914e | |||
| 2b017fe540 | |||
| 61cdd647b6 | |||
| f6d3d9d6e6 | |||
| ed71fe0460 | |||
| 89f70bdc46 | |||
| 3c9bd3177e | |||
| 4b80ef21b6 | |||
| d308985edc | |||
| 5aef87f120 | |||
| 70c0e104b5 | |||
| 01bf1c9021 | |||
| 530fb0808d | |||
| 48688b7b04 | |||
| 1ef8cad7bc | |||
| 86902d27b2 | |||
| 9877a9e6b7 | |||
| 75b6540bd8 | |||
| 21d77dd7c7 | |||
| 8bdc53ac5c | |||
| acb638f828 | |||
| c7e8989f16 | |||
| 78f65c4ca9 | |||
| 42e4ca1e2d | |||
| d23a108e76 | |||
| b9a612b637 | |||
| 1ee9223348 | |||
| 926c4aae52 | |||
| b61643b71a | |||
| 7efb4eade6 | |||
| e89cc9934f | |||
| 010b7375fc | |||
| 01c2e12fef | |||
| 90de1ab6d1 | |||
| 5a11569790 | |||
| 0087578469 | |||
| 1a79c54b01 | |||
| 4af7a6863b | |||
| c084936870 | |||
| 48bd81341f | |||
| 118d8133d3 | |||
| c2137a0d2f | |||
| 8c9c59eba0 | |||
| 8ed7f4703b | |||
| e04269cadd | |||
| aa73f6733e | |||
| b5147a034c | |||
| ebf6fff312 | |||
| dee12db50a | |||
| e53cce0778 | |||
| 8baf404893 | |||
| a33eac3ec0 | |||
| eb68e6a62a | |||
| a9240e0bac | |||
| 262b7d7c27 | |||
| 31d9f31806 | |||
| 0fd774e69f | |||
| ec5774e748 | |||
| 962ec38a61 | |||
| 3ec0452fed | |||
| cb3af52df2 | |||
| 5b114b7208 | |||
| f7cef2ab20 | |||
| 77a658f51b | |||
| cf4e77907f | |||
| 42a223b9cd | |||
| 6d2fec383b | |||
| 90ee22f2af | |||
| db5be44ff4 | |||
| a78c12b8d3 | |||
| dfc8406917 | |||
| 313e754a84 | |||
| 207ee9e307 | |||
| 3215880d08 | |||
| 274db2f7a9 | |||
| 1dd7d53de0 | |||
| 8535193df3 | |||
| a78701cc4b | |||
| 947387b4cd | |||
| 49ccd7ad9c | |||
| 5b97c47fc4 | |||
| 40661d62c1 | |||
| e17ca9a4e9 | |||
| 87761397b0 | |||
| ac58614be8 | |||
| 4ebd8d77b7 | |||
| 7ca47cfe6b | |||
| fd49c8f598 | |||
| ff234568d2 |
@@ -41,3 +41,6 @@ out
|
||||
test-output
|
||||
atlassian-ide-plugin.xml
|
||||
.gradletasknamecache
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
+3
-6
@@ -125,10 +125,7 @@ The reference documentation is in the [src/docs/asciidoc](src/docs/asciidoc) dir
|
||||
edit source files, and submit directly from GitHub.
|
||||
|
||||
When making changes locally, execute `./gradlew asciidoctor` and then browse the result under
|
||||
`build/asciidoc/html5/index.html`.
|
||||
`build/docs/ref-docs/html5/index.html`.
|
||||
|
||||
Asciidoctor also supports live editing. For more details read
|
||||
[Editing AsciiDoc with Live Preview](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/).
|
||||
Note that if you choose the
|
||||
[System Monitor](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/#using-a-system-monitor)
|
||||
option, you can find a Guardfile under `src/docs/asciidoc`.
|
||||
Asciidoctor also supports live editing. For more details see
|
||||
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
|
||||
|
||||
@@ -25,6 +25,10 @@ See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/w
|
||||
|
||||
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) Wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file.
|
||||
|
||||
## Continuous Integration Builds
|
||||
|
||||
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
|
||||
|
||||
## Stay in Touch
|
||||
|
||||
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news).
|
||||
|
||||
+37
-41
@@ -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.30' 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.30" 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,18 +27,19 @@ configure(allprojects) { project ->
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.1"
|
||||
mavenBom "io.netty:netty-bom:4.1.59.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.4"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR8"
|
||||
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.7"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR9"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.0"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.36.v20210114"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.30"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.2"
|
||||
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 {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.0') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
@@ -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
|
||||
@@ -65,18 +67,18 @@ configure(allprojects) { project ->
|
||||
dependency "io.reactivex:rxjava:1.3.8"
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.0.10"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.0.12"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.2.2"
|
||||
dependency("com.fasterxml.woodstox:woodstox-core:6.2.3") {
|
||||
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.14.0"
|
||||
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"
|
||||
}
|
||||
@@ -89,20 +91,16 @@ configure(allprojects) { project ->
|
||||
entry 'jibx-run'
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.27"
|
||||
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.0') {
|
||||
entry 'kotlinx-serialization-core'
|
||||
entry 'kotlinx-serialization-json'
|
||||
}
|
||||
dependency "org.yaml:snakeyaml:1.28"
|
||||
|
||||
dependency "com.h2database:h2:1.4.200"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.8.8"
|
||||
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"
|
||||
dependency "net.sf.jopt-simple:jopt-simple:5.0.4"
|
||||
dependencySet(group: 'org.apache.activemq', version: '5.16.0') {
|
||||
dependencySet(group: 'org.apache.activemq', version: '5.16.1') {
|
||||
entry 'activemq-broker'
|
||||
entry('activemq-kahadb-store') {
|
||||
exclude group: "org.springframework", name: "spring-context"
|
||||
@@ -117,30 +115,30 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "org.apache.poi:poi-ooxml:4.1.2"
|
||||
dependency "org.apache-extras.beanshell:bsh:2.0b6"
|
||||
dependency "org.freemarker:freemarker:2.3.30"
|
||||
dependency "org.freemarker:freemarker:2.3.31"
|
||||
dependency "org.hsqldb:hsqldb:2.5.1"
|
||||
dependency "org.quartz-scheduler:quartz:2.3.2"
|
||||
dependency "org.codehaus.fabric3.api:commonj:1.1.0"
|
||||
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.28.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"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.43') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.45') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.43') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.45') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.4.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.7.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-websockets-jsr') {
|
||||
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
|
||||
@@ -163,9 +161,9 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency 'org.apache.httpcomponents.client5:httpclient5:5.0.3'
|
||||
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.3'
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.5"
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.6"
|
||||
|
||||
dependency "org.jruby:jruby:9.2.13.0"
|
||||
dependency "org.jruby:jruby:9.2.16.0"
|
||||
dependency "org.python:jython-standalone:2.7.1"
|
||||
dependency "org.mozilla:rhino:1.7.11"
|
||||
|
||||
@@ -188,7 +186,7 @@ configure(allprojects) { project ->
|
||||
dependency("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1") {
|
||||
exclude group: "junit", name: "junit"
|
||||
}
|
||||
dependency "org.testng:testng:7.3.0"
|
||||
dependency "org.testng:testng:7.4.0"
|
||||
dependency "org.hamcrest:hamcrest:2.1"
|
||||
dependency "org.awaitility:awaitility:3.1.6"
|
||||
dependency "org.assertj:assertj-core:3.19.0"
|
||||
@@ -198,7 +196,7 @@ configure(allprojects) { project ->
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.mockito', version: '3.7.7') {
|
||||
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.47.1") {
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.49.1") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.47.1") {
|
||||
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") {
|
||||
@@ -217,7 +215,7 @@ configure(allprojects) { project ->
|
||||
exclude group: "io.netty", name: "netty"
|
||||
}
|
||||
dependency "org.skyscreamer:jsonassert:1.5.0"
|
||||
dependency "com.jayway.jsonpath:json-path:2.4.0"
|
||||
dependency "com.jayway.jsonpath:json-path:2.5.0"
|
||||
dependency "org.bouncycastle:bcpkix-jdk15on:1.66"
|
||||
|
||||
dependencySet(group: 'org.apache.tiles', version: '3.0.8') {
|
||||
@@ -309,23 +307,21 @@ configure([rootProject] + javaProjects) { project ->
|
||||
apply plugin: "java-test-fixtures"
|
||||
apply plugin: "checkstyle"
|
||||
apply plugin: 'org.springframework.build.compile'
|
||||
apply from: "${rootDir}/gradle/custom-java-home.gradle"
|
||||
apply from: "${rootDir}/gradle/toolchains.gradle"
|
||||
apply from: "${rootDir}/gradle/ide.gradle"
|
||||
|
||||
pluginManager.withPlugin("kotlin") {
|
||||
apply plugin: "org.jetbrains.dokka"
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
languageVersion = "1.3"
|
||||
apiVersion = "1.3"
|
||||
freeCompilerArgs = ["-Xjsr305=strict"]
|
||||
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = ["-Xjsr305=strict"]
|
||||
}
|
||||
}
|
||||
@@ -340,7 +336,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.39"
|
||||
toolVersion = "8.42"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
|
||||
+2
-8
@@ -8,16 +8,10 @@ They are declared in the `build.gradle` file in this folder.
|
||||
### Compiler conventions
|
||||
|
||||
The `org.springframework.build.compile` plugin applies the Java compiler conventions to the build.
|
||||
By default, the build compiles sources with Java `1.8` source and target compatibility.
|
||||
You can test a different source compatibility version on the CLI with a project property like:
|
||||
|
||||
```
|
||||
./gradlew test -PjavaSourceVersion=11
|
||||
```
|
||||
|
||||
## Build Plugins
|
||||
|
||||
## Optional dependencies
|
||||
### Optional dependencies
|
||||
|
||||
The `org.springframework.build.optional-dependencies` plugin creates a new `optional`
|
||||
Gradle configuration - it adds the dependencies to the project's compile and runtime classpath
|
||||
@@ -25,7 +19,7 @@ but doesn't affect the classpath of dependent projects.
|
||||
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
|
||||
configurations are preferred.
|
||||
|
||||
## API Diff
|
||||
### API Diff
|
||||
|
||||
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
|
||||
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
|
||||
|
||||
+3
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,31 +20,21 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
|
||||
* <p>One can override the default Java source compatibility version
|
||||
* with a dedicated property on the CLI: {@code "./gradlew test -PjavaSourceVersion=11"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class CompilerConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
/**
|
||||
* The project property that can be used to switch the Java source
|
||||
* compatibility version for building source and test classes.
|
||||
*/
|
||||
public static final String JAVA_SOURCE_VERSION_PROPERTY = "javaSourceVersion";
|
||||
|
||||
public static final JavaVersion DEFAULT_COMPILER_VERSION = JavaVersion.VERSION_1_8;
|
||||
|
||||
private static final List<String> COMPILER_ARGS;
|
||||
|
||||
private static final List<String> TEST_COMPILER_ARGS;
|
||||
@@ -69,7 +59,7 @@ public class CompilerConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> applyJavaCompileConventions(project));
|
||||
project.getPlugins().withType(JavaLibraryPlugin.class, javaPlugin -> applyJavaCompileConventions(project));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,15 +69,6 @@ public class CompilerConventionsPlugin implements Plugin<Project> {
|
||||
*/
|
||||
private void applyJavaCompileConventions(Project project) {
|
||||
JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class);
|
||||
if (project.hasProperty(JAVA_SOURCE_VERSION_PROPERTY)) {
|
||||
JavaVersion javaSourceVersion = JavaVersion.toVersion(project.property(JAVA_SOURCE_VERSION_PROPERTY));
|
||||
java.setSourceCompatibility(javaSourceVersion);
|
||||
}
|
||||
else {
|
||||
java.setSourceCompatibility(DEFAULT_COMPILER_VERSION);
|
||||
}
|
||||
java.setTargetCompatibility(DEFAULT_COMPILER_VERSION);
|
||||
|
||||
project.getTasks().withType(JavaCompile.class)
|
||||
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
|
||||
.forEach(compileTask -> {
|
||||
|
||||
+9
-3
@@ -1,7 +1,8 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
The Spring Framework is using https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io.
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
@@ -25,13 +26,17 @@ spring https://ci.spring.io spring-framework Wed, 25 Mar 20
|
||||
----
|
||||
|
||||
=== Pipeline configuration and structure
|
||||
|
||||
The build pipelines are described in `pipeline.yml` file.
|
||||
|
||||
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
|
||||
|
||||
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
|
||||
|
||||
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
|
||||
|
||||
You'll find in this folder the following resources:
|
||||
|
||||
* `pipeline.yml` the build pipeline
|
||||
* `parameters.yml` the build parameters used for the pipeline
|
||||
* `images/` holds the container images definitions used in this pipeline
|
||||
@@ -41,6 +46,7 @@ You'll find in this folder the following resources:
|
||||
=== Updating the build pipeline
|
||||
|
||||
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
|
||||
|
||||
The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
@@ -48,4 +54,4 @@ The pipeline can be deployed using the following command:
|
||||
$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
distribute:
|
||||
optional-deployments:
|
||||
- '.*\.zip'
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
sonatype:
|
||||
exclude:
|
||||
- 'build-info\.json'
|
||||
- '.*\.zip'
|
||||
|
||||
@@ -4,5 +4,8 @@ ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk
|
||||
ENV JAVA_HOME /opt/openjdk/java8
|
||||
ENV JDK11 /opt/openjdk/java11
|
||||
ENV JDK15 /opt/openjdk/java15
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
+14
-7
@@ -12,20 +12,27 @@ ln -fs /usr/share/zoneinfo/UTC /etc/localtime
|
||||
dpkg-reconfigure --frontend noninteractive tzdata
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.3/concourse-java.sh > /opt/concourse-java.sh
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
|
||||
|
||||
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.2.1/concourse-release-scripts-0.2.1.jar
|
||||
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
###########################################################
|
||||
JDK_URL=$( ./get-jdk-url.sh $1 )
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
cd /opt/openjdk
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f /opt/openjdk/bin/java
|
||||
test -f /opt/openjdk/bin/javac
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java8 java11 java15
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
pushd $jdk > /dev/null
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f bin/java
|
||||
test -f bin/javac
|
||||
popd > /dev/null
|
||||
done
|
||||
popd
|
||||
|
||||
###########################################################
|
||||
# GRADLE ENTERPRISE
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
FROM ubuntu:focal-20210119
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java11
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
@@ -1,8 +0,0 @@
|
||||
FROM ubuntu:focal-20210119
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java15
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
+3
-4
@@ -5,10 +5,9 @@ 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"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
bintray-subject: "spring"
|
||||
bintray-repo: "jars"
|
||||
task-timeout: 1h00m
|
||||
task-timeout: 1h00m
|
||||
|
||||
+112
-91
@@ -1,21 +1,29 @@
|
||||
anchors:
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
bintray-task-params: &bintray-task-params
|
||||
BINTRAY_SUBJECT: ((bintray-subject))
|
||||
BINTRAY_REPO: ((bintray-repo))
|
||||
BINTRAY_USERNAME: ((bintray-username))
|
||||
BINTRAY_API_KEY: ((bintray-api-key))
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
git-repo-resource-source: &git-repo-resource-source
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-password))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
SONATYPE_URL: ((sonatype-url))
|
||||
SONATYPE_STAGING_PROFILE_ID: ((sonatype-staging-profile-id))
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: ((milestone))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
@@ -24,9 +32,6 @@ anchors:
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USER_TOKEN: ((sonatype-user-token))
|
||||
SONATYPE_PASSWORD_TOKEN: ((sonatype-user-token-password))
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
@@ -40,27 +45,28 @@ resource_types:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.12
|
||||
tag: 0.0.13
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
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:
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-password))
|
||||
branch: ((branch))
|
||||
<<: *git-repo-resource-source
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
@@ -75,24 +81,12 @@ resources:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
paths: ["ci/images/*"]
|
||||
- name: spring-framework-ci-image
|
||||
- name: ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci-image
|
||||
- name: spring-framework-jdk11-ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-jdk11-ci-image
|
||||
- name: spring-framework-jdk15-ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-jdk15-ci-image
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -101,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
|
||||
@@ -147,43 +149,33 @@ resources:
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: false
|
||||
|
||||
jobs:
|
||||
- name: build-spring-framework-ci-images
|
||||
- name: build-ci-images
|
||||
plan:
|
||||
- get: ci-images-git-repo
|
||||
trigger: true
|
||||
- in_parallel:
|
||||
- put: spring-framework-ci-image
|
||||
- put: ci-image
|
||||
params:
|
||||
build: ci-images-git-repo/ci/images
|
||||
dockerfile: ci-images-git-repo/ci/images/spring-framework-ci-image/Dockerfile
|
||||
- put: spring-framework-jdk11-ci-image
|
||||
params:
|
||||
build: ci-images-git-repo/ci/images
|
||||
dockerfile: ci-images-git-repo/ci/images/spring-framework-jdk11-ci-image/Dockerfile
|
||||
- put: spring-framework-jdk15-ci-image
|
||||
params:
|
||||
build: ci-images-git-repo/ci/images
|
||||
dockerfile: ci-images-git-repo/ci/images/spring-framework-jdk15-ci-image/Dockerfile
|
||||
dockerfile: ci-images-git-repo/ci/images/ci-image/Dockerfile
|
||||
- name: build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
image: spring-framework-ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
@@ -195,6 +187,8 @@ jobs:
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
@@ -226,7 +220,7 @@ jobs:
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: spring-framework-jdk11-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
@@ -234,13 +228,13 @@ jobs:
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
image: spring-framework-jdk11-ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
TEST_TOOLCHAIN: 11
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
@@ -254,21 +248,21 @@ jobs:
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: spring-framework-jdk15-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk15-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
image: spring-framework-jdk15-ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 15
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk15-build
|
||||
@@ -278,14 +272,45 @@ 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:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
@@ -300,7 +325,7 @@ jobs:
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
@@ -310,7 +335,7 @@ jobs:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
@@ -326,11 +351,11 @@ jobs:
|
||||
- name: stage-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
@@ -345,7 +370,7 @@ jobs:
|
||||
- name: promote-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
@@ -355,7 +380,7 @@ jobs:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
@@ -371,11 +396,11 @@ jobs:
|
||||
- name: stage-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
@@ -390,26 +415,26 @@ jobs:
|
||||
- name: promote-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: spring-framework-ci-image
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *artifactory-task-params
|
||||
<<: *bintray-task-params
|
||||
- name: sync-to-maven-central
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
@@ -417,12 +442,6 @@ jobs:
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: sync-to-maven-central
|
||||
image: spring-framework-ci-image
|
||||
file: git-repo/ci/tasks/sync-to-maven-central.yml
|
||||
params:
|
||||
<<: *bintray-task-params
|
||||
<<: *sonatype-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
@@ -436,6 +455,8 @@ groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk11-build", "jdk15-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone","promote-rc", "promote-release", "sync-to-maven-central"]
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-spring-framework-ci-images"]
|
||||
jobs: ["build-ci-images"]
|
||||
- name: "pull-requests"
|
||||
jobs: [ "build-pull-requests" ]
|
||||
|
||||
Executable
+8
@@ -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
|
||||
@@ -4,5 +4,6 @@ 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
|
||||
./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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat version/version )
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
|
||||
@@ -6,11 +6,13 @@ CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
|
||||
java -jar /opt/concourse-release-scripts.jar promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
java -jar /opt/concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
|
||||
|
||||
java -jar /opt/concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
distribute $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
|
||||
@@ -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
|
||||
@@ -10,6 +10,8 @@ caches:
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.4'
|
||||
tag: '0.0.6'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: version
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: generated-changelog
|
||||
params:
|
||||
|
||||
@@ -10,9 +10,9 @@ params:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
BINTRAY_SUBJECT:
|
||||
BINTRAY_REPO:
|
||||
BINTRAY_USERNAME:
|
||||
BINTRAY_API_KEY:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE_ID:
|
||||
run:
|
||||
path: git-repo/ci/scripts/promote-version.sh
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
BINTRAY_REPO:
|
||||
BINTRAY_SUBJECT:
|
||||
BINTRAY_USERNAME:
|
||||
BINTRAY_API_KEY:
|
||||
SONATYPE_USER_TOKEN:
|
||||
SONATYPE_PASSWORD_TOKEN:
|
||||
run:
|
||||
path: git-repo/ci/scripts/sync-to-maven-central.sh
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.4-SNAPSHOT
|
||||
version=5.3.7
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This script adds support for the following two JVM system properties
|
||||
// that control the build for alternative JDKs (i.e., a JDK other than
|
||||
// the one used to launch the Gradle process).
|
||||
//
|
||||
// - customJavaHome: absolute path to the alternate JDK installation to
|
||||
// use to compile Java code and execute tests. This system property
|
||||
// is also used in spring-oxm.gradle to determine whether JiBX is
|
||||
// supported.
|
||||
//
|
||||
// - customJavaSourceVersion: Java version supplied to the `--release`
|
||||
// command line flag to control the Java source and target
|
||||
// compatibility version. Supported versions include 9 or higher.
|
||||
// Do not set this system property if Java 8 should be used.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ./gradlew -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home test
|
||||
//
|
||||
// ./gradlew --no-build-cache -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home test
|
||||
//
|
||||
// ./gradlew -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home -DcustomJavaSourceVersion=14 test
|
||||
//
|
||||
//
|
||||
// Credits: inspired by work from Marc Philipp and Stephane Nicoll
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
// import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
|
||||
|
||||
def customJavaHome = System.getProperty("customJavaHome")
|
||||
|
||||
if (customJavaHome) {
|
||||
def customJavaHomeDir = new File(customJavaHome)
|
||||
def customJavaSourceVersion = System.getProperty("customJavaSourceVersion")
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHomeDir)
|
||||
options.forkOptions.javaHome = customJavaHomeDir
|
||||
inputs.property("customJavaHome", customJavaHome)
|
||||
if (customJavaSourceVersion) {
|
||||
options.compilerArgs += [ "--release", customJavaSourceVersion]
|
||||
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(GroovyCompile) {
|
||||
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHomeDir)
|
||||
options.forkOptions.javaHome = customJavaHomeDir
|
||||
inputs.property("customJavaHome", customJavaHome)
|
||||
if (customJavaSourceVersion) {
|
||||
options.compilerArgs += [ "--release", customJavaSourceVersion]
|
||||
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
tasks.withType(KotlinJvmCompile) {
|
||||
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHome)
|
||||
kotlinOptions.jdkHome = customJavaHomeDir
|
||||
inputs.property("customJavaHome", customJavaHome)
|
||||
}
|
||||
*/
|
||||
|
||||
tasks.withType(Test) {
|
||||
def javaExecutable = customJavaHome + "/bin/java"
|
||||
if (OperatingSystem.current().isWindows()) {
|
||||
javaExecutable += ".exe"
|
||||
}
|
||||
logger.info("Java executable for " + it.name + " task in " + project.name + ": " + javaExecutable)
|
||||
executable = javaExecutable
|
||||
inputs.property("customJavaHome", customJavaHome)
|
||||
if (customJavaSourceVersion) {
|
||||
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'org.springframework.build.compile'
|
||||
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'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Apply the JVM Toolchain conventions
|
||||
* See https://docs.gradle.org/current/userguide/toolchains.html
|
||||
*
|
||||
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
|
||||
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
|
||||
* when available.
|
||||
* {@code "./gradlew check -PmainToolchain=8 -PtestToolchain=11"} will use:
|
||||
* <ul>
|
||||
* <li>a JDK8 toolchain for compiling the main SourceSet
|
||||
* <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
|
||||
* $ ./gradlew -q javaToolchains
|
||||
* }
|
||||
*
|
||||
* We can also configure ENV variables and let Gradle know about them:
|
||||
* {@code
|
||||
* $ echo JDK11
|
||||
* /opt/openjdk/java11
|
||||
* $ echo JDK15
|
||||
* /opt/openjdk/java15
|
||||
* $ ./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' is configured
|
||||
if (mainToolchainConfigured()) {
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = mainToolchainLanguageVersion()
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Fallback to JDK8
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
|
||||
javaCompiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
}
|
||||
tasks.withType(Test).configureEach{
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withType(GroovyPlugin) {
|
||||
// Fallback to JDK8
|
||||
if (!mainToolchainConfigured()) {
|
||||
compileGroovy {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin("kotlin") {
|
||||
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
|
||||
if (mainToolchainConfigured()) {
|
||||
def mainLanguageVersion = mainToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = mainLanguageVersion
|
||||
}
|
||||
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
|
||||
def javaVersion = mainLanguageVersion.toString() == '8' ? '1.8' : mainLanguageVersion.toString()
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
// Compile the test classes with the same version, 'testToolchain' will override if defined
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Fallback to JDK8
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
|
||||
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+1
-7
@@ -6,7 +6,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.5.1"
|
||||
id "com.gradle.enterprise" version "3.6.1"
|
||||
id "io.spring.ge.conventions" version "0.0.7"
|
||||
}
|
||||
|
||||
@@ -45,12 +45,6 @@ rootProject.children.each {project ->
|
||||
settings.gradle.projectsLoaded {
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
if (settings.gradle.rootProject.hasProperty('customJavaHome')) {
|
||||
value("Custom JAVA_HOME", settings.gradle.rootProject.getProperty('customJavaHome'))
|
||||
}
|
||||
if (settings.gradle.rootProject.hasProperty('customJavaSourceVersion')) {
|
||||
value("Custom Java Source Version", settings.gradle.rootProject.getProperty('customJavaSourceVersion'))
|
||||
}
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
buildDir.mkdirs()
|
||||
new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)"
|
||||
|
||||
@@ -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.
|
||||
@@ -26,7 +26,7 @@ import org.aopalliance.aop.Advice;
|
||||
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
|
||||
* objects that can achieve many of the goals of multiple inheritance in Java.
|
||||
*
|
||||
* <p>Compared to {qlink IntroductionInfo}, this interface allows an advice to
|
||||
* <p>Compared to {@link IntroductionInfo}, this interface allows an advice to
|
||||
* implement a range of interfaces that is not necessarily known in advance.
|
||||
* Thus an {@link IntroductionAdvisor} can be used to specify which interfaces
|
||||
* will be exposed in an advised object.
|
||||
|
||||
+9
-2
@@ -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.
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -89,7 +90,13 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
|
||||
}
|
||||
proxyFactory.addAdvisor(this.advisor);
|
||||
customizeProxyFactory(proxyFactory);
|
||||
return proxyFactory.getProxy(getProxyClassLoader());
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
// No proxy needed.
|
||||
|
||||
+12
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -458,7 +459,12 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
proxyFactory.setPreFiltered(true);
|
||||
}
|
||||
|
||||
return proxyFactory.getProxy(getProxyClassLoader());
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -503,7 +509,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
|
||||
List<Object> allInterceptors = new ArrayList<>();
|
||||
if (specificInterceptors != null) {
|
||||
allInterceptors.addAll(Arrays.asList(specificInterceptors));
|
||||
if (specificInterceptors.length > 0) {
|
||||
// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
|
||||
allInterceptors.addAll(Arrays.asList(specificInterceptors));
|
||||
}
|
||||
if (commonInterceptors.length > 0) {
|
||||
if (this.applyCommonInterceptorsFirst) {
|
||||
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
|
||||
|
||||
@@ -23,8 +23,6 @@ sourceSets {
|
||||
}
|
||||
|
||||
compileGroovy {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
options.compilerArgs += "-Werror"
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -305,8 +305,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
Class<?> componentType = propValue.getClass().getComponentType();
|
||||
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
|
||||
System.arraycopy(propValue, 0, newArray, 0, length);
|
||||
setPropertyValue(tokens.actualName, newArray);
|
||||
propValue = getPropertyValue(tokens.actualName);
|
||||
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
|
||||
String propName = tokens.canonicalName.substring(0, lastKeyIndex);
|
||||
setPropertyValue(propName, newArray);
|
||||
propValue = getPropertyValue(propName);
|
||||
}
|
||||
Array.set(propValue, arrayIndex, convertedValue);
|
||||
}
|
||||
|
||||
+11
-9
@@ -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.
|
||||
@@ -36,15 +36,16 @@ import org.springframework.util.StringValueResolver;
|
||||
* Example XML bean definition:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
|
||||
* <property name="driverClassName" value="${driver}"/>
|
||||
* <property name="url" value="jdbc:${dbname}"/>
|
||||
* <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
* <property name="driverClassName" value="${driver}" />
|
||||
* <property name="url" value="jdbc:${dbname}" />
|
||||
* </bean>
|
||||
* </pre>
|
||||
*
|
||||
* Example properties file:
|
||||
*
|
||||
* <pre class="code">driver=com.mysql.jdbc.Driver
|
||||
* <pre class="code">
|
||||
* driver=com.mysql.jdbc.Driver
|
||||
* dbname=mysql:mydb</pre>
|
||||
*
|
||||
* Annotated bean definitions may take advantage of property replacement using
|
||||
@@ -56,7 +57,8 @@ import org.springframework.util.StringValueResolver;
|
||||
* in bean references. Furthermore, placeholder values can also cross-reference
|
||||
* other placeholders, like:
|
||||
*
|
||||
* <pre class="code">rootPath=myrootdir
|
||||
* <pre class="code">
|
||||
* rootPath=myrootdir
|
||||
* subPath=${rootPath}/subdir</pre>
|
||||
*
|
||||
* In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
|
||||
@@ -71,13 +73,13 @@ import org.springframework.util.StringValueResolver;
|
||||
*
|
||||
* <p>Default property values can be defined globally for each configurer instance
|
||||
* via the {@link #setProperties properties} property, or on a property-by-property basis
|
||||
* using the default value separator which is {@code ":"} by default and
|
||||
* customizable via {@link #setValueSeparator(String)}.
|
||||
* using the value separator which is {@code ":"} by default and customizable via
|
||||
* {@link #setValueSeparator(String)}.
|
||||
*
|
||||
* <p>Example XML property with default value:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <property name="url" value="jdbc:${dbname:defaultdb}"/>
|
||||
* <property name="url" value="jdbc:${dbname:defaultdb}" />
|
||||
* </pre>
|
||||
*
|
||||
* @author Chris Beams
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -489,8 +489,8 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class<?>) args[1], constructorArgs);
|
||||
Map<?, ?> namedArgs = (Map<?, ?>) args[0];
|
||||
for (Object o : namedArgs.keySet()) {
|
||||
String propName = (String) o;
|
||||
for (Object key : namedArgs.keySet()) {
|
||||
String propName = (String) key;
|
||||
setProperty(propName, namedArgs.get(propName));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -894,7 +894,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if there are property values values defined for this bean.
|
||||
* Return if there are property values defined for this bean.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,8 +26,8 @@ import java.nio.file.Paths;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Editor for {@code java.nio.file.Path}, to directly populate a Path
|
||||
@@ -74,7 +74,7 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
boolean nioPathCandidate = !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
|
||||
boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX);
|
||||
if (nioPathCandidate && !text.startsWith("/")) {
|
||||
try {
|
||||
URI uri = new URI(text);
|
||||
@@ -85,9 +85,13 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException | FileSystemNotFoundException ex) {
|
||||
// Not a valid URI (let's try as Spring resource location),
|
||||
// or a URI scheme not registered for NIO (let's try URL
|
||||
catch (URISyntaxException ex) {
|
||||
// Not a valid URI; potentially a Windows-style path after
|
||||
// a file prefix (let's try as Spring resource location)
|
||||
nioPathCandidate = !text.startsWith(ResourceUtils.FILE_URL_PREFIX);
|
||||
}
|
||||
catch (FileSystemNotFoundException ex) {
|
||||
// URI scheme not registered for NIO (let's try URL
|
||||
// protocol handlers via Spring's resource mechanism).
|
||||
}
|
||||
}
|
||||
@@ -97,7 +101,7 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (resource == null) {
|
||||
setValue(null);
|
||||
}
|
||||
else if (!resource.exists() && nioPathCandidate) {
|
||||
else if (nioPathCandidate && !resource.exists()) {
|
||||
setValue(Paths.get(text).normalize());
|
||||
}
|
||||
else {
|
||||
|
||||
+4
-5
@@ -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);
|
||||
|
||||
+46
-8
@@ -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,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class BeanWrapperAutoGrowingTests {
|
||||
|
||||
@@ -66,11 +67,6 @@ public class BeanWrapperAutoGrowingTests {
|
||||
assertThat(bean.getArray()[0]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
private void assertNotNull(Object propertyValue) {
|
||||
assertThat(propertyValue).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrowArray() {
|
||||
wrapper.setPropertyValue("array[0].prop", "test");
|
||||
@@ -93,12 +89,39 @@ public class BeanWrapperAutoGrowingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrowMultiDimensionalArray() {
|
||||
public void getPropertyValueAutoGrow2dArray() {
|
||||
assertNotNull(wrapper.getPropertyValue("multiArray[0][0]"));
|
||||
assertThat(bean.getMultiArray()[0].length).isEqualTo(1);
|
||||
assertThat(bean.getMultiArray()[0][0]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrow3dArray() {
|
||||
assertNotNull(wrapper.getPropertyValue("threeDimensionalArray[1][2][3]"));
|
||||
assertThat(bean.getThreeDimensionalArray()[1].length).isEqualTo(3);
|
||||
assertThat(bean.getThreeDimensionalArray()[1][2][3]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrow2dArray() {
|
||||
Bean newBean = new Bean();
|
||||
newBean.setProp("enigma");
|
||||
wrapper.setPropertyValue("multiArray[2][3]", newBean);
|
||||
assertThat(bean.getMultiArray()[2][3])
|
||||
.isInstanceOf(Bean.class)
|
||||
.extracting(Bean::getProp).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrow3dArray() {
|
||||
Bean newBean = new Bean();
|
||||
newBean.setProp("enigma");
|
||||
wrapper.setPropertyValue("threeDimensionalArray[2][3][4]", newBean);
|
||||
assertThat(bean.getThreeDimensionalArray()[2][3][4])
|
||||
.isInstanceOf(Bean.class)
|
||||
.extracting(Bean::getProp).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrowList() {
|
||||
assertNotNull(wrapper.getPropertyValue("list[0]"));
|
||||
@@ -131,7 +154,7 @@ public class BeanWrapperAutoGrowingTests {
|
||||
public void getPropertyValueAutoGrowListFailsAgainstLimit() {
|
||||
wrapper.setAutoGrowCollectionLimit(2);
|
||||
assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() ->
|
||||
assertNotNull(wrapper.getPropertyValue("list[4]")))
|
||||
wrapper.getPropertyValue("list[4]"))
|
||||
.withRootCauseInstanceOf(IndexOutOfBoundsException.class);
|
||||
}
|
||||
|
||||
@@ -161,6 +184,11 @@ public class BeanWrapperAutoGrowingTests {
|
||||
}
|
||||
|
||||
|
||||
private static void assertNotNull(Object propertyValue) {
|
||||
assertThat(propertyValue).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class Bean {
|
||||
|
||||
@@ -174,6 +202,8 @@ public class BeanWrapperAutoGrowingTests {
|
||||
|
||||
private Bean[][] multiArray;
|
||||
|
||||
private Bean[][][] threeDimensionalArray;
|
||||
|
||||
private List<Bean> list;
|
||||
|
||||
private List<List<Bean>> multiList;
|
||||
@@ -214,6 +244,14 @@ public class BeanWrapperAutoGrowingTests {
|
||||
this.multiArray = multiArray;
|
||||
}
|
||||
|
||||
public Bean[][][] getThreeDimensionalArray() {
|
||||
return threeDimensionalArray;
|
||||
}
|
||||
|
||||
public void setThreeDimensionalArray(Bean[][][] threeDimensionalArray) {
|
||||
this.threeDimensionalArray = threeDimensionalArray;
|
||||
}
|
||||
|
||||
public List<Bean> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
+41
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,57 +34,78 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class PathEditorTests {
|
||||
|
||||
@Test
|
||||
public void testClasspathPathName() throws Exception {
|
||||
public void testClasspathPathName() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() throws Exception {
|
||||
public void testWithNonExistentResource() {
|
||||
PropertyEditor propertyEditor = new PathEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentPath() throws Exception {
|
||||
public void testWithNonExistentPath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("file:/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition1 = value instanceof Path;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
boolean condition = !path.toFile().exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsolutePath() throws Exception {
|
||||
public void testAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition1 = value instanceof Path;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
boolean condition = !path.toFile().exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameFound() throws Exception {
|
||||
public void testWindowsAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsAbsoluteFilePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
try {
|
||||
pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (File.separatorChar == '\\') { // on Windows, otherwise silently ignore
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isTrue();
|
||||
@@ -96,14 +117,13 @@ public class PathEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameNotFound() throws Exception {
|
||||
public void testUnqualifiedPathNameNotFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".clazz";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isFalse();
|
||||
|
||||
+3
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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<>();
|
||||
|
||||
+5
-5
@@ -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();
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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-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,6 +35,8 @@ import java.util.function.Consumer;
|
||||
* @param <E> the specific {@code ApplicationEvent} subclass to listen to
|
||||
* @see org.springframework.context.ApplicationEvent
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* @see org.springframework.context.event.SmartApplicationListener
|
||||
* @see org.springframework.context.event.GenericApplicationListener
|
||||
* @see org.springframework.context.event.EventListener
|
||||
*/
|
||||
@FunctionalInterface
|
||||
|
||||
+21
-11
@@ -75,16 +75,17 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* <p>Registered by default when using {@code <context:annotation-config/>} or
|
||||
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
|
||||
* with any other BeanFactoryPostProcessor.
|
||||
* with any other {@link BeanFactoryPostProcessor}.
|
||||
*
|
||||
* <p>This post processor is priority-ordered as it is important that any
|
||||
* {@link Bean} methods declared in {@code @Configuration} classes have
|
||||
* {@link Bean @Bean} methods declared in {@code @Configuration} classes have
|
||||
* their corresponding bean definitions registered before any other
|
||||
* {@link BeanFactoryPostProcessor} executes.
|
||||
* {@code BeanFactoryPostProcessor} executes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
|
||||
@@ -389,21 +390,30 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
|
||||
AnnotationMetadata annotationMetadata = null;
|
||||
MethodMetadata methodMetadata = null;
|
||||
if (beanDef instanceof AnnotatedBeanDefinition) {
|
||||
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
|
||||
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
|
||||
annotationMetadata = annotatedBeanDefinition.getMetadata();
|
||||
methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
|
||||
}
|
||||
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
|
||||
// Configuration class (full or lite) or a configuration-derived @Bean method
|
||||
// -> resolve bean class at this point...
|
||||
// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
|
||||
// or component class without @Bean methods.
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
|
||||
if (!abd.hasBeanClass()) {
|
||||
try {
|
||||
abd.resolveBeanClass(this.beanClassLoader);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||
boolean liteConfigurationCandidateWithoutBeanMethods =
|
||||
(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
|
||||
annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
|
||||
if (!liteConfigurationCandidateWithoutBeanMethods) {
|
||||
try {
|
||||
abd.resolveBeanClass(this.beanClassLoader);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-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.
|
||||
@@ -46,6 +46,7 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class ConfigurationClassUtils {
|
||||
@@ -162,6 +163,10 @@ abstract class ConfigurationClassUtils {
|
||||
}
|
||||
|
||||
// Finally, let's look for @Bean methods...
|
||||
return hasBeanMethods(metadata);
|
||||
}
|
||||
|
||||
static boolean hasBeanMethods(AnnotationMetadata metadata) {
|
||||
try {
|
||||
return metadata.hasAnnotatedMethods(Bean.class.getName());
|
||||
}
|
||||
|
||||
+18
-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.
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
@@ -137,6 +138,22 @@ public abstract class AbstractApplicationEventMulticaster
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListeners.removeIf(predicate);
|
||||
this.retrieverCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeApplicationListenerBeans(Predicate<String> predicate) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListenerBeans.removeIf(predicate);
|
||||
this.retrieverCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllListeners() {
|
||||
synchronized (this.defaultRetriever) {
|
||||
|
||||
+40
-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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -39,31 +41,68 @@ public interface ApplicationEventMulticaster {
|
||||
/**
|
||||
* Add a listener to be notified of all events.
|
||||
* @param listener the listener to add
|
||||
* @see #removeApplicationListener(ApplicationListener)
|
||||
* @see #removeApplicationListeners(Predicate)
|
||||
*/
|
||||
void addApplicationListener(ApplicationListener<?> listener);
|
||||
|
||||
/**
|
||||
* Add a listener bean to be notified of all events.
|
||||
* @param listenerBeanName the name of the listener bean to add
|
||||
* @see #removeApplicationListenerBean(String)
|
||||
* @see #removeApplicationListenerBeans(Predicate)
|
||||
*/
|
||||
void addApplicationListenerBean(String listenerBeanName);
|
||||
|
||||
/**
|
||||
* Remove a listener from the notification list.
|
||||
* @param listener the listener to remove
|
||||
* @see #addApplicationListener(ApplicationListener)
|
||||
* @see #removeApplicationListeners(Predicate)
|
||||
*/
|
||||
void removeApplicationListener(ApplicationListener<?> listener);
|
||||
|
||||
/**
|
||||
* Remove a listener bean from the notification list.
|
||||
* @param listenerBeanName the name of the listener bean to remove
|
||||
* @see #addApplicationListenerBean(String)
|
||||
* @see #removeApplicationListenerBeans(Predicate)
|
||||
*/
|
||||
void removeApplicationListenerBean(String listenerBeanName);
|
||||
|
||||
/**
|
||||
* Remove all matching listeners from the set of registered
|
||||
* {@code ApplicationListener} instances (which includes adapter classes
|
||||
* such as {@link ApplicationListenerMethodAdapter}, e.g. for annotated
|
||||
* {@link EventListener} methods).
|
||||
* <p>Note: This just applies to instance registrations, not to listeners
|
||||
* registered by bean name.
|
||||
* @param predicate the predicate to identify listener instances to remove,
|
||||
* e.g. checking {@link SmartApplicationListener#getListenerId()}
|
||||
* @since 5.3.5
|
||||
* @see #addApplicationListener(ApplicationListener)
|
||||
* @see #removeApplicationListener(ApplicationListener)
|
||||
*/
|
||||
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
|
||||
|
||||
/**
|
||||
* Remove all matching listener beans from the set of registered
|
||||
* listener bean names (referring to bean classes which in turn
|
||||
* implement the {@link ApplicationListener} interface directly).
|
||||
* <p>Note: This just applies to bean name registrations, not to
|
||||
* programmatically registered {@code ApplicationListener} instances.
|
||||
* @param predicate the predicate to identify listener bean names to remove
|
||||
* @since 5.3.5
|
||||
* @see #addApplicationListenerBean(String)
|
||||
* @see #removeApplicationListenerBean(String)
|
||||
*/
|
||||
void removeApplicationListenerBeans(Predicate<String> predicate);
|
||||
|
||||
/**
|
||||
* Remove all listeners registered with this multicaster.
|
||||
* <p>After a remove call, the multicaster will perform no action
|
||||
* on event notification until new listeners are registered.
|
||||
* @see #removeApplicationListeners(Predicate)
|
||||
*/
|
||||
void removeAllListeners();
|
||||
|
||||
|
||||
+33
-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.
|
||||
@@ -24,6 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -89,6 +90,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
|
||||
private final int order;
|
||||
|
||||
@Nullable
|
||||
private volatile String listenerId;
|
||||
|
||||
@Nullable
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -113,6 +117,8 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
|
||||
this.condition = (ann != null ? ann.condition() : null);
|
||||
this.order = resolveOrder(this.targetMethod);
|
||||
String id = (ann != null ? ann.id() : "");
|
||||
this.listenerId = (!id.isEmpty() ? id : null);
|
||||
}
|
||||
|
||||
private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) {
|
||||
@@ -186,6 +192,32 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getListenerId() {
|
||||
String id = this.listenerId;
|
||||
if (id == null) {
|
||||
id = getDefaultListenerId();
|
||||
this.listenerId = id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default id for the target listener, to be applied in case of
|
||||
* no {@link EventListener#id() annotation-specified id value}.
|
||||
* <p>The default implementation builds a method name with parameter types.
|
||||
* @since 5.3.5
|
||||
* @see #getListenerId()
|
||||
*/
|
||||
protected String getDefaultListenerId() {
|
||||
Method method = getTargetMethod();
|
||||
StringJoiner sj = new StringJoiner(",", "(", ")");
|
||||
for (Class<?> paramType : method.getParameterTypes()) {
|
||||
sj.add(paramType.getName());
|
||||
}
|
||||
return ClassUtils.getQualifiedMethodName(method) + sj.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the specified {@link ApplicationEvent}, checking if the condition
|
||||
|
||||
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -128,4 +129,13 @@ public @interface EventListener {
|
||||
*/
|
||||
String condition() default "";
|
||||
|
||||
/**
|
||||
* An optional identifier for the listener, defaulting to the fully-qualified
|
||||
* signature of the declaring method (e.g. "mypackage.MyClass.myMethod()").
|
||||
* @since 5.3.5
|
||||
* @see org.springframework.context.ApplicationListener#getListenerId()
|
||||
* @see ApplicationEventMulticaster#removeApplicationListeners(Predicate)
|
||||
*/
|
||||
String id() default "";
|
||||
|
||||
}
|
||||
|
||||
+15
-22
@@ -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.
|
||||
@@ -18,9 +18,7 @@ package org.springframework.context.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Extended variant of the standard {@link ApplicationListener} interface,
|
||||
@@ -28,13 +26,26 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* <p>As of Spring Framework 4.2, this interface supersedes the Class-based
|
||||
* {@link SmartApplicationListener} with full handling of generic event types.
|
||||
* As of 5.3.5, it formally extends {@link SmartApplicationListener}, adapting
|
||||
* {@link #supportsEventType(Class)} to {@link #supportsEventType(ResolvableType)}
|
||||
* with a default method.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.2
|
||||
* @see SmartApplicationListener
|
||||
* @see GenericApplicationListenerAdapter
|
||||
*/
|
||||
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
|
||||
public interface GenericApplicationListener extends SmartApplicationListener {
|
||||
|
||||
/**
|
||||
* Overrides {@link SmartApplicationListener#supportsEventType(Class)} with
|
||||
* delegation to {@link #supportsEventType(ResolvableType)}.
|
||||
*/
|
||||
@Override
|
||||
default boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return supportsEventType(ResolvableType.forClass(eventType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this listener actually supports the given event type.
|
||||
@@ -42,22 +53,4 @@ public interface GenericApplicationListener extends ApplicationListener<Applicat
|
||||
*/
|
||||
boolean supportsEventType(ResolvableType eventType);
|
||||
|
||||
/**
|
||||
* Determine whether this listener actually supports the given source type.
|
||||
* <p>The default implementation always returns {@code true}.
|
||||
* @param sourceType the source type, or {@code null} if no source
|
||||
*/
|
||||
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine this listener's order in a set of listeners for the same event.
|
||||
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
|
||||
*/
|
||||
@Override
|
||||
default int getOrder() {
|
||||
return LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-8
@@ -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.
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.ApplicationListener#onApplicationEvent
|
||||
*/
|
||||
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {
|
||||
public class GenericApplicationListenerAdapter implements GenericApplicationListener {
|
||||
|
||||
private static final Map<Class<?>, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
@@ -67,7 +67,10 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean supportsEventType(ResolvableType eventType) {
|
||||
if (this.delegate instanceof SmartApplicationListener) {
|
||||
if (this.delegate instanceof GenericApplicationListener) {
|
||||
return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
|
||||
}
|
||||
else if (this.delegate instanceof SmartApplicationListener) {
|
||||
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
|
||||
return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
|
||||
}
|
||||
@@ -76,11 +79,6 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return supportsEventType(ResolvableType.forClass(eventType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
|
||||
return !(this.delegate instanceof SmartApplicationListener) ||
|
||||
@@ -92,6 +90,12 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
|
||||
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getListenerId() {
|
||||
return (this.delegate instanceof SmartApplicationListener ?
|
||||
((SmartApplicationListener) this.delegate).getListenerId() : "");
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
|
||||
|
||||
+12
-1
@@ -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.
|
||||
@@ -59,4 +59,15 @@ public interface SmartApplicationListener extends ApplicationListener<Applicatio
|
||||
return LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an optional identifier for the listener.
|
||||
* <p>The default value is an empty String.
|
||||
* @since 5.3.5
|
||||
* @see EventListener#id
|
||||
* @see ApplicationEventMulticaster#removeApplicationListeners
|
||||
*/
|
||||
default String getListenerId() {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -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.
|
||||
@@ -34,7 +34,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.5
|
||||
*/
|
||||
public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener {
|
||||
public class SourceFilteringListener implements GenericApplicationListener {
|
||||
|
||||
private final Object source;
|
||||
|
||||
@@ -79,11 +79,6 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar
|
||||
return (this.delegate == null || this.delegate.supportsEventType(eventType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return supportsEventType(ResolvableType.forType(eventType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
|
||||
return (sourceType != null && sourceType.isInstance(this.source));
|
||||
@@ -94,6 +89,11 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar
|
||||
return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getListenerId() {
|
||||
return (this.delegate != null ? this.delegate.getListenerId() : "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually process the event, after having filtered according to the
|
||||
|
||||
+3
-3
@@ -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,13 +48,13 @@ public final class CandidateComponentsIndexLoader {
|
||||
public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";
|
||||
|
||||
/**
|
||||
* System property that instructs Spring to ignore the index, i.e.
|
||||
* System property that instructs Spring to ignore the components index, i.e.
|
||||
* to always return {@code null} from {@link #loadIndex(ClassLoader)}.
|
||||
* <p>The default is "false", allowing for regular use of the index. Switching this
|
||||
* flag to {@code true} fulfills a corner case scenario when an index is partially
|
||||
* available for some libraries (or use cases) but couldn't be built for the whole
|
||||
* application. In this case, the application context fallbacks to a regular
|
||||
* classpath arrangement (i.e. as no index was present at all).
|
||||
* classpath arrangement (i.e. as though no index were present at all).
|
||||
*/
|
||||
public static final String IGNORE_INDEX = "spring.index.ignore";
|
||||
|
||||
|
||||
+4
-3
@@ -46,6 +46,7 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ApplicationStartupAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
@@ -692,7 +693,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
|
||||
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
|
||||
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
|
||||
beanFactory.ignoreDependencyInterface(ApplicationStartup.class);
|
||||
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
|
||||
|
||||
// BeanFactory interface not registered as resolvable type in a plain factory.
|
||||
// MessageSource registered (and found for autowiring) as a bean.
|
||||
@@ -894,8 +895,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
|
||||
}
|
||||
|
||||
// Register a default embedded value resolver if no bean post-processor
|
||||
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
|
||||
// Register a default embedded value resolver if no BeanFactoryPostProcessor
|
||||
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
|
||||
// at this point, primarily for resolution in annotation attribute values.
|
||||
if (!beanFactory.hasEmbeddedValueResolver()) {
|
||||
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
|
||||
|
||||
+18
-17
@@ -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);
|
||||
|
||||
+72
-30
@@ -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.
|
||||
@@ -26,27 +26,44 @@ import java.lang.annotation.Target;
|
||||
* Declares that a field or method parameter should be formatted as a date or time.
|
||||
*
|
||||
* <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
|
||||
* Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code Long} (for
|
||||
* millisecond timestamps) as well as JSR-310 <code>java.time</code> and Joda-Time value types.
|
||||
* 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} value types.
|
||||
*
|
||||
* <p>For style-based formatting, set the {@link #style} attribute to be the style pattern code.
|
||||
* <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.
|
||||
* Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
|
||||
* A date or time may be omitted by specifying the style character '-'.
|
||||
* The date or time may be omitted by specifying the style character '-' — for example,
|
||||
* 'M-' specifies a medium format for the date with no time.
|
||||
*
|
||||
* <p>For ISO-based formatting, set the {@link #iso} attribute to be the desired {@link ISO} format,
|
||||
* such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern} attribute to be the
|
||||
* DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}.
|
||||
* <p>For ISO-based formatting, set the {@link #iso} attribute to the desired {@link ISO} format,
|
||||
* such as {@link ISO#DATE}.
|
||||
*
|
||||
* <p>For custom formatting, set the {@link #pattern} attribute to a date time pattern, such as
|
||||
* {@code "yyyy/MM/dd hh:mm:ss a"}.
|
||||
*
|
||||
* <p>Each attribute is mutually exclusive, so only set one attribute per annotation instance
|
||||
* (the one most convenient one for your formatting needs).
|
||||
* When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.
|
||||
* When the {@link #iso} attribute is specified, it takes precedence over the style attribute.
|
||||
* When no annotation attributes are specified, the default format applied is style-based
|
||||
* with a style code of 'SS' (short date, short time).
|
||||
* (the one most convenient for your formatting needs).
|
||||
*
|
||||
* <ul>
|
||||
* <li>When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.</li>
|
||||
* <li>When the {@link #iso} attribute is specified, it takes precedence over the style attribute.</li>
|
||||
* <li>When no annotation attributes are specified, the default format applied is style-based
|
||||
* with a style code of 'SS' (short date, short time).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Time Zones</h3>
|
||||
* <p>Whenever the {@link #style} or {@link #pattern} attribute is used, the
|
||||
* {@linkplain java.util.TimeZone#getDefault() default time zone} of the JVM will
|
||||
* be used when formatting {@link java.util.Date} values. Whenever the {@link #iso}
|
||||
* attribute is used when formatting {@link java.util.Date} values, {@code UTC}
|
||||
* will be used as the time zone. The same time zone will be applied to any
|
||||
* {@linkplain #fallbackPatterns fallback patterns} as well. In order to enforce
|
||||
* consistent use of {@code UTC} as the time zone, you can bootstrap the JVM with
|
||||
* {@code -Duser.timezone=UTC}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
* @see java.time.format.DateTimeFormatter
|
||||
* @see org.joda.time.format.DateTimeFormat
|
||||
@@ -57,34 +74,59 @@ import java.lang.annotation.Target;
|
||||
public @interface DateTimeFormat {
|
||||
|
||||
/**
|
||||
* The style pattern to use to format the field.
|
||||
* <p>Defaults to 'SS' for short date time. Set this attribute when you wish to format
|
||||
* your field in accordance with a common style other than the default style.
|
||||
* The style pattern to use to format the field or method parameter.
|
||||
* <p>Defaults to 'SS' for short date, short time. Set this attribute when you
|
||||
* wish to format your field or method parameter in accordance with a common
|
||||
* style other than the default style.
|
||||
* @see #fallbackPatterns
|
||||
*/
|
||||
String style() default "SS";
|
||||
|
||||
/**
|
||||
* The ISO pattern to use to format the field.
|
||||
* <p>The possible ISO patterns are defined in the {@link ISO} enum.
|
||||
* The ISO pattern to use to format the field or method parameter.
|
||||
* <p>Supported ISO patterns are defined in the {@link ISO} enum.
|
||||
* <p>Defaults to {@link ISO#NONE}, indicating this attribute should be ignored.
|
||||
* Set this attribute when you wish to format your field in accordance with an ISO format.
|
||||
* Set this attribute when you wish to format your field or method parameter
|
||||
* in accordance with an ISO format.
|
||||
* @see #fallbackPatterns
|
||||
*/
|
||||
ISO iso() default ISO.NONE;
|
||||
|
||||
/**
|
||||
* The custom pattern to use to format the field.
|
||||
* <p>Defaults to empty String, indicating no custom pattern String has been specified.
|
||||
* Set this attribute when you wish to format your field in accordance with a custom
|
||||
* date time pattern not represented by a style or ISO format.
|
||||
* The custom pattern to use to format the field or method parameter.
|
||||
* <p>Defaults to empty String, indicating no custom pattern String has been
|
||||
* specified. Set this attribute when you wish to format your field or method
|
||||
* parameter in accordance with a custom date time pattern not represented by
|
||||
* a style or ISO format.
|
||||
* <p>Note: This pattern follows the original {@link java.text.SimpleDateFormat} style,
|
||||
* as also supported by Joda-Time, with strict parsing semantics towards overflows
|
||||
* (e.g. rejecting a Feb 29 value for a non-leap-year). As a consequence, 'yy'
|
||||
* characters indicate a year in the traditional style, not a "year-of-era" as in the
|
||||
* {@link java.time.format.DateTimeFormatter} specification (i.e. 'yy' turns into 'uu'
|
||||
* when going through that {@code DateTimeFormatter} with strict resolution mode).
|
||||
* when going through a {@code DateTimeFormatter} with strict resolution mode).
|
||||
* @see #fallbackPatterns
|
||||
*/
|
||||
String pattern() default "";
|
||||
|
||||
/**
|
||||
* The set of custom patterns to use as a fallback in case parsing fails for
|
||||
* the primary {@link #pattern}, {@link #iso}, or {@link #style} attribute.
|
||||
* <p>For example, if you wish to use the ISO date format for parsing and
|
||||
* printing but allow for lenient parsing of user input for various date
|
||||
* formats, you could configure something similar to the following.
|
||||
* <pre style="code">
|
||||
* {@literal @}DateTimeFormat(iso = ISO.DATE, fallbackPatterns = { "M/d/yy", "dd.MM.yyyy" })
|
||||
* </pre>
|
||||
* <p>Fallback patterns are only used for parsing. They are not used for
|
||||
* printing the value as a String. The primary {@link #pattern}, {@link #iso},
|
||||
* or {@link #style} attribute is always used for printing. For details on
|
||||
* which time zone is used for fallback patterns, see the
|
||||
* {@linkplain DateTimeFormat class-level documentation}.
|
||||
* <p>Fallback patterns are not supported for Joda-Time value types.
|
||||
* @since 5.3.5
|
||||
*/
|
||||
String[] fallbackPatterns() default {};
|
||||
|
||||
|
||||
/**
|
||||
* Common ISO date time format patterns.
|
||||
@@ -92,20 +134,20 @@ public @interface DateTimeFormat {
|
||||
enum ISO {
|
||||
|
||||
/**
|
||||
* The most common ISO Date Format {@code yyyy-MM-dd},
|
||||
* e.g. "2000-10-31".
|
||||
* The most common ISO Date Format {@code yyyy-MM-dd} — for example,
|
||||
* "2000-10-31".
|
||||
*/
|
||||
DATE,
|
||||
|
||||
/**
|
||||
* The most common ISO Time Format {@code HH:mm:ss.SSSXXX},
|
||||
* e.g. "01:30:00.000-05:00".
|
||||
* The most common ISO Time Format {@code HH:mm:ss.SSSXXX} — for example,
|
||||
* "01:30:00.000-05:00".
|
||||
*/
|
||||
TIME,
|
||||
|
||||
/**
|
||||
* The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX},
|
||||
* e.g. "2000-10-31T01:30:00.000-05:00".
|
||||
* The most common ISO Date Time Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX}
|
||||
* — for example, "2000-10-31T01:30:00.000-05:00".
|
||||
*/
|
||||
DATE_TIME,
|
||||
|
||||
|
||||
+82
-11
@@ -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.
|
||||
@@ -27,17 +27,21 @@ import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A formatter for {@link java.util.Date} types.
|
||||
* Allows the configuration of an explicit date pattern and locale.
|
||||
* <p>Supports the configuration of an explicit date time pattern, timezone,
|
||||
* locale, and fallback date time patterns for lenient parsing.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
* @see SimpleDateFormat
|
||||
*/
|
||||
@@ -56,9 +60,15 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private Object source;
|
||||
|
||||
@Nullable
|
||||
private String pattern;
|
||||
|
||||
@Nullable
|
||||
private String[] fallbackPatterns;
|
||||
|
||||
private int style = DateFormat.DEFAULT;
|
||||
|
||||
@Nullable
|
||||
@@ -74,19 +84,33 @@ public class DateFormatter implements Formatter<Date> {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new default DateFormatter.
|
||||
* Create a new default {@code DateFormatter}.
|
||||
*/
|
||||
public DateFormatter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DateFormatter for the given date pattern.
|
||||
* Create a new {@code DateFormatter} for the given date time pattern.
|
||||
*/
|
||||
public DateFormatter(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the source of the configuration for this {@code DateFormatter} —
|
||||
* for example, an instance of the {@link DateTimeFormat @DateTimeFormat}
|
||||
* annotation if such an annotation was used to configure this {@code DateFormatter}.
|
||||
* <p>The supplied source object will only be used for descriptive purposes
|
||||
* by invoking its {@code toString()} method — for example, when
|
||||
* generating an exception message to provide further context.
|
||||
* @param source the source of the configuration
|
||||
* @since 5.3.5
|
||||
*/
|
||||
public void setSource(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pattern to use to format date values.
|
||||
* <p>If not specified, DateFormat's default style will be used.
|
||||
@@ -96,7 +120,19 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ISO format used for this date.
|
||||
* Set additional patterns to use as a fallback in case parsing fails for the
|
||||
* configured {@linkplain #setPattern pattern}, {@linkplain #setIso ISO format},
|
||||
* {@linkplain #setStyle style}, or {@linkplain #setStylePattern style pattern}.
|
||||
* @param fallbackPatterns the fallback parsing patterns
|
||||
* @since 5.3.5
|
||||
* @see DateTimeFormat#fallbackPatterns()
|
||||
*/
|
||||
public void setFallbackPatterns(String... fallbackPatterns) {
|
||||
this.fallbackPatterns = fallbackPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ISO format to use to format date values.
|
||||
* @param iso the {@link ISO} format
|
||||
* @since 3.2
|
||||
*/
|
||||
@@ -105,7 +141,7 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the style to use to format date values.
|
||||
* Set the {@link DateFormat} style to use to format date values.
|
||||
* <p>If not specified, DateFormat's default style will be used.
|
||||
* @see DateFormat#DEFAULT
|
||||
* @see DateFormat#SHORT
|
||||
@@ -118,8 +154,10 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the two character to use to format date values. The first character used for
|
||||
* the date style, the second is for the time style. Supported characters are
|
||||
* Set the two characters to use to format date values.
|
||||
* <p>The first character is used for the date style; the second is used for
|
||||
* the time style.
|
||||
* <p>Supported characters:
|
||||
* <ul>
|
||||
* <li>'S' = Small</li>
|
||||
* <li>'M' = Medium</li>
|
||||
@@ -136,7 +174,7 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TimeZone to normalize the date values into, if any.
|
||||
* Set the {@link TimeZone} to normalize the date values into, if any.
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
@@ -159,12 +197,45 @@ public class DateFormatter implements Formatter<Date> {
|
||||
|
||||
@Override
|
||||
public Date parse(String text, Locale locale) throws ParseException {
|
||||
return getDateFormat(locale).parse(text);
|
||||
try {
|
||||
return getDateFormat(locale).parse(text);
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
|
||||
for (String pattern : this.fallbackPatterns) {
|
||||
try {
|
||||
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
|
||||
// Align timezone for parsing format with printing format if ISO is set.
|
||||
if (this.iso != null && this.iso != ISO.NONE) {
|
||||
dateFormat.setTimeZone(UTC);
|
||||
}
|
||||
return dateFormat.parse(text);
|
||||
}
|
||||
catch (ParseException ignoredException) {
|
||||
// Ignore fallback parsing exceptions since the exception thrown below
|
||||
// will include information from the "source" if available -- for example,
|
||||
// the toString() of a @DateTimeFormat annotation.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.source != null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected DateFormat getDateFormat(Locale locale) {
|
||||
DateFormat dateFormat = createDateFormat(locale);
|
||||
return configureDateFormat(createDateFormat(locale));
|
||||
}
|
||||
|
||||
private DateFormat configureDateFormat(DateFormat dateFormat) {
|
||||
if (this.timeZone != null) {
|
||||
dateFormat.setTimeZone(this.timeZone);
|
||||
}
|
||||
|
||||
+20
-2
@@ -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.
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.format.datetime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.support.EmbeddedValueResolutionSupport;
|
||||
@@ -34,6 +36,7 @@ import org.springframework.util.StringUtils;
|
||||
* Formats fields annotated with the {@link DateTimeFormat} annotation using a {@link DateFormatter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
* @see org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory
|
||||
*/
|
||||
@@ -68,15 +71,30 @@ public class DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueReso
|
||||
|
||||
protected Formatter<Date> getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setSource(annotation);
|
||||
formatter.setIso(annotation.iso());
|
||||
|
||||
String style = resolveEmbeddedValue(annotation.style());
|
||||
if (StringUtils.hasLength(style)) {
|
||||
formatter.setStylePattern(style);
|
||||
}
|
||||
formatter.setIso(annotation.iso());
|
||||
|
||||
String pattern = resolveEmbeddedValue(annotation.pattern());
|
||||
if (StringUtils.hasLength(pattern)) {
|
||||
formatter.setPattern(pattern);
|
||||
}
|
||||
|
||||
List<String> resolvedFallbackPatterns = new ArrayList<>();
|
||||
for (String fallbackPattern : annotation.fallbackPatterns()) {
|
||||
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
|
||||
if (StringUtils.hasLength(resolvedFallbackPattern)) {
|
||||
resolvedFallbackPatterns.add(resolvedFallbackPattern);
|
||||
}
|
||||
}
|
||||
if (!resolvedFallbackPatterns.isEmpty()) {
|
||||
formatter.setFallbackPatterns(resolvedFallbackPatterns.toArray(new String[0]));
|
||||
}
|
||||
|
||||
return formatter;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -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.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* A context that holds user-specific <code>java.time</code> (JSR-310) settings
|
||||
* such as the user's Chronology (calendar system) and time zone.
|
||||
* A {@code null} property value indicate the user has not specified a setting.
|
||||
* <p>A {@code null} property value indicates the user has not specified a setting.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
@@ -81,8 +81,8 @@ public class DateTimeContext {
|
||||
|
||||
|
||||
/**
|
||||
* Get the DateTimeFormatter with the this context's settings
|
||||
* applied to the base {@code formatter}.
|
||||
* Get the DateTimeFormatter with this context's settings applied to the
|
||||
* base {@code formatter}.
|
||||
* @param formatter the base formatter that establishes default
|
||||
* formatting rules, generally context-independent
|
||||
* @return the contextual DateTimeFormatter
|
||||
|
||||
+2
-3
@@ -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.
|
||||
@@ -69,9 +69,8 @@ public final class DateTimeContextHolder {
|
||||
return dateTimeContextHolder.get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain a DateTimeFormatter with user-specific settings applied to the given base Formatter.
|
||||
* Obtain a DateTimeFormatter with user-specific settings applied to the given base formatter.
|
||||
* @param formatter the base formatter that establishes default formatting rules
|
||||
* (generally user independent)
|
||||
* @param locale the current user locale (may be {@code null} if not known)
|
||||
|
||||
+3
-7
@@ -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.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.format.datetime.standard;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
@@ -34,6 +33,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see #createDateTimeFormatter()
|
||||
* @see #createDateTimeFormatter(DateTimeFormatter)
|
||||
@@ -180,11 +180,7 @@ public class DateTimeFormatterFactory {
|
||||
public DateTimeFormatter createDateTimeFormatter(DateTimeFormatter fallbackFormatter) {
|
||||
DateTimeFormatter dateTimeFormatter = null;
|
||||
if (StringUtils.hasLength(this.pattern)) {
|
||||
// Using strict parsing to align with Joda-Time and standard DateFormat behavior:
|
||||
// otherwise, an overflow like e.g. Feb 29 for a non-leap-year wouldn't get rejected.
|
||||
// However, with strict parsing, a year digit needs to be specified as 'u'...
|
||||
String patternToUse = StringUtils.replace(this.pattern, "yy", "uu");
|
||||
dateTimeFormatter = DateTimeFormatter.ofPattern(patternToUse).withResolverStyle(ResolverStyle.STRICT);
|
||||
dateTimeFormatter = DateTimeFormatterUtils.createStrictDateTimeFormatter(this.pattern);
|
||||
}
|
||||
else if (this.iso != null && this.iso != ISO.NONE) {
|
||||
switch (this.iso) {
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.format.datetime.standard;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.ResolverStyle;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Internal {@link DateTimeFormatter} utilities.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3.5
|
||||
*/
|
||||
abstract class DateTimeFormatterUtils {
|
||||
|
||||
static DateTimeFormatter createStrictDateTimeFormatter(String pattern) {
|
||||
// Using strict parsing to align with Joda-Time and standard DateFormat behavior:
|
||||
// otherwise, an overflow like e.g. Feb 29 for a non-leap-year wouldn't get rejected.
|
||||
// However, with strict parsing, a year digit needs to be specified as 'u'...
|
||||
String patternToUse = StringUtils.replace(pattern, "yy", "uu");
|
||||
return DateTimeFormatter.ofPattern(patternToUse).withResolverStyle(ResolverStyle.STRICT);
|
||||
}
|
||||
|
||||
}
|
||||
+14
-2
@@ -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.
|
||||
@@ -24,8 +24,10 @@ import java.time.OffsetTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.support.EmbeddedValueResolutionSupport;
|
||||
@@ -40,6 +42,7 @@ import org.springframework.util.StringUtils;
|
||||
* JSR-310 <code>java.time</code> package in JDK 8.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see org.springframework.format.annotation.DateTimeFormat
|
||||
*/
|
||||
@@ -93,8 +96,17 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
List<String> resolvedFallbackPatterns = new ArrayList<>();
|
||||
for (String fallbackPattern : annotation.fallbackPatterns()) {
|
||||
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
|
||||
if (StringUtils.hasLength(resolvedFallbackPattern)) {
|
||||
resolvedFallbackPatterns.add(resolvedFallbackPattern);
|
||||
}
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
|
||||
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType, formatter);
|
||||
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType,
|
||||
formatter, resolvedFallbackPatterns.toArray(new String[0]), annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+48
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,16 +24,20 @@ import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link Parser} implementation for a JSR-310 {@link java.time.temporal.TemporalAccessor},
|
||||
* using a {@link java.time.format.DateTimeFormatter}) (the contextual one, if available).
|
||||
* using a {@link java.time.format.DateTimeFormatter} (the contextual one, if available).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see DateTimeContextHolder#getFormatter
|
||||
* @see java.time.LocalDate#parse(CharSequence, java.time.format.DateTimeFormatter)
|
||||
@@ -49,6 +53,12 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
|
||||
|
||||
private final DateTimeFormatter formatter;
|
||||
|
||||
@Nullable
|
||||
private final String[] fallbackPatterns;
|
||||
|
||||
@Nullable
|
||||
private final Object source;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new TemporalAccessorParser for the given TemporalAccessor type.
|
||||
@@ -57,14 +67,49 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
|
||||
* @param formatter the base DateTimeFormatter instance
|
||||
*/
|
||||
public TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter) {
|
||||
this(temporalAccessorType, formatter, null, null);
|
||||
}
|
||||
|
||||
TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter,
|
||||
@Nullable String[] fallbackPatterns, @Nullable Object source) {
|
||||
this.temporalAccessorType = temporalAccessorType;
|
||||
this.formatter = formatter;
|
||||
this.fallbackPatterns = fallbackPatterns;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TemporalAccessor parse(String text, Locale locale) throws ParseException {
|
||||
DateTimeFormatter formatterToUse = DateTimeContextHolder.getFormatter(this.formatter, locale);
|
||||
try {
|
||||
return doParse(text, locale, this.formatter);
|
||||
}
|
||||
catch (DateTimeParseException ex) {
|
||||
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
|
||||
for (String pattern : this.fallbackPatterns) {
|
||||
try {
|
||||
DateTimeFormatter fallbackFormatter = DateTimeFormatterUtils.createStrictDateTimeFormatter(pattern);
|
||||
return doParse(text, locale, fallbackFormatter);
|
||||
}
|
||||
catch (DateTimeParseException ignoredException) {
|
||||
// Ignore fallback parsing exceptions since the exception thrown below
|
||||
// will include information from the "source" if available -- for example,
|
||||
// the toString() of a @DateTimeFormat annotation.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.source != null) {
|
||||
throw new DateTimeParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
text, ex.getErrorIndex(), ex);
|
||||
}
|
||||
// else rethrow original exception
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private TemporalAccessor doParse(String text, Locale locale, DateTimeFormatter formatter) throws DateTimeParseException {
|
||||
DateTimeFormatter formatterToUse = DateTimeContextHolder.getFormatter(formatter, locale);
|
||||
if (LocalDate.class == this.temporalAccessorType) {
|
||||
return LocalDate.parse(text, formatterToUse);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -85,7 +85,7 @@ public @interface Scheduled {
|
||||
* trigger, primarily meant for externally specified values resolved by a
|
||||
* <code>${...}</code> placeholder.
|
||||
* @return an expression that can be parsed to a cron schedule
|
||||
* @see org.springframework.scheduling.support.CronSequenceGenerator
|
||||
* @see org.springframework.scheduling.support.CronExpression#parse(String)
|
||||
*/
|
||||
String cron() default "";
|
||||
|
||||
|
||||
+5
-5
@@ -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.
|
||||
@@ -358,9 +358,9 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
|
||||
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
|
||||
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
|
||||
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
|
||||
Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
|
||||
method, Scheduled.class, Schedules.class);
|
||||
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
|
||||
return (!scheduledAnnotations.isEmpty() ? scheduledAnnotations : null);
|
||||
});
|
||||
if (annotatedMethods.isEmpty()) {
|
||||
this.nonAnnotatedClasses.add(targetClass);
|
||||
@@ -370,8 +370,8 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
}
|
||||
else {
|
||||
// Non-empty set of methods
|
||||
annotatedMethods.forEach((method, scheduledMethods) ->
|
||||
scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
|
||||
annotatedMethods.forEach((method, scheduledAnnotations) ->
|
||||
scheduledAnnotations.forEach(scheduled -> processScheduled(scheduled, method, bean)));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
|
||||
"': " + annotatedMethods);
|
||||
|
||||
+5
-5
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -20,8 +20,8 @@ import org.springframework.scheduling.support.CronTrigger;
|
||||
|
||||
/**
|
||||
* {@link TriggerTask} implementation defining a {@code Runnable} to be executed according
|
||||
* to a {@linkplain org.springframework.scheduling.support.CronSequenceGenerator standard
|
||||
* cron expression}.
|
||||
* to a {@linkplain org.springframework.scheduling.support.CronExpression#parse(String)
|
||||
* standard cron expression}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.2
|
||||
|
||||
@@ -165,6 +165,10 @@ final class BitsCronField extends CronField {
|
||||
int max = Integer.parseInt(value.substring(hyphenPos + 1));
|
||||
min = type.checkValidValue(min);
|
||||
max = type.checkValidValue(max);
|
||||
if (type == Type.DAY_OF_WEEK && min == 7) {
|
||||
// If used as a minimum in a range, Sunday means 0 (not 7)
|
||||
min = 0;
|
||||
}
|
||||
return ValueRange.of(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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,11 +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) {
|
||||
return this.field.getBaseUnit().addTo(temporal, 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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ValueRange range = temporal.range(this.field);
|
||||
long amount = goal + range.getMaximum() - current + 1 - range.getMinimum();
|
||||
return this.field.getBaseUnit().addTo(temporal, amount);
|
||||
}
|
||||
|
||||
+1
-7
@@ -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);
|
||||
}
|
||||
|
||||
+70
@@ -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
-1
@@ -244,7 +244,7 @@
|
||||
<xsd:attribute name="cron" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A cron-based trigger. See the org.springframework.scheduling.support.CronSequenceGenerator
|
||||
A cron-based trigger. See the org.springframework.scheduling.support.CronExpression#parse
|
||||
JavaDoc for example patterns.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
+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.
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-2
@@ -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.
|
||||
@@ -111,6 +111,12 @@ public class AnnotationDrivenEventListenerTests {
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l ->
|
||||
l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().contains("TestEvent"));
|
||||
this.eventCollector.clear();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,6 +132,12 @@ public class AnnotationDrivenEventListenerTests {
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l ->
|
||||
l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().contains("TestEvent"));
|
||||
this.eventCollector.clear();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertNoEventReceived(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,6 +150,12 @@ public class AnnotationDrivenEventListenerTests {
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(bean, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l ->
|
||||
l instanceof SmartApplicationListener && ((SmartApplicationListener) l).getListenerId().equals("foo"));
|
||||
this.eventCollector.clear();
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertNoEventReceived(bean);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -711,7 +729,7 @@ public class AnnotationDrivenEventListenerTests {
|
||||
}
|
||||
|
||||
|
||||
@EventListener
|
||||
@EventListener(id = "foo")
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface FooListener {
|
||||
|
||||
+224
-55
@@ -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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.format.datetime;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -25,16 +26,23 @@ import java.util.Locale;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -42,10 +50,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Phillip Webb
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class DateFormattingTests {
|
||||
|
||||
private FormattingConversionService conversionService;
|
||||
private final FormattingConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
private DataBinder binder;
|
||||
|
||||
@@ -57,7 +66,6 @@ public class DateFormattingTests {
|
||||
}
|
||||
|
||||
private void setup(DateFormatterRegistrar registrar) {
|
||||
conversionService = new FormattingConversionService();
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
registrar.registerFormatters(conversionService);
|
||||
|
||||
@@ -87,34 +95,67 @@ public class DateFormattingTests {
|
||||
@Test
|
||||
void testBindLongAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("millisAnnotated", "10/31/09");
|
||||
propertyValues.add("styleMillis", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("millisAnnotated")).isEqualTo("10/31/09");
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleMillis")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindCalendarAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("calendarAnnotated", "10/31/09");
|
||||
propertyValues.add("styleCalendar", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("calendarAnnotated")).isEqualTo("10/31/09");
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleCalendar")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindDateAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotated", "10/31/09");
|
||||
propertyValues.add("styleDate", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateAnnotated")).isEqualTo("10/31/09");
|
||||
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();
|
||||
propertyValues.add("dateAnnotated", new String[]{"10/31/09 12:00 PM"});
|
||||
propertyValues.add("styleDate", new String[]{"10/31/09 12:00 PM"});
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
}
|
||||
@@ -122,10 +163,10 @@ public class DateFormattingTests {
|
||||
@Test
|
||||
void testBindDateAnnotatedWithError() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotated", "Oct X31, 2009");
|
||||
propertyValues.add("styleDate", "Oct X31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("dateAnnotated")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateAnnotated")).isEqualTo("Oct X31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("styleDate")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleDate")).isEqualTo("Oct X31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -133,19 +174,19 @@ public class DateFormattingTests {
|
||||
void testBindDateAnnotatedWithFallbackError() {
|
||||
// TODO This currently passes because of the Date(String) constructor fallback is used
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotated", "Oct 031, 2009");
|
||||
propertyValues.add("styleDate", "Oct 031, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("dateAnnotated")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateAnnotated")).isEqualTo("Oct 031, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("styleDate")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleDate")).isEqualTo("Oct 031, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindDateAnnotatedPattern() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotatedPattern", "10/31/09 1:05");
|
||||
propertyValues.add("patternDate", "10/31/09 1:05");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateAnnotatedPattern")).isEqualTo("10/31/09 1:05");
|
||||
assertThat(binder.getBindingResult().getFieldValue("patternDate")).isEqualTo("10/31/09 1:05");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,16 +197,17 @@ public class DateFormattingTests {
|
||||
registrar.setFormatter(dateFormatter);
|
||||
setup(registrar);
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotatedPattern", "10/31/09 1:05");
|
||||
propertyValues.add("patternDate", "10/31/09 1:05");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateAnnotatedPattern")).isEqualTo("10/31/09 1:05");
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue("patternDate")).isEqualTo("10/31/09 1:05");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindDateTimeOverflow() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateAnnotatedPattern", "02/29/09 12:00 PM");
|
||||
propertyValues.add("patternDate", "02/29/09 12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(1);
|
||||
}
|
||||
@@ -200,10 +242,10 @@ public class DateFormattingTests {
|
||||
@Test
|
||||
void testBindNestedDateAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("children[0].dateAnnotated", "10/31/09");
|
||||
propertyValues.add("children[0].styleDate", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].dateAnnotated")).isEqualTo("10/31/09");
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].styleDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -247,35 +289,130 @@ public class DateFormattingTests {
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FallbackPatternTests {
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void styleCalendar(String propertyValue) {
|
||||
String propertyName = "styleCalendarWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void styleDate(String propertyValue) {
|
||||
String propertyName = "styleDateWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void patternDate(String propertyValue) {
|
||||
String propertyName = "patternDateWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void isoDate(String propertyValue) {
|
||||
String propertyName = "isoDateWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
|
||||
}
|
||||
|
||||
@Test
|
||||
void patternDateWithUnsupportedPattern() {
|
||||
String propertyValue = "210302";
|
||||
String propertyName = "patternDateWithFallbackPatterns";
|
||||
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);
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).getCause()
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(ParseException.class).getCause()
|
||||
// Unable to parse date time value "210302" using configuration from
|
||||
// @org.springframework.format.annotation.DateTimeFormat(
|
||||
// pattern=yyyy-MM-dd, style=SS, iso=NONE, fallbackPatterns=[M/d/yy, yyyyMMdd, yyyy.MM.dd])
|
||||
.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")
|
||||
.hasCauseInstanceOf(ParseException.class).getCause()
|
||||
.hasMessageStartingWith("Unparseable date: \"210302\"")
|
||||
.hasNoCause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class SimpleDateBean {
|
||||
|
||||
private Long millis;
|
||||
|
||||
private Long millisAnnotated;
|
||||
private Long styleMillis;
|
||||
|
||||
@DateTimeFormat(style="S-")
|
||||
private Calendar calendarAnnotated;
|
||||
@DateTimeFormat(style = "S-")
|
||||
private Calendar styleCalendar;
|
||||
|
||||
@DateTimeFormat(style="S-")
|
||||
private Date dateAnnotated;
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private Calendar styleCalendarWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern="M/d/yy h:mm")
|
||||
private Date dateAnnotatedPattern;
|
||||
@DateTimeFormat(style = "S-")
|
||||
private Date styleDate;
|
||||
|
||||
@DateTimeFormat(iso=ISO.DATE)
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private Date styleDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern = "M/d/yy h:mm")
|
||||
private Date patternDate;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private Date patternDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE)
|
||||
private Date isoDate;
|
||||
|
||||
@DateTimeFormat(iso=ISO.TIME)
|
||||
@DateTimeFormat(iso = ISO.DATE, fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private Date isoDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(iso = ISO.TIME)
|
||||
private Date isoTime;
|
||||
|
||||
@DateTimeFormat(iso=ISO.DATE_TIME)
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME)
|
||||
private Date isoDateTime;
|
||||
|
||||
private final List<SimpleDateBean> children = new ArrayList<>();
|
||||
|
||||
|
||||
public Long getMillis() {
|
||||
return millis;
|
||||
return this.millis;
|
||||
}
|
||||
|
||||
public void setMillis(Long millis) {
|
||||
@@ -283,48 +420,80 @@ public class DateFormattingTests {
|
||||
}
|
||||
|
||||
@DateTimeFormat(style="S-")
|
||||
public Long getMillisAnnotated() {
|
||||
return millisAnnotated;
|
||||
public Long getStyleMillis() {
|
||||
return this.styleMillis;
|
||||
}
|
||||
|
||||
public void setMillisAnnotated(@DateTimeFormat(style="S-") Long millisAnnotated) {
|
||||
this.millisAnnotated = millisAnnotated;
|
||||
public void setStyleMillis(@DateTimeFormat(style="S-") Long styleMillis) {
|
||||
this.styleMillis = styleMillis;
|
||||
}
|
||||
|
||||
public Calendar getCalendarAnnotated() {
|
||||
return calendarAnnotated;
|
||||
public Calendar getStyleCalendar() {
|
||||
return this.styleCalendar;
|
||||
}
|
||||
|
||||
public void setCalendarAnnotated(Calendar calendarAnnotated) {
|
||||
this.calendarAnnotated = calendarAnnotated;
|
||||
public void setStyleCalendar(Calendar styleCalendar) {
|
||||
this.styleCalendar = styleCalendar;
|
||||
}
|
||||
|
||||
public Date getDateAnnotated() {
|
||||
return dateAnnotated;
|
||||
public Calendar getStyleCalendarWithFallbackPatterns() {
|
||||
return this.styleCalendarWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setDateAnnotated(Date dateAnnotated) {
|
||||
this.dateAnnotated = dateAnnotated;
|
||||
public void setStyleCalendarWithFallbackPatterns(Calendar styleCalendarWithFallbackPatterns) {
|
||||
this.styleCalendarWithFallbackPatterns = styleCalendarWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public Date getDateAnnotatedPattern() {
|
||||
return dateAnnotatedPattern;
|
||||
public Date getStyleDate() {
|
||||
return this.styleDate;
|
||||
}
|
||||
|
||||
public void setDateAnnotatedPattern(Date dateAnnotatedPattern) {
|
||||
this.dateAnnotatedPattern = dateAnnotatedPattern;
|
||||
public void setStyleDate(Date styleDate) {
|
||||
this.styleDate = styleDate;
|
||||
}
|
||||
|
||||
public Date getStyleDateWithFallbackPatterns() {
|
||||
return this.styleDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setStyleDateWithFallbackPatterns(Date styleDateWithFallbackPatterns) {
|
||||
this.styleDateWithFallbackPatterns = styleDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public Date getPatternDate() {
|
||||
return this.patternDate;
|
||||
}
|
||||
|
||||
public void setPatternDate(Date patternDate) {
|
||||
this.patternDate = patternDate;
|
||||
}
|
||||
|
||||
public Date getPatternDateWithFallbackPatterns() {
|
||||
return this.patternDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setPatternDateWithFallbackPatterns(Date patternDateWithFallbackPatterns) {
|
||||
this.patternDateWithFallbackPatterns = patternDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public Date getIsoDate() {
|
||||
return isoDate;
|
||||
return this.isoDate;
|
||||
}
|
||||
|
||||
public void setIsoDate(Date isoDate) {
|
||||
this.isoDate = isoDate;
|
||||
}
|
||||
|
||||
public Date getIsoDateWithFallbackPatterns() {
|
||||
return this.isoDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setIsoDateWithFallbackPatterns(Date isoDateWithFallbackPatterns) {
|
||||
this.isoDateWithFallbackPatterns = isoDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public Date getIsoTime() {
|
||||
return isoTime;
|
||||
return this.isoTime;
|
||||
}
|
||||
|
||||
public void setIsoTime(Date isoTime) {
|
||||
@@ -332,7 +501,7 @@ public class DateFormattingTests {
|
||||
}
|
||||
|
||||
public Date getIsoDateTime() {
|
||||
return isoDateTime;
|
||||
return this.isoDateTime;
|
||||
}
|
||||
|
||||
public void setIsoDateTime(Date isoDateTime) {
|
||||
@@ -340,7 +509,7 @@ public class DateFormattingTests {
|
||||
}
|
||||
|
||||
public List<SimpleDateBean> getChildren() {
|
||||
return children;
|
||||
return this.children;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+281
-117
@@ -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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.format.datetime.standard;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
@@ -28,6 +29,7 @@ import java.time.Year;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -38,15 +40,22 @@ import java.util.TimeZone;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -54,22 +63,22 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class DateTimeFormattingTests {
|
||||
class DateTimeFormattingTests {
|
||||
|
||||
private FormattingConversionService conversionService;
|
||||
private final FormattingConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
private DataBinder binder;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
setup(registrar);
|
||||
}
|
||||
|
||||
private void setup(DateTimeFormatterRegistrar registrar) {
|
||||
conversionService = new FormattingConversionService();
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
registrar.registerFormatters(conversionService);
|
||||
|
||||
@@ -85,14 +94,14 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
void cleanup() {
|
||||
LocaleContextHolder.setLocale(null);
|
||||
DateTimeContextHolder.setDateTimeContext(null);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBindLocalDate() {
|
||||
void testBindLocalDate() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
@@ -101,7 +110,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateWithSpecificStyle() {
|
||||
void testBindLocalDateWithSpecificStyle() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
registrar.setDateStyle(FormatStyle.LONG);
|
||||
setup(registrar);
|
||||
@@ -113,7 +122,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateWithSpecificFormatter() {
|
||||
void testBindLocalDateWithSpecificFormatter() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
setup(registrar);
|
||||
@@ -125,7 +134,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateArray() {
|
||||
void testBindLocalDateArray() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new String[] {"10/31/09"});
|
||||
binder.bind(propertyValues);
|
||||
@@ -133,54 +142,54 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateAnnotated() {
|
||||
void testBindLocalDateAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateAnnotated", "Oct 31, 2009");
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDateAnnotated")).isEqualTo("Oct 31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateAnnotatedWithError() {
|
||||
void testBindLocalDateAnnotatedWithError() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateAnnotated", "Oct -31, 2009");
|
||||
propertyValues.add("styleLocalDate", "Oct -31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("localDateAnnotated")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDateAnnotated")).isEqualTo("Oct -31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("styleLocalDate")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct -31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedLocalDateAnnotated() {
|
||||
void testBindNestedLocalDateAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("children[0].localDateAnnotated", "Oct 31, 2009");
|
||||
propertyValues.add("children[0].styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].localDateAnnotated")).isEqualTo("Oct 31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateAnnotatedWithDirectFieldAccess() {
|
||||
void testBindLocalDateAnnotatedWithDirectFieldAccess() {
|
||||
binder.initDirectFieldAccess();
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateAnnotated", "Oct 31, 2009");
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDateAnnotated")).isEqualTo("Oct 31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateAnnotatedWithDirectFieldAccessAndError() {
|
||||
void testBindLocalDateAnnotatedWithDirectFieldAccessAndError() {
|
||||
binder.initDirectFieldAccess();
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateAnnotated", "Oct -31, 2009");
|
||||
propertyValues.add("styleLocalDate", "Oct -31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("localDateAnnotated")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDateAnnotated")).isEqualTo("Oct -31, 2009");
|
||||
assertThat(binder.getBindingResult().getFieldErrorCount("styleLocalDate")).isEqualTo(1);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct -31, 2009");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateFromJavaUtilCalendar() {
|
||||
void testBindLocalDateFromJavaUtilCalendar() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
|
||||
binder.bind(propertyValues);
|
||||
@@ -189,7 +198,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalTime() {
|
||||
void testBindLocalTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
@@ -198,7 +207,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalTimeWithSpecificStyle() {
|
||||
void testBindLocalTimeWithSpecificStyle() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
registrar.setTimeStyle(FormatStyle.MEDIUM);
|
||||
setup(registrar);
|
||||
@@ -210,7 +219,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalTimeWithSpecificFormatter() {
|
||||
void testBindLocalTimeWithSpecificFormatter() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
registrar.setTimeFormatter(DateTimeFormatter.ofPattern("HHmmss"));
|
||||
setup(registrar);
|
||||
@@ -222,16 +231,16 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalTimeAnnotated() {
|
||||
void testBindLocalTimeAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTimeAnnotated", "12:00:00 PM");
|
||||
propertyValues.add("styleLocalTime", "12:00:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTimeAnnotated")).isEqualTo("12:00:00 PM");
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalTime")).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalTimeFromJavaUtilCalendar() {
|
||||
void testBindLocalTimeFromJavaUtilCalendar() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
@@ -240,7 +249,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateTime() {
|
||||
void testBindLocalDateTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
@@ -251,18 +260,18 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateTimeAnnotated() {
|
||||
void testBindLocalDateTimeAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTimeAnnotated", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
propertyValues.add("styleLocalDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTimeAnnotated").toString();
|
||||
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
|
||||
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
|
||||
assertThat(value.endsWith("12:00:00 PM")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindLocalDateTimeFromJavaUtilCalendar() {
|
||||
void testBindLocalDateTimeFromJavaUtilCalendar() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
@@ -273,7 +282,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateTimeWithSpecificStyle() {
|
||||
void testBindDateTimeWithSpecificStyle() {
|
||||
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
|
||||
registrar.setDateTimeStyle(FormatStyle.MEDIUM);
|
||||
setup(registrar);
|
||||
@@ -287,69 +296,99 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateTimeAnnotatedPattern() {
|
||||
void testBindPatternLocalDateTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateTimeAnnotatedPattern", "10/31/09 12:00 PM");
|
||||
propertyValues.add("patternLocalDateTime", "10/31/09 12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("dateTimeAnnotatedPattern")).isEqualTo("10/31/09 12:00 PM");
|
||||
assertThat(binder.getBindingResult().getFieldValue("patternLocalDateTime")).isEqualTo("10/31/09 12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateTimeOverflow() {
|
||||
void testBindDateTimeOverflow() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("dateTimeAnnotatedPattern", "02/29/09 12:00 PM");
|
||||
propertyValues.add("patternLocalDateTime", "02/29/09 12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindISODate() {
|
||||
void testBindISODate() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoDate", "2009-10-31");
|
||||
propertyValues.add("isoLocalDate", "2009-10-31");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoDate")).isEqualTo("2009-10-31");
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindISOTime() {
|
||||
void isoLocalDateWithInvalidFormat() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoTime", "12:00:00");
|
||||
String propertyName = "isoLocalDate";
|
||||
propertyValues.add(propertyName, "2009-31-10");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoTime")).isEqualTo("12:00:00");
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(1);
|
||||
FieldError fieldError = bindingResult.getFieldError(propertyName);
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'isoLocalDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).getCause()
|
||||
.hasMessageContaining("for value '2009-31-10'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
|
||||
.hasMessageContaining("Parse attempt failed for value [2009-31-10]")
|
||||
.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")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
|
||||
.hasMessageStartingWith("Text '2009-31-10'")
|
||||
.hasCauseInstanceOf(DateTimeException.class).getCause()
|
||||
.hasMessageContaining("Invalid value for MonthOfYear (valid values 1 - 12): 31")
|
||||
.hasNoCause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindISOTimeWithZone() {
|
||||
void testBindISOTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoTime", "12:00:00.000-05:00");
|
||||
propertyValues.add("isoLocalTime", "12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoTime")).isEqualTo("12:00:00");
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindISODateTime() {
|
||||
void testBindISOTimeWithZone() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoDateTime", "2009-10-31T12:00:00");
|
||||
propertyValues.add("isoLocalTime", "12:00:00.000-05:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindISODateTimeWithZone() {
|
||||
void testBindISODateTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoDateTime", "2009-10-31T12:00:00.000Z");
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindInstant() {
|
||||
void testBindISODateTimeWithZone() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindInstant() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("instant", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
@@ -359,7 +398,7 @@ public class DateTimeFormattingTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void testBindInstantFromJavaUtilDate() {
|
||||
void testBindInstantFromJavaUtilDate() {
|
||||
TimeZone defaultZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
|
||||
try {
|
||||
@@ -375,7 +414,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindPeriod() {
|
||||
void testBindPeriod() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("period", "P6Y3M1D");
|
||||
binder.bind(propertyValues);
|
||||
@@ -384,7 +423,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDuration() {
|
||||
void testBindDuration() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("duration", "PT8H6M12.345S");
|
||||
binder.bind(propertyValues);
|
||||
@@ -393,7 +432,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindYear() {
|
||||
void testBindYear() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("year", "2007");
|
||||
binder.bind(propertyValues);
|
||||
@@ -402,7 +441,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindMonth() {
|
||||
void testBindMonth() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "JULY");
|
||||
binder.bind(propertyValues);
|
||||
@@ -411,7 +450,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindMonthInAnyCase() {
|
||||
void testBindMonthInAnyCase() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "July");
|
||||
binder.bind(propertyValues);
|
||||
@@ -420,7 +459,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindYearMonth() {
|
||||
void testBindYearMonth() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("yearMonth", "2007-12");
|
||||
binder.bind(propertyValues);
|
||||
@@ -429,7 +468,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindMonthDay() {
|
||||
void testBindMonthDay() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("monthDay", "--12-03");
|
||||
binder.bind(propertyValues);
|
||||
@@ -437,35 +476,128 @@ public class DateTimeFormattingTests {
|
||||
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")).isTrue();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FallbackPatternTests {
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void styleLocalDate(String propertyValue) {
|
||||
String propertyName = "styleLocalDateWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02", "2021.03.02", "20210302", "3/2/21"})
|
||||
void patternLocalDate(String propertyValue) {
|
||||
String propertyName = "patternLocalDateWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"12:00:00 PM", "12:00:00", "12:00"})
|
||||
void styleLocalTime(String propertyValue) {
|
||||
String propertyName = "styleLocalTimeWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
@ValueSource(strings = {"2021-03-02T12:00:00", "2021-03-02 12:00:00", "3/2/21 12:00"})
|
||||
void isoLocalDateTime(String propertyValue) {
|
||||
String propertyName = "isoLocalDateTimeWithFallbackPatterns";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02T12:00:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void patternLocalDateWithUnsupportedPattern() {
|
||||
String propertyValue = "210302";
|
||||
String propertyName = "patternLocalDateWithFallbackPatterns";
|
||||
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);
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternLocalDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).getCause()
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
|
||||
// Unable to parse date time value "210302" using configuration from
|
||||
// @org.springframework.format.annotation.DateTimeFormat(
|
||||
// pattern=yyyy-MM-dd, style=SS, iso=NONE, fallbackPatterns=[M/d/yy, yyyyMMdd, yyyy.MM.dd])
|
||||
.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")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
|
||||
.hasMessageStartingWith("Text '210302'")
|
||||
.hasNoCause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class DateTimeBean {
|
||||
|
||||
private LocalDate localDate;
|
||||
|
||||
@DateTimeFormat(style = "M-")
|
||||
private LocalDate localDateAnnotated;
|
||||
private LocalDate styleLocalDate;
|
||||
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate styleLocalDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate patternLocalDateWithFallbackPatterns;
|
||||
|
||||
private LocalTime localTime;
|
||||
|
||||
@DateTimeFormat(style = "-M")
|
||||
private LocalTime localTimeAnnotated;
|
||||
private LocalTime styleLocalTime;
|
||||
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
|
||||
private LocalTime styleLocalTimeWithFallbackPatterns;
|
||||
|
||||
private LocalDateTime localDateTime;
|
||||
|
||||
@DateTimeFormat(style = "MM")
|
||||
private LocalDateTime localDateTimeAnnotated;
|
||||
private LocalDateTime styleLocalDateTime;
|
||||
|
||||
@DateTimeFormat(pattern = "M/d/yy h:mm a")
|
||||
private LocalDateTime dateTimeAnnotatedPattern;
|
||||
private LocalDateTime patternLocalDateTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE)
|
||||
private LocalDate isoDate;
|
||||
private LocalDate isoLocalDate;
|
||||
|
||||
@DateTimeFormat(iso = ISO.TIME)
|
||||
private LocalTime isoTime;
|
||||
private LocalTime isoLocalTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME)
|
||||
private LocalDateTime isoDateTime;
|
||||
private LocalDateTime isoLocalDateTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
|
||||
|
||||
private Instant instant;
|
||||
|
||||
@@ -483,88 +615,120 @@ public class DateTimeFormattingTests {
|
||||
|
||||
private final List<DateTimeBean> children = new ArrayList<>();
|
||||
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return localDate;
|
||||
return this.localDate;
|
||||
}
|
||||
|
||||
public void setLocalDate(LocalDate localDate) {
|
||||
this.localDate = localDate;
|
||||
}
|
||||
|
||||
public LocalDate getLocalDateAnnotated() {
|
||||
return localDateAnnotated;
|
||||
public LocalDate getStyleLocalDate() {
|
||||
return this.styleLocalDate;
|
||||
}
|
||||
|
||||
public void setLocalDateAnnotated(LocalDate localDateAnnotated) {
|
||||
this.localDateAnnotated = localDateAnnotated;
|
||||
public void setStyleLocalDate(LocalDate styleLocalDate) {
|
||||
this.styleLocalDate = styleLocalDate;
|
||||
}
|
||||
|
||||
public LocalDate getStyleLocalDateWithFallbackPatterns() {
|
||||
return this.styleLocalDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setStyleLocalDateWithFallbackPatterns(LocalDate styleLocalDateWithFallbackPatterns) {
|
||||
this.styleLocalDateWithFallbackPatterns = styleLocalDateWithFallbackPatterns;
|
||||
}
|
||||
public LocalDate getPatternLocalDateWithFallbackPatterns() {
|
||||
return this.patternLocalDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setPatternLocalDateWithFallbackPatterns(LocalDate patternLocalDateWithFallbackPatterns) {
|
||||
this.patternLocalDateWithFallbackPatterns = patternLocalDateWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public LocalTime getLocalTime() {
|
||||
return localTime;
|
||||
return this.localTime;
|
||||
}
|
||||
|
||||
public void setLocalTime(LocalTime localTime) {
|
||||
this.localTime = localTime;
|
||||
}
|
||||
|
||||
public LocalTime getLocalTimeAnnotated() {
|
||||
return localTimeAnnotated;
|
||||
public LocalTime getStyleLocalTime() {
|
||||
return this.styleLocalTime;
|
||||
}
|
||||
|
||||
public void setLocalTimeAnnotated(LocalTime localTimeAnnotated) {
|
||||
this.localTimeAnnotated = localTimeAnnotated;
|
||||
public void setStyleLocalTime(LocalTime styleLocalTime) {
|
||||
this.styleLocalTime = styleLocalTime;
|
||||
}
|
||||
|
||||
public LocalTime getStyleLocalTimeWithFallbackPatterns() {
|
||||
return this.styleLocalTimeWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setStyleLocalTimeWithFallbackPatterns(LocalTime styleLocalTimeWithFallbackPatterns) {
|
||||
this.styleLocalTimeWithFallbackPatterns = styleLocalTimeWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public LocalDateTime getLocalDateTime() {
|
||||
return localDateTime;
|
||||
return this.localDateTime;
|
||||
}
|
||||
|
||||
public void setLocalDateTime(LocalDateTime localDateTime) {
|
||||
this.localDateTime = localDateTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getLocalDateTimeAnnotated() {
|
||||
return localDateTimeAnnotated;
|
||||
public LocalDateTime getStyleLocalDateTime() {
|
||||
return this.styleLocalDateTime;
|
||||
}
|
||||
|
||||
public void setLocalDateTimeAnnotated(LocalDateTime localDateTimeAnnotated) {
|
||||
this.localDateTimeAnnotated = localDateTimeAnnotated;
|
||||
public void setStyleLocalDateTime(LocalDateTime styleLocalDateTime) {
|
||||
this.styleLocalDateTime = styleLocalDateTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getDateTimeAnnotatedPattern() {
|
||||
return dateTimeAnnotatedPattern;
|
||||
public LocalDateTime getPatternLocalDateTime() {
|
||||
return this.patternLocalDateTime;
|
||||
}
|
||||
|
||||
public void setDateTimeAnnotatedPattern(LocalDateTime dateTimeAnnotatedPattern) {
|
||||
this.dateTimeAnnotatedPattern = dateTimeAnnotatedPattern;
|
||||
public void setPatternLocalDateTime(LocalDateTime patternLocalDateTime) {
|
||||
this.patternLocalDateTime = patternLocalDateTime;
|
||||
}
|
||||
|
||||
public LocalDate getIsoDate() {
|
||||
return isoDate;
|
||||
public LocalDate getIsoLocalDate() {
|
||||
return this.isoLocalDate;
|
||||
}
|
||||
|
||||
public void setIsoDate(LocalDate isoDate) {
|
||||
this.isoDate = isoDate;
|
||||
public void setIsoLocalDate(LocalDate isoLocalDate) {
|
||||
this.isoLocalDate = isoLocalDate;
|
||||
}
|
||||
|
||||
public LocalTime getIsoTime() {
|
||||
return isoTime;
|
||||
public LocalTime getIsoLocalTime() {
|
||||
return this.isoLocalTime;
|
||||
}
|
||||
|
||||
public void setIsoTime(LocalTime isoTime) {
|
||||
this.isoTime = isoTime;
|
||||
public void setIsoLocalTime(LocalTime isoLocalTime) {
|
||||
this.isoLocalTime = isoLocalTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getIsoDateTime() {
|
||||
return isoDateTime;
|
||||
public LocalDateTime getIsoLocalDateTime() {
|
||||
return this.isoLocalDateTime;
|
||||
}
|
||||
|
||||
public void setIsoDateTime(LocalDateTime isoDateTime) {
|
||||
this.isoDateTime = isoDateTime;
|
||||
public void setIsoLocalDateTime(LocalDateTime isoLocalDateTime) {
|
||||
this.isoLocalDateTime = isoLocalDateTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getIsoLocalDateTimeWithFallbackPatterns() {
|
||||
return this.isoLocalDateTimeWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public void setIsoLocalDateTimeWithFallbackPatterns(LocalDateTime isoLocalDateTimeWithFallbackPatterns) {
|
||||
this.isoLocalDateTimeWithFallbackPatterns = isoLocalDateTimeWithFallbackPatterns;
|
||||
}
|
||||
|
||||
public Instant getInstant() {
|
||||
return instant;
|
||||
return this.instant;
|
||||
}
|
||||
|
||||
public void setInstant(Instant instant) {
|
||||
@@ -572,7 +736,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public Period getPeriod() {
|
||||
return period;
|
||||
return this.period;
|
||||
}
|
||||
|
||||
public void setPeriod(Period period) {
|
||||
@@ -580,7 +744,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public Duration getDuration() {
|
||||
return duration;
|
||||
return this.duration;
|
||||
}
|
||||
|
||||
public void setDuration(Duration duration) {
|
||||
@@ -588,7 +752,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public Year getYear() {
|
||||
return year;
|
||||
return this.year;
|
||||
}
|
||||
|
||||
public void setYear(Year year) {
|
||||
@@ -596,7 +760,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public Month getMonth() {
|
||||
return month;
|
||||
return this.month;
|
||||
}
|
||||
|
||||
public void setMonth(Month month) {
|
||||
@@ -604,7 +768,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public YearMonth getYearMonth() {
|
||||
return yearMonth;
|
||||
return this.yearMonth;
|
||||
}
|
||||
|
||||
public void setYearMonth(YearMonth yearMonth) {
|
||||
@@ -612,7 +776,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public MonthDay getMonthDay() {
|
||||
return monthDay;
|
||||
return this.monthDay;
|
||||
}
|
||||
|
||||
public void setMonthDay(MonthDay monthDay) {
|
||||
@@ -620,7 +784,7 @@ public class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
public List<DateTimeBean> getChildren() {
|
||||
return children;
|
||||
return this.children;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -48,6 +48,8 @@ class BitsCronFieldTests {
|
||||
assertThat(BitsCronField.parseMonth("1")).has(set(1)).has(clearRange(2, 12));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("0")).has(set(7, 7)).has(clearRange(0, 6));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5)).has(clear(6)).has(set(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+46
-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.
|
||||
@@ -1242,5 +1242,50 @@ class CronExpressionTests {
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sundayToFriday() {
|
||||
CronExpression expression = CronExpression.parse("0 0 0 ? * SUN-FRI");
|
||||
|
||||
LocalDateTime last = LocalDateTime.of(2021, 2, 25, 15, 0);
|
||||
LocalDateTime expected = LocalDateTime.of(2021, 2, 26, 0, 0);
|
||||
LocalDateTime actual = expression.next(last);
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
assertThat(actual.getDayOfWeek()).isEqualTo(FRIDAY);
|
||||
|
||||
last = actual;
|
||||
expected = LocalDateTime.of(2021, 2, 28, 0, 0);
|
||||
actual = expression.next(last);
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
assertThat(actual.getDayOfWeek()).isEqualTo(SUNDAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void daylightSaving() {
|
||||
CronExpression cronExpression = CronExpression.parse("0 0 9 * * *");
|
||||
|
||||
ZonedDateTime last = ZonedDateTime.parse("2021-03-27T09:00:00+01:00[Europe/Amsterdam]");
|
||||
ZonedDateTime expected = ZonedDateTime.parse("2021-03-28T09:00:00+02:00[Europe/Amsterdam]");
|
||||
ZonedDateTime actual = cronExpression.next(last);
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
|
||||
last = ZonedDateTime.parse("2021-10-30T09:00:00+02:00[Europe/Amsterdam]");
|
||||
expected = ZonedDateTime.parse("2021-10-31T09:00:00+01:00[Europe/Amsterdam]");
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+83
-71
@@ -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) {
|
||||
|
||||
@@ -8,7 +8,7 @@ apply plugin: "kotlin"
|
||||
// cglib itself depends on asm and is therefore further transformed by the ShadowJar task to
|
||||
// depend on org.springframework.asm; this avoids including two different copies of asm.
|
||||
def cglibVersion = "3.3.0"
|
||||
def objenesisVersion = "3.1"
|
||||
def objenesisVersion = "3.2"
|
||||
|
||||
configurations {
|
||||
cglib
|
||||
@@ -70,8 +70,8 @@ dependencies {
|
||||
|
||||
jar {
|
||||
reproducibleFileOrder = true
|
||||
preserveFileTimestamps = false // maybe not necessary here, but good for reproducibility
|
||||
manifest.attributes["Dependencies"] = "jdk.unsupported" // JBoss modules
|
||||
preserveFileTimestamps = false // maybe not necessary here, but good for reproducibility
|
||||
manifest.attributes["Dependencies"] = "jdk.unsupported" // for WildFly (-> Objenesis 3.2)
|
||||
|
||||
// Inline repackaged cglib classes directly into spring-core jar
|
||||
dependsOn cglibRepackJar
|
||||
|
||||
@@ -163,7 +163,7 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
}
|
||||
// A non-array type: compare the type itself.
|
||||
if (!candidateParameter.equals(genericParameter.toClass())) {
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,28 @@ public interface SmartClassLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original ClassLoader for this SmartClassLoader, or potentially
|
||||
* the present loader itself if it is self-sufficient.
|
||||
* <p>The default implementation returns the local ClassLoader reference as-is.
|
||||
* In case of a reloadable or other selectively overriding ClassLoader which
|
||||
* commonly deals with unaffected classes from a base application class loader,
|
||||
* this should get implemented to return the original ClassLoader that the
|
||||
* present loader got derived from (e.g. through {@code return getParent();}).
|
||||
* <p>This gets specifically used in Spring's AOP framework to determine the
|
||||
* class loader for a specific proxy in case the target class has not been
|
||||
* defined in the present class loader. In case of a reloadable class loader,
|
||||
* we prefer the base application class loader for proxying general classes
|
||||
* not defined in the reloadable class loader itself.
|
||||
* @return the original ClassLoader (the same reference by default)
|
||||
* @since 5.3.5
|
||||
* @see ClassLoader#getParent()
|
||||
* @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
|
||||
*/
|
||||
default ClassLoader getOriginalClassLoader() {
|
||||
return (ClassLoader) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a custom class (typically a CGLIB proxy class) in this class loader.
|
||||
* <p>This is a public equivalent of the protected
|
||||
|
||||
@@ -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.
|
||||
@@ -515,7 +515,7 @@ public class TypeDescriptor implements Serializable {
|
||||
for (Annotation ann : getAnnotations()) {
|
||||
builder.append("@").append(ann.annotationType().getName()).append(' ');
|
||||
}
|
||||
builder.append(getResolvableType().toString());
|
||||
builder.append(getResolvableType());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -60,9 +60,17 @@ public class StandardEnvironment extends AbstractEnvironment {
|
||||
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code StandardEnvironment} instance.
|
||||
*/
|
||||
public StandardEnvironment() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code StandardEnvironment} instance with a specific {@link MutablePropertySources} instance.
|
||||
* @param propertySources property sources to use
|
||||
* @since 5.3.4
|
||||
*/
|
||||
protected StandardEnvironment(MutablePropertySources propertySources) {
|
||||
super(propertySources);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -148,14 +148,21 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
*/
|
||||
@Nullable
|
||||
protected URL resolveURL() {
|
||||
if (this.clazz != null) {
|
||||
return this.clazz.getResource(this.path);
|
||||
try {
|
||||
if (this.clazz != null) {
|
||||
return this.clazz.getResource(this.path);
|
||||
}
|
||||
else if (this.classLoader != null) {
|
||||
return this.classLoader.getResource(this.path);
|
||||
}
|
||||
else {
|
||||
return ClassLoader.getSystemResource(this.path);
|
||||
}
|
||||
}
|
||||
else if (this.classLoader != null) {
|
||||
return this.classLoader.getResource(this.path);
|
||||
}
|
||||
else {
|
||||
return ClassLoader.getSystemResource(this.path);
|
||||
catch (IllegalArgumentException ex) {
|
||||
// Should not happen according to the JDK's contract:
|
||||
// see https://github.com/openjdk/jdk/pull/2662
|
||||
return 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.
|
||||
@@ -63,15 +63,28 @@ public final class LogDelegateFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "hidden" logger whose name is intentionally prefixed with "_"
|
||||
* because its output is either too verbose or otherwise deemed as optional
|
||||
* or unnecessary to see at any log level by default under the normal package
|
||||
* based log hierarchy.
|
||||
* Create a "hidden" logger with a category name prefixed with "_", thus
|
||||
* precluding it from being enabled together with other log categories from
|
||||
* the same package. This is useful for specialized output that is either
|
||||
* too verbose or otherwise optional or unnecessary to see all the time.
|
||||
* @param clazz the class for which to create a logger
|
||||
* @return a logger for the hidden category ("_" + fully-qualified class name)
|
||||
* @return a Log with the category {@code "_" + fully-qualified class name}
|
||||
*/
|
||||
public static Log getHiddenLog(Class<?> clazz) {
|
||||
return LogFactory.getLog("_" + clazz.getName());
|
||||
return getHiddenLog(clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "hidden" logger with a category name prefixed with "_", thus
|
||||
* precluding it from being enabled together with other log categories from
|
||||
* the same package. This is useful for specialized output that is either
|
||||
* too verbose or otherwise optional or unnecessary to see all the time.
|
||||
* @param category the log category to use
|
||||
* @return a Log with the category {@code "_" + category}
|
||||
* @since 5.3.5
|
||||
*/
|
||||
public static Log getHiddenLog(String category) {
|
||||
return LogFactory.getLog("_" + category);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Spring's repackaging of
|
||||
* <a href="http://objenesis.org">Objenesis 3.0</a>
|
||||
* <a href="http://objenesis.org">Objenesis 3.2</a>
|
||||
* (with SpringObjenesis entry point; for internal use only).
|
||||
*
|
||||
* <p>This repackaging technique avoids any potential conflicts with
|
||||
|
||||
@@ -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.
|
||||
@@ -384,7 +384,7 @@ public abstract class ReflectionUtils {
|
||||
* @throws IllegalStateException if introspection fails
|
||||
*/
|
||||
public static Method[] getAllDeclaredMethods(Class<?> leafClass) {
|
||||
final List<Method> methods = new ArrayList<>(32);
|
||||
final List<Method> methods = new ArrayList<>(20);
|
||||
doWithMethods(leafClass, methods::add);
|
||||
return methods.toArray(EMPTY_METHOD_ARRAY);
|
||||
}
|
||||
@@ -410,7 +410,7 @@ public abstract class ReflectionUtils {
|
||||
* @since 5.2
|
||||
*/
|
||||
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass, @Nullable MethodFilter mf) {
|
||||
final List<Method> methods = new ArrayList<>(32);
|
||||
final List<Method> methods = new ArrayList<>(20);
|
||||
doWithMethods(leafClass, method -> {
|
||||
boolean knownSignature = false;
|
||||
Method methodBeingOverriddenWithCovariantReturnType = null;
|
||||
@@ -625,6 +625,7 @@ public abstract class ReflectionUtils {
|
||||
* <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}.
|
||||
* @param field the field to set
|
||||
* @param target the target object on which to set the field
|
||||
* (or {@code null} for a static field)
|
||||
* @param value the value to set (may be {@code null})
|
||||
*/
|
||||
public static void setField(Field field, @Nullable Object target, @Nullable Object value) {
|
||||
@@ -644,6 +645,7 @@ public abstract class ReflectionUtils {
|
||||
* <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}.
|
||||
* @param field the field to get
|
||||
* @param target the target object from which to get the field
|
||||
* (or {@code null} for a static field)
|
||||
* @return the field's current value
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user