mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f0088831a9 | |||
| 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 | |||
| 297cbae299 | |||
| 274fba47f3 | |||
| 5dfec09edd | |||
| 5056e8cbfb | |||
| 4566e8685d | |||
| 1b84f970de | |||
| 41bc43b033 | |||
| 915d5bddea | |||
| dc86feaeb6 | |||
| ff412de247 | |||
| 5d572f6490 | |||
| b8b1f5b6be | |||
| 99e38ecf41 | |||
| ed0c2ff37f | |||
| e668e7767c | |||
| 30c75ffe8e | |||
| dd5fe68522 | |||
| 2aca714010 | |||
| 3b7c435134 | |||
| 6432b13a4c | |||
| 3478a702e5 | |||
| b31550fd85 | |||
| 701e9e410f | |||
| ce385d136d | |||
| e44f84e8ee | |||
| 464fa7ea0e | |||
| 6e1f583c06 | |||
| 57646a092e | |||
| 380f5d5ea4 | |||
| 1acc1c3a27 | |||
| 6ad75bdb4a |
@@ -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,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,63 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 8
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- 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: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
|
||||
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.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 8', github.ref_name) }}
|
||||
@@ -0,0 +1,80 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
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
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '8' || '' }}
|
||||
- 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: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- 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: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.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', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew check
|
||||
- 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) }}
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
@@ -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".
|
||||
|
||||
|
||||
+7
-5
@@ -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.41"
|
||||
mavenBom "io.netty:netty-bom:4.1.109.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.44"
|
||||
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"
|
||||
@@ -375,8 +375,9 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://tiles.apache.org/tiles-request/apidocs/",
|
||||
"https://tiles.apache.org/framework/apidocs/",
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
// Temporarily commenting out Ehcache and Quartz since javadoc on JDK 8 cannot access them.
|
||||
// "https://www.ehcache.org/apidocs/2.10.4/",
|
||||
// "https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
|
||||
@@ -388,7 +389,8 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// "https://junit.org/junit5/docs/5.8.2/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
"https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
// Temporarily commenting out R2DBC since javadoc on JDK 8 cannot access it.
|
||||
// "https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
// The external Javadoc link for JSR 305 must come last to ensure that types from
|
||||
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
|
||||
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
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].
|
||||
|
||||
@@ -17,4 +17,4 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20240111
|
||||
FROM ubuntu:jammy-20240125
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -8,4 +8,3 @@ milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+14
-122
@@ -5,9 +5,7 @@ anchors:
|
||||
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))
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
@@ -23,14 +21,6 @@ anchors:
|
||||
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
|
||||
@@ -45,7 +35,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.7.1
|
||||
tag: 1.8.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -64,25 +54,12 @@ resource_types:
|
||||
<<: *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
|
||||
@@ -105,27 +82,6 @@ resources:
|
||||
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
|
||||
@@ -160,37 +116,23 @@ jobs:
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
- name: stage-milestone
|
||||
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" }
|
||||
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
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
repo: libs-staging-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}"
|
||||
@@ -215,55 +157,9 @@ jobs:
|
||||
- "/**/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
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -304,7 +200,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -348,7 +243,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -391,8 +285,6 @@ jobs:
|
||||
<<: *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"
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
#!/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/' )
|
||||
|
||||
@@ -26,4 +26,5 @@ run:
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
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
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.7'
|
||||
tag: '0.0.8'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
tag: '0.4.0'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.32
|
||||
version=5.3.35
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -5,7 +5,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
sourceRoots.setFrom(file("src/main/kotlin"))
|
||||
classpath.from(sourceSets["main"].runtimeClasspath)
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/5.3.x/javadoc-api/"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
|
||||
+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()
|
||||
|
||||
+15
-10
@@ -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.
|
||||
@@ -174,25 +174,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 +274,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
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 +287,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -291,7 +298,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 +335,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,
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
@@ -186,10 +186,10 @@ public abstract class AopUtils {
|
||||
* this method resolves bridge methods in order to retrieve attributes from
|
||||
* the <i>original</i> method definition.
|
||||
* @param method the method to be invoked, which may come from an interface
|
||||
* @param targetClass the target class for the current invocation.
|
||||
* May be {@code null} or may not even implement the method.
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} doesn't implement it or is {@code null}
|
||||
* {@code targetClass} does not implement it
|
||||
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
|
||||
+34
-39
@@ -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,7 +39,6 @@ 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;
|
||||
|
||||
@@ -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
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getPointcut(expression).getClassFilter().matches(Object.class));
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -17,29 +17,35 @@
|
||||
package org.springframework.aop.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Sebastien Deleuze
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AopUtilsTests {
|
||||
class AopUtilsTests {
|
||||
|
||||
@Test
|
||||
public void testPointcutCanNeverApply() {
|
||||
void testPointcutCanNeverApply() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazzy) {
|
||||
@@ -52,13 +58,13 @@ public class AopUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointcutAlwaysApplies() {
|
||||
void testPointcutAlwaysApplies() {
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointcutAppliesToOneMethodOnObject() {
|
||||
void testPointcutAppliesToOneMethodOnObject() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazz) {
|
||||
@@ -78,7 +84,7 @@ public class AopUtilsTests {
|
||||
* that's subverted the singleton construction limitation.
|
||||
*/
|
||||
@Test
|
||||
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
|
||||
@@ -88,4 +94,45 @@ public class AopUtilsTests {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvokeJoinpointUsingReflection() throws Throwable {
|
||||
String name = "foo";
|
||||
TestBean testBean = new TestBean(name);
|
||||
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
|
||||
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
|
||||
assertThat(result).isEqualTo(name);
|
||||
}
|
||||
|
||||
@Test // gh-32365
|
||||
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
|
||||
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxy(getClass().getClassLoader()).getClass();
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
|
||||
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test // gh-32365
|
||||
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
|
||||
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxy(getClass().getClassLoader()).getClass();
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
|
||||
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
|
||||
interface ProxyInterface {
|
||||
|
||||
void handle(List<String> list);
|
||||
}
|
||||
|
||||
static class WithInterface implements ProxyInterface {
|
||||
|
||||
public void handle(List<String> list) {
|
||||
}
|
||||
}
|
||||
|
||||
static class WithoutInterface {
|
||||
|
||||
public void handle(List<String> list) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
+6
-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.
|
||||
@@ -166,6 +166,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
/** Map from scope identifier String to corresponding Scope. */
|
||||
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
|
||||
|
||||
/** Application startup metrics. **/
|
||||
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
|
||||
|
||||
/** Security context used when running with a SecurityManager. */
|
||||
@Nullable
|
||||
private SecurityContextProvider securityContextProvider;
|
||||
@@ -180,8 +183,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
|
||||
new NamedThreadLocal<>("Prototype beans currently in creation");
|
||||
|
||||
/** Application startup metrics. **/
|
||||
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new AbstractBeanFactory.
|
||||
@@ -1079,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;
|
||||
}
|
||||
|
||||
@@ -1694,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
|
||||
|
||||
+4
-7
@@ -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.
|
||||
@@ -600,13 +600,10 @@ class ConstructorResolver {
|
||||
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
|
||||
(mbd.getFactoryBeanName() != null ?
|
||||
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
|
||||
"Check that a method with the specified name " +
|
||||
(minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " +
|
||||
(isStatic ? "static" : "non-static") + ".");
|
||||
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
|
||||
}
|
||||
else if (void.class == factoryMethodToUse.getReturnType()) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
|
||||
+6
-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.
|
||||
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
*/
|
||||
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
|
||||
// Trigger destruction of dependent beans first...
|
||||
Set<String> dependencies;
|
||||
Set<String> dependentBeanNames;
|
||||
synchronized (this.dependentBeanMap) {
|
||||
// Within full synchronization in order to guarantee a disconnected Set
|
||||
dependencies = this.dependentBeanMap.remove(beanName);
|
||||
dependentBeanNames = this.dependentBeanMap.remove(beanName);
|
||||
}
|
||||
if (dependencies != null) {
|
||||
if (dependentBeanNames != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
|
||||
}
|
||||
for (String dependentBeanName : dependencies) {
|
||||
for (String dependentBeanName : dependentBeanNames) {
|
||||
destroySingleton(dependentBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-15
@@ -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.
|
||||
@@ -72,7 +72,6 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param metadataReader reader used to parse the underlying {@link Class}
|
||||
* @param beanName must not be {@code null}
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(MetadataReader metadataReader, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -86,10 +85,10 @@ final class ConfigurationClass {
|
||||
* using the {@link Import} annotation or automatically processed as a nested
|
||||
* configuration class (if importedBy is not {@code null}).
|
||||
* @param metadataReader reader used to parse the underlying {@link Class}
|
||||
* @param importedBy the configuration class importing this one or {@code null}
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
|
||||
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
||||
this.metadata = metadataReader.getAnnotationMetadata();
|
||||
this.resource = metadataReader.getResource();
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -99,7 +98,6 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param clazz the underlying {@link Class} to represent
|
||||
* @param beanName name of the {@code @Configuration} class bean
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(Class<?> clazz, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -113,10 +111,10 @@ final class ConfigurationClass {
|
||||
* using the {@link Import} annotation or automatically processed as a nested
|
||||
* configuration class (if imported is {@code true}).
|
||||
* @param clazz the underlying {@link Class} to represent
|
||||
* @param importedBy the configuration class importing this one (or {@code null})
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
|
||||
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
||||
this.metadata = AnnotationMetadata.introspect(clazz);
|
||||
this.resource = new DescriptiveResource(clazz.getName());
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -126,7 +124,6 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param metadata the metadata for the underlying class to represent
|
||||
* @param beanName name of the {@code @Configuration} class bean
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(AnnotationMetadata metadata, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -148,12 +145,12 @@ final class ConfigurationClass {
|
||||
return ClassUtils.getShortName(getMetadata().getClassName());
|
||||
}
|
||||
|
||||
void setBeanName(String beanName) {
|
||||
void setBeanName(@Nullable String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getBeanName() {
|
||||
String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@@ -163,7 +160,7 @@ final class ConfigurationClass {
|
||||
* @since 3.1.1
|
||||
* @see #getImportedBy()
|
||||
*/
|
||||
public boolean isImported() {
|
||||
boolean isImported() {
|
||||
return !this.importedBy.isEmpty();
|
||||
}
|
||||
|
||||
@@ -197,6 +194,10 @@ final class ConfigurationClass {
|
||||
this.importedResources.put(importedResource, readerClass);
|
||||
}
|
||||
|
||||
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
|
||||
return this.importedResources;
|
||||
}
|
||||
|
||||
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
|
||||
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
|
||||
}
|
||||
@@ -205,10 +206,6 @@ final class ConfigurationClass {
|
||||
return this.importBeanDefinitionRegistrars;
|
||||
}
|
||||
|
||||
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
|
||||
return this.importedResources;
|
||||
}
|
||||
|
||||
void validate(ProblemReporter problemReporter) {
|
||||
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
|
||||
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
|
||||
|
||||
+24
-21
@@ -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.
|
||||
@@ -136,17 +136,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
implements ConfigurableApplicationContext {
|
||||
|
||||
/**
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
/**
|
||||
* The name of the {@link MessageSource} bean in the context.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
@@ -167,6 +156,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
*/
|
||||
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
|
||||
|
||||
/**
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
|
||||
/**
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that
|
||||
* instructs Spring to ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
@@ -570,7 +571,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
|
||||
// Invoke factory processors registered as beans in the context.
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
|
||||
// Register bean processors that intercept bean creation.
|
||||
registerBeanPostProcessors(beanFactory);
|
||||
beanPostProcess.end();
|
||||
@@ -774,8 +774,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the MessageSource.
|
||||
* Use parent's if none defined in this context.
|
||||
* Initialize the {@link MessageSource}.
|
||||
* <p>Uses parent's {@code MessageSource} if none defined in this context.
|
||||
* @see #MESSAGE_SOURCE_BEAN_NAME
|
||||
*/
|
||||
protected void initMessageSource() {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
@@ -807,8 +808,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the ApplicationEventMulticaster.
|
||||
* Uses SimpleApplicationEventMulticaster if none defined in the context.
|
||||
* Initialize the {@link ApplicationEventMulticaster}.
|
||||
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
|
||||
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
*/
|
||||
protected void initApplicationEventMulticaster() {
|
||||
@@ -831,15 +833,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LifecycleProcessor.
|
||||
* Uses DefaultLifecycleProcessor if none defined in the context.
|
||||
* Initialize the {@link LifecycleProcessor}.
|
||||
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
|
||||
* @since 3.0
|
||||
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
*/
|
||||
protected void initLifecycleProcessor() {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
|
||||
this.lifecycleProcessor =
|
||||
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
|
||||
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+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.
|
||||
@@ -268,6 +268,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
scanner.scan("org.springframework.context.annotation2");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
|
||||
.withMessageContaining("myNamedDao")
|
||||
.withMessageContaining(NamedStubDao.class.getName())
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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
-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.
|
||||
@@ -496,7 +496,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
String rootDirPath = determineRootDir(locationPattern);
|
||||
String subPattern = locationPattern.substring(rootDirPath.length());
|
||||
Resource[] rootDirResources = getResources(rootDirPath);
|
||||
Set<Resource> result = new LinkedHashSet<>(16);
|
||||
Set<Resource> result = new LinkedHashSet<>(64);
|
||||
for (Resource rootDirResource : rootDirResources) {
|
||||
rootDirResource = resolveRootDirResource(rootDirResource);
|
||||
URL rootDirUrl = rootDirResource.getURL();
|
||||
@@ -648,7 +648,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// The Sun JRE does not return a slash here, but BEA JRockit does.
|
||||
rootEntryPath = rootEntryPath + "/";
|
||||
}
|
||||
Set<Resource> result = new LinkedHashSet<>(8);
|
||||
Set<Resource> result = new LinkedHashSet<>(64);
|
||||
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryPath = entry.getName();
|
||||
@@ -864,7 +864,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
private final String rootPath;
|
||||
|
||||
private final Set<Resource> resources = new LinkedHashSet<>();
|
||||
private final Set<Resource> resources = new LinkedHashSet<>(64);
|
||||
|
||||
public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) {
|
||||
this.subPattern = subPattern;
|
||||
@@ -895,7 +895,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
else if ("toString".equals(methodName)) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unexpected method invocation: " + method);
|
||||
}
|
||||
|
||||
|
||||
+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() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-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.
|
||||
@@ -21,10 +21,10 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* {@link StartupStep} implementation for the Java Flight Recorder.
|
||||
*
|
||||
* <p>This variant delegates to a {@link FlightRecorderStartupEvent JFR event extension}
|
||||
* to collect and record data in Java Flight Recorder.
|
||||
*
|
||||
@@ -114,12 +114,12 @@ class FlightRecorderStartupStep implements StartupStep {
|
||||
add(key, value.get());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Iterator<Tag> iterator() {
|
||||
return new TagsIterator();
|
||||
}
|
||||
|
||||
|
||||
private class TagsIterator implements Iterator<Tag> {
|
||||
|
||||
private int idx = 0;
|
||||
|
||||
+5
-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.
|
||||
@@ -73,20 +73,20 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
|
||||
// Optimization to avoid creating ClassReader for superclass.
|
||||
Boolean superClassMatch = matchSuperClass(superClassName);
|
||||
if (superClassMatch != null) {
|
||||
if (superClassMatch.booleanValue()) {
|
||||
if (superClassMatch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Need to read superclass to determine a match...
|
||||
try {
|
||||
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
|
||||
if (match(superClassName, metadataReaderFactory)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
|
||||
logger.debug("Could not read superclass [" + superClassName +
|
||||
"] of type-filtered class [" + metadata.getClassName() + "]");
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
|
||||
// Optimization to avoid creating ClassReader for superclass
|
||||
Boolean interfaceMatch = matchInterface(ifc);
|
||||
if (interfaceMatch != null) {
|
||||
if (interfaceMatch.booleanValue()) {
|
||||
if (interfaceMatch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -44,7 +44,8 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Miscellaneous {@code java.lang.Class} utility methods.
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* <p>Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
@@ -243,7 +244,7 @@ public abstract class ClassUtils {
|
||||
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
|
||||
* @param name the name of the Class
|
||||
* @param classLoader the class loader to use
|
||||
* (may be {@code null}, which indicates the default class loader)
|
||||
* (can be {@code null}, which indicates the default class loader)
|
||||
* @return a class instance for the supplied name
|
||||
* @throws ClassNotFoundException if the class was not found
|
||||
* @throws LinkageError if the class file could not be loaded
|
||||
@@ -314,7 +315,7 @@ public abstract class ClassUtils {
|
||||
* the exceptions thrown in case of class loading failure.
|
||||
* @param className the name of the Class
|
||||
* @param classLoader the class loader to use
|
||||
* (may be {@code null}, which indicates the default class loader)
|
||||
* (can be {@code null}, which indicates the default class loader)
|
||||
* @return a class instance for the supplied name
|
||||
* @throws IllegalArgumentException if the class name was not resolvable
|
||||
* (that is, the class could not be found or the class file could not be loaded)
|
||||
@@ -348,7 +349,7 @@ public abstract class ClassUtils {
|
||||
* one of its dependencies is not present or cannot be loaded.
|
||||
* @param className the name of the class to check
|
||||
* @param classLoader the class loader to use
|
||||
* (may be {@code null} which indicates the default class loader)
|
||||
* (can be {@code null} which indicates the default class loader)
|
||||
* @return whether the specified class is present (including all of its
|
||||
* superclasses and interfaces)
|
||||
* @throws IllegalStateException if the corresponding class is resolvable but
|
||||
@@ -375,7 +376,7 @@ public abstract class ClassUtils {
|
||||
* Check whether the given class is visible in the given ClassLoader.
|
||||
* @param clazz the class to check (typically an interface)
|
||||
* @param classLoader the ClassLoader to check against
|
||||
* (may be {@code null} in which case this method will always return {@code true})
|
||||
* (can be {@code null} in which case this method will always return {@code true})
|
||||
*/
|
||||
public static boolean isVisible(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
@@ -399,7 +400,7 @@ public abstract class ClassUtils {
|
||||
* i.e. whether it is loaded by the given ClassLoader or a parent of it.
|
||||
* @param clazz the class to analyze
|
||||
* @param classLoader the ClassLoader to potentially cache metadata in
|
||||
* (may be {@code null} which indicates the system class loader)
|
||||
* (can be {@code null} which indicates the system class loader)
|
||||
*/
|
||||
public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
@@ -539,9 +540,10 @@ public abstract class ClassUtils {
|
||||
* Check if the right-hand side type may be assigned to the left-hand side
|
||||
* type, assuming setting by reflection. Considers primitive wrapper
|
||||
* classes as assignable to the corresponding primitive types.
|
||||
* @param lhsType the target type
|
||||
* @param rhsType the value type that should be assigned to the target type
|
||||
* @return if the target type is assignable from the value type
|
||||
* @param lhsType the target type (left-hand side (LHS) type)
|
||||
* @param rhsType the value type (right-hand side (RHS) type) that should
|
||||
* be assigned to the target type
|
||||
* @return {@code true} if {@code rhsType} is assignable to {@code lhsType}
|
||||
* @see TypeUtils#isAssignable(java.lang.reflect.Type, java.lang.reflect.Type)
|
||||
*/
|
||||
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
|
||||
@@ -662,7 +664,7 @@ public abstract class ClassUtils {
|
||||
* in the given collection.
|
||||
* <p>Basically like {@code AbstractCollection.toString()}, but stripping
|
||||
* the "class "/"interface " prefix before every class name.
|
||||
* @param classes a Collection of Class objects (may be {@code null})
|
||||
* @param classes a Collection of Class objects (can be {@code null})
|
||||
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
|
||||
* @see java.util.AbstractCollection#toString()
|
||||
*/
|
||||
@@ -717,7 +719,7 @@ public abstract class ClassUtils {
|
||||
* <p>If the class itself is an interface, it gets returned as sole interface.
|
||||
* @param clazz the class to analyze for interfaces
|
||||
* @param classLoader the ClassLoader that the interfaces need to be visible in
|
||||
* (may be {@code null} when accepting all declared interfaces)
|
||||
* (can be {@code null} when accepting all declared interfaces)
|
||||
* @return all interfaces that the given object implements as an array
|
||||
*/
|
||||
public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
@@ -752,7 +754,7 @@ public abstract class ClassUtils {
|
||||
* <p>If the class itself is an interface, it gets returned as sole interface.
|
||||
* @param clazz the class to analyze for interfaces
|
||||
* @param classLoader the ClassLoader that the interfaces need to be visible in
|
||||
* (may be {@code null} when accepting all declared interfaces)
|
||||
* (can be {@code null} when accepting all declared interfaces)
|
||||
* @return all interfaces that the given object implements as a Set
|
||||
*/
|
||||
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
@@ -866,9 +868,9 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the given object is a CGLIB proxy.
|
||||
* @param object the object to check
|
||||
* @see #isCglibProxyClass(Class)
|
||||
* @see org.springframework.aop.support.AopUtils#isCglibProxy(Object)
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* such as for a Spring AOP proxy
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxy(Object object) {
|
||||
@@ -878,8 +880,9 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the specified class is a CGLIB-generated class.
|
||||
* @param clazz the class to check
|
||||
* @see #isCglibProxyClassName(String)
|
||||
* @see #getUserClass(Class)
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* or simply a check for containing {@link #CGLIB_CLASS_SEPARATOR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxyClass(@Nullable Class<?> clazz) {
|
||||
@@ -889,7 +892,9 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the specified class name is a CGLIB-generated class.
|
||||
* @param className the class name to check
|
||||
* @see #CGLIB_CLASS_SEPARATOR
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* or simply a check for containing {@link #CGLIB_CLASS_SEPARATOR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxyClassName(@Nullable String className) {
|
||||
@@ -913,6 +918,7 @@ public abstract class ClassUtils {
|
||||
* class, but the original class in case of a CGLIB-generated subclass.
|
||||
* @param clazz the class to check
|
||||
* @return the user-defined class
|
||||
* @see #CGLIB_CLASS_SEPARATOR
|
||||
*/
|
||||
public static Class<?> getUserClass(Class<?> clazz) {
|
||||
if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
|
||||
@@ -1065,7 +1071,7 @@ public abstract class ClassUtils {
|
||||
* fully qualified interface/class name + "." + method name.
|
||||
* @param method the method
|
||||
* @param clazz the clazz that the method is being invoked on
|
||||
* (may be {@code null} to indicate the method's declaring class)
|
||||
* (can be {@code null} to indicate the method's declaring class)
|
||||
* @return the qualified name of the method
|
||||
* @since 4.3.4
|
||||
*/
|
||||
@@ -1146,7 +1152,7 @@ public abstract class ClassUtils {
|
||||
* @param clazz the clazz to analyze
|
||||
* @param methodName the name of the method
|
||||
* @param paramTypes the parameter types of the method
|
||||
* (may be {@code null} to indicate any signature)
|
||||
* (can be {@code null} to indicate any signature)
|
||||
* @return the method (never {@code null})
|
||||
* @throws IllegalStateException if the method has not been found
|
||||
* @see Class#getMethod
|
||||
@@ -1185,7 +1191,7 @@ public abstract class ClassUtils {
|
||||
* @param clazz the clazz to analyze
|
||||
* @param methodName the name of the method
|
||||
* @param paramTypes the parameter types of the method
|
||||
* (may be {@code null} to indicate any signature)
|
||||
* (can be {@code null} to indicate any signature)
|
||||
* @return the method, or {@code null} if not found
|
||||
* @see Class#getMethod
|
||||
*/
|
||||
@@ -1261,26 +1267,27 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Given a method, which may come from an interface, and a target class used
|
||||
* in the current reflective invocation, find the corresponding target method
|
||||
* if there is one. E.g. the method may be {@code IFoo.bar()} and the
|
||||
* target class may be {@code DefaultFoo}. In this case, the method may be
|
||||
* if there is one — for example, the method may be {@code IFoo.bar()},
|
||||
* and the target class may be {@code DefaultFoo}. In this case, the method may be
|
||||
* {@code DefaultFoo.bar()}. This enables attributes on that method to be found.
|
||||
* <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod},
|
||||
* this method does <i>not</i> resolve bridge methods automatically.
|
||||
* Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
|
||||
* if bridge method resolution is desirable (e.g. for obtaining metadata from
|
||||
* the original method definition).
|
||||
* <p><b>NOTE:</b> Since Spring 3.1.1, if Java security settings disallow reflective
|
||||
* access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation
|
||||
* will fall back to returning the originally provided method.
|
||||
* if bridge method resolution is desirable — for example, to obtain
|
||||
* metadata from the original method definition.
|
||||
* <p><b>NOTE:</b> If Java security settings disallow reflective access —
|
||||
* for example, calls to {@code Class#getDeclaredMethods}, etc. — this
|
||||
* implementation will fall back to returning the originally provided method.
|
||||
* @param method the method to be invoked, which may come from an interface
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (may be {@code null} or may not even implement the method)
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} does not implement it
|
||||
* @see #getInterfaceMethodIfPossible(Method, Class)
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
|
||||
if (targetClass != null && targetClass != method.getDeclaringClass() &&
|
||||
(isOverridable(method, targetClass) || !method.getDeclaringClass().isAssignableFrom(targetClass))) {
|
||||
try {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
try {
|
||||
|
||||
@@ -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.
|
||||
@@ -58,7 +58,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
@Nullable
|
||||
public V getFirst(K key) {
|
||||
List<V> values = this.targetMap.get(key);
|
||||
return (values != null && !values.isEmpty() ? values.get(0) : null);
|
||||
return (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +69,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
|
||||
@Override
|
||||
public void addAll(K key, List<? extends V> values) {
|
||||
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new ArrayList<>(1));
|
||||
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new ArrayList<>(values.size()));
|
||||
currentValues.addAll(values);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
public Map<K, V> toSingleValueMap() {
|
||||
Map<K, V> singleValueMap = CollectionUtils.newLinkedHashMap(this.targetMap.size());
|
||||
this.targetMap.forEach((key, values) -> {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
singleValueMap.put(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
@@ -781,6 +781,7 @@ public abstract class StringUtils {
|
||||
* and {@code "0"} through {@code "9"} stay the same.</li>
|
||||
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
|
||||
* <li>A sequence "{@code %<i>xy</i>}" is interpreted as a hexadecimal representation of the character.</li>
|
||||
* <li>For all other characters (including those already decoded), the output is undefined.</li>
|
||||
* </ul>
|
||||
* @param source the encoded String
|
||||
* @param charset the character set
|
||||
@@ -829,7 +830,7 @@ public abstract class StringUtils {
|
||||
* the {@link Locale#toString} format as well as BCP 47 language tags as
|
||||
* specified by {@link Locale#forLanguageTag}.
|
||||
* @param localeValue the locale value: following either {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* separators (as an alternative to underscores), or BCP 47 (e.g. "en-UK")
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
@@ -859,7 +860,7 @@ public abstract class StringUtils {
|
||||
* <p><b>Note: This delegate does not accept the BCP 47 language tag format.
|
||||
* Please use {@link #parseLocale} for lenient parsing of both formats.</b>
|
||||
* @param localeString the locale {@code String}: following {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* separators (as an alternative to underscores)
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodIntrospector.MetadataLookup;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;
|
||||
|
||||
/**
|
||||
* Tests for {@link MethodIntrospector}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.3.34
|
||||
*/
|
||||
class MethodIntrospectorTests {
|
||||
|
||||
@Test // gh-32586
|
||||
void selectMethodsAndClearDeclaredMethodsCacheBetweenInvocations() {
|
||||
Class<?> targetType = ActualController.class;
|
||||
|
||||
// Preconditions for this use case.
|
||||
assertThat(targetType).isPublic();
|
||||
assertThat(targetType.getSuperclass()).isPackagePrivate();
|
||||
|
||||
MetadataLookup<String> metadataLookup = (MetadataLookup<String>) method -> {
|
||||
if (MergedAnnotations.from(method, TYPE_HIERARCHY).isPresent(Mapped.class)) {
|
||||
return method.getName();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Start with a clean slate.
|
||||
ReflectionUtils.clearCache();
|
||||
|
||||
// Round #1
|
||||
Map<Method, String> methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
|
||||
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
|
||||
|
||||
// Simulate ConfigurableApplicationContext#refresh() which clears the
|
||||
// ReflectionUtils#declaredMethodsCache but NOT the BridgeMethodResolver#cache.
|
||||
// As a consequence, ReflectionUtils.getDeclaredMethods(...) will return a
|
||||
// new set of methods that are logically equivalent to but not identical
|
||||
// to (in terms of object identity) any bridged methods cached in the
|
||||
// BridgeMethodResolver cache.
|
||||
ReflectionUtils.clearCache();
|
||||
|
||||
// Round #2
|
||||
methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
|
||||
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Mapped {
|
||||
}
|
||||
|
||||
interface Controller {
|
||||
|
||||
void unmappedMethod();
|
||||
|
||||
@Mapped
|
||||
void update();
|
||||
|
||||
@Mapped
|
||||
void delete();
|
||||
}
|
||||
|
||||
// Must NOT be public.
|
||||
abstract static class AbstractController implements Controller {
|
||||
|
||||
@Override
|
||||
public void unmappedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
}
|
||||
}
|
||||
|
||||
// MUST be public.
|
||||
public static class ActualController extends AbstractController {
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+63
-1
@@ -24,6 +24,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -175,7 +176,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
|
||||
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
|
||||
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
|
||||
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
|
||||
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
|
||||
@@ -240,6 +241,44 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
assertThat(annotationTypes).containsExactly(WithRepeatedMetaAnnotations.class, Noninherited.class, Noninherited.class);
|
||||
}
|
||||
|
||||
@Test // gh-32731
|
||||
void searchFindsRepeatableContainerAnnotationAndRepeatedAnnotations() {
|
||||
Class<?> clazz = StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class;
|
||||
|
||||
// NO RepeatableContainers
|
||||
MergedAnnotations mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
|
||||
ContainerWithMultipleAttributes container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
RepeatableWithContainerWithMultipleAttributes[] repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
Set<RepeatableWithContainerWithMultipleAttributes> set =
|
||||
mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Only finds the locally declared repeated annotation.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("C");
|
||||
|
||||
// Standard RepeatableContainers
|
||||
mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables());
|
||||
container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
set = mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Finds the locally declared repeated annotation plus the 2 in the container.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B", "C");
|
||||
}
|
||||
|
||||
private <A extends Annotation> Set<A> getAnnotations(Class<? extends Annotation> container,
|
||||
Class<A> repeatable, SearchStrategy searchStrategy, AnnotatedElement element) {
|
||||
|
||||
@@ -449,4 +488,27 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
static class WithRepeatedMetaAnnotationsClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ContainerWithMultipleAttributes {
|
||||
|
||||
RepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(ContainerWithMultipleAttributes.class)
|
||||
@interface RepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@ContainerWithMultipleAttributes(name = "enigma", value = {
|
||||
@RepeatableWithContainerWithMultipleAttributes("A"),
|
||||
@RepeatableWithContainerWithMultipleAttributes("B")
|
||||
})
|
||||
@RepeatableWithContainerWithMultipleAttributes("C")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-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.
|
||||
@@ -18,7 +18,10 @@ package org.springframework.core.env;
|
||||
|
||||
import java.security.AccessControlException;
|
||||
import java.security.Permission;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -307,6 +310,12 @@ class StandardEnvironmentTests {
|
||||
// non-string keys and values work fine... until the security manager is introduced below
|
||||
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
|
||||
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
|
||||
|
||||
PropertiesPropertySource systemPropertySource = (PropertiesPropertySource)
|
||||
environment.getPropertySources().get(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
Set<String> expectedKeys = new HashSet<>(System.getProperties().stringPropertyNames());
|
||||
expectedKeys.add(STRING_PROPERTY_NAME); // filtered out by stringPropertyNames due to non-String value
|
||||
assertThat(new HashSet<>(Arrays.asList(systemPropertySource.getPropertyNames()))).isEqualTo(expectedKeys);
|
||||
}
|
||||
|
||||
SecurityManager securityManager = new SecurityManager() {
|
||||
@@ -407,6 +416,7 @@ class StandardEnvironmentTests {
|
||||
EnvironmentTestUtils.getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GetActiveProfiles {
|
||||
|
||||
@@ -456,6 +466,7 @@ class StandardEnvironmentTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class AcceptsProfilesTests {
|
||||
|
||||
@@ -538,9 +549,9 @@ class StandardEnvironmentTests {
|
||||
environment.addActiveProfile("p2");
|
||||
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class MatchesProfilesTests {
|
||||
|
||||
@@ -650,7 +661,6 @@ class StandardEnvironmentTests {
|
||||
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
|
||||
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-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.
|
||||
@@ -48,14 +48,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
class PathMatchingResourcePatternResolverTests {
|
||||
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = {"EncodedResource.class",
|
||||
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
|
||||
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
|
||||
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
|
||||
"ResourcePatternUtils.class", "SpringFactoriesLoader.class"};
|
||||
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = {"PathMatchingResourcePatternResolverTests.class"};
|
||||
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = {"NonNull.class", "NonNullApi.class", "Nullable.class"};
|
||||
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
@@ -166,7 +166,7 @@ class PathMatchingResourcePatternResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
|
||||
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
|
||||
String pathPrefix = ".+?resources/";
|
||||
@@ -294,8 +294,8 @@ class PathMatchingResourcePatternResolverTests {
|
||||
}
|
||||
|
||||
private String getPath(Resource resource) {
|
||||
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
|
||||
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
|
||||
// Tests fail if we use resource.getURL().getPath(). They would also fail on macOS when
|
||||
// using resource.getURI().getPath() if the resource paths are not Unicode normalized.
|
||||
//
|
||||
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
|
||||
// we use FileSystemResource#getPath since this test class is sometimes run within a
|
||||
|
||||
@@ -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.
|
||||
@@ -45,6 +45,7 @@ class StreamUtilsTests {
|
||||
|
||||
private String string = "";
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
new Random().nextBytes(bytes);
|
||||
@@ -53,6 +54,7 @@ class StreamUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void copyToByteArray() throws Exception {
|
||||
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
@@ -127,4 +129,5 @@ class StreamUtilsTests {
|
||||
ordered.verify(source).write(bytes, 1, 2);
|
||||
ordered.verify(source, never()).close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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,7 +20,7 @@ import org.springframework.dao.DataRetrievalFailureException;
|
||||
|
||||
/**
|
||||
* Data access exception thrown when a result set did not have the correct column count,
|
||||
* for example when expecting a single column but getting 0 or more than 1 columns.
|
||||
* for example when expecting a single column but getting 0 or more than 1 column.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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,7 +20,7 @@ import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
|
||||
|
||||
/**
|
||||
* Exception thrown when a JDBC update affects an unexpected number of rows.
|
||||
* Typically we expect an update to affect a single row, meaning it's an
|
||||
* Typically, we expect an update to affect a single row, meaning it is an
|
||||
* error if it affects multiple rows.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
|
||||
+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.
|
||||
@@ -22,7 +22,7 @@ package org.springframework.jdbc.core;
|
||||
*
|
||||
* <p>This interface allows you to signal the end of a batch rather than
|
||||
* having to determine the exact batch size upfront. Batch size is still
|
||||
* being honored but it is now the maximum size of the batch.
|
||||
* being honored, but it is now the maximum size of the batch.
|
||||
*
|
||||
* <p>The {@link #isBatchExhausted} method is called after each call to
|
||||
* {@link #setValues} to determine whether there were some values added,
|
||||
|
||||
@@ -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.
|
||||
@@ -128,8 +128,8 @@ public abstract class RdbmsOperation implements InitializingBean {
|
||||
|
||||
/**
|
||||
* Set the maximum number of rows for this RDBMS operation. This is important
|
||||
* for processing subsets of large result sets, avoiding to read and hold
|
||||
* the entire result set in the database or in the JDBC driver.
|
||||
* for processing subsets of large result sets, in order to avoid reading and
|
||||
* holding the entire result set in the database or in the JDBC driver.
|
||||
* <p>Default is -1, indicating to use the driver's default.
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate#setMaxRows
|
||||
*/
|
||||
@@ -175,7 +175,7 @@ public abstract class RdbmsOperation implements InitializingBean {
|
||||
public void setUpdatableResults(boolean updatableResults) {
|
||||
if (isCompiled()) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"The updateableResults flag must be set before the operation is compiled");
|
||||
"The updatableResults flag must be set before the operation is compiled");
|
||||
}
|
||||
this.updatableResults = updatableResults;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.jdbc.core.SqlParameter;
|
||||
|
||||
/**
|
||||
* Superclass for object abstractions of RDBMS stored procedures.
|
||||
* This class is abstract and it is intended that subclasses will provide a typed
|
||||
* This class is abstract, and it is intended that subclasses will provide a typed
|
||||
* method for invocation that delegates to the supplied {@link #execute} method.
|
||||
*
|
||||
* <p>The inherited {@link #setSql sql} property is the name of the stored procedure
|
||||
|
||||
+5
-5
@@ -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.
|
||||
@@ -25,9 +25,9 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with
|
||||
* specific databases allowing for overriding translation based on values contained in the configuration file
|
||||
* named "sql-error-codes.xml".
|
||||
* Registry for custom {@link SQLExceptionTranslator} instances associated with
|
||||
* specific databases allowing for overriding translation based on values
|
||||
* contained in the configuration file named "sql-error-codes.xml".
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @since 3.1.1
|
||||
@@ -38,7 +38,7 @@ public final class CustomSQLExceptionTranslatorRegistry {
|
||||
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class);
|
||||
|
||||
/**
|
||||
* Keep track of a single instance so we can return it to classes that request it.
|
||||
* Keep track of a single instance, so we can return it to classes that request it.
|
||||
*/
|
||||
private static final CustomSQLExceptionTranslatorRegistry instance = new CustomSQLExceptionTranslatorRegistry();
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -218,7 +218,7 @@ public abstract class JdbcUtils {
|
||||
return NumberUtils.convertNumberToTargetClass((Number) obj, Integer.class);
|
||||
}
|
||||
else {
|
||||
// e.g. on Postgres: getObject returns a PGObject but we need a String
|
||||
// e.g. on Postgres: getObject returns a PGObject, but we need a String
|
||||
return rs.getString(index);
|
||||
}
|
||||
}
|
||||
@@ -228,14 +228,14 @@ public abstract class JdbcUtils {
|
||||
try {
|
||||
return rs.getObject(index, requiredType);
|
||||
}
|
||||
catch (AbstractMethodError err) {
|
||||
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
|
||||
}
|
||||
catch (SQLFeatureNotSupportedException ex) {
|
||||
catch (SQLFeatureNotSupportedException | AbstractMethodError ex) {
|
||||
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JDBC driver has limited support for 'getObject(int, Class)' with column type: " +
|
||||
requiredType.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
|
||||
@@ -416,14 +416,14 @@ public abstract class JdbcUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given JDBC driver supports JDBC 2.0 batch updates.
|
||||
* Return whether the given JDBC driver supports JDBC batch updates.
|
||||
* <p>Typically invoked right before execution of a given set of statements:
|
||||
* to decide whether the set of SQL statements should be executed through
|
||||
* the JDBC 2.0 batch mechanism or simply in a traditional one-by-one fashion.
|
||||
* the JDBC batch mechanism or simply in a traditional one-by-one fashion.
|
||||
* <p>Logs a warning if the "supportsBatchUpdates" methods throws an exception
|
||||
* and simply returns {@code false} in that case.
|
||||
* @param con the Connection to check
|
||||
* @return whether JDBC 2.0 batch updates are supported
|
||||
* @return whether JDBC batch updates are supported
|
||||
* @see java.sql.DatabaseMetaData#supportsBatchUpdates()
|
||||
*/
|
||||
public static boolean supportsBatchUpdates(Connection con) {
|
||||
@@ -496,8 +496,8 @@ public abstract class JdbcUtils {
|
||||
/**
|
||||
* Determine the column name to use. The column name is determined based on a
|
||||
* lookup using ResultSetMetaData.
|
||||
* <p>This method implementation takes into account recent clarifications
|
||||
* expressed in the JDBC 4.0 specification:
|
||||
* <p>This method's implementation takes into account clarifications expressed
|
||||
* in the JDBC 4.0 specification:
|
||||
* <p><i>columnLabel - the label for the column specified with the SQL AS clause.
|
||||
* If the SQL AS clause was not specified, then the label is the name of the column</i>.
|
||||
* @param resultSetMetaData the current meta-data to use
|
||||
|
||||
+3
-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.
|
||||
@@ -67,7 +67,7 @@ public class SQLErrorCodesFactory {
|
||||
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
|
||||
|
||||
/**
|
||||
* Keep track of a single instance so we can return it to classes that request it.
|
||||
* Keep track of a single instance, so we can return it to classes that request it.
|
||||
*/
|
||||
private static final SQLErrorCodesFactory instance = new SQLErrorCodesFactory();
|
||||
|
||||
@@ -101,6 +101,7 @@ public class SQLErrorCodesFactory {
|
||||
* @see #loadResource(String)
|
||||
*/
|
||||
protected SQLErrorCodesFactory() {
|
||||
|
||||
Map<String, SQLErrorCodes> errorCodes;
|
||||
|
||||
try {
|
||||
|
||||
+10
-8
@@ -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.
|
||||
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The default implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* The common implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* {@link java.sql.ResultSet}, catching any {@link SQLException SQLExceptions} and
|
||||
* translating them to a corresponding Spring {@link InvalidResultSetAccessException}.
|
||||
*
|
||||
@@ -43,17 +43,17 @@ import org.springframework.util.CollectionUtils;
|
||||
* <p>Note: Since JDBC 4.0, it has been clarified that any methods using a String to identify
|
||||
* the column should be using the column label. The column label is assigned using the ALIAS
|
||||
* keyword in the SQL query string. When the query doesn't use an ALIAS, the default label is
|
||||
* the column name. Most JDBC ResultSet implementations follow this new pattern but there are
|
||||
* the column name. Most JDBC ResultSet implementations follow this pattern, but there are
|
||||
* exceptions such as the {@code com.sun.rowset.CachedRowSetImpl} class which only uses
|
||||
* the column name, ignoring any column labels. As of Spring 3.0.5, ResultSetWrappingSqlRowSet
|
||||
* will translate column labels to the correct column index to provide better support for the
|
||||
* the column name, ignoring any column labels. {@code ResultSetWrappingSqlRowSet}
|
||||
* will translate column labels to the correct column index to provide better support for
|
||||
* {@code com.sun.rowset.CachedRowSetImpl} which is the default implementation used by
|
||||
* {@link org.springframework.jdbc.core.JdbcTemplate} when working with RowSets.
|
||||
*
|
||||
* <p>Note: This class implements the {@code java.io.Serializable} marker interface
|
||||
* through the SqlRowSet interface, but is only actually serializable if the disconnected
|
||||
* ResultSet/RowSet contained in it is serializable. Most CachedRowSet implementations
|
||||
* are actually serializable, so this should usually work out.
|
||||
* are actually serializable, so serialization should usually work.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Juergen Hoeller
|
||||
@@ -67,16 +67,18 @@ public class ResultSetWrappingSqlRowSet implements SqlRowSet {
|
||||
/** use serialVersionUID from Spring 1.2 for interoperability. */
|
||||
private static final long serialVersionUID = -4688694393146734764L;
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private final ResultSet resultSet;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private final SqlRowSetMetaData rowSetMetaData;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private final Map<String, Integer> columnLabelMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ResultSetWrappingSqlRowSet for the given ResultSet.
|
||||
* Create a new {@code ResultSetWrappingSqlRowSet} for the given {@link ResultSet}.
|
||||
* @param resultSet a disconnected ResultSet to wrap
|
||||
* (usually a {@code javax.sql.rowset.CachedRowSet})
|
||||
* @throws InvalidResultSetAccessException if extracting
|
||||
|
||||
+30
-37
@@ -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.
|
||||
@@ -25,7 +25,6 @@ import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jdbc.InvalidResultSetAccessException;
|
||||
@@ -38,174 +37,168 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public class ResultSetWrappingRowSetTests {
|
||||
class ResultSetWrappingRowSetTests {
|
||||
|
||||
private ResultSet resultSet;
|
||||
private final ResultSet resultSet = mock(ResultSet.class);
|
||||
|
||||
private ResultSetWrappingSqlRowSet rowSet;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
resultSet = mock(ResultSet.class);
|
||||
rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
}
|
||||
private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetBigDecimalInt() throws Exception {
|
||||
void testGetBigDecimalInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
doTest(rset, rowset, 1, BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBigDecimalString() throws Exception {
|
||||
void testGetBigDecimalString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", String.class);
|
||||
doTest(rset, rowset, "test", BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringInt() throws Exception {
|
||||
void testGetStringInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", int.class);
|
||||
doTest(rset, rowset, 1, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringString() throws Exception {
|
||||
void testGetStringString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", String.class);
|
||||
doTest(rset, rowset, "test", "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimestampInt() throws Exception {
|
||||
void testGetTimestampInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
doTest(rset, rowset, 1, new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimestampString() throws Exception {
|
||||
void testGetTimestampString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", String.class);
|
||||
doTest(rset, rowset, "test", new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDateInt() throws Exception {
|
||||
void testGetDateInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", int.class);
|
||||
doTest(rset, rowset, 1, new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDateString() throws Exception {
|
||||
void testGetDateString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", String.class);
|
||||
doTest(rset, rowset, "test", new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimeInt() throws Exception {
|
||||
void testGetTimeInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", int.class);
|
||||
doTest(rset, rowset, 1, new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimeString() throws Exception {
|
||||
void testGetTimeString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", String.class);
|
||||
doTest(rset, rowset, "test", new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectInt() throws Exception {
|
||||
void testGetObjectInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", int.class);
|
||||
doTest(rset, rowset, 1, new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectString() throws Exception {
|
||||
void testGetObjectString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", String.class);
|
||||
doTest(rset, rowset, "test", new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIntInt() throws Exception {
|
||||
void testGetIntInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", int.class);
|
||||
doTest(rset, rowset, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIntString() throws Exception {
|
||||
void testGetIntString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", String.class);
|
||||
doTest(rset, rowset, "test", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFloatInt() throws Exception {
|
||||
void testGetFloatInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
doTest(rset, rowset, 1, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFloatString() throws Exception {
|
||||
void testGetFloatString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", String.class);
|
||||
doTest(rset, rowset, "test", 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDoubleInt() throws Exception {
|
||||
void testGetDoubleInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
doTest(rset, rowset, 1, 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDoubleString() throws Exception {
|
||||
void testGetDoubleString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", String.class);
|
||||
doTest(rset, rowset, "test", 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLongInt() throws Exception {
|
||||
void testGetLongInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", int.class);
|
||||
doTest(rset, rowset, 1, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLongString() throws Exception {
|
||||
void testGetLongString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", String.class);
|
||||
doTest(rset, rowset, "test", 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBooleanInt() throws Exception {
|
||||
void testGetBooleanInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
doTest(rset, rowset, 1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBooleanString() throws Exception {
|
||||
void testGetBooleanString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", String.class);
|
||||
doTest(rset, rowset, "test", true);
|
||||
}
|
||||
|
||||
|
||||
private void doTest(Method rsetMethod, Method rowsetMethod, Object arg, Object ret) throws Exception {
|
||||
if (arg instanceof String) {
|
||||
given(resultSet.findColumn((String) arg)).willReturn(1);
|
||||
@@ -215,9 +208,9 @@ public class ResultSetWrappingRowSetTests {
|
||||
given(rsetMethod.invoke(resultSet, arg)).willReturn(ret).willThrow(new SQLException("test"));
|
||||
}
|
||||
rowsetMethod.invoke(rowSet, arg);
|
||||
assertThatExceptionOfType(InvocationTargetException.class).isThrownBy(() ->
|
||||
rowsetMethod.invoke(rowSet, arg)).
|
||||
satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
assertThatExceptionOfType(InvocationTargetException.class)
|
||||
.isThrownBy(() -> rowsetMethod.invoke(rowSet, arg))
|
||||
.satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -189,11 +189,6 @@ public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload) throws MessagingException {
|
||||
convertAndSend(payload, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
|
||||
Destination defaultDestination = getDefaultDestination();
|
||||
|
||||
@@ -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.
|
||||
@@ -290,7 +290,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -303,7 +303,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destination the destination to receive a message from
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -317,7 +317,7 @@ public interface JmsOperations {
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destinationName the name of the destination to send this message to
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -332,7 +332,7 @@ public interface JmsOperations {
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -347,7 +347,7 @@ public interface JmsOperations {
|
||||
* @param destination the destination to receive a message from
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -363,7 +363,7 @@ public interface JmsOperations {
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -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.
|
||||
@@ -217,7 +217,7 @@ public abstract class JmsUtils {
|
||||
try {
|
||||
session.commit();
|
||||
}
|
||||
catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException ex) {
|
||||
catch (javax.jms.TransactionInProgressException ex) {
|
||||
// Ignore -> can only happen in case of a JTA transaction.
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ public abstract class JmsUtils {
|
||||
try {
|
||||
session.rollback();
|
||||
}
|
||||
catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException ex) {
|
||||
catch (javax.jms.TransactionInProgressException ex) {
|
||||
// Ignore -> can only happen in case of a JTA transaction.
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -253,8 +253,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
|
||||
* @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
|
||||
* @see #setPersistenceUnitManager
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource));
|
||||
public void setDataSource(@Nullable DataSource dataSource) {
|
||||
this.internalPersistenceUnitManager.setDataSourceLookup(
|
||||
dataSource != null ? new SingleDataSourceLookup(dataSource) : null);
|
||||
this.internalPersistenceUnitManager.setDefaultDataSource(dataSource);
|
||||
}
|
||||
|
||||
@@ -270,8 +271,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
|
||||
* @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
|
||||
* @see #setPersistenceUnitManager
|
||||
*/
|
||||
public void setJtaDataSource(DataSource jtaDataSource) {
|
||||
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(jtaDataSource));
|
||||
public void setJtaDataSource(@Nullable DataSource jtaDataSource) {
|
||||
this.internalPersistenceUnitManager.setDataSourceLookup(
|
||||
jtaDataSource != null ? new SingleDataSourceLookup(jtaDataSource) : null);
|
||||
this.internalPersistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
|
||||
}
|
||||
|
||||
@@ -416,6 +418,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DataSource getDataSource() {
|
||||
if (this.persistenceUnitInfo != null) {
|
||||
return (this.persistenceUnitInfo.getJtaDataSource() != null ?
|
||||
|
||||
+8
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -28,28 +28,18 @@ import javax.persistence.spi.PersistenceProvider;
|
||||
* shared JPA EntityManagerFactory in a Spring application context; the
|
||||
* EntityManagerFactory can then be passed to JPA-based DAOs via
|
||||
* dependency injection. Note that switching to a JNDI lookup or to a
|
||||
* {@link LocalContainerEntityManagerFactoryBean}
|
||||
* definition is just a matter of configuration!
|
||||
* {@link LocalContainerEntityManagerFactoryBean} definition based on the
|
||||
* JPA container contract is just a matter of configuration!
|
||||
*
|
||||
* <p>Configuration settings are usually read from a {@code META-INF/persistence.xml}
|
||||
* config file, residing in the class path, according to the JPA standalone bootstrap
|
||||
* contract. Additionally, most JPA providers will require a special VM agent
|
||||
* (specified on JVM startup) that allows them to instrument application classes.
|
||||
* See the Java Persistence API specification and your provider documentation
|
||||
* for setup details.
|
||||
*
|
||||
* <p>This EntityManagerFactory bootstrap is appropriate for standalone applications
|
||||
* which solely use JPA for data access. If you want to set up your persistence
|
||||
* provider for an external DataSource and/or for global transactions which span
|
||||
* multiple resources, you will need to either deploy it into a full Java EE
|
||||
* application server and access the deployed EntityManagerFactory via JNDI,
|
||||
* or use Spring's {@link LocalContainerEntityManagerFactoryBean} with appropriate
|
||||
* configuration for local setup according to JPA's container contract.
|
||||
* contract. See the Java Persistence API specification and your persistence provider
|
||||
* documentation for setup details. Additionally, JPA properties can also be added
|
||||
* on this FactoryBean via {@link #setJpaProperties}/{@link #setJpaPropertyMap}.
|
||||
*
|
||||
* <p><b>Note:</b> This FactoryBean has limited configuration power in terms of
|
||||
* what configuration it is able to pass to the JPA provider. If you need more
|
||||
* flexible configuration, for example passing a Spring-managed JDBC DataSource
|
||||
* to the JPA provider, consider using Spring's more powerful
|
||||
* the configuration that it is able to pass to the JPA provider. If you need
|
||||
* more flexible configuration options, consider using Spring's more powerful
|
||||
* {@link LocalContainerEntityManagerFactoryBean} instead.
|
||||
*
|
||||
* <p><b>NOTE: Spring's JPA support requires JPA 2.1 or higher, as of Spring 5.0.</b>
|
||||
|
||||
+4
-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.
|
||||
@@ -390,7 +390,9 @@ public abstract class SharedEntityManagerCreator {
|
||||
else if (targetClass.isInstance(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
break;
|
||||
else {
|
||||
return this.target.unwrap(targetClass);
|
||||
}
|
||||
case "getOutputParameterValue":
|
||||
if (this.entityManager == null) {
|
||||
Object key = args[0];
|
||||
|
||||
+70
-50
@@ -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.
|
||||
@@ -38,16 +38,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SharedEntityManagerCreator}.
|
||||
* Tests for {@link SharedEntityManagerCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SharedEntityManagerCreatorTests {
|
||||
class SharedEntityManagerCreatorTests {
|
||||
|
||||
@Test
|
||||
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class,
|
||||
withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
|
||||
// EntityManagerFactoryInfo.getEntityManagerInterface returns null
|
||||
@@ -55,7 +55,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnJoinTransaction() {
|
||||
void transactionRequiredExceptionOnJoinTransaction() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -63,7 +63,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnFlush() {
|
||||
void transactionRequiredExceptionOnFlush() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -71,7 +71,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnPersist() {
|
||||
void transactionRequiredExceptionOnPersist() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -79,7 +79,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnMerge() {
|
||||
void transactionRequiredExceptionOnMerge() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -87,7 +87,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnRemove() {
|
||||
void transactionRequiredExceptionOnRemove() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -95,7 +95,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnRefresh() {
|
||||
void transactionRequiredExceptionOnRefresh() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -103,78 +103,98 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithUpdate() {
|
||||
void deferredQueryWithUpdate() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
Query query = mock(Query.class);
|
||||
Query targetQuery = mock(Query.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").executeUpdate();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.executeUpdate();
|
||||
|
||||
verify(query).executeUpdate();
|
||||
verify(targetQuery).executeUpdate();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithSingleResult() {
|
||||
void deferredQueryWithSingleResult() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
Query query = mock(Query.class);
|
||||
Query targetQuery = mock(Query.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getSingleResult();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getSingleResult();
|
||||
|
||||
verify(query).getSingleResult();
|
||||
verify(targetQuery).getSingleResult();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithResultList() {
|
||||
void deferredQueryWithResultList() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
Query query = mock(Query.class);
|
||||
Query targetQuery = mock(Query.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getResultList();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultList();
|
||||
|
||||
verify(query).getResultList();
|
||||
verify(targetQuery).getResultList();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithResultStream() {
|
||||
void deferredQueryWithResultStream() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
Query query = mock(Query.class);
|
||||
Query targetQuery = mock(Query.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getResultStream();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultStream();
|
||||
|
||||
verify(query).getResultStream();
|
||||
verify(targetQuery).getResultStream();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
StoredProcedureQuery query = mock(StoredProcedureQuery.class);
|
||||
StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue(0);
|
||||
willReturn("z").given(query).getOutputParameterValue(2);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue(0);
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue(2);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -188,24 +208,24 @@ public class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue(1));
|
||||
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
|
||||
|
||||
verify(query).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetQuery).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class);
|
||||
EntityManager targetEm = mock(EntityManager.class);
|
||||
StoredProcedureQuery query = mock(StoredProcedureQuery.class);
|
||||
StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue("a");
|
||||
willReturn("z").given(query).getOutputParameterValue("c");
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue("a");
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue("c");
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -219,12 +239,12 @@ public class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue("b"));
|
||||
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
|
||||
|
||||
verify(query).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetQuery).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
|
||||
+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.
|
||||
@@ -70,13 +70,15 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
|
||||
private boolean suppressClose;
|
||||
|
||||
/** Override auto-commit state?. */
|
||||
private @Nullable Boolean autoCommit;
|
||||
@Nullable
|
||||
private Boolean autoCommit;
|
||||
|
||||
/** Wrapped Connection. */
|
||||
private final AtomicReference<Connection> target = new AtomicReference<>();
|
||||
|
||||
/** Proxy Connection. */
|
||||
private @Nullable Connection connection;
|
||||
@Nullable
|
||||
private Connection connection;
|
||||
|
||||
private final Mono<? extends Connection> connectionEmitter;
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -60,7 +60,6 @@ import javax.servlet.http.Part;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
@@ -990,7 +989,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private static String encodeCookies(@NonNull Cookie... cookies) {
|
||||
private static String encodeCookies(Cookie... cookies) {
|
||||
return Arrays.stream(cookies)
|
||||
.map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue()))
|
||||
.collect(Collectors.joining("; "));
|
||||
|
||||
+1
-3
@@ -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.
|
||||
@@ -31,7 +31,6 @@ import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
@@ -280,7 +279,6 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) {
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
|
||||
|
||||
+4
-3
@@ -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.
|
||||
@@ -44,10 +44,11 @@ public class IncorrectUpdateSemanticsDataAccessException extends InvalidDataAcce
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return whether data was updated.
|
||||
* If this method returns false, there's nothing to roll back.
|
||||
* <p>The default implementation always returns true.
|
||||
* If this method returns {@code false}, there is nothing to roll back.
|
||||
* <p>The default implementation always returns {@code true}.
|
||||
* This can be overridden in subclasses.
|
||||
*/
|
||||
public boolean wasDataUpdated() {
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionAttribute, with default settings.
|
||||
* Create a new {@code DefaultTransactionAttribute} with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -76,7 +76,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionAttribute with the given
|
||||
* Create a new {@code DefaultTransactionAttribute} with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
+6
-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.
|
||||
@@ -210,11 +210,13 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status);
|
||||
}
|
||||
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
return Mono.just(prepareReactiveTransaction(
|
||||
synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,7 +332,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
if (resourcesHolder != null) {
|
||||
Object suspendedResources = resourcesHolder.suspendedResources;
|
||||
if (suspendedResources != null) {
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
}
|
||||
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
|
||||
if (suspendedSynchronizations != null) {
|
||||
|
||||
+4
-3
@@ -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.
|
||||
@@ -451,7 +451,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
if (useSavepointForNestedTransaction()) {
|
||||
// Create savepoint within existing Spring-managed transaction,
|
||||
// through the SavepointManager API implemented by TransactionStatus.
|
||||
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
|
||||
// Usually uses JDBC savepoints. Never activates Spring synchronization.
|
||||
DefaultTransactionStatus status =
|
||||
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
|
||||
status.createAndHoldSavepoint();
|
||||
@@ -465,7 +465,8 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
}
|
||||
}
|
||||
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -65,7 +65,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionDefinition, with default settings.
|
||||
* Create a new {@code DefaultTransactionDefinition} with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -93,7 +93,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionDefinition with the given
|
||||
* Create a new {@code DefaultTransactionDefinition} with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
@@ -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.
|
||||
@@ -110,7 +110,7 @@ public final class ResponseCookie extends HttpCookie {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the cookie has the "HttpOnly" attribute.
|
||||
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
|
||||
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
|
||||
*/
|
||||
public boolean isHttpOnly() {
|
||||
return this.httpOnly;
|
||||
@@ -326,7 +326,7 @@ public final class ResponseCookie extends HttpCookie {
|
||||
|
||||
/**
|
||||
* Add the "HttpOnly" attribute to the cookie.
|
||||
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
|
||||
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
|
||||
*/
|
||||
ResponseCookieBuilder httpOnly(boolean httpOnly);
|
||||
|
||||
|
||||
+40
-10
@@ -16,8 +16,12 @@
|
||||
|
||||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
@@ -54,16 +58,7 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.cookies = cookies;
|
||||
this.body = singleSubscription(body);
|
||||
}
|
||||
|
||||
private static Flux<DataBuffer> singleSubscription(Flux<DataBuffer> body) {
|
||||
AtomicBoolean subscribed = new AtomicBoolean();
|
||||
return body.doOnSubscribe(s -> {
|
||||
if (!subscribed.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException("The client response body can only be consumed once");
|
||||
}
|
||||
});
|
||||
this.body = Flux.from(new SingleSubscriberPublisher<>(body));
|
||||
}
|
||||
|
||||
|
||||
@@ -91,4 +86,39 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
|
||||
private static final class SingleSubscriberPublisher<T> implements Publisher<T> {
|
||||
|
||||
private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() {
|
||||
@Override
|
||||
public void request(long l) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
};
|
||||
|
||||
private final Publisher<T> delegate;
|
||||
|
||||
private final AtomicBoolean subscribed = new AtomicBoolean();
|
||||
|
||||
|
||||
public SingleSubscriberPublisher(Publisher<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(Subscriber<? super T> subscriber) {
|
||||
Objects.requireNonNull(subscriber, "Subscriber must not be null");
|
||||
if (this.subscribed.compareAndSet(false, true)) {
|
||||
this.delegate.subscribe(subscriber);
|
||||
}
|
||||
else {
|
||||
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
|
||||
subscriber.onError(new IllegalStateException("The client response body can only be consumed once"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -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.
|
||||
@@ -39,11 +39,10 @@ import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation for the Apache HttpComponents HttpClient 5.x.
|
||||
* @author Martin Tarjányi
|
||||
@@ -123,16 +122,14 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
HttpHeaders headers = getHeaders();
|
||||
|
||||
headers.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> !HttpHeaders.CONTENT_LENGTH.equals(entry.getKey()))
|
||||
.forEach(entry -> entry.getValue().forEach(v -> this.httpRequest.addHeader(entry.getKey(), v)));
|
||||
|
||||
if (!this.httpRequest.containsHeader(HttpHeaders.ACCEPT)) {
|
||||
this.httpRequest.addHeader(HttpHeaders.ACCEPT, ALL_VALUE);
|
||||
this.httpRequest.addHeader(HttpHeaders.ACCEPT, MediaType.ALL_VALUE);
|
||||
}
|
||||
|
||||
this.contentLength = headers.getContentLength();
|
||||
}
|
||||
|
||||
@@ -143,7 +140,6 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
}
|
||||
|
||||
CookieStore cookieStore = this.context.getCookieStore();
|
||||
|
||||
getCookies().values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
|
||||
+11
-6
@@ -21,7 +21,6 @@ import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.reactive.client.ReactiveResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@@ -55,23 +54,29 @@ class JettyClientHttpResponse extends AbstractClientHttpResponse {
|
||||
}
|
||||
|
||||
private static HttpHeaders adaptHeaders(ReactiveResponse response) {
|
||||
MultiValueMap<String, String> headers = new JettyHeadersAdapter(response.getHeaders());
|
||||
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
|
||||
Jetty10HttpFieldsHelper.getHttpHeaders(response.getResponse()) :
|
||||
new JettyHeadersAdapter(response.getHeaders()));
|
||||
return HttpHeaders.readOnlyHttpHeaders(headers);
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, ResponseCookie> adaptCookies(ReactiveResponse response) {
|
||||
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
|
||||
List<HttpField> cookieHeaders = response.getHeaders().getFields(HttpHeaders.SET_COOKIE);
|
||||
cookieHeaders.forEach(header ->
|
||||
HttpCookie.parse(header.getValue()).forEach(cookie -> result.add(cookie.getName(),
|
||||
MultiValueMap<String, String> headers = adaptHeaders(response);
|
||||
List<String> cookieHeader = headers.get(HttpHeaders.SET_COOKIE);
|
||||
if (!CollectionUtils.isEmpty(cookieHeader)) {
|
||||
cookieHeader.forEach(header ->
|
||||
HttpCookie.parse(header).forEach(cookie -> result.add(cookie.getName(),
|
||||
ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue())
|
||||
.domain(cookie.getDomain())
|
||||
.path(cookie.getPath())
|
||||
.maxAge(cookie.getMaxAge())
|
||||
.secure(cookie.getSecure())
|
||||
.httpOnly(cookie.isHttpOnly())
|
||||
.sameSite(parseSameSite(header.getValue()))
|
||||
.sameSite(parseSameSite(header))
|
||||
.build()))
|
||||
);
|
||||
}
|
||||
return CollectionUtils.unmodifiableMultiValueMap(result);
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -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.
|
||||
@@ -81,6 +81,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
if (this.supportsReadStreaming && InputStreamResource.class == clazz) {
|
||||
return new InputStreamResource(inputMessage.getBody()) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return inputMessage.getHeaders().getContentDisposition().getFilename();
|
||||
}
|
||||
@@ -106,12 +107,20 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
writeContent(resource, outputMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getDefaultContentType(Resource resource) {
|
||||
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Long getContentLength(Resource resource, @Nullable MediaType contentType) throws IOException {
|
||||
// Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
|
||||
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
|
||||
@@ -122,15 +131,10 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
return (contentLength < 0 ? null : contentLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
writeContent(resource, outputMessage);
|
||||
}
|
||||
|
||||
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
// We cannot use try-with-resources here for the InputStream, since we have
|
||||
// custom handling of the close() method in a finally-block.
|
||||
try {
|
||||
|
||||
+21
-21
@@ -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,8 +37,8 @@ import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can write a single {@link ResourceRegion},
|
||||
* or Collections of {@link ResourceRegion ResourceRegions}.
|
||||
* Implementation of {@link HttpMessageConverter} that can write a single
|
||||
* {@link ResourceRegion} or Collections of {@link ResourceRegion ResourceRegions}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Juergen Hoeller
|
||||
@@ -51,22 +51,6 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected MediaType getDefaultContentType(Object object) {
|
||||
Resource resource = null;
|
||||
if (object instanceof ResourceRegion) {
|
||||
resource = ((ResourceRegion) object).getResource();
|
||||
}
|
||||
else {
|
||||
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
|
||||
if (!regions.isEmpty()) {
|
||||
resource = regions.iterator().next().getResource();
|
||||
}
|
||||
}
|
||||
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
return false;
|
||||
@@ -123,7 +107,6 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
@@ -131,16 +114,33 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
|
||||
writeResourceRegion((ResourceRegion) object, outputMessage);
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
|
||||
if (regions.size() == 1) {
|
||||
writeResourceRegion(regions.iterator().next(), outputMessage);
|
||||
}
|
||||
else {
|
||||
writeResourceRegionCollection((Collection<ResourceRegion>) object, outputMessage);
|
||||
writeResourceRegionCollection(regions, outputMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getDefaultContentType(Object object) {
|
||||
Resource resource = null;
|
||||
if (object instanceof ResourceRegion) {
|
||||
resource = ((ResourceRegion) object).getResource();
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
|
||||
if (!regions.isEmpty()) {
|
||||
resource = regions.iterator().next().getResource();
|
||||
}
|
||||
}
|
||||
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
|
||||
protected void writeResourceRegion(ResourceRegion region, HttpOutputMessage outputMessage) throws IOException {
|
||||
Assert.notNull(region, "ResourceRegion must not be null");
|
||||
|
||||
+2
-1
@@ -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.
|
||||
@@ -519,6 +519,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Long getContentLength(Object object, @Nullable MediaType contentType) throws IOException {
|
||||
if (object instanceof MappingJacksonValue) {
|
||||
object = ((MappingJacksonValue) object).getValue();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user