mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55be1e62f2 | |||
| f00bc7bb24 | |||
| 582bfccbb7 | |||
| 406b33d380 | |||
| f9c3d00af0 | |||
| d2715d2fa1 | |||
| 57b02da8b9 | |||
| df33bf27bd | |||
| f75cebd8f2 | |||
| 26f2dad388 | |||
| 7e390784ba | |||
| d5c5c91f41 | |||
| 1847d2f686 | |||
| 351a17a28b | |||
| dffb6c5bb6 | |||
| 286f5f295e | |||
| d8e5613ef7 | |||
| e319bd7016 | |||
| c8e9ad6289 | |||
| 562351f42f | |||
| 2302b30c2b | |||
| 96e3aa68a8 | |||
| 2303ea92fe | |||
| fa05de1d96 | |||
| 6b62b93d43 | |||
| 2eed02ea44 | |||
| bafb1c7e2b | |||
| c8df1861bf | |||
| ef81f06528 | |||
| a65ea7983d | |||
| 0ce1ef97ce | |||
| e26ea007fa | |||
| c10b6c2c3e | |||
| 63568e6c4f | |||
| d41048528f | |||
| 2384474615 | |||
| 0f04052ba1 | |||
| 98aa03c0c9 | |||
| 3e45b76132 | |||
| d42c9204ef | |||
| d2babd46a2 | |||
| eff9f5b92a | |||
| f1fed9c174 | |||
| d2e7cf4395 | |||
| 47a7abee8f | |||
| ef2c140d3c | |||
| de6cf845b1 | |||
| 7da43a80e4 | |||
| 8323c87ea2 | |||
| 0c65a5e04a | |||
| ca9eb57c94 | |||
| df8374693b | |||
| 9f35debdc6 | |||
| 8fe545edcd | |||
| f5baa329f7 | |||
| 924b684345 | |||
| a48e2f31a3 | |||
| 1833aafc3f | |||
| b8bc780365 | |||
| b3724ebf84 | |||
| 2e74a4d878 | |||
| 0c307b513e | |||
| 6b6beec4ee | |||
| 520a1130b8 | |||
| ceeb55291a | |||
| cbc2ab6ab9 | |||
| 7678286fb3 | |||
| 510ff87721 | |||
| 7609727433 | |||
| a0ae96da69 | |||
| 1d2daa5d6e | |||
| 239594595c | |||
| 76c0017180 | |||
| 0628b479f1 | |||
| f6205d4207 | |||
| d21100fea0 | |||
| f7f1028428 | |||
| 1cb0c7c036 | |||
| 51d70dcf34 | |||
| 61f7087911 | |||
| 2ff8a00e9a | |||
| a653b85378 | |||
| 17650e0741 | |||
| 0c17d257fb |
@@ -0,0 +1,20 @@
|
||||
name: Await HTTP Resource
|
||||
description: Waits for an HTTP resource to be available (a HEAD request succeeds)
|
||||
inputs:
|
||||
url:
|
||||
description: 'The URL of the resource to await'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Await HTTP resource
|
||||
shell: bash
|
||||
run: |
|
||||
url=${{ inputs.url }}
|
||||
echo "Waiting for $url"
|
||||
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
|
||||
do
|
||||
echo "."
|
||||
sleep 60
|
||||
done
|
||||
echo "$url is available"
|
||||
@@ -0,0 +1,56 @@
|
||||
name: 'Build'
|
||||
description: 'Builds the project, optionally publishing it to a local deployment repository'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '8'
|
||||
description: 'The Java version to compile and test with'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
publish:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether to publish artifacts ready for deployment to Artifactory'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
outputs:
|
||||
build-scan-url:
|
||||
description: 'The URL, if any, of the build scan produced by the build'
|
||||
value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }}
|
||||
version:
|
||||
description: 'The version that was built'
|
||||
value: ${{ steps.read-version.outputs.version }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Prepare Gradle Build
|
||||
uses: ./.github/actions/prepare-gradle-build
|
||||
with:
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
java-version: ${{ inputs.java-version }}
|
||||
java-early-access: ${{ inputs.java-early-access }}
|
||||
java-toolchain: ${{ inputs.java-toolchain }}
|
||||
- name: Build
|
||||
id: build
|
||||
if: ${{ inputs.publish == 'false' }}
|
||||
shell: bash
|
||||
run: ./gradlew check
|
||||
- name: Publish
|
||||
id: publish
|
||||
if: ${{ inputs.publish == 'true' }}
|
||||
shell: bash
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Read Version From gradle.properties
|
||||
id: read-version
|
||||
shell: bash
|
||||
run: |
|
||||
version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties)
|
||||
echo "Version is $version"
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Create GitHub Release
|
||||
description: Create the release on GitHub with a changelog
|
||||
inputs:
|
||||
milestone:
|
||||
description: Name of the GitHub milestone for which a release will be created
|
||||
required: true
|
||||
token:
|
||||
description: Token to use for authentication with GitHub
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Generate Changelog
|
||||
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
|
||||
with:
|
||||
milestone: ${{ inputs.milestone }}
|
||||
token: ${{ inputs.token }}
|
||||
config-file: .github/actions/create-github-release/changelog-generator.yml
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
shell: bash
|
||||
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
|
||||
+9
-1
@@ -17,4 +17,12 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
names:
|
||||
- "bclozel"
|
||||
- "jhoeller"
|
||||
- "poutsma"
|
||||
- "rstoyanchev"
|
||||
- "sbrannen"
|
||||
- "sdeleuze"
|
||||
- "simonbasle"
|
||||
- "snicoll"
|
||||
@@ -0,0 +1,49 @@
|
||||
name: 'Prepare Gradle Build'
|
||||
description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '8'
|
||||
description: 'The Java version to use for the build'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || 'liberica' }}
|
||||
java-version: |
|
||||
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
|
||||
${{ inputs.java-toolchain == 'true' && '8' || '' }}
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
|
||||
with:
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure Toolchain Properties
|
||||
if: ${{ inputs.java-toolchain == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ inputs.java-version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $HOME/.gradle/gradle.properties
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Send Notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Sync to Maven Central
|
||||
description: Syncs a release to Maven Central and waits for it to be available for use
|
||||
inputs:
|
||||
jfrog-cli-config-token:
|
||||
description: 'Config token for the JFrog CLI'
|
||||
required: true
|
||||
spring-framework-version:
|
||||
description: 'The version of Spring Framework that is being synced to Central'
|
||||
required: true
|
||||
ossrh-s01-token-username:
|
||||
description: 'Username for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-token-password:
|
||||
description: 'Password for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-staging-profile:
|
||||
description: 'Staging profile to use when syncing to Central'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Release Artifacts
|
||||
shell: bash
|
||||
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
|
||||
- name: Sync
|
||||
uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1
|
||||
with:
|
||||
username: ${{ inputs.ossrh-s01-token-username }}
|
||||
password: ${{ inputs.ossrh-s01-token-password }}
|
||||
staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }}
|
||||
create: true
|
||||
upload: true
|
||||
close: true
|
||||
release: true
|
||||
generate-checksums: true
|
||||
- name: Await
|
||||
uses: ./.github/actions/await-http-resource
|
||||
with:
|
||||
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"aql": {
|
||||
"items.find": {
|
||||
"$and": [
|
||||
{
|
||||
"@build.name": "${buildName}",
|
||||
"@build.number": "${buildNumber}",
|
||||
"path": {
|
||||
"$nmatch": "org/springframework/spring-*.zip"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": "nexus/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
@@ -0,0 +1,59 @@
|
||||
name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
name: Build and Deploy Snapshot
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: 'spring-framework-5.3.x'
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/spring-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/spring-*-docs.zip::zip.type=docs
|
||||
/**/spring-*-dist.zip::zip.type=dist
|
||||
/**/spring-*-schema.zip::zip.type=schema
|
||||
- name: Send Notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 8', github.ref_name) }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-deploy-snapshot
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
version: ${{ needs.build-and-deploy-snapshot.outputs.version }}
|
||||
@@ -0,0 +1,55 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
timeout-minutes: 60
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 8
|
||||
toolchain: false
|
||||
- version: 17
|
||||
toolchain: true
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 8
|
||||
steps:
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
id: build
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
java-version: ${{ matrix.java.version }}
|
||||
java-early-access: ${{ matrix.java.early-access || 'false' }}
|
||||
java-toolchain: ${{ matrix.java.toolchain }}
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
- name: Send Notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -1,13 +0,0 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v5.3.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and Stage Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
|
||||
repository: 'libs-staging-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/spring-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/spring-*-docs.zip::zip.type=docs
|
||||
/**/spring-*-dist.zip::zip.type=dist
|
||||
/**/spring-*-schema.zip::zip.type=schema
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
sync-to-maven-central:
|
||||
name: Sync to Maven Central
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Sync to Maven Central
|
||||
uses: ./.github/actions/sync-to-maven-central
|
||||
with:
|
||||
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
ossrh-s01-staging-profile: ${{ secrets.OSSRH_S01_STAGING_PROFILE }}
|
||||
ossrh-s01-token-password: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
|
||||
ossrh-s01-token-username: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
|
||||
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
promote-release:
|
||||
name: Promote Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- sync-to-maven-central
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote build
|
||||
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- promote-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Create GitHub Release
|
||||
uses: ./.github/actions/create-github-release
|
||||
with:
|
||||
milestone: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -0,0 +1,11 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validate Gradle Wrapper"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Verify
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
staging:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
secrets:
|
||||
repository-username:
|
||||
required: false
|
||||
repository-password:
|
||||
required: false
|
||||
google-chat-webhook-url:
|
||||
required: true
|
||||
token:
|
||||
required: true
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Release Verification Tests
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: spring-projects/spring-framework-release-verification
|
||||
ref: 'v0.0.2'
|
||||
token: ${{ secrets.token }}
|
||||
- name: Check Out Send Notification Action
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: spring-framework
|
||||
sparse-checkout: .github/actions/send-notification
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 8
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
- name: Run Release Verification Tests
|
||||
env:
|
||||
RVT_VERSION: ${{ inputs.version }}
|
||||
RVT_RELEASE_TYPE: oss
|
||||
RVT_STAGING: ${{ inputs.staging }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
- name: Send Notification
|
||||
uses: ./spring-framework/.github/actions/send-notification
|
||||
if: failure()
|
||||
with:
|
||||
webhook-url: ${{ secrets.google-chat-webhook-url }}
|
||||
status: ${{ job.status }}
|
||||
run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }}
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A5.3.x) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
+3
-3
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.107.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.42"
|
||||
mavenBom "io.netty:netty-bom:4.1.112.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.47"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
|
||||
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
|
||||
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
|
||||
|
||||
You can then login with the instance using:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring login -n spring-framework -c https://ci.spring.io
|
||||
----
|
||||
|
||||
Once logged in, you should get something like:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly ts
|
||||
name url team expiry
|
||||
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
|
||||
----
|
||||
|
||||
=== Pipeline configuration and structure
|
||||
|
||||
The build pipelines are described in `pipeline.yml` file.
|
||||
|
||||
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
|
||||
|
||||
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
|
||||
|
||||
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
|
||||
|
||||
You'll find in this folder the following resources:
|
||||
|
||||
* `pipeline.yml` the build pipeline
|
||||
* `parameters.yml` the build parameters used for the pipeline
|
||||
* `images/` holds the container images definitions used in this pipeline
|
||||
* `scripts/` holds the build scripts that ship within the CI container images
|
||||
* `tasks` contains the task definitions used in the main `pipeline.yml`
|
||||
|
||||
=== Updating the build pipeline
|
||||
|
||||
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
|
||||
|
||||
The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
@@ -1,10 +0,0 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
sonatype:
|
||||
exclude:
|
||||
- 'build-info\.json'
|
||||
- '.*\.zip'
|
||||
@@ -1,21 +0,0 @@
|
||||
== CI Images
|
||||
|
||||
These images are used by CI to run the actual builds.
|
||||
|
||||
To build the image locally run the following from this directory:
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f <image-folder>/Dockerfile .
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
|
||||
----
|
||||
|
||||
To test run:
|
||||
|
||||
----
|
||||
$ docker run -it --entrypoint /bin/bash <SHA>
|
||||
----
|
||||
@@ -1,10 +0,0 @@
|
||||
FROM ubuntu:jammy-20240111
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java8
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u402%2B7/bellsoft-jdk8u402+7-linux-amd64.tar.gz"
|
||||
;;
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
###########################################################
|
||||
# UTILS
|
||||
###########################################################
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
|
||||
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
|
||||
dpkg-reconfigure --frontend noninteractive tzdata
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
###########################################################
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java8 java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
pushd $jdk > /dev/null
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f bin/java
|
||||
test -f bin/javac
|
||||
popd > /dev/null
|
||||
done
|
||||
popd
|
||||
|
||||
###########################################################
|
||||
# GRADLE ENTERPRISE
|
||||
###########################################################
|
||||
cd /
|
||||
mkdir ~/.gradle
|
||||
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
|
||||
@@ -1,11 +0,0 @@
|
||||
github-repo: "https://github.com/spring-projects/spring-framework.git"
|
||||
github-repo-name: "spring-projects/spring-framework"
|
||||
sonatype-staging-profile: "org.springframework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "5.3.x"
|
||||
milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
-399
@@ -1,399 +0,0 @@
|
||||
anchors:
|
||||
git-repo-resource-source: &git-repo-resource-source
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
SONATYPE_URL: ((sonatype-url))
|
||||
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
body: generated-changelog/changelog.md
|
||||
github-task-params: &github-task-params
|
||||
GITHUB_USERNAME: ((github-username))
|
||||
GITHUB_TOKEN: ((github-ci-release-token))
|
||||
|
||||
resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.7.1
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.18
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.8.0
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
<<: *git-repo-resource-source
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
paths: ["ci/images/*"]
|
||||
- name: ci-image
|
||||
type: registry-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
source:
|
||||
uri: ((artifactory-server))
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk17-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk17-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: true
|
||||
release: false
|
||||
- name: github-release
|
||||
type: github-release
|
||||
icon: briefcase-download
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: false
|
||||
jobs:
|
||||
- name: build-ci-images
|
||||
plan:
|
||||
- get: git-repo
|
||||
- get: ci-images-git-repo
|
||||
trigger: true
|
||||
- task: build-ci-image
|
||||
privileged: true
|
||||
file: git-repo/ci/tasks/build-ci-image.yml
|
||||
output_mapping:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *docker-resource-source
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/spring-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/spring-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/spring-*-dist.zip"
|
||||
properties:
|
||||
"zip.type": "dist"
|
||||
- include:
|
||||
- "/**/spring-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk17-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 17
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-milestone]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-rc]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *artifactory-task-params
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
passed: [promote-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *github-task-params
|
||||
- put: github-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk17-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-ci-images"]
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,2 +0,0 @@
|
||||
source /opt/concourse-java.sh
|
||||
setup_symlinks
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
${version} generated-changelog/changelog.md
|
||||
|
||||
echo ${version} > generated-changelog/version
|
||||
echo v${version} > generated-changelog/tag
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
git fetch --tags --all > /dev/null
|
||||
popd > /dev/null
|
||||
|
||||
git clone git-repo stage-git-repo > /dev/null
|
||||
|
||||
pushd stage-git-repo > /dev/null
|
||||
|
||||
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
|
||||
if [[ $RELEASE_TYPE = "M" ]]; then
|
||||
stageVersion=$( get_next_milestone_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RC" ]]; then
|
||||
stageVersion=$( get_next_rc_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
|
||||
stageVersion=$( get_next_release $snapshotVersion)
|
||||
nextVersion=$( bump_version_number $snapshotVersion)
|
||||
else
|
||||
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
|
||||
fi
|
||||
|
||||
echo "Staging $stageVersion (next version will be $nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
|
||||
|
||||
git config user.name "Spring Builds" > /dev/null
|
||||
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Release v$stageVersion" > /dev/null
|
||||
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
|
||||
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
if [[ $nextVersion != $snapshotVersion ]]; then
|
||||
echo "Setting next development version (v$nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Next development version (v$nextVersion)" > /dev/null
|
||||
fi;
|
||||
|
||||
echo "Staging Complete"
|
||||
|
||||
popd > /dev/null
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.10.0
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
- name: image
|
||||
caches:
|
||||
- path: ci-image-cache
|
||||
params:
|
||||
CONTEXT: ci-images-git-repo/ci/images
|
||||
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
|
||||
DOCKER_HUB_AUTH: ((docker-hub-auth))
|
||||
run:
|
||||
path: /bin/sh
|
||||
args:
|
||||
- "-c"
|
||||
- |
|
||||
mkdir -p /root/.docker
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
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,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- 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-project.sh
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
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/check-project.sh
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.7'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: generated-changelog
|
||||
params:
|
||||
GITHUB_ORGANIZATION:
|
||||
GITHUB_REPO:
|
||||
GITHUB_USERNAME:
|
||||
GITHUB_TOKEN:
|
||||
RELEASE_TYPE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/generate-changelog.sh
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/promote-version.sh
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: stage-git-repo
|
||||
- name: distribution-repository
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
caches:
|
||||
- path: gradle
|
||||
run:
|
||||
path: git-repo/ci/scripts/stage-version.sh
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.33
|
||||
version=5.3.38
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class EnableCachingIntegrationTests {
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in .integration-tests that it's not found.
|
||||
assertThatException().isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-2
@@ -561,8 +561,7 @@ public class EnvironmentSystemIntegrationTests {
|
||||
{
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.getEnvironment().setRequiredProperties("foo", "bar");
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
|
||||
ctx::refresh);
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(ctx::refresh);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class EnableTransactionManagementIntegrationTests {
|
||||
ctx.register(Config.class, AspectJTxConfig.class);
|
||||
// this test is a bit fragile, but gets the job done, proving that an
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in .integration-tests that it's not found.
|
||||
// in integration-tests that it's not found.
|
||||
assertThatException()
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.12.1"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -42,7 +42,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
gradleEnterprise {
|
||||
develocity {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
buildDir.mkdirs()
|
||||
|
||||
+47
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.aop.aspectj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
@@ -42,6 +43,7 @@ import org.aspectj.weaver.tools.PointcutParameter;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
||||
@@ -85,6 +87,8 @@ import org.springframework.util.StringUtils;
|
||||
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
|
||||
|
||||
static {
|
||||
@@ -106,6 +110,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private Class<?> pointcutDeclarationScope;
|
||||
|
||||
private boolean aspectCompiledByAjc;
|
||||
|
||||
private String[] pointcutParameterNames = new String[0];
|
||||
|
||||
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
|
||||
@@ -119,6 +125,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private transient PointcutExpression pointcutExpression;
|
||||
|
||||
private transient boolean pointcutParsingFailed = false;
|
||||
|
||||
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
|
||||
|
||||
|
||||
@@ -135,7 +143,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
* @param paramTypes the parameter types for the pointcut
|
||||
*/
|
||||
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
|
||||
this.pointcutDeclarationScope = declarationScope;
|
||||
setPointcutDeclarationScope(declarationScope);
|
||||
if (paramNames.length != paramTypes.length) {
|
||||
throw new IllegalStateException(
|
||||
"Number of pointcut parameter names must match number of pointcut parameter types");
|
||||
@@ -150,6 +158,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
*/
|
||||
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
|
||||
this.pointcutDeclarationScope = pointcutDeclarationScope;
|
||||
this.aspectCompiledByAjc = compiledByAjc(pointcutDeclarationScope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,25 +183,30 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
* Check whether this pointcut is ready to match.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
private void checkExpression() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutClassLoader = determinePointcutClassLoader();
|
||||
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
|
||||
@@ -269,10 +283,18 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
if (this.pointcutParsingFailed) {
|
||||
// Pointcut parsing failed before below -> avoid trying again.
|
||||
return false;
|
||||
}
|
||||
if (this.aspectCompiledByAjc && compiledByAjc(targetClass)) {
|
||||
// ajc-compiled aspect class for ajc-compiled target class -> already weaved.
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -283,6 +305,12 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
|
||||
this.pointcutParsingFailed = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -291,7 +319,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
@@ -329,7 +356,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
@@ -517,6 +543,16 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return shadowMatch;
|
||||
}
|
||||
|
||||
private static boolean compiledByAjc(Class<?> clazz) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
return (superclass != null && compiledByAjc(superclass));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
|
||||
+3
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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.aop.aspectj.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
@@ -57,8 +56,6 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
|
||||
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
|
||||
|
||||
@@ -69,37 +66,11 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
|
||||
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
|
||||
* is that aspects written in the code-style (AspectJ language) also have the annotation present
|
||||
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to detect this as "code-style" AspectJ aspects should not be
|
||||
* interpreted by Spring AOP.
|
||||
*/
|
||||
private boolean compiledByAjc(Class<?> clazz) {
|
||||
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
|
||||
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
|
||||
// an implementation detail of the AspectJ compiler.
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
// If the parent has the annotation and isn't abstract it's an error
|
||||
@@ -124,6 +95,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...).
|
||||
@@ -163,7 +135,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
|
||||
|
||||
/**
|
||||
* Class modelling an AspectJ annotation, exposing its type enumeration and
|
||||
* Class modeling an AspectJ annotation, exposing its type enumeration and
|
||||
* pointcut String.
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
|
||||
+11
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,10 +126,16 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
Aspect ann = aspectClass.getAnnotation(Aspect.class);
|
||||
if (ann == null) {
|
||||
return "";
|
||||
}
|
||||
String value = ann.value();
|
||||
int beginIndex = value.indexOf('(');
|
||||
if (beginIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(beginIndex + 1, value.length() - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,9 +22,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -40,6 +43,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanFactoryAspectJAdvisorsBuilder.class);
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final AspectJAdvisorFactory advisorFactory;
|
||||
@@ -102,30 +107,37 @@ public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
continue;
|
||||
}
|
||||
if (this.advisorFactory.isAspect(beanType)) {
|
||||
aspectNames.add(beanName);
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
try {
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
aspectNames.add(beanName);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConvertingComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -133,17 +134,19 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
for (Method method : getAdvisorMethods(aspectClass)) {
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,8 +213,16 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
try {
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,7 +44,8 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP proxy configuration managers.
|
||||
* These are not themselves AOP proxies, but subclasses of this class are
|
||||
*
|
||||
* <p>These are not themselves AOP proxies, but subclasses of this class are
|
||||
* normally factories from which AOP proxy instances are obtained directly.
|
||||
*
|
||||
* <p>This class frees subclasses of the housekeeping of Advices
|
||||
@@ -52,7 +53,8 @@ import org.springframework.util.CollectionUtils;
|
||||
* methods, which are provided by subclasses.
|
||||
*
|
||||
* <p>This class is serializable; subclasses need not be.
|
||||
* This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* <p>This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -104,7 +106,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a AdvisedSupport instance with the given parameters.
|
||||
* Create an {@code AdvisedSupport} instance with the given parameters.
|
||||
* @param interfaces the proxied interfaces
|
||||
*/
|
||||
public AdvisedSupport(Class<?>... interfaces) {
|
||||
@@ -115,7 +117,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
/**
|
||||
* Set the given object as target.
|
||||
* Will create a SingletonTargetSource for the object.
|
||||
* <p>Will create a SingletonTargetSource for the object.
|
||||
* @see #setTargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
*/
|
||||
@@ -344,8 +346,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
@@ -491,9 +492,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the AOP configuration from the given AdvisedSupport object,
|
||||
* but allow substitution of a fresh TargetSource and a given interceptor chain.
|
||||
* @param other the AdvisedSupport object to take proxy configuration from
|
||||
* Copy the AOP configuration from the given {@link AdvisedSupport} object,
|
||||
* but allow substitution of a fresh {@link TargetSource} and a given interceptor chain.
|
||||
* @param other the {@code AdvisedSupport} object to take proxy configuration from
|
||||
* @param targetSource the new TargetSource
|
||||
* @param advisors the Advisors for the chain
|
||||
*/
|
||||
@@ -513,8 +514,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a configuration-only copy of this AdvisedSupport,
|
||||
* replacing the TargetSource.
|
||||
* Build a configuration-only copy of this {@link AdvisedSupport},
|
||||
* replacing the {@link TargetSource}.
|
||||
*/
|
||||
AdvisedSupport getConfigurationOnlyCopy() {
|
||||
AdvisedSupport copy = new AdvisedSupport();
|
||||
|
||||
+35
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,6 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
@@ -42,14 +39,13 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AspectJExpressionPointcutTests {
|
||||
|
||||
@@ -65,7 +61,7 @@ public class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
public void setup() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
@@ -175,25 +171,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -210,8 +206,10 @@ public class AspectJExpressionPointcutTests {
|
||||
// not currently testable in a reliable fashion
|
||||
//assertDoesNotMatchStringClass(classFilter);
|
||||
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
|
||||
.as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
|
||||
.as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
|
||||
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
|
||||
}
|
||||
@@ -246,14 +244,13 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
Pointcut pointcut = getPointcut(pointcutExpression);
|
||||
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
|
||||
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
|
||||
advisor.setAdvice(interceptor);
|
||||
@@ -277,40 +274,29 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
|
||||
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndSubstitution() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndSubstitutions() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
}
|
||||
|
||||
private Pointcut getPointcut(String expression) {
|
||||
private AspectJExpressionPointcut getPointcut(String expression) {
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression(expression);
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
@@ -505,6 +491,15 @@ public class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HasGeneric {
|
||||
|
||||
public void setFriends(List<TestBean> friends) {
|
||||
|
||||
+48
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,6 @@ import org.aspectj.lang.annotation.DeclareParents;
|
||||
import org.aspectj.lang.annotation.DeclarePrecedence;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.DefaultLockable;
|
||||
import test.aop.Lockable;
|
||||
@@ -76,25 +75,24 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
/**
|
||||
* To be overridden by concrete test subclasses.
|
||||
* @return the fixture
|
||||
*/
|
||||
protected abstract AspectJAdvisorFactory getFixture();
|
||||
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
.withMessageContaining("PERCFLOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowBelowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -385,8 +383,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).as("Already locked").isTrue();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
lockable.unlock());
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -413,9 +410,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
lockable.locked();
|
||||
}
|
||||
|
||||
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
|
||||
@Test
|
||||
@Disabled
|
||||
void introductionWithArgumentBinding() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
@@ -523,6 +518,16 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parentAspect() {
|
||||
TestBean target = new TestBean("Jane", 42);
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
|
||||
new IncrementingAspect(), "incrementingAspect");
|
||||
ITestBean proxy = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
|
||||
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureWithoutExplicitDeclarePrecedence() {
|
||||
TestBean target = new TestBean();
|
||||
@@ -647,7 +652,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
static class NamedPointcutAspectWithFQN {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Pointcut("execution(* getAge())")
|
||||
void getAge() {
|
||||
@@ -767,6 +772,31 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
abstract static class DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Override
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
private static class InvocationTrackingAspect {
|
||||
|
||||
@@ -824,7 +854,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 666;
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +874,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 666;
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,7 +1096,7 @@ class PerThisAspect {
|
||||
|
||||
// Just to check that this doesn't cause problems with introduction processing
|
||||
@SuppressWarnings("unused")
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Around("execution(int *.getAge())")
|
||||
int returnCountAsAge() {
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,12 +22,13 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AutoProxyWithCodeStyleAspectsTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void noAutoproxyingOfAjcCompiledAspects() {
|
||||
public void noAutoProxyingOfAjcCompiledAspects() {
|
||||
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,10 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SpringConfiguredWithAutoProxyingTests {
|
||||
|
||||
@Test
|
||||
|
||||
+17
-4
@@ -2,16 +2,29 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache-3.1.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
|
||||
factory-method="aspectOf">
|
||||
<context:spring-configured/>
|
||||
|
||||
<cache:annotation-driven mode="aspectj"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager"/>
|
||||
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
|
||||
<property name="foo" value="bar"/>
|
||||
</bean>
|
||||
|
||||
<bean id="otherBean" class="java.lang.Object"/>
|
||||
<bean id="otherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="yetAnotherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Vendored
+1
-2
@@ -24,8 +24,7 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
|
||||
+2
-4
@@ -7,12 +7,10 @@
|
||||
http://www.springframework.org/schema/task
|
||||
https://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor"
|
||||
exception-handler="testExceptionHandler"/>
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor" exception-handler="testExceptionHandler"/>
|
||||
|
||||
<task:executor id="testExecutor"/>
|
||||
|
||||
<bean id="testExceptionHandler"
|
||||
class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
<bean id="testExceptionHandler" class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -239,7 +239,7 @@ public interface BeanFactory {
|
||||
* specific type, specify the actual bean type as an argument here and subsequently
|
||||
* use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options.
|
||||
* <p>Also, generics matching is strict here, as per the Java assignment rules.
|
||||
* For lenient fallback matching with unchecked semantics (similar to the ´unchecked´
|
||||
* For lenient fallback matching with unchecked semantics (similar to the 'unchecked'
|
||||
* Java compiler warning), consider calling {@link #getBeanProvider(Class)} with the
|
||||
* raw type as a second step if no full generic match is
|
||||
* {@link ObjectProvider#getIfAvailable() available} with this variant.
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -835,11 +835,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
/**
|
||||
* This implementation attempts to query the FactoryBean's generic parameter metadata
|
||||
* if present to determine the object type. If not present, i.e. the FactoryBean is
|
||||
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
|
||||
* on a plain instance of the FactoryBean, without bean properties applied yet.
|
||||
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
|
||||
* creation of the FactoryBean is attempted as fallback (through delegation to the
|
||||
* superclass implementation).
|
||||
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
|
||||
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
|
||||
* it will be fully created to check the type of its exposed object.
|
||||
|
||||
+2
-2
@@ -1080,7 +1080,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "applicationStartup should not be null");
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -1695,7 +1695,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. The implementation is allowed to instantiate the target factory bean if
|
||||
* {@code allowInit} is {@code true} and the type cannot be determined another way;
|
||||
* otherwise it is restricted to introspecting signatures and related metadata.
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
|
||||
+11
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -92,9 +92,9 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
// 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).
|
||||
catch (FileSystemNotFoundException | IllegalArgumentException ex) {
|
||||
// URI scheme not registered for NIO or not meeting Paths requirements:
|
||||
// let's try URL protocol handlers via Spring's resource mechanism.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,13 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
setValue(resource.getFile().toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not retrieve file for " + resource + ": " + ex.getMessage());
|
||||
String msg = "Could not resolve \"" + text + "\" to 'java.nio.file.Path' for " + resource + ": " +
|
||||
ex.getMessage();
|
||||
if (nioPathCandidate) {
|
||||
msg += " - In case of ambiguity, consider adding the 'file:' prefix for an explicit reference " +
|
||||
"to a file system resource of the same name: \"file:" + text + "\"";
|
||||
}
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,6 +42,8 @@ import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.beans.testfixture.beans.DerivedTestBean;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cglib.proxy.MethodInterceptor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -52,7 +54,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BeanUtils}.
|
||||
* Tests for {@link BeanUtils}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
@@ -136,7 +138,7 @@ class BeanUtilsTests {
|
||||
PropertyDescriptor[] actual = Introspector.getBeanInfo(TestBean.class).getPropertyDescriptors();
|
||||
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(TestBean.class);
|
||||
assertThat(descriptors).as("Descriptors should not be null").isNotNull();
|
||||
assertThat(descriptors.length).as("Invalid number of descriptors returned").isEqualTo(actual.length);
|
||||
assertThat(descriptors).as("Invalid number of descriptors returned").hasSameSizeAs(actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,13 +164,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
TestBean tb2 = new TestBean();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,13 +180,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
TestBean tb2 = new TestBean();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -194,13 +196,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
DerivedTestBean tb2 = new DerivedTestBean();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,8 +229,8 @@ class BeanUtilsTests {
|
||||
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, integerListHolder2);
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder2.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder2.getList()).containsExactly(42);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,7 +259,7 @@ class BeanUtilsTests {
|
||||
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, wildcardListHolder2);
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(wildcardListHolder2.getList()).isEqualTo(Arrays.asList(42));
|
||||
}
|
||||
|
||||
@@ -271,9 +273,8 @@ class BeanUtilsTests {
|
||||
NumberUpperBoundedWildcardListHolder numberListHolder = new NumberUpperBoundedWildcardListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, numberListHolder);
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(numberListHolder.getList()).hasSize(1);
|
||||
assertThat(numberListHolder.getList().contains(Integer.valueOf(42))).isTrue();
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(numberListHolder.getList()).isEqualTo(Arrays.asList(42));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +283,7 @@ class BeanUtilsTests {
|
||||
@Test
|
||||
void copyPropertiesDoesNotCopyFromSuperTypeToSubType() {
|
||||
NumberHolder numberHolder = new NumberHolder();
|
||||
numberHolder.setNumber(Integer.valueOf(42));
|
||||
numberHolder.setNumber(42);
|
||||
IntegerHolder integerHolder = new IntegerHolder();
|
||||
|
||||
BeanUtils.copyProperties(numberHolder, integerHolder);
|
||||
@@ -300,7 +301,7 @@ class BeanUtilsTests {
|
||||
LongListHolder longListHolder = new LongListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder, longListHolder);
|
||||
assertThat(integerListHolder.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder.getList()).containsExactly(42);
|
||||
assertThat(longListHolder.getList()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -314,7 +315,7 @@ class BeanUtilsTests {
|
||||
NumberListHolder numberListHolder = new NumberListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder, numberListHolder);
|
||||
assertThat(integerListHolder.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder.getList()).containsExactly(42);
|
||||
assertThat(numberListHolder.getList()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -323,12 +324,13 @@ class BeanUtilsTests {
|
||||
Order original = new Order("test", Arrays.asList("foo", "bar"));
|
||||
|
||||
// Create a Proxy that loses the generic type information for the getLineItems() method.
|
||||
OrderSummary proxy = proxyOrder(original);
|
||||
OrderSummary proxy = (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] {OrderSummary.class}, new OrderInvocationHandler(original));
|
||||
assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
|
||||
// Ensure that our custom Proxy works as expected.
|
||||
assertThat(proxy.getId()).isEqualTo("test");
|
||||
@@ -341,40 +343,57 @@ class BeanUtilsTests {
|
||||
assertThat(target.getLineItems()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // gh-32888
|
||||
public void copyPropertiesWithGenericCglibClass() {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(User.class);
|
||||
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
|
||||
User user = (User) enhancer.create();
|
||||
user.setId(1);
|
||||
user.setName("proxy");
|
||||
user.setAddress("addr");
|
||||
|
||||
User target = new User();
|
||||
BeanUtils.copyProperties(user, target);
|
||||
assertThat(target.getId()).isEqualTo(user.getId());
|
||||
assertThat(target.getName()).isEqualTo(user.getName());
|
||||
assertThat(target.getAddress()).isEqualTo(user.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyPropertiesWithEditable() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
assertThat(tb.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb.getName()).as("Name empty").isNull();
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("bla");
|
||||
TestBean tb2 = new TestBean();
|
||||
tb2.setName("rod");
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
|
||||
// "touchy" should not be copied: it's not defined in ITestBean
|
||||
BeanUtils.copyProperties(tb, tb2, ITestBean.class);
|
||||
assertThat(tb2.getName() == null).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == 32).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
|
||||
assertThat(tb2.getName()).as("Name copied").isNull();
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(32);
|
||||
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyPropertiesWithIgnore() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
assertThat(tb.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb.getName()).as("Name empty").isNull();
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("bla");
|
||||
TestBean tb2 = new TestBean();
|
||||
tb2.setName("rod");
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
|
||||
// "spouse", "touchy", "age" should not be copied
|
||||
BeanUtils.copyProperties(tb, tb2, "spouse", "touchy", "age");
|
||||
assertThat(tb2.getName() == null).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age still empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
|
||||
assertThat(tb2.getName()).as("Name copied").isNull();
|
||||
assertThat(tb2.getAge()).as("Age still empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,7 +402,7 @@ class BeanUtilsTests {
|
||||
source.setName("name");
|
||||
TestBean target = new TestBean();
|
||||
BeanUtils.copyProperties(source, target, "specialProperty");
|
||||
assertThat("name").isEqualTo(target.getName());
|
||||
assertThat(target.getName()).isEqualTo("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -520,6 +539,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerHolder {
|
||||
|
||||
@@ -534,6 +554,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder1 {
|
||||
|
||||
@@ -548,6 +569,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder2 {
|
||||
|
||||
@@ -562,6 +584,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberUpperBoundedWildcardListHolder {
|
||||
|
||||
@@ -576,6 +599,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberListHolder {
|
||||
|
||||
@@ -590,6 +614,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder1 {
|
||||
|
||||
@@ -604,6 +629,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder2 {
|
||||
|
||||
@@ -618,6 +644,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class LongListHolder {
|
||||
|
||||
@@ -798,6 +825,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithNullableTypes {
|
||||
|
||||
private Integer counter;
|
||||
@@ -828,6 +856,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithPrimitiveTypes {
|
||||
|
||||
private boolean flag;
|
||||
@@ -840,7 +869,6 @@ class BeanUtilsTests {
|
||||
private char character;
|
||||
private String text;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public BeanWithPrimitiveTypes(boolean flag, byte byteCount, short shortCount, int intCount, long longCount,
|
||||
float floatCount, double doubleCount, char character, String text) {
|
||||
@@ -891,21 +919,22 @@ class BeanUtilsTests {
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class PrivateBeanWithPrivateConstructor {
|
||||
|
||||
private PrivateBeanWithPrivateConstructor() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Order {
|
||||
|
||||
private String id;
|
||||
private List<String> lineItems;
|
||||
|
||||
private List<String> lineItems;
|
||||
|
||||
Order() {
|
||||
}
|
||||
@@ -937,6 +966,7 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private interface OrderSummary {
|
||||
|
||||
String getId();
|
||||
@@ -945,17 +975,10 @@ class BeanUtilsTests {
|
||||
}
|
||||
|
||||
|
||||
private OrderSummary proxyOrder(Order order) {
|
||||
return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order));
|
||||
}
|
||||
|
||||
|
||||
private static class OrderInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Order order;
|
||||
|
||||
|
||||
OrderInvocationHandler(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
@@ -973,4 +996,46 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class GenericBaseModel<T> {
|
||||
|
||||
private T id;
|
||||
|
||||
private String name;
|
||||
|
||||
public T getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(T id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class User extends GenericBaseModel<Integer> {
|
||||
|
||||
private String address;
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,79 +31,82 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class FileEditorTests {
|
||||
class FileEditorTests {
|
||||
|
||||
@Test
|
||||
public void testClasspathFileName() throws Exception {
|
||||
void testClasspathFileName() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
assertThat(file.exists()).isTrue();
|
||||
assertThat(file).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() throws Exception {
|
||||
PropertyEditor propertyEditor = new FileEditor();
|
||||
void testWithNonExistentResource() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
propertyEditor.setAsText("classpath:no_way_this_file_is_found.doc"));
|
||||
fileEditor.setAsText("classpath:no_way_this_file_is_found.doc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentFile() throws Exception {
|
||||
void testWithNonExistentFile() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("file:no_way_this_file_is_found.doc");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition1 = value instanceof File;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
boolean condition = !file.exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(file).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsoluteFileName() throws Exception {
|
||||
void testAbsoluteFileName() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("/no_way_this_file_is_found.doc");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition1 = value instanceof File;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
boolean condition = !file.exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(file).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedFileNameFound() throws Exception {
|
||||
void testCurrentDirectory() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("file:.");
|
||||
Object value = fileEditor.getValue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
assertThat(file).isEqualTo(new File("."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnqualifiedFileNameFound() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class";
|
||||
fileEditor.setAsText(fileName);
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
assertThat(file.exists()).isTrue();
|
||||
assertThat(file).exists();
|
||||
String absolutePath = file.getAbsolutePath().replace('\\', '/');
|
||||
assertThat(absolutePath.endsWith(fileName)).isTrue();
|
||||
assertThat(absolutePath).endsWith(fileName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedFileNameNotFound() throws Exception {
|
||||
void testUnqualifiedFileNameNotFound() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".clazz";
|
||||
fileEditor.setAsText(fileName);
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value).isInstanceOf(File.class);
|
||||
File file = (File) value;
|
||||
assertThat(file.exists()).isFalse();
|
||||
assertThat(file).doesNotExist();
|
||||
String absolutePath = file.getAbsolutePath().replace('\\', '/');
|
||||
assertThat(absolutePath.endsWith(fileName)).isTrue();
|
||||
assertThat(absolutePath).endsWith(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+39
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.beans.propertyeditors;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -31,65 +32,65 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public class PathEditorTests {
|
||||
class PathEditorTests {
|
||||
|
||||
@Test
|
||||
public void testClasspathPathName() {
|
||||
void testClasspathPathName() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(path.toFile().exists()).isTrue();
|
||||
assertThat(path.toFile()).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() {
|
||||
PropertyEditor propertyEditor = new PathEditor();
|
||||
void testWithNonExistentResource() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"));
|
||||
pathEditor.setAsText("classpath:/no_way_this_file_is_found.doc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentPath() {
|
||||
void testWithNonExistentPath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("file:/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
assertThat(path.toFile()).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsolutePath() {
|
||||
void testAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
assertThat(path.toFile()).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsAbsolutePath() {
|
||||
void testWindowsAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
assertThat(path.toFile()).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsAbsoluteFilePath() {
|
||||
void testWindowsAbsoluteFilePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
try {
|
||||
pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
assertThat(path.toFile()).doesNotExist();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (File.separatorChar == '\\') { // on Windows, otherwise silently ignore
|
||||
@@ -99,39 +100,49 @@ public class PathEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameFound() {
|
||||
void testCurrentDirectory() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("file:.");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
assertThat(path).isEqualTo(Paths.get("."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnqualifiedPathNameFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isTrue();
|
||||
assertThat(file).exists();
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
if (File.separatorChar == '\\') {
|
||||
absolutePath = absolutePath.replace('\\', '/');
|
||||
}
|
||||
assertThat(absolutePath.endsWith(fileName)).isTrue();
|
||||
assertThat(absolutePath).endsWith(fileName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameNotFound() {
|
||||
void testUnqualifiedPathNameNotFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".clazz";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
assertThat(value).isInstanceOf(Path.class);
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isFalse();
|
||||
assertThat(file).doesNotExist();
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
if (File.separatorChar == '\\') {
|
||||
absolutePath = absolutePath.replace('\\', '/');
|
||||
}
|
||||
assertThat(absolutePath.endsWith(fileName)).isTrue();
|
||||
assertThat(absolutePath).endsWith(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.context.ApplicationEvent;
|
||||
public abstract class ApplicationContextEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* Create a new ContextStartedEvent.
|
||||
* Create a new {@code ApplicationContextEvent}.
|
||||
* @param source the {@code ApplicationContext} that the event is raised for
|
||||
* (must not be {@code null})
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.context.ApplicationContext;
|
||||
public class ContextClosedEvent extends ApplicationContextEvent {
|
||||
|
||||
/**
|
||||
* Creates a new ContextClosedEvent.
|
||||
* Create a new {@code ContextClosedEvent}.
|
||||
* @param source the {@code ApplicationContext} that has been closed
|
||||
* (must not be {@code null})
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.context.ApplicationContext;
|
||||
public class ContextRefreshedEvent extends ApplicationContextEvent {
|
||||
|
||||
/**
|
||||
* Create a new ContextRefreshedEvent.
|
||||
* Create a new {@code ContextRefreshedEvent}.
|
||||
* @param source the {@code ApplicationContext} that has been initialized
|
||||
* or refreshed (must not be {@code null})
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,7 +30,7 @@ import org.springframework.context.ApplicationContext;
|
||||
public class ContextStartedEvent extends ApplicationContextEvent {
|
||||
|
||||
/**
|
||||
* Create a new ContextStartedEvent.
|
||||
* Create a new {@code ContextStartedEvent}.
|
||||
* @param source the {@code ApplicationContext} that has been started
|
||||
* (must not be {@code null})
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,7 +30,7 @@ import org.springframework.context.ApplicationContext;
|
||||
public class ContextStoppedEvent extends ApplicationContextEvent {
|
||||
|
||||
/**
|
||||
* Create a new ContextStoppedEvent.
|
||||
* Create a new {@code ContextStoppedEvent}.
|
||||
* @param source the {@code ApplicationContext} that has been stopped
|
||||
* (must not be {@code null})
|
||||
*/
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,6 +126,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
try {
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactory();
|
||||
beanFactory.setSerializationId(getId());
|
||||
beanFactory.setApplicationStartup(getApplicationStartup());
|
||||
customizeBeanFactory(beanFactory);
|
||||
loadBeanDefinitions(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
+14
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,11 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link LifecycleProcessor} strategy.
|
||||
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
|
||||
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
|
||||
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
@@ -145,13 +149,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
|
||||
int phase = getPhase(bean);
|
||||
phases.computeIfAbsent(
|
||||
phase,
|
||||
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
int startupPhase = getPhase(bean);
|
||||
phases.computeIfAbsent(startupPhase,
|
||||
phase -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
).add(beanName, bean);
|
||||
}
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
phases.values().forEach(LifecycleGroup::start);
|
||||
}
|
||||
@@ -191,6 +195,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
private void stopBeans() {
|
||||
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
|
||||
Map<Integer, LifecycleGroup> phases = new HashMap<>();
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
int shutdownPhase = getPhase(bean);
|
||||
LifecycleGroup group = phases.get(shutdownPhase);
|
||||
@@ -200,6 +205,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
}
|
||||
group.add(beanName, bean);
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
List<Integer> keys = new ArrayList<>(phases.keySet());
|
||||
keys.sort(Collections.reverseOrder());
|
||||
@@ -314,6 +320,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
/**
|
||||
* Helper class for maintaining a group of Lifecycle beans that should be started
|
||||
* and stopped together based on their 'phase' value (or the default value of 0).
|
||||
* The group is expected to be created in an ad-hoc fashion and group members are
|
||||
* expected to always have the same 'phase' value.
|
||||
*/
|
||||
private class LifecycleGroup {
|
||||
|
||||
|
||||
+28
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,8 +22,10 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.format.Formatter;
|
||||
@@ -35,9 +37,14 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A formatter for {@link java.util.Date} types.
|
||||
*
|
||||
* <p>Supports the configuration of an explicit date time pattern, timezone,
|
||||
* locale, and fallback date time patterns for lenient parsing.
|
||||
*
|
||||
* <p>Common ISO patterns for UTC instants are applied at millisecond precision.
|
||||
* Note that {@link org.springframework.format.datetime.standard.InstantFormatter}
|
||||
* is recommended for flexible UTC parsing into a {@link java.time.Instant} instead.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
@@ -51,12 +58,21 @@ public class DateFormatter implements Formatter<Date> {
|
||||
|
||||
private static final Map<ISO, String> ISO_PATTERNS;
|
||||
|
||||
private static final Map<ISO, String> ISO_FALLBACK_PATTERNS;
|
||||
|
||||
static {
|
||||
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
|
||||
Map<ISO, String> formats = new EnumMap<>(ISO.class);
|
||||
formats.put(ISO.DATE, "yyyy-MM-dd");
|
||||
formats.put(ISO.TIME, "HH:mm:ss.SSSXXX");
|
||||
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
ISO_PATTERNS = Collections.unmodifiableMap(formats);
|
||||
|
||||
// Fallback format for the time part without milliseconds.
|
||||
Map<ISO, String> fallbackFormats = new EnumMap<>(ISO.class);
|
||||
fallbackFormats.put(ISO.TIME, "HH:mm:ssXXX");
|
||||
fallbackFormats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
||||
ISO_FALLBACK_PATTERNS = Collections.unmodifiableMap(fallbackFormats);
|
||||
}
|
||||
|
||||
|
||||
@@ -201,8 +217,16 @@ public class DateFormatter implements Formatter<Date> {
|
||||
return getDateFormat(locale).parse(text);
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
Set<String> fallbackPatterns = new LinkedHashSet<>();
|
||||
String isoPattern = ISO_FALLBACK_PATTERNS.get(this.iso);
|
||||
if (isoPattern != null) {
|
||||
fallbackPatterns.add(isoPattern);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
|
||||
for (String pattern : this.fallbackPatterns) {
|
||||
Collections.addAll(fallbackPatterns, this.fallbackPatterns);
|
||||
}
|
||||
if (!fallbackPatterns.isEmpty()) {
|
||||
for (String pattern : fallbackPatterns) {
|
||||
try {
|
||||
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
|
||||
// Align timezone for parsing format with printing format if ISO is set.
|
||||
@@ -220,8 +244,8 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
if (this.source != null) {
|
||||
ParseException parseException = new ParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
parseException.initCause(ex);
|
||||
throw parseException;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,12 +41,12 @@ public class InstantFormatter implements Formatter<Instant> {
|
||||
|
||||
@Override
|
||||
public Instant parse(String text, Locale locale) throws ParseException {
|
||||
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
|
||||
if (!text.isEmpty() && Character.isAlphabetic(text.charAt(0))) {
|
||||
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
|
||||
}
|
||||
else {
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.000Z"
|
||||
return Instant.parse(text);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -133,11 +133,6 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
* execution callback (which may be a wrapper around the user-supplied task).
|
||||
* <p>The primary use case is to set some execution context around the task's
|
||||
* invocation, or to provide some monitoring/statistics for task execution.
|
||||
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
|
||||
* is limited to plain {@code Runnable} execution via {@code execute} calls.
|
||||
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a
|
||||
* {@code FutureTask} which does not propagate any exceptions; you might
|
||||
* have to cast it and call {@code Future#get} to evaluate exceptions.
|
||||
* @since 4.3
|
||||
*/
|
||||
public final void setTaskDecorator(TaskDecorator taskDecorator) {
|
||||
@@ -178,11 +173,10 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
}
|
||||
|
||||
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
|
||||
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
|
||||
return new ManagedTaskExecutorAdapter(concurrentExecutor);
|
||||
}
|
||||
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
|
||||
TaskExecutorAdapter adapter =
|
||||
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
|
||||
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
|
||||
if (this.taskDecorator != null) {
|
||||
adapter.setTaskDecorator(this.taskDecorator);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,6 +177,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,8 +45,9 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
|
||||
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
|
||||
* all applicable configuration options for it.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
@@ -154,6 +155,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+3
-6
@@ -28,17 +28,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class OverloadedAdviceTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void testExceptionOnConfigParsingWithMismatchedAdviceMethod() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass()))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.as("invalidAbsoluteTypeName should be detected by AJ").withMessageContaining("invalidAbsoluteTypeName");
|
||||
void testConfigParsingWithMismatchedAdviceMethod() {
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+60
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,9 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -39,51 +41,62 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link FactoryBean FactoryBeans} defined in the configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
|
||||
@Test
|
||||
public void preFreezeDirect() {
|
||||
void preFreezeDirect() {
|
||||
assertPreFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeDirect() {
|
||||
void postFreezeDirect() {
|
||||
assertPostFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericMethod() {
|
||||
void preFreezeGenericMethod() {
|
||||
assertPreFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericMethod() {
|
||||
void postFreezeGenericMethod() {
|
||||
assertPostFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericClass() {
|
||||
void preFreezeGenericClass() {
|
||||
assertPreFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericClass() {
|
||||
void postFreezeGenericClass() {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeAttribute() {
|
||||
void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeAttribute() {
|
||||
void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeUnresolvedGenericFactoryBean() {
|
||||
void preFreezeTargetType() {
|
||||
assertPreFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeTargetType() {
|
||||
assertPostFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeUnresolvedGenericFactoryBean() {
|
||||
// Covers the case where a @Configuration is picked up via component scanning
|
||||
// and its bean definition only has a String bean class. In such cases
|
||||
// beanDefinition.hasBeanClass() returns false so we need to actually
|
||||
@@ -108,14 +121,13 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void assertPostFreeze(Class<?> configurationClass) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configurationClass);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
|
||||
assertContainsMyBeanName(context);
|
||||
}
|
||||
|
||||
private void assertPreFreeze(Class<?> configurationClass,
|
||||
BeanFactoryPostProcessor... postProcessors) {
|
||||
private void assertPreFreeze(Class<?> configurationClass, BeanFactoryPostProcessor... postProcessors) {
|
||||
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
try {
|
||||
@@ -138,41 +150,38 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
assertThat(names).containsExactly("myBean");
|
||||
}
|
||||
|
||||
private static class NameCollectingBeanFactoryPostProcessor
|
||||
implements BeanFactoryPostProcessor {
|
||||
|
||||
private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
private String[] names;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
|
||||
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DirectConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
MyBean<String> myBean() {
|
||||
return new MyBean<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GenericMethodConfiguration {
|
||||
|
||||
@Bean
|
||||
FactoryBean<MyBean> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean());
|
||||
FactoryBean<MyBean<String>> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -182,13 +191,11 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
MyFactoryBean myBean() {
|
||||
return new MyFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
@@ -197,16 +204,34 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TargetTypeRegistrar.class)
|
||||
static class TargetTypeConfiguration {
|
||||
}
|
||||
|
||||
static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
|
||||
definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class AbstractMyBean {
|
||||
}
|
||||
|
||||
static class MyBean extends AbstractMyBean {
|
||||
static class MyBean<T> extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -218,7 +243,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
public T getObject() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -226,31 +251,26 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public Class<?> getObjectType() {
|
||||
return this.instance.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean> {
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
|
||||
|
||||
public MyFactoryBean() {
|
||||
super(new MyBean());
|
||||
super(new MyBean<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
|
||||
|
||||
private final Object object = new MyBean();
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
return object;
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+44
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import javax.inject.Provider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -92,8 +94,8 @@ class AutowiredConfigurationTests {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
OptionalAutowiredMethodConfig.class);
|
||||
|
||||
assertThat(context.getBeansOfType(Colour.class).isEmpty()).isTrue();
|
||||
assertThat(context.getBean(TestBean.class).getName()).isEqualTo("");
|
||||
assertThat(context.getBeansOfType(Colour.class)).isEmpty();
|
||||
assertThat(context.getBean(TestBean.class).getName()).isEmpty();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -184,14 +186,22 @@ class AutowiredConfigurationTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValueInjectionWithAccidentalAutowiredAnnotations() {
|
||||
AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(ValueConfigWithAccidentalAutowiredAnnotations.class);
|
||||
doTestValueInjection(context);
|
||||
context.close();
|
||||
}
|
||||
|
||||
private void doTestValueInjection(BeanFactory context) {
|
||||
System.clearProperty("myProp");
|
||||
|
||||
TestBean testBean = context.getBean("testBean", TestBean.class);
|
||||
assertThat((Object) testBean.getName()).isNull();
|
||||
assertThat(testBean.getName()).isNull();
|
||||
|
||||
testBean = context.getBean("testBean2", TestBean.class);
|
||||
assertThat((Object) testBean.getName()).isNull();
|
||||
assertThat(testBean.getName()).isNull();
|
||||
|
||||
System.setProperty("myProp", "foo");
|
||||
|
||||
@@ -204,10 +214,10 @@ class AutowiredConfigurationTests {
|
||||
System.clearProperty("myProp");
|
||||
|
||||
testBean = context.getBean("testBean", TestBean.class);
|
||||
assertThat((Object) testBean.getName()).isNull();
|
||||
assertThat(testBean.getName()).isNull();
|
||||
|
||||
testBean = context.getBean("testBean2", TestBean.class);
|
||||
assertThat((Object) testBean.getName()).isNull();
|
||||
assertThat(testBean.getName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -271,7 +281,7 @@ class AutowiredConfigurationTests {
|
||||
return new TestBean("");
|
||||
}
|
||||
else {
|
||||
return new TestBean(colour.get().toString() + "-" + colours.get().get(0).toString());
|
||||
return new TestBean(colour.get() + "-" + colours.get().get(0).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,6 +494,32 @@ class AutowiredConfigurationTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ValueConfigWithAccidentalAutowiredAnnotations implements InitializingBean {
|
||||
|
||||
boolean invoked;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state(!invoked, "Factory method must not get invoked on startup");
|
||||
}
|
||||
|
||||
@Bean @Scope("prototype")
|
||||
@Autowired
|
||||
public TestBean testBean(@Value("#{systemProperties[myProp]}") Provider<String> name) {
|
||||
invoked = true;
|
||||
return new TestBean(name.get());
|
||||
}
|
||||
|
||||
@Bean @Scope("prototype")
|
||||
@Autowired
|
||||
public TestBean testBean2(@Value("#{systemProperties[myProp]}") Provider<String> name2) {
|
||||
invoked = true;
|
||||
return new TestBean(name2.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class PropertiesConfig {
|
||||
|
||||
|
||||
+36
-57
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,6 @@ import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
@@ -33,83 +30,88 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link DateFormatter}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class DateFormatterTests {
|
||||
class DateFormatterTests {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseDefault() throws Exception {
|
||||
void shouldPrintAndParseDefault() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-06-01", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseShort() throws Exception {
|
||||
void shouldPrintAndParseShort() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("6/1/09");
|
||||
assertThat(formatter.parse("6/1/09", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseMedium() throws Exception {
|
||||
void shouldPrintAndParseMedium() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.MEDIUM);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseLong() throws Exception {
|
||||
void shouldPrintAndParseLong() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.LONG);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("June 1, 2009");
|
||||
assertThat(formatter.parse("June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseFull() throws Exception {
|
||||
void shouldPrintAndParseFull() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.FULL);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Monday, June 1, 2009");
|
||||
assertThat(formatter.parse("Monday, June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseISODate() throws Exception {
|
||||
void shouldPrintAndParseIsoDate() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-6-01", Locale.US))
|
||||
@@ -117,79 +119,56 @@ public class DateFormatterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseISOTime() throws Exception {
|
||||
void shouldPrintAndParseIsoTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.003Z");
|
||||
assertThat(formatter.parse("14:23:05.003Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3));
|
||||
|
||||
date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.000Z");
|
||||
assertThat(formatter.parse("14:23:05Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 0).toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPrintAndParseISODateTime() throws Exception {
|
||||
void shouldPrintAndParseIsoDateTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.003Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05.003Z", Locale.US)).isEqualTo(date);
|
||||
|
||||
date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.000Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05Z", Locale.US)).isEqualTo(date.toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportJodaStylePatterns() throws Exception {
|
||||
String[] chars = { "S", "M", "-" };
|
||||
for (String d : chars) {
|
||||
for (String t : chars) {
|
||||
String style = d + t;
|
||||
if (!style.equals("--")) {
|
||||
Date date = getDate(2009, Calendar.JUNE, 10, 14, 23, 0, 0);
|
||||
if (t.equals("-")) {
|
||||
date = getDate(2009, Calendar.JUNE, 10);
|
||||
}
|
||||
else if (d.equals("-")) {
|
||||
date = getDate(1970, Calendar.JANUARY, 1, 14, 23, 0, 0);
|
||||
}
|
||||
testJodaStylePatterns(style, Locale.US, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testJodaStylePatterns(String style, Locale locale, Date date) throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStylePattern(style);
|
||||
DateTimeFormatter jodaFormatter = DateTimeFormat.forStyle(style).withLocale(locale).withZone(DateTimeZone.UTC);
|
||||
String jodaPrinted = jodaFormatter.print(date.getTime());
|
||||
assertThat(formatter.print(date, locale))
|
||||
.as("Unable to print style pattern " + style)
|
||||
.isEqualTo(jodaPrinted);
|
||||
assertThat(formatter.parse(jodaPrinted, locale))
|
||||
.as("Unable to parse style pattern " + style)
|
||||
.isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowOnUnsupportedStylePattern() throws Exception {
|
||||
void shouldThrowOnUnsupportedStylePattern() {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setStylePattern("OO");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseCorrectOrder() throws Exception {
|
||||
void shouldUseCorrectOrder() {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
formatter.setStylePattern("L-");
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
formatter.setPattern("yyyy");
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).as("uses pattern").isEqualTo("2009");
|
||||
|
||||
formatter.setPattern("");
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class DateFormattingTests {
|
||||
class DateFormattingTests {
|
||||
|
||||
private final FormattingConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
|
||||
+57
-59
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -105,7 +105,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "October 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("October 31, 2009");
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "20091031");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("20091031");
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new String[] {"10/31/09"});
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +146,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("children[0].styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "12:00:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "130000");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("130000");
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalTime", "12:00:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalTime")).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
|
||||
}
|
||||
|
||||
@@ -253,10 +253,9 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value.startsWith("10/31/09")).isTrue();
|
||||
assertThat(value.endsWith("12:00 PM")).isTrue();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,10 +263,9 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
|
||||
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
|
||||
assertThat(value.endsWith("12:00:00 PM")).isTrue();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -275,10 +273,9 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value.startsWith("10/31/09")).isTrue();
|
||||
assertThat(value.endsWith("12:00 PM")).isTrue();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -289,10 +286,9 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
|
||||
assertThat(value.endsWith("12:00:00 PM")).isTrue();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -300,7 +296,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternLocalDateTime", "10/31/09 12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("patternLocalDateTime")).isEqualTo("10/31/09 12:00 PM");
|
||||
}
|
||||
|
||||
@@ -317,7 +313,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDate", "2009-10-31");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
|
||||
}
|
||||
|
||||
@@ -356,7 +352,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalTime", "12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@@ -365,7 +361,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalTime", "12:00:00.000-05:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@@ -374,7 +370,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@@ -383,7 +379,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@@ -392,8 +388,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("instant", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31T12:00");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -405,8 +401,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("instant", new Date(109, 9, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31");
|
||||
}
|
||||
finally {
|
||||
TimeZone.setDefault(defaultZone);
|
||||
@@ -418,8 +414,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("period", "P6Y3M1D");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("period").toString().equals("P6Y3M1D")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("period").toString()).isEqualTo("P6Y3M1D");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -427,8 +423,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("duration", "PT8H6M12.345S");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("duration").toString().equals("PT8H6M12.345S")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("duration").toString()).isEqualTo("PT8H6M12.345S");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -436,8 +432,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("year", "2007");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("year").toString().equals("2007")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("year").toString()).isEqualTo("2007");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -445,8 +441,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "JULY");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -454,8 +450,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "July");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -463,8 +459,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("yearMonth", "2007-12");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString()).isEqualTo("2007-12");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -472,10 +468,11 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("monthDay", "--12-03");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")).isTrue();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString()).isEqualTo("--12-03");
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FallbackPatternTests {
|
||||
|
||||
@@ -487,7 +484,7 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
|
||||
}
|
||||
|
||||
@@ -499,11 +496,12 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
// @ValueSource(strings = {"12:00:00\u202FPM", "12:00:00", "12:00"})
|
||||
@ValueSource(strings = {"12:00:00 PM", "12:00:00", "12:00"})
|
||||
void styleLocalTime(String propertyValue) {
|
||||
String propertyName = "styleLocalTimeWithFallbackPatterns";
|
||||
@@ -511,7 +509,8 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
// assertThat(bindingResult.getFieldValue(propertyName)).asString().matches("12:00:00\\SPM");
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -523,7 +522,7 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02T12:00:00");
|
||||
}
|
||||
|
||||
@@ -565,10 +564,10 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "M-")
|
||||
private LocalDate styleLocalDate;
|
||||
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = {"yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
private LocalDate styleLocalDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = {"M/d/yy", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
private LocalDate patternLocalDateWithFallbackPatterns;
|
||||
|
||||
private LocalTime localTime;
|
||||
@@ -576,7 +575,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "-M")
|
||||
private LocalTime styleLocalTime;
|
||||
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = {"HH:mm:ss", "HH:mm"})
|
||||
private LocalTime styleLocalTimeWithFallbackPatterns;
|
||||
|
||||
private LocalDateTime localDateTime;
|
||||
@@ -596,7 +595,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME)
|
||||
private LocalDateTime isoLocalDateTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = {"yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
|
||||
|
||||
private Instant instant;
|
||||
@@ -615,7 +614,6 @@ class DateTimeFormattingTests {
|
||||
|
||||
private final List<DateTimeBean> children = new ArrayList<>();
|
||||
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return this.localDate;
|
||||
}
|
||||
|
||||
+9
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.format.datetime.standard;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -49,13 +50,12 @@ class InstantFormatterTests {
|
||||
|
||||
private final InstantFormatter instantFormatter = new InstantFormatter();
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ISOSerializedInstantProvider.class)
|
||||
void should_parse_an_ISO_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -63,9 +63,7 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RFC1123SerializedInstantProvider.class)
|
||||
void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -73,12 +71,11 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RandomInstantProvider.class)
|
||||
void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale(Instant input) {
|
||||
String expected = DateTimeFormatter.ISO_INSTANT.format(input);
|
||||
|
||||
String actual = instantFormatter.print(input, null);
|
||||
|
||||
String actual = instantFormatter.print(input, Locale.US);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
|
||||
private static class RandomInstantProvider implements ArgumentsProvider {
|
||||
|
||||
private static final long DATA_SET_SIZE = 10;
|
||||
@@ -100,6 +97,7 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
@Override
|
||||
@@ -108,6 +106,7 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
// RFC-1123 supports only 4-digit years
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,8 +17,8 @@
|
||||
package org.springframework.scheduling.concurrent;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -52,8 +52,8 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
|
||||
@AfterEach
|
||||
void shutdownExecutor() {
|
||||
for (Runnable task : concurrentExecutor.shutdownNow()) {
|
||||
if (task instanceof RunnableFuture) {
|
||||
((RunnableFuture<?>) task).cancel(true);
|
||||
if (task instanceof Future) {
|
||||
((Future<?>) task).cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
|
||||
private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
|
||||
@@ -97,7 +97,7 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
|
||||
}
|
||||
|
||||
@Test
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
|
||||
TestTask task = new TestTask(this.testName, 0);
|
||||
Future<?> future = scheduler.schedule(task, new Date());
|
||||
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
|
||||
@@ -149,7 +149,7 @@ public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutor
|
||||
catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
assertThat(latch.getCount()).as("latch did not count down,").isEqualTo(0);
|
||||
assertThat(latch.getCount()).as("latch did not count down").isEqualTo(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
@@ -18,4 +18,6 @@
|
||||
|
||||
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
<bean id="testBean2" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,11 +57,11 @@ public final class BridgeMethodResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Find the original method for the supplied {@link Method bridge Method}.
|
||||
* Find the local original method for the supplied {@link Method bridge Method}.
|
||||
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
|
||||
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
|
||||
* Callers are <strong>not</strong> required to check for bridging before calling this method.
|
||||
* @param bridgeMethod the method to introspect
|
||||
* @param bridgeMethod the method to introspect against its declaring class
|
||||
* @return the original method (either the bridged method or the passed-in method
|
||||
* if no more specific one could be found)
|
||||
*/
|
||||
@@ -73,8 +73,7 @@ public final class BridgeMethodResolver {
|
||||
if (bridgedMethod == null) {
|
||||
// Gather all methods with matching name and parameter size.
|
||||
List<Method> candidateMethods = new ArrayList<>();
|
||||
MethodFilter filter = candidateMethod ->
|
||||
isBridgedCandidateFor(candidateMethod, bridgeMethod);
|
||||
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
|
||||
ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
bridgedMethod = candidateMethods.size() == 1 ?
|
||||
@@ -95,10 +94,10 @@ public final class BridgeMethodResolver {
|
||||
* Returns {@code true} if the supplied '{@code candidateMethod}' can be
|
||||
* considered a valid candidate for the {@link Method} that is {@link Method#isBridge() bridged}
|
||||
* by the supplied {@link Method bridge Method}. This method performs inexpensive
|
||||
* checks and can be used quickly to filter for a set of possible matches.
|
||||
* checks and can be used to quickly filter for a set of possible matches.
|
||||
*/
|
||||
private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
|
||||
return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
|
||||
return (!candidateMethod.isBridge() &&
|
||||
candidateMethod.getName().equals(bridgeMethod.getName()) &&
|
||||
candidateMethod.getParameterCount() == bridgeMethod.getParameterCount());
|
||||
}
|
||||
@@ -121,8 +120,8 @@ public final class BridgeMethodResolver {
|
||||
return candidateMethod;
|
||||
}
|
||||
else if (previousMethod != null) {
|
||||
sameSig = sameSig &&
|
||||
Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
sameSig = sameSig && Arrays.equals(
|
||||
candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
}
|
||||
previousMethod = candidateMethod;
|
||||
}
|
||||
@@ -163,7 +162,8 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
}
|
||||
// A non-array type: compare the type itself.
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
|
||||
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -226,8 +226,8 @@ public final class BridgeMethodResolver {
|
||||
/**
|
||||
* Compare the signatures of the bridge method and the method which it bridges. If
|
||||
* the parameter and return types are the same, it is a 'visibility' bridge method
|
||||
* introduced in Java 6 to fix https://bugs.openjdk.org/browse/JDK-6342411.
|
||||
* See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
|
||||
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
|
||||
* JDK-6342411</a>.
|
||||
* @return whether signatures match as described
|
||||
*/
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public final class MethodIntrospector {
|
||||
@@ -75,6 +76,7 @@ public final class MethodIntrospector {
|
||||
if (result != null) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
if (bridgedMethod == specificMethod || bridgedMethod == method ||
|
||||
bridgedMethod.equals(specificMethod) || bridgedMethod.equals(method) ||
|
||||
metadataLookup.inspect(bridgedMethod) == null) {
|
||||
methodMap.put(specificMethod, result);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -98,7 +98,9 @@ public final class ReactiveTypeDescriptor {
|
||||
*/
|
||||
public Object getEmptyValue() {
|
||||
Assert.state(this.emptySupplier != null, "Empty values not supported");
|
||||
return this.emptySupplier.get();
|
||||
Object emptyValue = this.emptySupplier.get();
|
||||
Assert.notNull(emptyValue, "Invalid null return value from emptySupplier");
|
||||
return emptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +132,7 @@ public final class ReactiveTypeDescriptor {
|
||||
|
||||
|
||||
/**
|
||||
* Descriptor for a reactive type that can produce 0..N values.
|
||||
* Descriptor for a reactive type that can produce {@code 0..N} values.
|
||||
* @param type the reactive type
|
||||
* @param emptySupplier a supplier of an empty-value instance of the reactive type
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -750,7 +750,7 @@ public class ResolvableType implements Serializable {
|
||||
* Convenience method that will {@link #getGenerics() get} and
|
||||
* {@link #resolve() resolve} generic parameters.
|
||||
* @return an array of resolved generic parameters (the resulting array
|
||||
* will never be {@code null}, but it may contain {@code null} elements})
|
||||
* will never be {@code null}, but it may contain {@code null} elements)
|
||||
* @see #getGenerics()
|
||||
* @see #resolve()
|
||||
*/
|
||||
|
||||
@@ -1052,16 +1052,16 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
for (Method method : annotation.annotationType().getDeclaredMethods()) {
|
||||
if (method.getName().equals(attributeName) && method.getParameterCount() == 0) {
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -410,7 +410,10 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType(), repeatableContainers, annotationFilter);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,20 +95,19 @@ public interface Decoder<T> {
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
|
||||
|
||||
CompletableFuture<T> future = decodeToMono(Mono.just(buffer), targetType, mimeType, hints).toFuture();
|
||||
Assert.state(future.isDone(), "DataBuffer decoding should have completed.");
|
||||
Assert.state(future.isDone(), "DataBuffer decoding should have completed");
|
||||
|
||||
Throwable failure;
|
||||
try {
|
||||
return future.get();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
failure = ex.getCause();
|
||||
Throwable cause = ex.getCause();
|
||||
throw (cause instanceof CodecException ? (CodecException) cause :
|
||||
new DecodingException("Failed to decode: " + (cause != null ? cause.getMessage() : ex), cause));
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
failure = ex;
|
||||
throw new DecodingException("Interrupted during decode", ex);
|
||||
}
|
||||
throw (failure instanceof CodecException ? (CodecException) failure :
|
||||
new DecodingException("Failed to decode: " + failure.getMessage(), failure));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,10 +76,11 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
}
|
||||
|
||||
Class<?> clazz = elementType.toClass();
|
||||
String filename = hints != null ? (String) hints.get(FILENAME_HINT) : null;
|
||||
String filename = (hints != null ? (String) hints.get(FILENAME_HINT) : null);
|
||||
if (clazz == InputStreamResource.class) {
|
||||
return new InputStreamResource(new ByteArrayInputStream(bytes)) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
@@ -92,6 +93,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
else if (Resource.class.isAssignableFrom(clazz)) {
|
||||
return new ByteArrayResource(bytes) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,6 @@ import org.springframework.util.ObjectUtils;
|
||||
@SuppressWarnings("serial")
|
||||
public class TypeDescriptor implements Serializable {
|
||||
|
||||
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
||||
|
||||
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);
|
||||
|
||||
private static final Class<?>[] CACHED_COMMON_TYPES = {
|
||||
@@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(MethodParameter methodParameter) {
|
||||
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
|
||||
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
|
||||
}
|
||||
|
||||
@@ -96,7 +94,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(Field field) {
|
||||
this.resolvableType = ResolvableType.forField(field);
|
||||
this.type = this.resolvableType.resolve(field.getType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +107,7 @@ public class TypeDescriptor implements Serializable {
|
||||
Assert.notNull(property, "Property must not be null");
|
||||
this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
|
||||
this.type = this.resolvableType.resolve(property.getType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +123,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
|
||||
this.resolvableType = resolvableType;
|
||||
this.type = (type != null ? type : resolvableType.toClass());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(annotations);
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(annotations);
|
||||
}
|
||||
|
||||
|
||||
@@ -734,18 +732,26 @@ public class TypeDescriptor implements Serializable {
|
||||
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
|
||||
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
|
||||
*/
|
||||
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
|
||||
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
|
||||
|
||||
@Nullable
|
||||
private final Annotation[] annotations;
|
||||
|
||||
public AnnotatedElementAdapter(@Nullable Annotation[] annotations) {
|
||||
private AnnotatedElementAdapter(Annotation[] annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) {
|
||||
if (annotations == null || annotations.length == 0) {
|
||||
return EMPTY;
|
||||
}
|
||||
return new AnnotatedElementAdapter(annotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return true;
|
||||
}
|
||||
@@ -757,7 +763,7 @@ public class TypeDescriptor implements Serializable {
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return (T) annotation;
|
||||
}
|
||||
@@ -767,7 +773,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
|
||||
return (isEmpty() ? this.annotations : this.annotations.clone());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -776,7 +782,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return ObjectUtils.isEmpty(this.annotations);
|
||||
return (this.annotations.length == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -792,7 +798,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TypeDescriptor.this.toString();
|
||||
return Arrays.toString(this.annotations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
final class ArrayToArrayConverter implements ConditionalGenericConverter {
|
||||
@@ -64,7 +65,7 @@ final class ArrayToArrayConverter implements ConditionalGenericConverter {
|
||||
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (this.conversionService instanceof GenericConversionService) {
|
||||
TypeDescriptor targetElement = targetType.getElementTypeDescriptor();
|
||||
if (targetElement != null &&
|
||||
if (targetElement != null && targetType.getType().isInstance(source) &&
|
||||
((GenericConversionService) this.conversionService).canBypassConvert(
|
||||
sourceType.getElementTypeDescriptor(), targetElement)) {
|
||||
return source;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -138,7 +138,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
@Nullable
|
||||
private static Executable getValidatedExecutable(Class<?> targetClass, Class<?> sourceClass) {
|
||||
Executable executable = conversionExecutableCache.get(targetClass);
|
||||
if (isApplicable(executable, sourceClass)) {
|
||||
if (executable != null && isApplicable(executable, sourceClass)) {
|
||||
return executable;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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 @@ public class PropertiesPropertySource extends MapPropertySource {
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
synchronized (this.source) {
|
||||
return super.getPropertyNames();
|
||||
return ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,7 +62,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class DataBufferUtils {
|
||||
|
||||
private final static Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
private static final Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
|
||||
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
|
||||
|
||||
@@ -728,7 +728,7 @@ public abstract class DataBufferUtils {
|
||||
*/
|
||||
private static class SingleByteMatcher implements NestedMatcher {
|
||||
|
||||
static SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
static final SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -767,7 +767,7 @@ public abstract class DataBufferUtils {
|
||||
/**
|
||||
* Base class for a {@link NestedMatcher}.
|
||||
*/
|
||||
private static abstract class AbstractNestedMatcher implements NestedMatcher {
|
||||
private abstract static class AbstractNestedMatcher implements NestedMatcher {
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -1005,11 +1005,11 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, DataBuffer dataBuffer) {
|
||||
public void failed(Throwable ex, DataBuffer dataBuffer) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
this.state.set(State.DISPOSED);
|
||||
this.sink.error(exc);
|
||||
this.sink.error(ex);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
@@ -1064,7 +1064,6 @@ public abstract class DataBufferUtils {
|
||||
public Context currentContext() {
|
||||
return Context.of(this.sink.contextView());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1145,9 +1144,9 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer byteBuffer) {
|
||||
public void failed(Throwable ex, ByteBuffer byteBuffer) {
|
||||
sinkDataBuffer();
|
||||
this.sink.error(exc);
|
||||
this.sink.error(ex);
|
||||
}
|
||||
|
||||
private void sinkDataBuffer() {
|
||||
@@ -1161,7 +1160,6 @@ public abstract class DataBufferUtils {
|
||||
public Context currentContext() {
|
||||
return Context.of(this.sink.contextView());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author 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,8 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Default "no op" {@code ApplicationStartup} implementation.
|
||||
*
|
||||
@@ -52,6 +54,7 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Long getParentId() {
|
||||
return null;
|
||||
}
|
||||
@@ -73,7 +76,6 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user