mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
170 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 |
+29
-31
@@ -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.31' 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.31" 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.2"
|
||||
mavenBom "io.netty:netty-bom:4.1.60.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.5"
|
||||
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.38.v20210224"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.30"
|
||||
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.11"
|
||||
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.4") {
|
||||
dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") {
|
||||
exclude group: "stax", name: "stax-api"
|
||||
}
|
||||
dependency "com.google.code.gson:gson:2.8.6"
|
||||
dependency "com.google.protobuf:protobuf-java-util:3.15.5"
|
||||
dependency "com.google.protobuf:protobuf-java-util:3.15.8"
|
||||
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
|
||||
dependency("com.thoughtworks.xstream:xstream:1.4.15") {
|
||||
dependency("com.thoughtworks.xstream:xstream:1.4.16") {
|
||||
exclude group: "xpp3", name: "xpp3_min"
|
||||
exclude group: "xmlpull", name: "xmlpull"
|
||||
}
|
||||
@@ -90,14 +92,10 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.28"
|
||||
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.1') {
|
||||
entry 'kotlinx-serialization-core'
|
||||
entry 'kotlinx-serialization-json'
|
||||
}
|
||||
|
||||
dependency "com.h2database:h2:1.4.200"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.0"
|
||||
dependency "com.github.librepdf:openpdf:1.3.25"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.1"
|
||||
dependency "com.github.librepdf:openpdf:1.3.26"
|
||||
dependency "com.rometools:rome:1.15.0"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.3"
|
||||
@@ -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.29.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.5.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"
|
||||
@@ -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.8.0') {
|
||||
dependencySet(group: 'org.mockito', version: '3.9.0') {
|
||||
entry('mockito-core') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
@@ -206,10 +204,10 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "io.mockk:mockk:1.10.2"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.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") {
|
||||
@@ -318,7 +316,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
kotlinOptions {
|
||||
languageVersion = "1.3"
|
||||
apiVersion = "1.3"
|
||||
freeCompilerArgs = ["-Xjsr305=strict"]
|
||||
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
@@ -338,7 +336,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.41"
|
||||
toolVersion = "8.42"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ rm -rf /var/lib/apt/lists/*
|
||||
|
||||
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.3.0/concourse-release-scripts-0.3.0.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
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ github-repo: "https://github.com/spring-projects/spring-framework.git"
|
||||
github-repo-name: "spring-projects/spring-framework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "master"
|
||||
branch: "main"
|
||||
milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
|
||||
+59
-11
@@ -9,8 +9,8 @@ anchors:
|
||||
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_USER_TOKEN: ((sonatype-user-token))
|
||||
SONATYPE_PASSWORD_TOKEN: ((sonatype-user-token-password))
|
||||
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
|
||||
@@ -18,9 +18,6 @@ anchors:
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
@@ -54,6 +51,11 @@ resource_types:
|
||||
source:
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: pull-request
|
||||
type: registry-image
|
||||
source:
|
||||
repository: teliaoss/github-pr-resource
|
||||
tag: v0.23.0
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -93,6 +95,14 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: git-pull-request
|
||||
type: pull-request
|
||||
icon: source-pull
|
||||
source:
|
||||
access_token: ((github-ci-pull-request-token))
|
||||
repository: ((github-repo-name))
|
||||
base_branch: ((branch))
|
||||
ignore_paths: ["ci/*"]
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
@@ -162,7 +172,10 @@ jobs:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
<<: *build-project-task-params
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
@@ -217,10 +230,11 @@ jobs:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
MAIN_TOOLCHAIN: 8
|
||||
TEST_TOOLCHAIN: 11
|
||||
<<: *build-project-task-params
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
@@ -244,10 +258,11 @@ jobs:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
MAIN_TOOLCHAIN: 8
|
||||
TEST_TOOLCHAIN: 15
|
||||
<<: *build-project-task-params
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk15-build
|
||||
@@ -257,6 +272,37 @@ jobs:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk15-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: build-pull-requests
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
resource: git-pull-request
|
||||
trigger: true
|
||||
version: every
|
||||
- do:
|
||||
- put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: pending
|
||||
- task: build-pr
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-pr.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_success:
|
||||
put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: success
|
||||
on_failure:
|
||||
put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: failure
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -376,7 +422,7 @@ jobs:
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: ci-image
|
||||
@@ -412,3 +458,5 @@ groups:
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-ci-images"]
|
||||
- name: "pull-requests"
|
||||
jobs: [ "build-pull-requests" ]
|
||||
|
||||
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,6 +4,6 @@ set -e
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 \
|
||||
-PmainToolchain=$MAIN_TOOLCHAIN -PtestToolchain=$TEST_TOOLCHAIN --no-daemon --max-workers=4 check
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.6'
|
||||
|
||||
@@ -10,8 +10,8 @@ params:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
SONATYPE_USER_TOKEN:
|
||||
SONATYPE_PASSWORD_TOKEN:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE_ID:
|
||||
run:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.5-SNAPSHOT
|
||||
version=5.3.7
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ apply plugin: 'org.springframework.build.optional-dependencies'
|
||||
// Uncomment the following for Shadow support in the jmhJar block.
|
||||
// Currently commented out due to ZipException: archive is not a ZIP archive
|
||||
// apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'me.champeau.gradle.jmh'
|
||||
apply plugin: 'me.champeau.jmh'
|
||||
apply from: "$rootDir/gradle/publications.gradle"
|
||||
|
||||
dependencies {
|
||||
jmh 'org.openjdk.jmh:jmh-core:1.25'
|
||||
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.25'
|
||||
jmh 'org.openjdk.jmh:jmh-core:1.28'
|
||||
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.28'
|
||||
jmh 'net.sf.jopt-simple:jopt-simple:4.6'
|
||||
}
|
||||
|
||||
|
||||
+76
-21
@@ -11,6 +11,8 @@
|
||||
* <li>a JDK11 toolchain for compiling and running the test SourceSet
|
||||
* </ul>
|
||||
*
|
||||
* By default, the build will fall back to using the current JDK and 1.8 language level for all sourceSets.
|
||||
*
|
||||
* Gradle will automatically detect JDK distributions in well-known locations.
|
||||
* The following command will list the detected JDKs on the host.
|
||||
* {@code
|
||||
@@ -23,21 +25,41 @@
|
||||
* /opt/openjdk/java11
|
||||
* $ echo JDK15
|
||||
* /opt/openjdk/java15
|
||||
* $ ./gradlew -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 check
|
||||
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 check
|
||||
* }
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
def mainToolchain = 'mainToolchain'
|
||||
def testToolchain = 'testToolchain'
|
||||
|
||||
def mainToolchainConfigured() {
|
||||
return project.hasProperty('mainToolchain') && project.mainToolchain
|
||||
}
|
||||
|
||||
def testToolchainConfigured() {
|
||||
return project.hasProperty('testToolchain') && project.testToolchain
|
||||
}
|
||||
|
||||
def mainToolchainLanguageVersion() {
|
||||
if (mainToolchainConfigured()) {
|
||||
return JavaLanguageVersion.of(project.mainToolchain.toString())
|
||||
}
|
||||
return JavaLanguageVersion.of(8)
|
||||
}
|
||||
|
||||
def testToolchainLanguageVersion() {
|
||||
if (testToolchainConfigured()) {
|
||||
return JavaLanguageVersion.of(project.testToolchain.toString())
|
||||
}
|
||||
return mainToolchainLanguageVersion()
|
||||
}
|
||||
|
||||
plugins.withType(JavaPlugin) {
|
||||
// Configure the Java Toolchain if the 'mainToolchain' property is defined
|
||||
if (project.hasProperty(mainToolchain)) {
|
||||
def mainLanguageVersion = JavaLanguageVersion.of(project.property(mainToolchain).toString())
|
||||
// Configure the Java Toolchain if the 'mainToolchain' is configured
|
||||
if (mainToolchainConfigured()) {
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = mainLanguageVersion
|
||||
languageVersion = mainToolchainLanguageVersion()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +70,8 @@ plugins.withType(JavaPlugin) {
|
||||
}
|
||||
}
|
||||
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
|
||||
if (project.hasProperty(testToolchain)) {
|
||||
def testLanguageVersion = JavaLanguageVersion.of(project.property(testToolchain).toString());
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
|
||||
javaCompiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
@@ -65,17 +87,17 @@ plugins.withType(JavaPlugin) {
|
||||
|
||||
plugins.withType(GroovyPlugin) {
|
||||
// Fallback to JDK8
|
||||
if (!project.hasProperty(mainToolchain)) {
|
||||
if (!mainToolchainConfigured()) {
|
||||
compileGroovy {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
|
||||
pluginManager.withPlugin("kotlin") {
|
||||
if (project.hasProperty(mainToolchain)) {
|
||||
def mainLanguageVersion = JavaLanguageVersion.of(project.property(mainToolchain).toString());
|
||||
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
|
||||
if (mainToolchainConfigured()) {
|
||||
def mainLanguageVersion = mainToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = mainLanguageVersion
|
||||
}
|
||||
@@ -96,15 +118,21 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Fallback to JDK8
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty(testToolchain)) {
|
||||
def testLanguageVersion = JavaLanguageVersion.of(project.property(testToolchain).toString());
|
||||
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
@@ -117,11 +145,38 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
// 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
-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('mainToolchain')) {
|
||||
value("Main toolchain", 'JDK' + settings.gradle.rootProject.getProperty('mainToolchain'))
|
||||
}
|
||||
if (settings.gradle.rootProject.hasProperty('testToolchain')) {
|
||||
value("Test toolchain", 'JDK' + settings.gradle.rootProject.getProperty('testToolchain'))
|
||||
}
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
buildDir.mkdirs()
|
||||
new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)"
|
||||
|
||||
+1
-1
@@ -510,7 +510,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
List<Object> allInterceptors = new ArrayList<>();
|
||||
if (specificInterceptors != null) {
|
||||
if (specificInterceptors.length > 0) {
|
||||
// specificInterceptors may equals PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
|
||||
// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
|
||||
allInterceptors.addAll(Arrays.asList(specificInterceptors));
|
||||
}
|
||||
if (commonInterceptors.length > 0) {
|
||||
|
||||
+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
|
||||
|
||||
+10
-7
@@ -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,8 +101,7 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (resource == null) {
|
||||
setValue(null);
|
||||
}
|
||||
else if (!resource.isFile() && !resource.exists() && nioPathCandidate) {
|
||||
// Prefer getFile().toPath() below for non-existent file handles
|
||||
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);
|
||||
|
||||
+21
-25
@@ -39,8 +39,7 @@ public class PathEditorTests {
|
||||
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();
|
||||
}
|
||||
@@ -57,11 +56,9 @@ public class PathEditorTests {
|
||||
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
|
||||
@@ -69,11 +66,9 @@ public class PathEditorTests {
|
||||
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
|
||||
@@ -81,23 +76,26 @@ public class PathEditorTests {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("C:\\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 testWindowsAbsoluteFilePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition1 = value instanceof Path;
|
||||
assertThat(condition1).isTrue();
|
||||
Path path = (Path) value;
|
||||
boolean condition = !path.toFile().exists();
|
||||
assertThat(condition).isTrue();
|
||||
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
|
||||
@@ -107,8 +105,7 @@ public class PathEditorTests {
|
||||
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();
|
||||
@@ -126,8 +123,7 @@ public class PathEditorTests {
|
||||
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;
|
||||
|
||||
+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);
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
|
||||
* Can be applied to {@link java.util.Date}, {@link java.util.Calendar}, {@link Long} (for
|
||||
* millisecond timestamps) as well as JSR-310 {@code java.time} and Joda-Time value types.
|
||||
* millisecond timestamps) as well as JSR-310 {@code java.time} value types.
|
||||
*
|
||||
* <p>For style-based formatting, set the {@link #style} attribute to the desired style pattern code.
|
||||
* The first character of the code is the date style, and the second character is the time style.
|
||||
|
||||
@@ -219,9 +219,11 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
}
|
||||
if (this.source != null) {
|
||||
throw new ParseException(
|
||||
ParseException parseException = new ParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
parseException.initCause(ex);
|
||||
throw parseException;
|
||||
}
|
||||
// else rethrow original exception
|
||||
throw ex;
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
|
||||
if (this.source != null) {
|
||||
throw new DateTimeParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
text, ex.getErrorIndex());
|
||||
text, ex.getErrorIndex(), ex);
|
||||
}
|
||||
// else rethrow original exception
|
||||
throw ex;
|
||||
|
||||
+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-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});
|
||||
}
|
||||
|
||||
}
|
||||
+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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-1
@@ -119,6 +119,39 @@ public class DateFormattingTests {
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@Test
|
||||
void styleDateWithInvalidFormat() {
|
||||
String propertyName = "styleDate";
|
||||
String propertyValue = "99/01/01";
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(1);
|
||||
FieldError fieldError = bindingResult.getFieldError(propertyName);
|
||||
TypeMismatchException exception = fieldError.unwrap(TypeMismatchException.class);
|
||||
assertThat(exception)
|
||||
.hasMessageContaining("for property 'styleDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).getCause()
|
||||
.hasMessageContaining("for value '99/01/01'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).getCause()
|
||||
.hasMessageContaining("Parse attempt failed for value [99/01/01]")
|
||||
.hasCauseInstanceOf(ParseException.class).getCause()
|
||||
// Unable to parse date time value "99/01/01" using configuration from
|
||||
// @org.springframework.format.annotation.DateTimeFormat(pattern=, style=S-, iso=NONE, fallbackPatterns=[])
|
||||
// We do not check "fallbackPatterns=[]", since the array representation in the toString()
|
||||
// implementation for annotations changed from [] to {} in Java 9. In addition, strings
|
||||
// are enclosed in double quotes beginning with Java 9. Thus, we cannot check directly
|
||||
// for the presence of "style=S-".
|
||||
.hasMessageContainingAll(
|
||||
"Unable to parse date time value \"99/01/01\" using configuration from",
|
||||
"@org.springframework.format.annotation.DateTimeFormat",
|
||||
"style=", "S-", "iso=NONE")
|
||||
.hasCauseInstanceOf(ParseException.class).getCause()
|
||||
.hasMessageStartingWith("Unparseable date: \"99/01/01\"")
|
||||
.hasNoCause();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindDateArray() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
@@ -330,7 +363,10 @@ public class DateFormattingTests {
|
||||
.hasMessageContainingAll(
|
||||
"Unable to parse date time value \"210302\" using configuration from",
|
||||
"@org.springframework.format.annotation.DateTimeFormat",
|
||||
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd");
|
||||
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd")
|
||||
.hasCauseInstanceOf(ParseException.class).getCause()
|
||||
.hasMessageStartingWith("Unparseable date: \"210302\"")
|
||||
.hasNoCause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-2
@@ -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;
|
||||
@@ -320,6 +321,36 @@ class DateTimeFormattingTests {
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isoLocalDateWithInvalidFormat() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
String propertyName = "isoLocalDate";
|
||||
propertyValues.add(propertyName, "2009-31-10");
|
||||
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 '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
|
||||
void testBindISOTime() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
@@ -519,9 +550,12 @@ class DateTimeFormattingTests {
|
||||
.hasMessageContainingAll(
|
||||
"Unable to parse date time value \"210302\" using configuration from",
|
||||
"@org.springframework.format.annotation.DateTimeFormat",
|
||||
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd");
|
||||
"yyyy-MM-dd", "M/d/yy", "yyyyMMdd", "yyyy.MM.dd")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).getCause()
|
||||
.hasMessageStartingWith("Text '210302'")
|
||||
.hasNoCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class DateTimeBean {
|
||||
|
||||
+26
-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.
|
||||
@@ -1261,6 +1261,31 @@ class CronExpressionTests {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -292,7 +292,7 @@ class StaxEventXMLReader extends AbstractStaxXMLReader {
|
||||
|
||||
private void handleDtd(DTD dtd) throws SAXException {
|
||||
if (getLexicalHandler() != null) {
|
||||
javax.xml.stream.Location location = dtd.getLocation();
|
||||
Location location = dtd.getLocation();
|
||||
getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
|
||||
}
|
||||
if (getLexicalHandler() != null) {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ operator fun PropertyResolver.get(key: String) : String? = getProperty(key)
|
||||
|
||||
/**
|
||||
* Extension for [PropertyResolver.getProperty] providing a `getProperty<Foo>(...)`
|
||||
* variant returning a nullable [String].
|
||||
* variant returning a nullable `Foo`.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.1
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,6 +99,12 @@ class LinkedCaseInsensitiveMapTests {
|
||||
assertThat(map.computeIfAbsent("key", key2 -> "value1")).isEqualTo("value3");
|
||||
assertThat(map.computeIfAbsent("KEY", key1 -> "value2")).isEqualTo("value3");
|
||||
assertThat(map.computeIfAbsent("Key", key -> "value3")).isEqualTo("value3");
|
||||
|
||||
assertThat(map.put("null", null)).isNull();
|
||||
assertThat(map.putIfAbsent("NULL", "value")).isNull();
|
||||
assertThat(map.put("null", null)).isEqualTo("value");
|
||||
assertThat(map.computeIfAbsent("NULL", s -> "value")).isEqualTo("value");
|
||||
assertThat(map.get("null")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.core
|
||||
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class KotlinBridgeMethodResolverTests {
|
||||
|
||||
@Test
|
||||
fun findBridgedMethod() {
|
||||
val unbridged = GenericRepository::class.java.getDeclaredMethod("delete", Int::class.java)
|
||||
val bridged = GenericRepository::class.java.getDeclaredMethod("delete", Any::class.java)
|
||||
Assertions.assertThat(unbridged.isBridge).isFalse
|
||||
Assertions.assertThat(bridged.isBridge).isTrue
|
||||
|
||||
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
|
||||
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun findBridgedMethodWithArrays() {
|
||||
val unbridged = GenericRepository::class.java.getDeclaredMethod("delete", Array<Int>::class.java)
|
||||
val bridged = GenericRepository::class.java.getDeclaredMethod("delete", Array<Any>::class.java)
|
||||
Assertions.assertThat(unbridged.isBridge).isFalse
|
||||
Assertions.assertThat(bridged.isBridge).isTrue
|
||||
|
||||
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(unbridged)).`as`("Unbridged method not returned directly").isEqualTo(unbridged)
|
||||
Assertions.assertThat(BridgeMethodResolver.findBridgedMethod(bridged)).`as`("Incorrect bridged method returned").isEqualTo(unbridged)
|
||||
}
|
||||
}
|
||||
|
||||
interface GenericInterface<ID> {
|
||||
fun delete(id: ID)
|
||||
fun delete(ids: Array<ID>)
|
||||
}
|
||||
|
||||
abstract class AbstractGenericClass<ID> : GenericInterface<ID> {
|
||||
|
||||
override fun delete(id: ID) {
|
||||
}
|
||||
|
||||
override fun delete(ids: Array<ID>) {
|
||||
}
|
||||
}
|
||||
|
||||
class GenericRepository : AbstractGenericClass<Int>() {
|
||||
|
||||
override fun delete(id: Int) {
|
||||
error("gotcha")
|
||||
}
|
||||
|
||||
override fun delete(ids: Array<Int>) {
|
||||
error("gotcha")
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -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,6 +20,7 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -89,8 +90,12 @@ public abstract class AbstractDataBufferAllocatingTests {
|
||||
}
|
||||
|
||||
protected Consumer<DataBuffer> stringConsumer(String expected) {
|
||||
return stringConsumer(expected, UTF_8);
|
||||
}
|
||||
|
||||
protected Consumer<DataBuffer> stringConsumer(String expected, Charset charset) {
|
||||
return dataBuffer -> {
|
||||
String value = dataBuffer.toString(UTF_8);
|
||||
String value = dataBuffer.toString(charset);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
assertThat(value).isEqualTo(expected);
|
||||
};
|
||||
|
||||
+2
-1
@@ -136,6 +136,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
|
||||
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
|
||||
String className = "spel/Ex" + getNextSuffix();
|
||||
String evaluationContextClass = "org/springframework/expression/EvaluationContext";
|
||||
ClassWriter cw = new ExpressionClassWriter();
|
||||
cw.visit(V1_8, ACC_PUBLIC, className, null, "org/springframework/expression/spel/CompiledExpression", null);
|
||||
|
||||
@@ -151,7 +152,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
// Create getValue() method
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "getValue",
|
||||
"(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
|
||||
"(Ljava/lang/Object;L" + evaluationContextClass + ";)Ljava/lang/Object;", null,
|
||||
new String[] {"org/springframework/expression/EvaluationException"});
|
||||
mv.visitCode();
|
||||
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,7 +49,7 @@ public abstract class AbstractExpressionTests {
|
||||
|
||||
/**
|
||||
* Evaluate an expression and check that the actual result matches the
|
||||
* expectedValue and the class of the result matches the expectedClassOfResult.
|
||||
* expectedValue and the class of the result matches the expectedResultType.
|
||||
* @param expression the expression to evaluate
|
||||
* @param expectedValue the expected result for evaluating the expression
|
||||
* @param expectedResultType the expected class of the evaluation result
|
||||
@@ -106,15 +106,15 @@ public abstract class AbstractExpressionTests {
|
||||
|
||||
/**
|
||||
* Evaluate an expression and check that the actual result matches the
|
||||
* expectedValue and the class of the result matches the expectedClassOfResult.
|
||||
* expectedValue and the class of the result matches the expectedResultType.
|
||||
* This method can also check if the expression is writable (for example,
|
||||
* it is a variable or property reference).
|
||||
* @param expression the expression to evaluate
|
||||
* @param expectedValue the expected result for evaluating the expression
|
||||
* @param expectedClassOfResult the expected class of the evaluation result
|
||||
* @param expectedResultType the expected class of the evaluation result
|
||||
* @param shouldBeWritable should the parsed expression be writable?
|
||||
*/
|
||||
public void evaluate(String expression, Object expectedValue, Class<?> expectedClassOfResult, boolean shouldBeWritable) {
|
||||
public void evaluate(String expression, Object expectedValue, Class<?> expectedResultType, boolean shouldBeWritable) {
|
||||
Expression expr = parser.parseExpression(expression);
|
||||
assertThat(expr).as("expression").isNotNull();
|
||||
if (DEBUG) {
|
||||
@@ -134,7 +134,7 @@ public abstract class AbstractExpressionTests {
|
||||
else {
|
||||
assertThat(value).as("Did not get expected value for expression '" + expression + "'.").isEqualTo(expectedValue);
|
||||
}
|
||||
assertThat(expectedClassOfResult.equals(resultType)).as("Type of the result was not as expected. Expected '" + expectedClassOfResult +
|
||||
assertThat(expectedResultType.equals(resultType)).as("Type of the result was not as expected. Expected '" + expectedResultType +
|
||||
"' but result was of type '" + resultType + "'").isTrue();
|
||||
|
||||
assertThat(expr.isWritable(context)).as("isWritable").isEqualTo(shouldBeWritable);
|
||||
|
||||
+60
-119
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -40,98 +39,79 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SelectionAndProjectionTests {
|
||||
class SelectionAndProjectionTests {
|
||||
|
||||
@Test
|
||||
public void selectionWithList() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void selectionWithList() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(5);
|
||||
assertThat(list.get(0)).isEqualTo(0);
|
||||
assertThat(list.get(1)).isEqualTo(1);
|
||||
assertThat(list.get(2)).isEqualTo(2);
|
||||
assertThat(list.get(3)).isEqualTo(3);
|
||||
assertThat(list.get(4)).isEqualTo(4);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(0, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectFirstItemInList() throws Exception {
|
||||
void selectFirstItemInList() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectLastItemInList() throws Exception {
|
||||
void selectLastItemInList() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectionWithSet() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void selectionWithSet() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(5);
|
||||
assertThat(list.get(0)).isEqualTo(0);
|
||||
assertThat(list.get(1)).isEqualTo(1);
|
||||
assertThat(list.get(2)).isEqualTo(2);
|
||||
assertThat(list.get(3)).isEqualTo(3);
|
||||
assertThat(list.get(4)).isEqualTo(4);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(0, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectFirstItemInSet() throws Exception {
|
||||
void selectFirstItemInSet() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectLastItemInSet() throws Exception {
|
||||
void selectLastItemInSet() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectionWithIterable() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void selectionWithIterable() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new IterableTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(5);
|
||||
assertThat(list.get(0)).isEqualTo(0);
|
||||
assertThat(list.get(1)).isEqualTo(1);
|
||||
assertThat(list.get(2)).isEqualTo(2);
|
||||
assertThat(list.get(3)).isEqualTo(3);
|
||||
assertThat(list.get(4)).isEqualTo(4);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(0, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectionWithArray() throws Exception {
|
||||
void selectionWithArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
@@ -139,36 +119,29 @@ public class SelectionAndProjectionTests {
|
||||
TypedValue typedValue = new TypedValue(value);
|
||||
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
|
||||
Integer[] array = (Integer[]) value;
|
||||
assertThat(array.length).isEqualTo(5);
|
||||
assertThat(array[0]).isEqualTo(0);
|
||||
assertThat(array[1]).isEqualTo(1);
|
||||
assertThat(array[2]).isEqualTo(2);
|
||||
assertThat(array[3]).isEqualTo(3);
|
||||
assertThat(array[4]).isEqualTo(4);
|
||||
assertThat(array).containsExactly(0, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectFirstItemInArray() throws Exception {
|
||||
void selectFirstItemInArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectLastItemInArray() throws Exception {
|
||||
void selectLastItemInArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectionWithPrimitiveArray() throws Exception {
|
||||
void selectionWithPrimitiveArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("ints.?[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
@@ -176,51 +149,41 @@ public class SelectionAndProjectionTests {
|
||||
TypedValue typedValue = new TypedValue(value);
|
||||
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
|
||||
Integer[] array = (Integer[]) value;
|
||||
assertThat(array.length).isEqualTo(5);
|
||||
assertThat(array[0]).isEqualTo(0);
|
||||
assertThat(array[1]).isEqualTo(1);
|
||||
assertThat(array[2]).isEqualTo(2);
|
||||
assertThat(array[3]).isEqualTo(3);
|
||||
assertThat(array[4]).isEqualTo(4);
|
||||
assertThat(array).containsExactly(0, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectFirstItemInPrimitiveArray() throws Exception {
|
||||
void selectFirstItemInPrimitiveArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectLastItemInPrimitiveArray() throws Exception {
|
||||
void selectLastItemInPrimitiveArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this<5]");
|
||||
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(Integer.class);
|
||||
assertThat(value).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void selectionWithMap() {
|
||||
void selectionWithMap() {
|
||||
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
Expression exp = parser.parseExpression("colors.?[key.startsWith('b')]");
|
||||
|
||||
Map<String, String> colorsMap = (Map<String, String>) exp.getValue(context);
|
||||
assertThat(colorsMap.size()).isEqualTo(3);
|
||||
assertThat(colorsMap.containsKey("beige")).isTrue();
|
||||
assertThat(colorsMap.containsKey("blue")).isTrue();
|
||||
assertThat(colorsMap.containsKey("brown")).isTrue();
|
||||
assertThat(colorsMap).containsOnlyKeys("beige", "blue", "brown");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void selectFirstItemInMap() {
|
||||
void selectFirstItemInMap() {
|
||||
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
@@ -232,7 +195,7 @@ public class SelectionAndProjectionTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void selectLastItemInMap() {
|
||||
void selectLastItemInMap() {
|
||||
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
@@ -243,52 +206,43 @@ public class SelectionAndProjectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectionWithList() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void projectionWithList() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setVariable("testList", IntegerTestBean.createList());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(3);
|
||||
assertThat(list.get(0)).isEqualTo(5);
|
||||
assertThat(list.get(1)).isEqualTo(6);
|
||||
assertThat(list.get(2)).isEqualTo(7);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(5, 6, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectionWithSet() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void projectionWithSet() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setVariable("testList", IntegerTestBean.createSet());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(3);
|
||||
assertThat(list.get(0)).isEqualTo(5);
|
||||
assertThat(list.get(1)).isEqualTo(6);
|
||||
assertThat(list.get(2)).isEqualTo(7);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(5, 6, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectionWithIterable() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
void projectionWithIterable() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setVariable("testList", IntegerTestBean.createIterable());
|
||||
Object value = expression.getValue(context);
|
||||
boolean condition = value instanceof List;
|
||||
assertThat(condition).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list.size()).isEqualTo(3);
|
||||
assertThat(list.get(0)).isEqualTo(5);
|
||||
assertThat(list.get(1)).isEqualTo(6);
|
||||
assertThat(list.get(2)).isEqualTo(7);
|
||||
assertThat(value).isInstanceOf(List.class);
|
||||
List<Integer> list = (List<Integer>) value;
|
||||
assertThat(list).containsExactly(5, 6, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectionWithArray() throws Exception {
|
||||
void projectionWithArray() throws Exception {
|
||||
Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]");
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setVariable("testArray", IntegerTestBean.createArray());
|
||||
@@ -297,10 +251,7 @@ public class SelectionAndProjectionTests {
|
||||
TypedValue typedValue = new TypedValue(value);
|
||||
assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Number.class);
|
||||
Number[] array = (Number[]) value;
|
||||
assertThat(array.length).isEqualTo(3);
|
||||
assertThat(array[0]).isEqualTo(5);
|
||||
assertThat(array[1]).isEqualTo(5.9f);
|
||||
assertThat(array[2]).isEqualTo(7);
|
||||
assertThat(array).containsExactly(5, 5.9f, 7);
|
||||
}
|
||||
|
||||
|
||||
@@ -347,12 +298,7 @@ public class SelectionAndProjectionTests {
|
||||
}
|
||||
|
||||
public Iterable<Integer> getIntegers() {
|
||||
return new Iterable<Integer>() {
|
||||
@Override
|
||||
public Iterator<Integer> iterator() {
|
||||
return integers.iterator();
|
||||
}
|
||||
};
|
||||
return integers::iterator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,12 +375,7 @@ public class SelectionAndProjectionTests {
|
||||
|
||||
static Iterable<IntegerTestBean> createIterable() {
|
||||
final Set<IntegerTestBean> set = createSet();
|
||||
return new Iterable<IntegerTestBean>() {
|
||||
@Override
|
||||
public Iterator<IntegerTestBean> iterator() {
|
||||
return set.iterator();
|
||||
}
|
||||
};
|
||||
return set::iterator;
|
||||
}
|
||||
|
||||
static IntegerTestBean[] createArray() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,15 +31,12 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
* entry for each column, with the column name as key.
|
||||
*
|
||||
* <p>The Map implementation to use and the key to use for each column
|
||||
* in the column Map can be customized through overriding
|
||||
* {@link #createColumnMap} and {@link #getColumnKey}, respectively.
|
||||
* in the column Map can be customized by overriding {@link #createColumnMap}
|
||||
* and {@link #getColumnKey}, respectively.
|
||||
*
|
||||
* <p><b>Note:</b> By default, ColumnMapRowMapper will try to build a linked Map
|
||||
* <p><b>Note:</b> By default, {@code ColumnMapRowMapper} will try to build a linked Map
|
||||
* with case-insensitive keys, to preserve column order as well as allow any
|
||||
* casing to be used for column names. This requires Commons Collections on the
|
||||
* classpath (which will be autodetected). Else, the fallback is a standard linked
|
||||
* HashMap, which will still preserve column order but requires the application
|
||||
* to specify the column names in the same casing as exposed by the driver.
|
||||
* casing to be used for column names.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
@@ -74,6 +71,7 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
|
||||
|
||||
/**
|
||||
* Determine the key to use for the given column in the column Map.
|
||||
* <p>By default, the supplied column name will be returned unmodified.
|
||||
* @param columnName the column name as returned by the ResultSet
|
||||
* @return the column key to use
|
||||
* @see java.sql.ResultSetMetaData#getColumnName
|
||||
@@ -86,9 +84,9 @@ public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {
|
||||
* Retrieve a JDBC object value for the specified column.
|
||||
* <p>The default implementation uses the {@code getObject} method.
|
||||
* Additionally, this implementation includes a "hack" to get around Oracle
|
||||
* returning a non standard object for their TIMESTAMP datatype.
|
||||
* @param rs is the ResultSet holding the data
|
||||
* @param index is the column index
|
||||
* returning a non standard object for their TIMESTAMP data type.
|
||||
* @param rs the ResultSet holding the data
|
||||
* @param index the column index
|
||||
* @return the Object returned
|
||||
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,9 @@ import java.sql.SQLException;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -50,7 +52,7 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
|
||||
private String[] constructorParameterNames;
|
||||
|
||||
@Nullable
|
||||
private Class<?>[] constructorParameterTypes;
|
||||
private TypeDescriptor[] constructorParameterTypes;
|
||||
|
||||
|
||||
/**
|
||||
@@ -75,9 +77,13 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
|
||||
super.initialize(mappedClass);
|
||||
|
||||
this.mappedConstructor = BeanUtils.getResolvableConstructor(mappedClass);
|
||||
if (this.mappedConstructor.getParameterCount() > 0) {
|
||||
int paramCount = this.mappedConstructor.getParameterCount();
|
||||
if (paramCount > 0) {
|
||||
this.constructorParameterNames = BeanUtils.getParameterNames(this.mappedConstructor);
|
||||
this.constructorParameterTypes = this.mappedConstructor.getParameterTypes();
|
||||
this.constructorParameterTypes = new TypeDescriptor[paramCount];
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
this.constructorParameterTypes[i] = new TypeDescriptor(new MethodParameter(this.mappedConstructor, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +96,9 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
|
||||
args = new Object[this.constructorParameterNames.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String name = underscoreName(this.constructorParameterNames[i]);
|
||||
Class<?> type = this.constructorParameterTypes[i];
|
||||
args[i] = tc.convertIfNecessary(getColumnValue(rs, rs.findColumn(name), type), type);
|
||||
TypeDescriptor td = this.constructorParameterTypes[i];
|
||||
Object value = getColumnValue(rs, rs.findColumn(name), td.getType());
|
||||
args[i] = tc.convertIfNecessary(value, td.getType(), td);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+10
-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.
|
||||
@@ -40,22 +40,27 @@ import org.springframework.jdbc.support.JdbcUtils;
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
|
||||
* <pre class="code">
|
||||
* create table tab (id int unsigned not null primary key, text varchar(100));
|
||||
* create table tab_sequence (value int not null);
|
||||
* insert into tab_sequence values(0);</pre>
|
||||
*
|
||||
* If "cacheSize" is set, the intermediate values are served without querying the
|
||||
* <p>If {@code cacheSize} is set, the intermediate values are served without querying the
|
||||
* database. If the server or your application is stopped or crashes or a transaction
|
||||
* is rolled back, the unused values will never be served. The maximum hole size in
|
||||
* numbering is consequently the value of cacheSize.
|
||||
* numbering is consequently the value of {@code cacheSize}.
|
||||
*
|
||||
* <p>It is possible to avoid acquiring a new connection for the incrementer by setting the
|
||||
* "useNewConnection" property to false. In this case you <i>MUST</i> use a non-transactional
|
||||
* storage engine like MYISAM when defining the incrementer table.
|
||||
*
|
||||
* <p>As of Spring Framework 5.3.7, {@code MySQLMaxValueIncrementer} is compatible with
|
||||
* <a href="https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates">MySQL safe updates mode</a>.
|
||||
*
|
||||
* @author Jean-Pierre Pawlak
|
||||
* @author Thomas Risberg
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer {
|
||||
|
||||
@@ -141,7 +146,7 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
|
||||
String columnName = getColumnName();
|
||||
try {
|
||||
stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
|
||||
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
|
||||
" = last_insert_id(" + columnName + " + " + getCacheSize() + ") limit 1");
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -135,6 +135,7 @@ public abstract class AbstractRowMapperTests {
|
||||
given(resultSet.getObject(anyInt(), any(Class.class))).willThrow(new SQLFeatureNotSupportedException());
|
||||
given(resultSet.getDate(3)).willReturn(new java.sql.Date(1221222L));
|
||||
given(resultSet.getBigDecimal(4)).willReturn(new BigDecimal("1234.56"));
|
||||
given(resultSet.getObject(4)).willReturn(new BigDecimal("1234.56"));
|
||||
given(resultSet.wasNull()).willReturn(type == MockType.TWO);
|
||||
|
||||
given(resultSetMetaData.getColumnCount()).willReturn(4);
|
||||
|
||||
+21
-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.
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
package org.springframework.jdbc.core;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jdbc.core.test.ConstructorPerson;
|
||||
import org.springframework.jdbc.core.test.ConstructorPersonWithGenerics;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -42,4 +46,20 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests {
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticQueryWithDataClassAndGenerics() throws Exception {
|
||||
Mock mock = new Mock();
|
||||
List<ConstructorPersonWithGenerics> result = mock.getJdbcTemplate().query(
|
||||
"select name, age, birth_date, balance from people",
|
||||
new DataClassRowMapper<>(ConstructorPersonWithGenerics.class));
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
ConstructorPersonWithGenerics person = result.get(0);
|
||||
assertThat(person.name()).isEqualTo("Bubba");
|
||||
assertThat(person.age()).isEqualTo(22L);
|
||||
assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
|
||||
assertThat(person.balance()).isEqualTo(Collections.singletonList(new BigDecimal("1234.56")));
|
||||
|
||||
mock.verifyClosed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,13 +24,13 @@ import java.util.Date;
|
||||
*/
|
||||
public class ConstructorPerson {
|
||||
|
||||
private String name;
|
||||
private final String name;
|
||||
|
||||
private long age;
|
||||
private final long age;
|
||||
|
||||
private java.util.Date birth_date;
|
||||
private final Date birth_date;
|
||||
|
||||
private BigDecimal balance;
|
||||
private final BigDecimal balance;
|
||||
|
||||
|
||||
public ConstructorPerson(String name, long age, Date birth_date, BigDecimal balance) {
|
||||
@@ -42,19 +42,19 @@ public class ConstructorPerson {
|
||||
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public long age() {
|
||||
return age;
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public Date birth_date() {
|
||||
return birth_date;
|
||||
return this.birth_date;
|
||||
}
|
||||
|
||||
public BigDecimal balance() {
|
||||
return balance;
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jdbc.core.test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ConstructorPersonWithGenerics {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final long age;
|
||||
|
||||
private final Date birth_date;
|
||||
|
||||
private final List<BigDecimal> balance;
|
||||
|
||||
|
||||
public ConstructorPersonWithGenerics(String name, long age, Date birth_date, List<BigDecimal> balance) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.birth_date = birth_date;
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public long age() {
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public Date birth_date() {
|
||||
return this.birth_date;
|
||||
}
|
||||
|
||||
public List<BigDecimal> balance() {
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
}
|
||||
+13
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,7 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
|
||||
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
|
||||
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
|
||||
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
|
||||
@@ -38,10 +39,13 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DataFieldMaxValueIncrementer} implementations.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 27.02.2004
|
||||
*/
|
||||
public class DataFieldMaxValueIncrementerTests {
|
||||
class DataFieldMaxValueIncrementerTests {
|
||||
|
||||
private final DataSource dataSource = mock(DataSource.class);
|
||||
|
||||
@@ -53,7 +57,7 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void testHanaSequenceMaxValueIncrementer() throws SQLException {
|
||||
void hanaSequenceMaxValueIncrementer() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select myseq.nextval from dummy")).willReturn(resultSet);
|
||||
@@ -75,7 +79,7 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHsqlMaxValueIncrementer() throws SQLException {
|
||||
void hsqlMaxValueIncrementer() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
|
||||
@@ -105,7 +109,7 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
|
||||
void hsqlMaxValueIncrementerWithDeleteSpecificValues() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select max(identity()) from myseq")).willReturn(resultSet);
|
||||
@@ -136,7 +140,7 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMySQLMaxValueIncrementer() throws SQLException {
|
||||
void mySQLMaxValueIncrementer() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select last_insert_id()")).willReturn(resultSet);
|
||||
@@ -156,14 +160,14 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
assertThat(incrementer.nextStringValue()).isEqualTo("3");
|
||||
assertThat(incrementer.nextLongValue()).isEqualTo(4);
|
||||
|
||||
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2)");
|
||||
verify(statement, times(2)).executeUpdate("update myseq set seq = last_insert_id(seq + 2) limit 1");
|
||||
verify(resultSet, times(2)).close();
|
||||
verify(statement, times(2)).close();
|
||||
verify(connection, times(2)).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOracleSequenceMaxValueIncrementer() throws SQLException {
|
||||
void oracleSequenceMaxValueIncrementer() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select myseq.nextval from dual")).willReturn(resultSet);
|
||||
@@ -185,7 +189,7 @@ public class DataFieldMaxValueIncrementerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostgresSequenceMaxValueIncrementer() throws SQLException {
|
||||
void postgresSequenceMaxValueIncrementer() throws SQLException {
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
given(connection.createStatement()).willReturn(statement);
|
||||
given(statement.executeQuery("select nextval('myseq')")).willReturn(resultSet);
|
||||
|
||||
+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.
|
||||
@@ -179,6 +179,23 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a current session count, indicating the number of sessions currently
|
||||
* cached by this connection factory.
|
||||
* @since 5.3.7
|
||||
*/
|
||||
public int getCachedSessionCount() {
|
||||
int count = 0;
|
||||
synchronized (this.cachedSessions) {
|
||||
for (Deque<Session> sessionList : this.cachedSessions.values()) {
|
||||
synchronized (sessionList) {
|
||||
count += sessionList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the Session cache as well.
|
||||
*/
|
||||
|
||||
+24
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,6 +30,7 @@ import io.rsocket.transport.ClientTransport;
|
||||
import io.rsocket.transport.netty.client.TcpClientTransport;
|
||||
import io.rsocket.transport.netty.client.WebsocketClientTransport;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.Disposable;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -49,7 +50,7 @@ import org.springframework.util.MimeType;
|
||||
* @author Brian Clozel
|
||||
* @since 5.2
|
||||
*/
|
||||
public interface RSocketRequester {
|
||||
public interface RSocketRequester extends Disposable {
|
||||
|
||||
/**
|
||||
* Return the underlying {@link RSocketClient} used to make requests with.
|
||||
@@ -110,6 +111,27 @@ public interface RSocketRequester {
|
||||
*/
|
||||
RequestSpec metadata(Object metadata, @Nullable MimeType mimeType);
|
||||
|
||||
/**
|
||||
* Shortcut method that delegates to the same on the underlying
|
||||
* {@link #rsocketClient()} in order to close the connection from the
|
||||
* underlying transport and notify subscribers.
|
||||
* @since 5.3.7
|
||||
*/
|
||||
@Override
|
||||
default void dispose() {
|
||||
rsocketClient().dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method that delegates to the same on the underlying
|
||||
* {@link #rsocketClient()}.
|
||||
* @since 5.3.7
|
||||
*/
|
||||
@Override
|
||||
default boolean isDisposed() {
|
||||
return rsocketClient().isDisposed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a builder to create a client {@link RSocketRequester} by connecting
|
||||
* to an RSocket server.
|
||||
|
||||
+8
-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.
|
||||
@@ -42,10 +42,16 @@ public abstract class AbstractBrokerRegistration {
|
||||
private final List<String> destinationPrefixes;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new broker registration.
|
||||
* @param clientInboundChannel the inbound channel
|
||||
* @param clientOutboundChannel the outbound channel
|
||||
* @param destinationPrefixes the destination prefixes
|
||||
*/
|
||||
public AbstractBrokerRegistration(SubscribableChannel clientInboundChannel,
|
||||
MessageChannel clientOutboundChannel, @Nullable String[] destinationPrefixes) {
|
||||
|
||||
Assert.notNull(clientOutboundChannel, "'clientInboundChannel' must not be null");
|
||||
Assert.notNull(clientInboundChannel, "'clientInboundChannel' must not be null");
|
||||
Assert.notNull(clientOutboundChannel, "'clientOutboundChannel' must not be null");
|
||||
|
||||
this.clientInboundChannel = clientInboundChannel;
|
||||
|
||||
+11
-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.
|
||||
@@ -40,8 +40,16 @@ public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
|
||||
private String selectorHeaderName = "selector";
|
||||
|
||||
|
||||
public SimpleBrokerRegistration(SubscribableChannel inChannel, MessageChannel outChannel, String[] prefixes) {
|
||||
super(inChannel, outChannel, prefixes);
|
||||
/**
|
||||
* Create a new {@code SimpleBrokerRegistration}.
|
||||
* @param clientInboundChannel the inbound channel
|
||||
* @param clientOutboundChannel the outbound channel
|
||||
* @param destinationPrefixes the destination prefixes
|
||||
*/
|
||||
public SimpleBrokerRegistration(SubscribableChannel clientInboundChannel,
|
||||
MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
|
||||
|
||||
super(clientInboundChannel, clientOutboundChannel, destinationPrefixes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+7
-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.
|
||||
@@ -68,6 +68,12 @@ public class StompBrokerRelayRegistration extends AbstractBrokerRegistration {
|
||||
private String userRegistryBroadcast;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code StompBrokerRelayRegistration}.
|
||||
* @param clientInboundChannel the inbound channel
|
||||
* @param clientOutboundChannel the outbound channel
|
||||
* @param destinationPrefixes the destination prefixes
|
||||
*/
|
||||
public StompBrokerRelayRegistration(SubscribableChannel clientInboundChannel,
|
||||
MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
|
||||
|
||||
|
||||
+5
-2
@@ -166,7 +166,10 @@ public class InvocableHandlerMethodTests {
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Handler {
|
||||
static class Handler {
|
||||
|
||||
public Handler() {
|
||||
}
|
||||
|
||||
public String handle(Integer intArg, String stringArg) {
|
||||
return intArg + "-" + stringArg;
|
||||
@@ -181,7 +184,7 @@ public class InvocableHandlerMethodTests {
|
||||
}
|
||||
|
||||
|
||||
private static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
|
||||
+2
@@ -183,6 +183,8 @@ public class InvocableHandlerMethodTests {
|
||||
|
||||
private AtomicReference<String> result = new AtomicReference<>();
|
||||
|
||||
public Handler() {
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return this.result.get();
|
||||
|
||||
+2
-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.
|
||||
@@ -224,8 +224,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests
|
||||
q.setFlushMode(FlushModeType.AUTO);
|
||||
List<Person> people = q.getResultList();
|
||||
assertThat(people.size()).isEqualTo(0);
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(() ->
|
||||
q.getSingleResult())
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(q::getSingleResult)
|
||||
.withMessageContaining("closed");
|
||||
// We would typically expect an IllegalStateException, but Hibernate throws a
|
||||
// PersistenceException. So we assert the contents of the exception message instead.
|
||||
|
||||
@@ -1,56 +1,24 @@
|
||||
plugins {
|
||||
id "org.unbroken-dome.xjc"
|
||||
}
|
||||
|
||||
description = "Spring Object/XML Marshalling"
|
||||
|
||||
configurations {
|
||||
jibx
|
||||
xjc
|
||||
}
|
||||
|
||||
dependencies {
|
||||
jibx "org.jibx:jibx-bind:1.3.3"
|
||||
jibx "org.apache.bcel:bcel:6.0"
|
||||
xjc "javax.xml.bind:jaxb-api:2.3.1"
|
||||
xjc "com.sun.xml.bind:jaxb-core:2.3.0.1"
|
||||
xjc "com.sun.xml.bind:jaxb-impl:2.3.0.1"
|
||||
xjc "com.sun.xml.bind:jaxb-xjc:2.3.1"
|
||||
xjc "com.sun.activation:javax.activation:1.2.0"
|
||||
}
|
||||
|
||||
ext.genSourcesDir = "${buildDir}/generated-sources"
|
||||
ext.flightSchema = "${projectDir}/src/test/resources/org/springframework/oxm/flight.xsd"
|
||||
|
||||
task genJaxb {
|
||||
ext.sourcesDir = "${genSourcesDir}/jaxb"
|
||||
ext.classesDir = "${buildDir}/classes/jaxb"
|
||||
|
||||
inputs.files(flightSchema).withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
outputs.dir classesDir
|
||||
|
||||
doLast() {
|
||||
project.ant {
|
||||
taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
|
||||
classpath: configurations.xjc.asPath
|
||||
mkdir(dir: sourcesDir)
|
||||
mkdir(dir: classesDir)
|
||||
|
||||
xjc(destdir: sourcesDir, schema: flightSchema,
|
||||
package: "org.springframework.oxm.jaxb.test") {
|
||||
produces(dir: sourcesDir, includes: "**/*.java")
|
||||
}
|
||||
|
||||
javac(destdir: classesDir, source: 1.8, target: 1.8, debug: true,
|
||||
debugLevel: "lines,vars,source",
|
||||
classpath: configurations.xjc.asPath) {
|
||||
src(path: sourcesDir)
|
||||
include(name: "**/*.java")
|
||||
include(name: "*.java")
|
||||
}
|
||||
|
||||
copy(todir: classesDir) {
|
||||
fileset(dir: sourcesDir, erroronmissingdir: false) {
|
||||
exclude(name: "**/*.java")
|
||||
}
|
||||
}
|
||||
}
|
||||
xjc {
|
||||
xjcVersion = '2.2'
|
||||
}
|
||||
sourceSets {
|
||||
test {
|
||||
xjcTargetPackage = 'org.springframework.oxm.jaxb.test'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +35,7 @@ dependencies {
|
||||
testCompile("org.codehaus.jettison:jettison") {
|
||||
exclude group: "stax", module: "stax-api"
|
||||
}
|
||||
testCompile(files(genJaxb.classesDir).builtBy(genJaxb))
|
||||
//testCompile(files(genJaxb.classesDir).builtBy(genJaxb))
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testRuntime("com.sun.xml.bind:jaxb-core")
|
||||
@@ -76,7 +44,7 @@ dependencies {
|
||||
|
||||
// JiBX compiler is currently not compatible with JDK 9+.
|
||||
// If customJavaHome has been set, we assume the custom JDK version is 9+.
|
||||
if ((JavaVersion.current() == JavaVersion.VERSION_1_8) && !System.getProperty("customJavaSourceVersion")) {
|
||||
if ((JavaVersion.current() == JavaVersion.VERSION_1_8) && !project.hasProperty("testToolchain")) {
|
||||
compileTestJava {
|
||||
def bindingXml = "${projectDir}/src/test/resources/org/springframework/oxm/jibx/binding.xml"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,7 +78,7 @@ import static org.xmlunit.diff.DifferenceEvaluators.downgradeDifferencesToEqual;
|
||||
* @author Biju Kunjummen
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshaller> {
|
||||
class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshaller> {
|
||||
|
||||
private static final String CONTEXT_PATH = "org.springframework.oxm.jaxb.test";
|
||||
|
||||
@@ -104,7 +104,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
|
||||
|
||||
@Test
|
||||
public void marshalSAXResult() throws Exception {
|
||||
void marshalSAXResult() throws Exception {
|
||||
ContentHandler contentHandler = mock(ContentHandler.class);
|
||||
SAXResult result = new SAXResult(contentHandler);
|
||||
marshaller.marshal(flights, result);
|
||||
@@ -124,7 +124,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lazyInit() throws Exception {
|
||||
void lazyInit() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setContextPath(CONTEXT_PATH);
|
||||
marshaller.setLazyInit(true);
|
||||
@@ -137,48 +137,44 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test
|
||||
public void properties() throws Exception {
|
||||
void properties() throws Exception {
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setContextPath(CONTEXT_PATH);
|
||||
marshaller.setMarshallerProperties(
|
||||
Collections.<String, Object>singletonMap(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT,
|
||||
Boolean.TRUE));
|
||||
Collections.singletonMap(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE));
|
||||
marshaller.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noContextPathOrClassesToBeBound() throws Exception {
|
||||
void noContextPathOrClassesToBeBound() throws Exception {
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
marshaller::afterPropertiesSet);
|
||||
assertThatIllegalArgumentException().isThrownBy(marshaller::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidContextPath() throws Exception {
|
||||
void testInvalidContextPath() throws Exception {
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setContextPath("ab");
|
||||
assertThatExceptionOfType(UncategorizedMappingException.class).isThrownBy(
|
||||
marshaller::afterPropertiesSet);
|
||||
assertThatExceptionOfType(UncategorizedMappingException.class).isThrownBy(marshaller::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void marshalInvalidClass() throws Exception {
|
||||
void marshalInvalidClass() throws Exception {
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setClassesToBeBound(FlightType.class);
|
||||
marshaller.afterPropertiesSet();
|
||||
Result result = new StreamResult(new StringWriter());
|
||||
Flights flights = new Flights();
|
||||
assertThatExceptionOfType(XmlMappingException.class).isThrownBy(() ->
|
||||
marshaller.marshal(flights, result));
|
||||
assertThatExceptionOfType(XmlMappingException.class).isThrownBy(() -> marshaller.marshal(flights, result));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsContextPath() throws Exception {
|
||||
void supportsContextPath() throws Exception {
|
||||
testSupports();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsClassesToBeBound() throws Exception {
|
||||
void supportsClassesToBeBound() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setClassesToBeBound(Flights.class, FlightType.class);
|
||||
marshaller.afterPropertiesSet();
|
||||
@@ -186,7 +182,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPackagesToScan() throws Exception {
|
||||
void supportsPackagesToScan() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setPackagesToScan(CONTEXT_PATH);
|
||||
marshaller.afterPropertiesSet();
|
||||
@@ -224,11 +220,11 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
|
||||
private void testSupportsPrimitives() {
|
||||
final Primitives primitives = new Primitives();
|
||||
ReflectionUtils.doWithMethods(Primitives.class, new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
ReflectionUtils.doWithMethods(Primitives.class, method -> {
|
||||
Type returnType = method.getGenericReturnType();
|
||||
assertThat(marshaller.supports(returnType)).as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(9) + ">").isTrue();
|
||||
assertThat(marshaller.supports(returnType))
|
||||
.as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(9) + ">")
|
||||
.isTrue();
|
||||
try {
|
||||
// make sure the marshalling does not result in errors
|
||||
Object returnValue = method.invoke(primitives);
|
||||
@@ -237,22 +233,18 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
catch (InvocationTargetException e) {
|
||||
throw new AssertionError(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}, new ReflectionUtils.MethodFilter() {
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
return method.getName().startsWith("primitive");
|
||||
}
|
||||
});
|
||||
},
|
||||
method -> method.getName().startsWith("primitive")
|
||||
);
|
||||
}
|
||||
|
||||
private void testSupportsStandardClasses() throws Exception {
|
||||
final StandardClasses standardClasses = new StandardClasses();
|
||||
ReflectionUtils.doWithMethods(StandardClasses.class, new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
ReflectionUtils.doWithMethods(StandardClasses.class, method -> {
|
||||
Type returnType = method.getGenericReturnType();
|
||||
assertThat(marshaller.supports(returnType)).as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(13) + ">").isTrue();
|
||||
assertThat(marshaller.supports(returnType))
|
||||
.as("Jaxb2Marshaller does not support JAXBElement<" + method.getName().substring(13) + ">")
|
||||
.isTrue();
|
||||
try {
|
||||
// make sure the marshalling does not result in errors
|
||||
Object returnValue = method.invoke(standardClasses);
|
||||
@@ -261,17 +253,13 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
catch (InvocationTargetException e) {
|
||||
throw new AssertionError(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}, new ReflectionUtils.MethodFilter() {
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
return method.getName().startsWith("standardClass");
|
||||
}
|
||||
});
|
||||
},
|
||||
method -> method.getName().startsWith("standardClass")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsXmlRootElement() throws Exception {
|
||||
void supportsXmlRootElement() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setClassesToBeBound(DummyRootElement.class, DummyType.class);
|
||||
marshaller.afterPropertiesSet();
|
||||
@@ -284,7 +272,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
|
||||
|
||||
@Test
|
||||
public void marshalAttachments() throws Exception {
|
||||
void marshalAttachments() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setClassesToBeBound(BinaryObject.class);
|
||||
marshaller.setMtomEnabled(true);
|
||||
@@ -304,7 +292,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test // SPR-10714
|
||||
public void marshalAWrappedObjectHoldingAnXmlElementDeclElement() throws Exception {
|
||||
void marshalAWrappedObjectHoldingAnXmlElementDeclElement() throws Exception {
|
||||
marshaller = new Jaxb2Marshaller();
|
||||
marshaller.setPackagesToScan("org.springframework.oxm.jaxb");
|
||||
marshaller.afterPropertiesSet();
|
||||
@@ -318,7 +306,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test // SPR-10806
|
||||
public void unmarshalStreamSourceWithXmlOptions() throws Exception {
|
||||
void unmarshalStreamSourceWithXmlOptions() throws Exception {
|
||||
final javax.xml.bind.Unmarshaller unmarshaller = mock(javax.xml.bind.Unmarshaller.class);
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
|
||||
@Override
|
||||
@@ -352,7 +340,7 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests<Jaxb2Marshalle
|
||||
}
|
||||
|
||||
@Test // SPR-10806
|
||||
public void unmarshalSaxSourceWithXmlOptions() throws Exception {
|
||||
void unmarshalSaxSourceWithXmlOptions() throws Exception {
|
||||
final javax.xml.bind.Unmarshaller unmarshaller = mock(javax.xml.bind.Unmarshaller.class);
|
||||
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,7 @@ import javax.xml.transform.stream.StreamSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.oxm.AbstractUnmarshallerTests;
|
||||
import org.springframework.oxm.jaxb.test.FlightType;
|
||||
@@ -56,7 +57,7 @@ public class Jaxb2UnmarshallerTests extends AbstractUnmarshallerTests<Jaxb2Marsh
|
||||
protected Jaxb2Marshaller createUnmarshaller() throws Exception {
|
||||
Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller();
|
||||
unmarshaller.setContextPath("org.springframework.oxm.jaxb.test");
|
||||
unmarshaller.setSchema(new ClassPathResource("org/springframework/oxm/flight.xsd"));
|
||||
unmarshaller.setSchema(new FileSystemResource("src/test/schema/flight.xsd"));
|
||||
unmarshaller.afterPropertiesSet();
|
||||
return unmarshaller;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
|
||||
targetNamespace="https://samples.springframework.org/order"
|
||||
xmlns:tns="https://samples.springframework.org/order">
|
||||
<element name="order">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="order-item" type="tns:orderItemType"
|
||||
maxOccurs="unbounded">
|
||||
</element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<complexType name="orderItemType">
|
||||
<attribute name="id" type="string" />
|
||||
<attribute name="quantity" type="int" />
|
||||
</complexType>
|
||||
</schema>
|
||||
+16
@@ -1,4 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2021 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
|
||||
targetNamespace="http://samples.springframework.org/flight"
|
||||
xmlns:tns="http://samples.springframework.org/flight">
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -315,8 +315,8 @@ public class MockServletContext implements ServletContext {
|
||||
return resourcePaths;
|
||||
}
|
||||
catch (InvalidPathException | IOException ex ) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not get resource paths for " +
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not get resource paths for " +
|
||||
(resource != null ? resource : resourceLocation), ex);
|
||||
}
|
||||
return null;
|
||||
@@ -339,8 +339,8 @@ public class MockServletContext implements ServletContext {
|
||||
throw ex;
|
||||
}
|
||||
catch (InvalidPathException | IOException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not get URL for resource " +
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not get URL for resource " +
|
||||
(resource != null ? resource : resourceLocation), ex);
|
||||
}
|
||||
return null;
|
||||
@@ -360,8 +360,8 @@ public class MockServletContext implements ServletContext {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (InvalidPathException | IOException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not open InputStream for resource " +
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not open InputStream for resource " +
|
||||
(resource != null ? resource : resourceLocation), ex);
|
||||
}
|
||||
return null;
|
||||
@@ -476,8 +476,8 @@ public class MockServletContext implements ServletContext {
|
||||
return resource.getFile().getAbsolutePath();
|
||||
}
|
||||
catch (InvalidPathException | IOException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not determine real path of resource " +
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not determine real path of resource " +
|
||||
(resource != null ? resource : resourceLocation), ex);
|
||||
}
|
||||
return null;
|
||||
|
||||
+24
-3
@@ -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,6 +34,9 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -105,6 +108,7 @@ abstract class AbstractExpressionEvaluatingCondition implements ExecutionConditi
|
||||
|
||||
boolean loadContext = loadContextExtractor.apply(annotation.get());
|
||||
boolean evaluatedToTrue = evaluateExpression(expression, loadContext, annotationType, context);
|
||||
ConditionEvaluationResult result;
|
||||
|
||||
if (evaluatedToTrue) {
|
||||
String adjective = (enabledOnTrue ? "enabled" : "disabled");
|
||||
@@ -114,7 +118,7 @@ abstract class AbstractExpressionEvaluatingCondition implements ExecutionConditi
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(reason);
|
||||
}
|
||||
return (enabledOnTrue ? ConditionEvaluationResult.enabled(reason)
|
||||
result = (enabledOnTrue ? ConditionEvaluationResult.enabled(reason)
|
||||
: ConditionEvaluationResult.disabled(reason));
|
||||
}
|
||||
else {
|
||||
@@ -124,9 +128,26 @@ abstract class AbstractExpressionEvaluatingCondition implements ExecutionConditi
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(reason);
|
||||
}
|
||||
return (enabledOnTrue ? ConditionEvaluationResult.disabled(reason) :
|
||||
result = (enabledOnTrue ? ConditionEvaluationResult.disabled(reason) :
|
||||
ConditionEvaluationResult.enabled(reason));
|
||||
}
|
||||
|
||||
// If we eagerly loaded the ApplicationContext to evaluate SpEL expressions
|
||||
// and the test class ends up being disabled, we have to check if the
|
||||
// user asked for the ApplicationContext to be closed via @DirtiesContext,
|
||||
// since the DirtiesContextTestExecutionListener will never be invoked for
|
||||
// a disabled test class.
|
||||
// See https://github.com/spring-projects/spring-framework/issues/26694
|
||||
if (loadContext && result.isDisabled() && element instanceof Class) {
|
||||
Class<?> testClass = (Class<?>) element;
|
||||
DirtiesContext dirtiesContext = TestContextAnnotationUtils.findMergedAnnotation(testClass, DirtiesContext.class);
|
||||
if (dirtiesContext != null) {
|
||||
HierarchyMode hierarchyMode = dirtiesContext.hierarchyMode();
|
||||
SpringExtension.getTestContextManager(context).getTestContext().markApplicationContextDirty(hierarchyMode);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private <A extends Annotation> boolean evaluateExpression(String expression, boolean loadContext,
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -287,7 +287,7 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
* Get the {@link TestContextManager} associated with the supplied {@code ExtensionContext}.
|
||||
* @return the {@code TestContextManager} (never {@code null})
|
||||
*/
|
||||
private static TestContextManager getTestContextManager(ExtensionContext context) {
|
||||
static TestContextManager getTestContextManager(ExtensionContext context) {
|
||||
Assert.notNull(context, "ExtensionContext must not be null");
|
||||
Class<?> testClass = context.getRequiredTestClass();
|
||||
Store store = getStore(context);
|
||||
|
||||
+11
-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.
|
||||
@@ -373,8 +373,16 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable {
|
||||
for (NameValuePair param : this.webRequest.getRequestParameters()) {
|
||||
if (param instanceof KeyDataPair) {
|
||||
KeyDataPair pair = (KeyDataPair) param;
|
||||
MockPart part = new MockPart(pair.getName(), pair.getFile().getName(), readAllBytes(pair.getFile()));
|
||||
part.getHeaders().setContentType(MediaType.valueOf(pair.getMimeType()));
|
||||
File file = pair.getFile();
|
||||
MockPart part;
|
||||
if (file != null) {
|
||||
part = new MockPart(pair.getName(), file.getName(), readAllBytes(file));
|
||||
part.getHeaders().setContentType(MediaType.valueOf(pair.getMimeType()));
|
||||
}
|
||||
else { // mimic empty file upload
|
||||
part = new MockPart(pair.getName(), "", null);
|
||||
part.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
request.addPart(part);
|
||||
}
|
||||
else {
|
||||
|
||||
-1
@@ -496,7 +496,6 @@ class MockHttpServletResponseTests {
|
||||
String expiryDate = "Tue, 8 Oct 2019 19:50:00 GMT";
|
||||
String cookieValue = "SESSION=123; Path=/; Expires=" + expiryDate;
|
||||
response.addHeader(SET_COOKIE, cookieValue);
|
||||
System.err.println(response.getCookie("SESSION"));
|
||||
assertThat(response.getHeader(SET_COOKIE)).isEqualTo(cookieValue);
|
||||
|
||||
assertNumCookies(1);
|
||||
|
||||
+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.
|
||||
@@ -557,10 +557,10 @@ class TestContextAnnotationUtilsTests {
|
||||
@Target(ElementType.TYPE)
|
||||
@interface MetaConfig {
|
||||
|
||||
static class DevConfig {
|
||||
class DevConfig {
|
||||
}
|
||||
|
||||
static class ProductionConfig {
|
||||
class ProductionConfig {
|
||||
}
|
||||
|
||||
|
||||
@@ -607,11 +607,11 @@ class TestContextAnnotationUtilsTests {
|
||||
}
|
||||
|
||||
@MetaConfig
|
||||
class MetaConfigWithDefaultAttributesTestCase {
|
||||
static class MetaConfigWithDefaultAttributesTestCase {
|
||||
}
|
||||
|
||||
@MetaConfig(classes = TestContextAnnotationUtilsTests.class)
|
||||
class MetaConfigWithOverriddenAttributesTestCase {
|
||||
static class MetaConfigWithOverriddenAttributesTestCase {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.test.context.junit.jupiter;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.platform.testkit.engine.EngineTestKit;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
|
||||
|
||||
/**
|
||||
* Integration tests which verify support for {@link DisabledIf @DisabledIf} in
|
||||
* conjunction with {@link DirtiesContext @DirtiesContext} and the
|
||||
* {@link SpringExtension} in a JUnit Jupiter environment.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.14
|
||||
* @see EnabledIfAndDirtiesContextTests
|
||||
*/
|
||||
class DisabledIfAndDirtiesContextTests {
|
||||
|
||||
private static AtomicBoolean contextClosed = new AtomicBoolean();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
contextClosed.set(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextShouldBeClosedForEnabledTestClass() {
|
||||
assertThat(contextClosed).as("context closed").isFalse();
|
||||
EngineTestKit.engine("junit-jupiter").selectors(
|
||||
selectClass(EnabledAndDirtiesContextTestCase.class))//
|
||||
.execute()//
|
||||
.testEvents()//
|
||||
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
|
||||
assertThat(contextClosed).as("context closed").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextShouldBeClosedForDisabledTestClass() {
|
||||
assertThat(contextClosed).as("context closed").isFalse();
|
||||
EngineTestKit.engine("junit-jupiter").selectors(
|
||||
selectClass(DisabledAndDirtiesContextTestCase.class))//
|
||||
.execute()//
|
||||
.testEvents()//
|
||||
.assertStatistics(stats -> stats.started(0).succeeded(0).failed(0));
|
||||
assertThat(contextClosed).as("context closed").isTrue();
|
||||
}
|
||||
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisabledIf(expression = "false", loadContext = true)
|
||||
@DirtiesContext
|
||||
static class EnabledAndDirtiesContextTestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisabledIf(expression = "true", loadContext = true)
|
||||
@DirtiesContext
|
||||
static class DisabledAndDirtiesContextTestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
fail("This test must be disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
DisposableBean disposableBean() {
|
||||
return () -> contextClosed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.test.context.junit.jupiter;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.platform.testkit.engine.EngineTestKit;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
|
||||
|
||||
/**
|
||||
* Integration tests which verify support for {@link EnabledIf @EnabledIf} in
|
||||
* conjunction with {@link DirtiesContext @DirtiesContext} and the
|
||||
* {@link SpringExtension} in a JUnit Jupiter environment.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.14
|
||||
* @see DisabledIfAndDirtiesContextTests
|
||||
*/
|
||||
class EnabledIfAndDirtiesContextTests {
|
||||
|
||||
private static AtomicBoolean contextClosed = new AtomicBoolean();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
contextClosed.set(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextShouldBeClosedForEnabledTestClass() {
|
||||
assertThat(contextClosed).as("context closed").isFalse();
|
||||
EngineTestKit.engine("junit-jupiter").selectors(
|
||||
selectClass(EnabledAndDirtiesContextTestCase.class))//
|
||||
.execute()//
|
||||
.testEvents()//
|
||||
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
|
||||
assertThat(contextClosed).as("context closed").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void contextShouldBeClosedForDisabledTestClass() {
|
||||
assertThat(contextClosed).as("context closed").isFalse();
|
||||
EngineTestKit.engine("junit-jupiter").selectors(
|
||||
selectClass(DisabledAndDirtiesContextTestCase.class))//
|
||||
.execute()//
|
||||
.testEvents()//
|
||||
.assertStatistics(stats -> stats.started(0).succeeded(0).failed(0));
|
||||
assertThat(contextClosed).as("context closed").isTrue();
|
||||
}
|
||||
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@EnabledIf(expression = "true", loadContext = true)
|
||||
@DirtiesContext
|
||||
static class EnabledAndDirtiesContextTestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@EnabledIf(expression = "false", loadContext = true)
|
||||
@DirtiesContext
|
||||
static class DisabledAndDirtiesContextTestCase {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
fail("This test must be disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
DisposableBean disposableBean() {
|
||||
return () -> contextClosed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+6
-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.
|
||||
@@ -67,7 +67,7 @@ class TimedTransactionalSpringExtensionTests {
|
||||
event(test("WithExceededJUnitJupiterTimeout"),
|
||||
finishedWithFailure(
|
||||
instanceOf(TimeoutException.class),
|
||||
message(msg -> msg.endsWith("timed out after 50 milliseconds")))));
|
||||
message(msg -> msg.endsWith("timed out after 10 milliseconds")))));
|
||||
}
|
||||
|
||||
|
||||
@@ -83,10 +83,10 @@ class TimedTransactionalSpringExtensionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
|
||||
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
|
||||
void transactionalWithExceededJUnitJupiterTimeout() throws Exception {
|
||||
assertThatTransaction().isActive();
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,11 +97,11 @@ class TimedTransactionalSpringExtensionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
|
||||
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
void notTransactionalWithExceededJUnitJupiterTimeout() throws Exception {
|
||||
assertThatTransaction().isNotActive();
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,14 +76,14 @@ public class TimedSpringRunnerTests {
|
||||
}
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test(timeout = 100)
|
||||
@Test(timeout = 10)
|
||||
public void jUnitTimeoutWithSleep() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test
|
||||
@Timed(millis = 100)
|
||||
@Timed(millis = 10)
|
||||
public void springTimeoutWithSleep() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class TimedSpringRunnerTests {
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test
|
||||
@MetaTimedWithOverride(millis = 100)
|
||||
@MetaTimedWithOverride(millis = 10)
|
||||
public void springTimeoutWithSleepAndMetaAnnotationAndOverride() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class TimedSpringRunnerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Timed(millis = 100)
|
||||
@Timed(millis = 10)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private static @interface MetaTimed {
|
||||
}
|
||||
|
||||
+30
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -52,6 +54,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
/**
|
||||
@@ -423,8 +426,7 @@ public class HtmlUnitRequestBuilderTests {
|
||||
}
|
||||
|
||||
@Test // gh-24926
|
||||
public void buildRequestParameterMapViaWebRequestDotSetFileToUploadAsParameter() throws Exception {
|
||||
|
||||
public void buildRequestParameterMapViaWebRequestDotSetRequestParametersWithFileToUploadAsParameter() throws Exception {
|
||||
webRequest.setRequestParameters(Collections.singletonList(
|
||||
new KeyDataPair("key",
|
||||
new ClassPathResource("org/springframework/test/web/htmlunit/test.txt").getFile(),
|
||||
@@ -432,7 +434,7 @@ public class HtmlUnitRequestBuilderTests {
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getParts().size()).isEqualTo(1);
|
||||
assertThat(actualRequest.getParts()).hasSize(1);
|
||||
Part part = actualRequest.getPart("key");
|
||||
assertThat(part).isNotNull();
|
||||
assertThat(part.getName()).isEqualTo("key");
|
||||
@@ -441,6 +443,30 @@ public class HtmlUnitRequestBuilderTests {
|
||||
assertThat(part.getContentType()).isEqualTo(MimeType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
@Test // gh-26799
|
||||
public void buildRequestParameterMapViaWebRequestDotSetRequestParametersWithNullFileToUploadAsParameter() throws Exception {
|
||||
webRequest.setRequestParameters(Collections.singletonList(new KeyDataPair("key", null, null, null, (Charset) null)));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getParts()).hasSize(1);
|
||||
Part part = actualRequest.getPart("key");
|
||||
|
||||
assertSoftly(softly -> {
|
||||
softly.assertThat(part).isNotNull();
|
||||
softly.assertThat(part.getName()).as("name").isEqualTo("key");
|
||||
softly.assertThat(part.getSize()).as("size").isEqualTo(0);
|
||||
try {
|
||||
softly.assertThat(part.getInputStream()).isEmpty();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
softly.fail("failed to get InputStream", ex);
|
||||
}
|
||||
softly.assertThat(part.getSubmittedFileName()).as("filename").isEqualTo("");
|
||||
softly.assertThat(part.getContentType()).as("content-type").isEqualTo("application/octet-stream");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestParameterMapFromSingleQueryParam() throws Exception {
|
||||
webRequest.setUrl(new URL("https://example.com/example/?name=value"));
|
||||
|
||||
+14
-13
@@ -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.
|
||||
@@ -32,6 +32,7 @@ import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -99,7 +100,7 @@ public class AsyncTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredResultWithImmediateValue() throws Exception {
|
||||
public void deferredResultWithImmediateValue() {
|
||||
this.testClient.get()
|
||||
.uri("/1?deferredResultWithImmediateValue=true")
|
||||
.exchange()
|
||||
@@ -109,7 +110,7 @@ public class AsyncTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredResultWithDelayedError() throws Exception {
|
||||
public void deferredResultWithDelayedError() {
|
||||
this.testClient.get()
|
||||
.uri("/1?deferredResultWithDelayedError=true")
|
||||
.exchange()
|
||||
@@ -118,7 +119,7 @@ public class AsyncTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenableFuture() throws Exception {
|
||||
public void listenableFuture() {
|
||||
this.testClient.get()
|
||||
.uri("/1?listenableFuture=true")
|
||||
.exchange()
|
||||
@@ -142,17 +143,17 @@ public class AsyncTests {
|
||||
@RequestMapping(path = "/{id}", produces = "application/json")
|
||||
private static class AsyncController {
|
||||
|
||||
@RequestMapping(params = "callable")
|
||||
@GetMapping(params = "callable")
|
||||
public Callable<Person> getCallable() {
|
||||
return () -> new Person("Joe");
|
||||
}
|
||||
|
||||
@RequestMapping(params = "streaming")
|
||||
@GetMapping(params = "streaming")
|
||||
public StreamingResponseBody getStreaming() {
|
||||
return os -> os.write("name=Joe".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@RequestMapping(params = "streamingSlow")
|
||||
@GetMapping(params = "streamingSlow")
|
||||
public StreamingResponseBody getStreamingSlow() {
|
||||
return os -> {
|
||||
os.write("name=Joe".getBytes());
|
||||
@@ -166,41 +167,41 @@ public class AsyncTests {
|
||||
};
|
||||
}
|
||||
|
||||
@RequestMapping(params = "streamingJson")
|
||||
@GetMapping(params = "streamingJson")
|
||||
public ResponseEntity<StreamingResponseBody> getStreamingJson() {
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
@RequestMapping(params = "deferredResult")
|
||||
@GetMapping(params = "deferredResult")
|
||||
public DeferredResult<Person> getDeferredResult() {
|
||||
DeferredResult<Person> result = new DeferredResult<>();
|
||||
delay(100, () -> result.setResult(new Person("Joe")));
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(params = "deferredResultWithImmediateValue")
|
||||
@GetMapping(params = "deferredResultWithImmediateValue")
|
||||
public DeferredResult<Person> getDeferredResultWithImmediateValue() {
|
||||
DeferredResult<Person> result = new DeferredResult<>();
|
||||
result.setResult(new Person("Joe"));
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(params = "deferredResultWithDelayedError")
|
||||
@GetMapping(params = "deferredResultWithDelayedError")
|
||||
public DeferredResult<Person> getDeferredResultWithDelayedError() {
|
||||
DeferredResult<Person> result = new DeferredResult<>();
|
||||
delay(100, () -> result.setErrorResult(new RuntimeException("Delayed Error")));
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(params = "listenableFuture")
|
||||
@GetMapping(params = "listenableFuture")
|
||||
public ListenableFuture<Person> getListenableFuture() {
|
||||
ListenableFutureTask<Person> futureTask = new ListenableFutureTask<>(() -> new Person("Joe"));
|
||||
delay(100, futureTask);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
@RequestMapping(params = "completableFutureWithImmediateValue")
|
||||
@GetMapping(params = "completableFutureWithImmediateValue")
|
||||
public CompletableFuture<Person> getCompletableFutureWithImmediateValue() {
|
||||
CompletableFuture<Person> future = new CompletableFuture<>();
|
||||
future.complete(new Person("Joe"));
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.test.web.servlet.samples.client.standalone;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static java.time.Duration.ofMillis;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* SSE controller tests with MockMvc and WebTestClient.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class SseTests {
|
||||
|
||||
private final WebTestClient testClient =
|
||||
MockMvcWebTestClient.bindToController(new SseController()).build();
|
||||
|
||||
|
||||
@Test
|
||||
public void sse() {
|
||||
FluxExchangeResult<Person> exchangeResult = this.testClient.get()
|
||||
.uri("/persons")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType("text/event-stream")
|
||||
.returnResult(Person.class);
|
||||
|
||||
StepVerifier.create(exchangeResult.getResponseBody())
|
||||
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
|
||||
.expectNextCount(4)
|
||||
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
|
||||
.thenCancel()
|
||||
.verify();
|
||||
}
|
||||
|
||||
|
||||
@RestController
|
||||
private static class SseController {
|
||||
|
||||
@GetMapping(path = "/persons", produces = "text/event-stream")
|
||||
public Flux<Person> getPersonStream() {
|
||||
return Flux.interval(ofMillis(100)).take(50).onBackpressureBuffer(50)
|
||||
.map(index -> new Person("N" + index));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
<Logger name="org.springframework.test.context.support.DelegatingSmartContextLoader" level="info" />
|
||||
<Logger name="org.springframework.test.context.support.AbstractGenericContextLoader" level="info" />
|
||||
<Logger name="org.springframework.test.context.support.AnnotationConfigContextLoader" level="info" />
|
||||
<Logger name="org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener" level="warn" />
|
||||
<Logger name="org.springframework.test.context.support.TestPropertySourceUtils" level="trace" />
|
||||
<Logger name="org.springframework.beans" level="warn" />
|
||||
<Logger name="org.springframework.test.web.servlet.result" level="debug" />
|
||||
|
||||
+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.
|
||||
@@ -28,7 +28,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -320,7 +320,7 @@ public abstract class TransactionSynchronizationManager {
|
||||
else {
|
||||
// Sort lazily here, not in registerSynchronization.
|
||||
List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);
|
||||
AnnotationAwareOrderComparator.sort(sortedSynchs);
|
||||
OrderComparator.sort(sortedSynchs);
|
||||
return Collections.unmodifiableList(sortedSynchs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ dependencies {
|
||||
compile(project(":spring-beans"))
|
||||
compile(project(":spring-core"))
|
||||
compileOnly(project(":kotlin-coroutines"))
|
||||
compileOnly("io.projectreactor.tools:blockhound")
|
||||
optional(project(":spring-aop"))
|
||||
optional(project(":spring-context"))
|
||||
optional(project(":spring-oxm"))
|
||||
@@ -75,6 +76,7 @@ dependencies {
|
||||
testCompile("org.skyscreamer:jsonassert")
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testCompile("io.projectreactor.tools:blockhound")
|
||||
testRuntime("com.sun.mail:javax.mail")
|
||||
testRuntime("com.sun.xml.bind:jaxb-core")
|
||||
testRuntime("com.sun.xml.bind:jaxb-impl")
|
||||
|
||||
@@ -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.
|
||||
@@ -99,6 +99,12 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.5">Section 5.3.5 of RFC 7231</a>
|
||||
*/
|
||||
public static final String ACCEPT_LANGUAGE = "Accept-Language";
|
||||
/**
|
||||
* The HTTP {@code Accept-Patch} header field name.
|
||||
* @since 5.3.6
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5789#section-3.1">Section 3.1 of RFC 5789</a>
|
||||
*/
|
||||
public static final String ACCEPT_PATCH = "Accept-Patch";
|
||||
/**
|
||||
* The HTTP {@code Accept-Ranges} header field name.
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
|
||||
@@ -525,6 +531,25 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types} for
|
||||
* {@code PATCH} methods, as specified by the {@code Accept-Patch} header.
|
||||
* @since 5.3.6
|
||||
*/
|
||||
public void setAcceptPatch(List<MediaType> mediaTypes) {
|
||||
set(ACCEPT_PATCH, MediaType.toString(mediaTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of acceptable {@linkplain MediaType media types} for
|
||||
* {@code PATCH} methods, as specified by the {@code Accept-Patch} header.
|
||||
* <p>Returns an empty list when the acceptable media types are unspecified.
|
||||
* @since 5.3.6
|
||||
*/
|
||||
public List<MediaType> getAcceptPatch() {
|
||||
return MediaType.parseMediaTypes(get(ACCEPT_PATCH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,14 +57,13 @@ public enum HttpMethod {
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether this {@code HttpMethod} matches the given
|
||||
* method value.
|
||||
* @param method the method value as a String
|
||||
* Determine whether this {@code HttpMethod} matches the given method value.
|
||||
* @param method the HTTP method as a String
|
||||
* @return {@code true} if it matches, {@code false} otherwise
|
||||
* @since 4.2.4
|
||||
*/
|
||||
public boolean matches(String method) {
|
||||
return (this == resolve(method));
|
||||
return name().equals(method);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -416,6 +416,13 @@ public enum HttpStatus {
|
||||
NETWORK_AUTHENTICATION_REQUIRED(511, Series.SERVER_ERROR, "Network Authentication Required");
|
||||
|
||||
|
||||
private static final HttpStatus[] VALUES;
|
||||
|
||||
static {
|
||||
VALUES = values();
|
||||
}
|
||||
|
||||
|
||||
private final int value;
|
||||
|
||||
private final Series series;
|
||||
@@ -550,7 +557,8 @@ public enum HttpStatus {
|
||||
*/
|
||||
@Nullable
|
||||
public static HttpStatus resolve(int statusCode) {
|
||||
for (HttpStatus status : values()) {
|
||||
// used cached VALUES instead of values() to prevent array allocation
|
||||
for (HttpStatus status : VALUES) {
|
||||
if (status.value == statusCode) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -65,6 +66,7 @@ public final class MediaTypeFactory {
|
||||
*/
|
||||
private static MultiValueMap<String, MediaType> parseMimeTypes() {
|
||||
InputStream is = MediaTypeFactory.class.getResourceAsStream(MIME_TYPES_FILE_NAME);
|
||||
Assert.state(is != null, MIME_TYPES_FILE_NAME + " not found in classpath");
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.US_ASCII))) {
|
||||
MultiValueMap<String, MediaType> result = new LinkedMultiValueMap<>();
|
||||
String line;
|
||||
@@ -82,7 +84,7 @@ public final class MediaTypeFactory {
|
||||
return result;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not load '" + MIME_TYPES_FILE_NAME + "'", ex);
|
||||
throw new IllegalStateException("Could not read " + MIME_TYPES_FILE_NAME, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -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.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.http.codec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -171,12 +170,14 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
if (this.encoder == null) {
|
||||
throw new CodecException("No SSE encoder configured and the data is not String.");
|
||||
}
|
||||
DataBuffer buffer = ((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints);
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
return Flux.just(factory.join(Arrays.asList(
|
||||
encodeText(eventContent, mediaType, factory),
|
||||
buffer,
|
||||
encodeText("\n\n", mediaType, factory))));
|
||||
|
||||
return Flux.defer(() -> {
|
||||
DataBuffer startBuffer = encodeText(eventContent, mediaType, factory);
|
||||
DataBuffer endBuffer = encodeText("\n\n", mediaType, factory);
|
||||
DataBuffer dataBuffer = ((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints);
|
||||
Hints.touchDataBuffer(dataBuffer, hints, logger);
|
||||
return Flux.just(startBuffer, dataBuffer, endBuffer);
|
||||
});
|
||||
}
|
||||
|
||||
private void writeField(String fieldName, Object fieldValue, StringBuilder sb) {
|
||||
|
||||
+28
-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.
|
||||
@@ -17,10 +17,9 @@
|
||||
package org.springframework.http.codec.multipart;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -62,8 +61,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> {
|
||||
|
||||
private static final String IDENTIFIER = "spring-multipart";
|
||||
|
||||
private int maxInMemorySize = 256 * 1024;
|
||||
|
||||
private int maxHeadersSize = 8 * 1024;
|
||||
@@ -76,7 +73,9 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
|
||||
private Scheduler blockingOperationScheduler = Schedulers.boundedElastic();
|
||||
|
||||
private Mono<Path> fileStorageDirectory = Mono.defer(this::defaultFileStorageDirectory).cache();
|
||||
private FileStorage fileStorage = FileStorage.tempDirectory(this::getBlockingOperationScheduler);
|
||||
|
||||
private Charset headersCharset = StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
/**
|
||||
@@ -132,7 +131,7 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory used to store parts larger than
|
||||
* Set the directory used to store parts larger than
|
||||
* {@link #setMaxInMemorySize(int) maxInMemorySize}. By default, a directory
|
||||
* named {@code spring-webflux-multipart} is created under the system
|
||||
* temporary directory.
|
||||
@@ -144,14 +143,11 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
*/
|
||||
public void setFileStorageDirectory(Path fileStorageDirectory) throws IOException {
|
||||
Assert.notNull(fileStorageDirectory, "FileStorageDirectory must not be null");
|
||||
if (!Files.exists(fileStorageDirectory)) {
|
||||
Files.createDirectory(fileStorageDirectory);
|
||||
}
|
||||
this.fileStorageDirectory = Mono.just(fileStorageDirectory);
|
||||
this.fileStorage = FileStorage.fromPath(fileStorageDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Reactor {@link Scheduler} to be used for creating files and
|
||||
* Set the Reactor {@link Scheduler} to be used for creating files and
|
||||
* directories, and writing to files. By default,
|
||||
* {@link Schedulers#boundedElastic()} is used, but this property allows for
|
||||
* changing it to an externally managed scheduler.
|
||||
@@ -165,19 +161,21 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
this.blockingOperationScheduler = blockingOperationScheduler;
|
||||
}
|
||||
|
||||
private Scheduler getBlockingOperationScheduler() {
|
||||
return this.blockingOperationScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* When set to {@code true}, the {@linkplain Part#content() part content}
|
||||
* is streamed directly from the parsed input buffer stream, and not stored
|
||||
* in memory nor file.
|
||||
* When {@code false}, parts are backed by
|
||||
* in-memory and/or file storage. Defaults to {@code false}.
|
||||
*
|
||||
* <p><strong>NOTE</strong> that with streaming enabled, the
|
||||
* {@code Flux<Part>} that is produced by this message reader must be
|
||||
* consumed in the original order, i.e. the order of the HTTP message.
|
||||
* Additionally, the {@linkplain Part#content() body contents} must either
|
||||
* be completely consumed or canceled before moving to the next part.
|
||||
*
|
||||
* <p>Also note that enabling this property effectively ignores
|
||||
* {@link #setMaxInMemorySize(int) maxInMemorySize},
|
||||
* {@link #setMaxDiskUsagePerPart(long) maxDiskUsagePerPart},
|
||||
@@ -188,6 +186,18 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
this.streaming = streaming;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character set used to decode headers.
|
||||
* Defaults to UTF-8 as per RFC 7578.
|
||||
* @param headersCharset the charset to use for decoding headers
|
||||
* @since 5.3.6
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7578#section-5.1">RFC-7578 Section 5.2</a>
|
||||
*/
|
||||
public void setHeadersCharset(Charset headersCharset) {
|
||||
Assert.notNull(headersCharset, "HeadersCharset must not be null");
|
||||
this.headersCharset = headersCharset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return Collections.singletonList(MediaType.MULTIPART_FORM_DATA);
|
||||
@@ -214,15 +224,15 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
message.getHeaders().getContentType() + "\""));
|
||||
}
|
||||
Flux<MultipartParser.Token> tokens = MultipartParser.parse(message.getBody(), boundary,
|
||||
this.maxHeadersSize);
|
||||
this.maxHeadersSize, this.headersCharset);
|
||||
|
||||
return PartGenerator.createParts(tokens, this.maxParts, this.maxInMemorySize, this.maxDiskUsagePerPart,
|
||||
this.streaming, this.fileStorageDirectory, this.blockingOperationScheduler);
|
||||
this.streaming, this.fileStorage.directory(), this.blockingOperationScheduler);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] boundary(HttpMessage message) {
|
||||
private byte[] boundary(HttpMessage message) {
|
||||
MediaType contentType = message.getHeaders().getContentType();
|
||||
if (contentType != null) {
|
||||
String boundary = contentType.getParameter("boundary");
|
||||
@@ -231,22 +241,10 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements
|
||||
if (len > 2 && boundary.charAt(0) == '"' && boundary.charAt(len - 1) == '"') {
|
||||
boundary = boundary.substring(1, len - 1);
|
||||
}
|
||||
return boundary.getBytes(StandardCharsets.ISO_8859_1);
|
||||
return boundary.getBytes(this.headersCharset);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BlockingMethodInNonBlockingContext")
|
||||
private Mono<Path> defaultFileStorageDirectory() {
|
||||
return Mono.fromCallable(() -> {
|
||||
Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir"), IDENTIFIER);
|
||||
if (!Files.exists(tempDirectory)) {
|
||||
Files.createDirectory(tempDirectory);
|
||||
}
|
||||
return tempDirectory;
|
||||
}).subscribeOn(this.blockingOperationScheduler);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.codec.multipart;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
|
||||
/**
|
||||
* Represents a directory used to store parts larger than
|
||||
* {@link DefaultPartHttpMessageReader#setMaxInMemorySize(int)}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3.7
|
||||
*/
|
||||
abstract class FileStorage {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FileStorage.class);
|
||||
|
||||
|
||||
protected FileStorage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mono of the directory to store files in.
|
||||
*/
|
||||
public abstract Mono<Path> directory();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code FileStorage} from a user-specified path. Creates the
|
||||
* path if it does not exist.
|
||||
*/
|
||||
public static FileStorage fromPath(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectory(path);
|
||||
}
|
||||
return new PathFileStorage(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code FileStorage} based a on a temporary directory.
|
||||
* @param scheduler scheduler to use for blocking operations
|
||||
*/
|
||||
public static FileStorage tempDirectory(Supplier<Scheduler> scheduler) {
|
||||
return new TempFileStorage(scheduler);
|
||||
}
|
||||
|
||||
|
||||
private static final class PathFileStorage extends FileStorage {
|
||||
|
||||
private final Mono<Path> directory;
|
||||
|
||||
public PathFileStorage(Path directory) {
|
||||
this.directory = Mono.just(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Path> directory() {
|
||||
return this.directory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class TempFileStorage extends FileStorage {
|
||||
|
||||
private static final String IDENTIFIER = "spring-multipart-";
|
||||
|
||||
private final Supplier<Scheduler> scheduler;
|
||||
|
||||
private volatile Mono<Path> directory = tempDirectory();
|
||||
|
||||
|
||||
public TempFileStorage(Supplier<Scheduler> scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Path> directory() {
|
||||
return this.directory
|
||||
.flatMap(this::createNewDirectoryIfDeleted)
|
||||
.subscribeOn(this.scheduler.get());
|
||||
}
|
||||
|
||||
private Mono<Path> createNewDirectoryIfDeleted(Path directory) {
|
||||
if (!Files.exists(directory)) {
|
||||
// Some daemons remove temp directories. Let's create a new one.
|
||||
Mono<Path> newDirectory = tempDirectory();
|
||||
this.directory = newDirectory;
|
||||
return newDirectory;
|
||||
}
|
||||
else {
|
||||
return Mono.just(directory);
|
||||
}
|
||||
}
|
||||
|
||||
private static Mono<Path> tempDirectory() {
|
||||
return Mono.fromCallable(() -> {
|
||||
Path directory = Files.createTempDirectory(IDENTIFIER);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Created temporary storage directory: " + directory);
|
||||
}
|
||||
return directory;
|
||||
}).cache();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.http.codec.multipart;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -69,11 +69,14 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
||||
|
||||
private final AtomicBoolean requestOutstanding = new AtomicBoolean();
|
||||
|
||||
private final Charset headersCharset;
|
||||
|
||||
private MultipartParser(FluxSink<Token> sink, byte[] boundary, int maxHeadersSize) {
|
||||
|
||||
private MultipartParser(FluxSink<Token> sink, byte[] boundary, int maxHeadersSize, Charset headersCharset) {
|
||||
this.sink = sink;
|
||||
this.boundary = boundary;
|
||||
this.maxHeadersSize = maxHeadersSize;
|
||||
this.headersCharset = headersCharset;
|
||||
this.state = new AtomicReference<>(new PreambleState());
|
||||
}
|
||||
|
||||
@@ -82,11 +85,13 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
||||
* @param buffers the input buffers
|
||||
* @param boundary the multipart boundary, as found in the {@code Content-Type} header
|
||||
* @param maxHeadersSize the maximum buffered header size
|
||||
* @param headersCharset the charset to use for decoding headers
|
||||
* @return a stream of parsed tokens
|
||||
*/
|
||||
public static Flux<Token> parse(Flux<DataBuffer> buffers, byte[] boundary, int maxHeadersSize) {
|
||||
public static Flux<Token> parse(Flux<DataBuffer> buffers, byte[] boundary, int maxHeadersSize,
|
||||
Charset headersCharset) {
|
||||
return Flux.create(sink -> {
|
||||
MultipartParser parser = new MultipartParser(sink, boundary, maxHeadersSize);
|
||||
MultipartParser parser = new MultipartParser(sink, boundary, maxHeadersSize, headersCharset);
|
||||
sink.onCancel(parser::onSinkCancel);
|
||||
sink.onRequest(n -> parser.requestBuffer());
|
||||
buffers.subscribe(parser);
|
||||
@@ -180,7 +185,7 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
||||
|
||||
|
||||
/**
|
||||
* Represents the output of {@link #parse(Flux, byte[], int)}.
|
||||
* Represents the output of {@link #parse(Flux, byte[], int, Charset)}.
|
||||
*/
|
||||
public abstract static class Token {
|
||||
|
||||
@@ -372,7 +377,6 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
||||
DataBufferUtils.release(buf);
|
||||
|
||||
emitHeaders(parseHeaders());
|
||||
// TODO: no need to check result of changeState, no further statements
|
||||
changeState(this, new BodyState(), bodyBuf);
|
||||
}
|
||||
else {
|
||||
@@ -408,7 +412,7 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
||||
}
|
||||
DataBuffer joined = this.buffers.get(0).factory().join(this.buffers);
|
||||
this.buffers.clear();
|
||||
String string = joined.toString(StandardCharsets.ISO_8859_1);
|
||||
String string = joined.toString(MultipartParser.this.headersCharset);
|
||||
DataBufferUtils.release(joined);
|
||||
String[] lines = string.split(HEADER_ENTRY_SEPARATOR);
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
|
||||
+3
-2
@@ -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,6 +20,7 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
@@ -53,7 +54,7 @@ import org.springframework.util.FastByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* Subscribes to a token stream (i.e. the result of
|
||||
* {@link MultipartParser#parse(Flux, byte[], int)}, and produces a flux of {@link Part} objects.
|
||||
* {@link MultipartParser#parse(Flux, byte[], int, Charset)}, and produces a flux of {@link Part} objects.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
|
||||
+38
-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.
|
||||
@@ -17,11 +17,13 @@
|
||||
package org.springframework.http.codec.multipart;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
@@ -78,12 +80,16 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> {
|
||||
|
||||
private static final String FILE_STORAGE_DIRECTORY_PREFIX = "synchronoss-file-upload-";
|
||||
|
||||
private int maxInMemorySize = 256 * 1024;
|
||||
|
||||
private long maxDiskUsagePerPart = -1;
|
||||
|
||||
private int maxParts = -1;
|
||||
|
||||
private Path fileStorageDirectory = createTempDirectory();
|
||||
|
||||
|
||||
/**
|
||||
* Configure the maximum amount of memory that is allowed to use per part.
|
||||
@@ -144,6 +150,22 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
return this.maxParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory used to store parts larger than
|
||||
* {@link #setMaxInMemorySize(int) maxInMemorySize}. By default, a new
|
||||
* temporary directory is created.
|
||||
* @throws IOException if an I/O error occurs, or the parent directory
|
||||
* does not exist
|
||||
* @since 5.3.7
|
||||
*/
|
||||
public void setFileStorageDirectory(Path fileStorageDirectory) throws IOException {
|
||||
Assert.notNull(fileStorageDirectory, "FileStorageDirectory must not be null");
|
||||
if (!Files.exists(fileStorageDirectory)) {
|
||||
Files.createDirectory(fileStorageDirectory);
|
||||
}
|
||||
this.fileStorageDirectory = fileStorageDirectory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
@@ -167,7 +189,7 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
|
||||
@Override
|
||||
public Flux<Part> read(ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) {
|
||||
return Flux.create(new SynchronossPartGenerator(message))
|
||||
return Flux.create(new SynchronossPartGenerator(message, this.fileStorageDirectory))
|
||||
.doOnNext(part -> {
|
||||
if (!Hints.isLoggingSuppressed(hints)) {
|
||||
LogFormatUtils.traceDebug(logger, traceOn -> Hints.getLogPrefix(hints) + "Parsed " +
|
||||
@@ -183,6 +205,15 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
return Mono.error(new UnsupportedOperationException("Cannot read multipart request body into single Part"));
|
||||
}
|
||||
|
||||
private static Path createTempDirectory() {
|
||||
try {
|
||||
return Files.createTempDirectory(FILE_STORAGE_DIRECTORY_PREFIX);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subscribe to the input stream and feed the Synchronoss parser. Then listen
|
||||
@@ -194,14 +225,17 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
|
||||
private final LimitedPartBodyStreamStorageFactory storageFactory = new LimitedPartBodyStreamStorageFactory();
|
||||
|
||||
private final Path fileStorageDirectory;
|
||||
|
||||
@Nullable
|
||||
private NioMultipartParserListener listener;
|
||||
|
||||
@Nullable
|
||||
private NioMultipartParser parser;
|
||||
|
||||
public SynchronossPartGenerator(ReactiveHttpInputMessage inputMessage) {
|
||||
public SynchronossPartGenerator(ReactiveHttpInputMessage inputMessage, Path fileStorageDirectory) {
|
||||
this.inputMessage = inputMessage;
|
||||
this.fileStorageDirectory = fileStorageDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,6 +252,7 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
|
||||
|
||||
this.parser = Multipart
|
||||
.multipart(context)
|
||||
.saveTemporaryFilesTo(this.fileStorageDirectory.toString())
|
||||
.usePartBodyStreamStorageFactory(this.storageFactory)
|
||||
.forNIO(this.listener);
|
||||
|
||||
|
||||
+41
-23
@@ -68,10 +68,10 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
@Nullable
|
||||
private volatile Subscriber<? super T> subscriber;
|
||||
|
||||
private volatile boolean completionBeforeDemand;
|
||||
private volatile boolean completionPending;
|
||||
|
||||
@Nullable
|
||||
private volatile Throwable errorBeforeDemand;
|
||||
private volatile Throwable errorPending;
|
||||
|
||||
private final String logPrefix;
|
||||
|
||||
@@ -186,7 +186,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
*/
|
||||
private boolean readAndPublish() throws IOException {
|
||||
long r;
|
||||
while ((r = this.demand) > 0 && !this.state.get().equals(State.COMPLETED)) {
|
||||
while ((r = this.demand) > 0 && (this.state.get() != State.COMPLETED)) {
|
||||
T data = read();
|
||||
if (data != null) {
|
||||
if (r != Long.MAX_VALUE) {
|
||||
@@ -222,27 +222,30 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
// Protect from infinite recursion in Undertow, where we can't check if data
|
||||
// is available, so all we can do is to try to read.
|
||||
// Generally, no need to check if we just came out of readAndPublish()...
|
||||
if (!oldState.equals(State.READING)) {
|
||||
if (oldState != State.READING) {
|
||||
checkOnDataAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCompletionOrErrorBeforeDemand() {
|
||||
private boolean handlePendingCompletionOrError() {
|
||||
State state = this.state.get();
|
||||
if (!state.equals(State.UNSUBSCRIBED) && !state.equals(State.SUBSCRIBING)) {
|
||||
if (this.completionBeforeDemand) {
|
||||
rsReadLogger.trace(getLogPrefix() + "Completed before demand");
|
||||
if (state == State.DEMAND || state == State.NO_DEMAND) {
|
||||
if (this.completionPending) {
|
||||
rsReadLogger.trace(getLogPrefix() + "Processing pending completion");
|
||||
this.state.get().onAllDataRead(this);
|
||||
return true;
|
||||
}
|
||||
Throwable ex = this.errorBeforeDemand;
|
||||
Throwable ex = this.errorPending;
|
||||
if (ex != null) {
|
||||
if (rsReadLogger.isTraceEnabled()) {
|
||||
rsReadLogger.trace(getLogPrefix() + "Completed with error before demand: " + ex);
|
||||
rsReadLogger.trace(getLogPrefix() + "Processing pending completion with error: " + ex);
|
||||
}
|
||||
this.state.get().onError(this, ex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Subscription createSubscription() {
|
||||
@@ -305,7 +308,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
publisher.subscriber = subscriber;
|
||||
subscriber.onSubscribe(subscription);
|
||||
publisher.changeState(SUBSCRIBING, NO_DEMAND);
|
||||
publisher.handleCompletionOrErrorBeforeDemand();
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Failed to transition to SUBSCRIBING, " +
|
||||
@@ -315,14 +318,14 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
|
||||
@Override
|
||||
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
|
||||
publisher.completionBeforeDemand = true;
|
||||
publisher.handleCompletionOrErrorBeforeDemand();
|
||||
publisher.completionPending = true;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
|
||||
publisher.errorBeforeDemand = ex;
|
||||
publisher.handleCompletionOrErrorBeforeDemand();
|
||||
publisher.errorPending = ex;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -341,14 +344,14 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
|
||||
@Override
|
||||
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
|
||||
publisher.completionBeforeDemand = true;
|
||||
publisher.handleCompletionOrErrorBeforeDemand();
|
||||
publisher.completionPending = true;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
|
||||
publisher.errorBeforeDemand = ex;
|
||||
publisher.handleCompletionOrErrorBeforeDemand();
|
||||
publisher.errorPending = ex;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -379,14 +382,17 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
boolean demandAvailable = publisher.readAndPublish();
|
||||
if (demandAvailable) {
|
||||
publisher.changeToDemandState(READING);
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
else {
|
||||
publisher.readingPaused();
|
||||
if (publisher.changeState(READING, NO_DEMAND)) {
|
||||
// Demand may have arrived since readAndPublish returned
|
||||
long r = publisher.demand;
|
||||
if (r > 0) {
|
||||
publisher.changeToDemandState(NO_DEMAND);
|
||||
if (!publisher.handlePendingCompletionOrError()) {
|
||||
// Demand may have arrived since readAndPublish returned
|
||||
long r = publisher.demand;
|
||||
if (r > 0) {
|
||||
publisher.changeToDemandState(NO_DEMAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,6 +414,18 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
|
||||
publisher.changeToDemandState(NO_DEMAND);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> void onAllDataRead(AbstractListenerReadPublisher<T> publisher) {
|
||||
publisher.completionPending = true;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> void onError(AbstractListenerReadPublisher<T> publisher, Throwable ex) {
|
||||
publisher.errorPending = ex;
|
||||
publisher.handlePendingCompletionOrError();
|
||||
}
|
||||
},
|
||||
|
||||
COMPLETED {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user