Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds df041ba032 Release v5.3.33 2024-03-14 09:05:01 +00:00
81 changed files with 710 additions and 940 deletions
@@ -1,33 +0,0 @@
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
-34
View File
@@ -1,34 +0,0 @@
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"
@@ -1,63 +0,0 @@
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) }}
-80
View File
@@ -1,80 +0,0 @@
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@v4
- uses: gradle/wrapper-validation-action@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=5.3.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A5.3.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](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".
+2 -2
View File
@@ -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.109.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.44"
mavenBom "io.netty:netty-bom:4.1.107.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.42"
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"
-2
View File
@@ -1,7 +1,5 @@
== 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].
+1 -1
View File
@@ -17,4 +17,4 @@ changelog:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20240125
FROM ubuntu:jammy-20240111
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1
View File
@@ -8,3 +8,4 @@ milestone: "5.3.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
task-timeout: 1h00m
+122 -14
View File
@@ -5,7 +5,9 @@ anchors:
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
@@ -21,6 +23,14 @@ 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
@@ -35,7 +45,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.8.0
tag: 1.7.1
- name: artifactory-resource
type: registry-image
source:
@@ -54,12 +64,25 @@ 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
@@ -82,6 +105,27 @@ 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
@@ -116,23 +160,37 @@ jobs:
- put: ci-image
params:
image: ci-image/image.tar
- name: stage-milestone
- name: build
serial: true
public: 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
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-build
params: { state: "success", commit: "git-repo" }
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-staging-local
repo: libs-snapshot-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
@@ -157,9 +215,55 @@ jobs:
- "/**/spring-*-schema.zip"
properties:
"zip.type": "schema"
- put: git-repo
params:
repository: stage-git-repo
get_params:
threads: 8
- name: jdk17-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk17-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 17
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk17-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk17-build
params: { state: "success", commit: "git-repo" }
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
@@ -200,6 +304,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -243,6 +348,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -285,6 +391,8 @@ 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"
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
+9
View File
@@ -0,0 +1,9 @@
#!/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
+9
View File
@@ -0,0 +1,9 @@
#!/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
View File
@@ -1,5 +1,6 @@
#!/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/' )
+1 -2
View File
@@ -26,5 +26,4 @@ run:
cat > /root/.docker/config.json <<EOF
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
EOF
build
build
+19
View File
@@ -0,0 +1,19 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
+22
View File
@@ -0,0 +1,22 @@
---
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
+24
View File
@@ -0,0 +1,24 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/check-project.sh
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
tag: '0.0.7'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0'
tag: '0.3.4'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.35
version=5.3.33
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+3 -3
View File
@@ -7,8 +7,8 @@ pluginManagement {
}
plugins {
id "com.gradle.develocity" version "3.17.2"
id "io.spring.ge.conventions" version "0.0.17"
id "com.gradle.enterprise" version "3.12.1"
id "io.spring.ge.conventions" version "0.0.13"
}
include "spring-aop"
@@ -42,7 +42,7 @@ rootProject.children.each {project ->
}
settings.gradle.projectsLoaded {
develocity {
gradleEnterprise {
buildScan {
File buildDir = settings.gradle.rootProject.getBuildDir()
buildDir.mkdirs()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,30 +174,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public ClassFilter getClassFilter() {
checkExpression();
obtainPointcutExpression();
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
checkExpression();
obtainPointcutExpression();
return this;
}
/**
* Check whether this pointcut is ready to match.
* Check whether this pointcut is ready to match,
* lazily building the underlying AspectJ pointcut expression.
*/
private void checkExpression() {
private PointcutExpression obtainPointcutExpression() {
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);
@@ -274,9 +269,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
try {
try {
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
@@ -287,9 +283,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
}
catch (IllegalArgumentException | IllegalStateException ex) {
throw ex;
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
@@ -298,6 +291,7 @@ 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
@@ -335,6 +329,7 @@ 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,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ 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;
@@ -56,6 +57,8 @@ 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};
@@ -66,11 +69,37 @@ 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
@@ -95,7 +124,6 @@ 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...).
@@ -135,7 +163,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
/**
* Class modeling an AspectJ annotation, exposing its type enumeration and
* Class modelling an AspectJ annotation, exposing its type enumeration and
* pointcut String.
* @param <A> the annotation type
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,16 +126,10 @@ public class AspectMetadata implements Serializable {
* Extract contents from String of form {@code pertarget(contents)}.
*/
private String findPerClause(Class<?> aspectClass) {
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);
String str = aspectClass.getAnnotation(Aspect.class).value();
int beginIndex = str.indexOf('(') + 1;
int endIndex = str.length() - 1;
return str.substring(beginIndex, endIndex);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,8 +44,7 @@ import org.springframework.util.CollectionUtils;
/**
* Base class for AOP proxy configuration managers.
*
* <p>These are not themselves AOP proxies, but subclasses of this class are
* 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
@@ -53,8 +52,7 @@ import org.springframework.util.CollectionUtils;
* methods, which are provided by subclasses.
*
* <p>This class is serializable; subclasses need not be.
*
* <p>This class is used to hold snapshots of proxies.
* This class is used to hold snapshots of proxies.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -106,7 +104,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Create an {@code AdvisedSupport} instance with the given parameters.
* Create a AdvisedSupport instance with the given parameters.
* @param interfaces the proxied interfaces
*/
public AdvisedSupport(Class<?>... interfaces) {
@@ -117,7 +115,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/**
* Set the given object as target.
* <p>Will create a SingletonTargetSource for the object.
* Will create a SingletonTargetSource for the object.
* @see #setTargetSource
* @see org.springframework.aop.target.SingletonTargetSource
*/
@@ -346,7 +344,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
advisor.validateInterfaces();
// If the advisor passed validation, we can make the change.
for (Class<?> ifc : advisor.getInterfaces()) {
Class<?>[] ifcs = advisor.getInterfaces();
for (Class<?> ifc : ifcs) {
addInterface(ifc);
}
}
@@ -492,9 +491,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* 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
* 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
* @param targetSource the new TargetSource
* @param advisors the Advisors for the chain
*/
@@ -514,8 +513,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Build a configuration-only copy of this {@link AdvisedSupport},
* replacing the {@link TargetSource}.
* Build a configuration-only copy of this AdvisedSupport,
* replacing the TargetSource.
*/
AdvisedSupport getConfigurationOnlyCopy() {
AdvisedSupport copy = new AdvisedSupport();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,9 @@ 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;
@@ -39,6 +42,7 @@ 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;
@@ -61,7 +65,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);
@@ -171,25 +175,25 @@ public class AspectJExpressionPointcutTests {
@Test
public void testFriendlyErrorOnNoLocationClassMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation2ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation3ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
}
@@ -206,10 +210,8 @@ 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();
}
@@ -244,13 +246,14 @@ 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().matches(Object.class));
assertThatIllegalArgumentException().isThrownBy(
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
}
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
TestBean target = new TestBean();
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
Pointcut pointcut = getPointcut(pointcutExpression);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setAdvice(interceptor);
@@ -274,29 +277,40 @@ public class AspectJExpressionPointcutTests {
@Test
public void testWithUnsupportedPointcutPrimitive() {
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
}
@Test
public void testAndSubstitution() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
}
@Test
public void testMultipleAndSubstitutions() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
}
private AspectJExpressionPointcut getPointcut(String expression) {
private Pointcut 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>) )";
@@ -491,15 +505,6 @@ 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-2024 the original author or authors.
* Copyright 2002-2023 the original author 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, it checks the FactoryBean's {@code getObjectType} method
* declared as a raw type, 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}, full
* creation of the FactoryBean is attempted as fallback (through delegation to the
* superclass implementation).
* 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).
* <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.
@@ -1080,7 +1080,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
Assert.notNull(applicationStartup, "applicationStartup should not be null");
this.applicationStartup = applicationStartup;
}
@@ -1695,7 +1695,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* already. The implementation is allowed to instantiate the target factory bean if
* {@code allowInit} is {@code true} and the type cannot be determined another way;
* otherwise it is restricted to introspecting signatures and related metadata.
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if 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
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -126,7 +126,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
beanFactory.setApplicationStartup(getApplicationStartup());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,6 +133,11 @@ 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) {
@@ -173,10 +178,11 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
}
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
TaskExecutorAdapter adapter =
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
return new ManagedTaskExecutorAdapter(concurrentExecutor);
}
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
if (this.taskDecorator != null) {
adapter.setTaskDecorator(this.taskDecorator);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,7 +177,6 @@ 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;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,9 +45,8 @@ import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
/**
* 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.
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
*
* @author Juergen Hoeller
* @author Mark Fisher
@@ -155,7 +154,6 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
* @see Clock#systemDefaultZone()
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "Clock must not be null");
this.clock = clock;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author 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,9 +29,7 @@ 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;
@@ -41,62 +39,51 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link FactoryBean FactoryBeans} defined in the configuration.
*
* @author Phillip Webb
* @author Juergen Hoeller
*/
class ConfigurationWithFactoryBeanEarlyDeductionTests {
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
@Test
void preFreezeDirect() {
public void preFreezeDirect() {
assertPreFreeze(DirectConfiguration.class);
}
@Test
void postFreezeDirect() {
public void postFreezeDirect() {
assertPostFreeze(DirectConfiguration.class);
}
@Test
void preFreezeGenericMethod() {
public void preFreezeGenericMethod() {
assertPreFreeze(GenericMethodConfiguration.class);
}
@Test
void postFreezeGenericMethod() {
public void postFreezeGenericMethod() {
assertPostFreeze(GenericMethodConfiguration.class);
}
@Test
void preFreezeGenericClass() {
public void preFreezeGenericClass() {
assertPreFreeze(GenericClassConfiguration.class);
}
@Test
void postFreezeGenericClass() {
public void postFreezeGenericClass() {
assertPostFreeze(GenericClassConfiguration.class);
}
@Test
void preFreezeAttribute() {
public void preFreezeAttribute() {
assertPreFreeze(AttributeClassConfiguration.class);
}
@Test
void postFreezeAttribute() {
public void postFreezeAttribute() {
assertPostFreeze(AttributeClassConfiguration.class);
}
@Test
void preFreezeTargetType() {
assertPreFreeze(TargetTypeConfiguration.class);
}
@Test
void postFreezeTargetType() {
assertPostFreeze(TargetTypeConfiguration.class);
}
@Test
void preFreezeUnresolvedGenericFactoryBean() {
public 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
@@ -121,13 +108,14 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
}
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 {
@@ -150,38 +138,41 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
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 {
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
}
public String[] getNames() {
return this.names;
}
}
@Configuration
static class DirectConfiguration {
@Bean
MyBean<String> myBean() {
return new MyBean<>();
MyBean myBean() {
return new MyBean();
}
}
@Configuration
static class GenericMethodConfiguration {
@Bean
FactoryBean<MyBean<String>> myBean() {
return new TestFactoryBean<>(new MyBean<>());
FactoryBean<MyBean> myBean() {
return new TestFactoryBean<>(new MyBean());
}
}
@Configuration
@@ -191,11 +182,13 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
MyFactoryBean myBean() {
return new MyFactoryBean();
}
}
@Configuration
@Import(AttributeClassRegistrar.class)
static class AttributeClassConfiguration {
}
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
@@ -204,34 +197,16 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.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<T> extends AbstractMyBean {
static class MyBean extends AbstractMyBean {
}
static class TestFactoryBean<T> implements FactoryBean<T> {
@@ -243,7 +218,7 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
@Override
public T getObject() {
public T getObject() throws Exception {
return this.instance;
}
@@ -251,26 +226,31 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public Class<?> getObjectType() {
return this.instance.getClass();
}
}
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
static class MyFactoryBean extends TestFactoryBean<MyBean> {
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 {
throw new IllegalStateException();
return object;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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 Future) {
((Future<?>) task).cancel(true);
if (task instanceof RunnableFuture) {
((RunnableFuture<?>) task).cancel(true);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Sam Brannen
* @since 3.0
*/
class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
@@ -97,7 +97,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
}
@Test
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
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 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
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-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@ import org.springframework.util.ReflectionUtils;
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 4.2.3
*/
public final class MethodIntrospector {
@@ -76,7 +75,6 @@ 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-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,9 +98,7 @@ public final class ReactiveTypeDescriptor {
*/
public Object getEmptyValue() {
Assert.state(this.emptySupplier != null, "Empty values not supported");
Object emptyValue = this.emptySupplier.get();
Assert.notNull(emptyValue, "Invalid null return value from emptySupplier");
return emptyValue;
return this.emptySupplier.get();
}
/**
@@ -132,7 +130,7 @@ public final class ReactiveTypeDescriptor {
/**
* Descriptor for a reactive type that can produce {@code 0..N} values.
* Descriptor for a reactive type that can produce 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-2024 the original author or authors.
* Copyright 2002-2023 the original author 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()
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -410,10 +410,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
if (repeatedAnnotations != null) {
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
if (result != null) {
return result;
}
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
}
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
annotation.annotationType(), repeatableContainers, annotationFilter);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,19 +95,20 @@ 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) {
Throwable cause = ex.getCause();
throw (cause instanceof CodecException ? (CodecException) cause :
new DecodingException("Failed to decode: " + (cause != null ? cause.getMessage() : ex), cause));
failure = ex.getCause();
}
catch (InterruptedException ex) {
throw new DecodingException("Interrupted during decode", ex);
failure = ex;
}
throw (failure instanceof CodecException ? (CodecException) failure :
new DecodingException("Failed to decode: " + failure.getMessage(), failure));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -76,11 +76,10 @@ 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;
}
@@ -93,7 +92,6 @@ 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-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,6 +52,8 @@ 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 = {
@@ -82,7 +84,7 @@ public class TypeDescriptor implements Serializable {
public TypeDescriptor(MethodParameter methodParameter) {
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
}
@@ -94,7 +96,7 @@ public class TypeDescriptor implements Serializable {
public TypeDescriptor(Field field) {
this.resolvableType = ResolvableType.forField(field);
this.type = this.resolvableType.resolve(field.getType());
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
}
/**
@@ -107,7 +109,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 = AnnotatedElementAdapter.from(property.getAnnotations());
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
}
/**
@@ -123,7 +125,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 = AnnotatedElementAdapter.from(annotations);
this.annotatedElement = new AnnotatedElementAdapter(annotations);
}
@@ -732,26 +734,18 @@ public class TypeDescriptor implements Serializable {
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
*/
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
@Nullable
private final Annotation[] annotations;
private AnnotatedElementAdapter(Annotation[] annotations) {
public AnnotatedElementAdapter(@Nullable 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 : this.annotations) {
for (Annotation annotation : getAnnotations()) {
if (annotation.annotationType() == annotationClass) {
return true;
}
@@ -763,7 +757,7 @@ public class TypeDescriptor implements Serializable {
@Nullable
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
for (Annotation annotation : this.annotations) {
for (Annotation annotation : getAnnotations()) {
if (annotation.annotationType() == annotationClass) {
return (T) annotation;
}
@@ -773,7 +767,7 @@ public class TypeDescriptor implements Serializable {
@Override
public Annotation[] getAnnotations() {
return (isEmpty() ? this.annotations : this.annotations.clone());
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
}
@Override
@@ -782,7 +776,7 @@ public class TypeDescriptor implements Serializable {
}
public boolean isEmpty() {
return (this.annotations.length == 0);
return ObjectUtils.isEmpty(this.annotations);
}
@Override
@@ -798,7 +792,7 @@ public class TypeDescriptor implements Serializable {
@Override
public String toString() {
return Arrays.toString(this.annotations);
return TypeDescriptor.this.toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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 (executable != null && isApplicable(executable, sourceClass)) {
if (isApplicable(executable, sourceClass)) {
return executable;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author 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 ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
return super.getPropertyNames();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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 static final Log logger = LogFactory.getLog(DataBufferUtils.class);
private final static 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 final SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
static 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 abstract static class AbstractNestedMatcher implements NestedMatcher {
private static abstract class AbstractNestedMatcher implements NestedMatcher {
private final byte[] delimiter;
@@ -1005,11 +1005,11 @@ public abstract class DataBufferUtils {
}
@Override
public void failed(Throwable ex, DataBuffer dataBuffer) {
public void failed(Throwable exc, DataBuffer dataBuffer) {
release(dataBuffer);
closeChannel(this.channel);
this.state.set(State.DISPOSED);
this.sink.error(ex);
this.sink.error(exc);
}
private enum State {
@@ -1064,6 +1064,7 @@ public abstract class DataBufferUtils {
public Context currentContext() {
return Context.of(this.sink.contextView());
}
}
@@ -1144,9 +1145,9 @@ public abstract class DataBufferUtils {
}
@Override
public void failed(Throwable ex, ByteBuffer byteBuffer) {
public void failed(Throwable exc, ByteBuffer byteBuffer) {
sinkDataBuffer();
this.sink.error(ex);
this.sink.error(exc);
}
private void sinkDataBuffer() {
@@ -1160,6 +1161,7 @@ public abstract class DataBufferUtils {
public Context currentContext() {
return Context.of(this.sink.contextView());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.function.Supplier;
import org.springframework.lang.Nullable;
/**
* Default "no op" {@code ApplicationStartup} implementation.
*
@@ -54,7 +52,6 @@ class DefaultApplicationStartup implements ApplicationStartup {
}
@Override
@Nullable
public Long getParentId() {
return null;
}
@@ -76,6 +73,7 @@ class DefaultApplicationStartup implements ApplicationStartup {
@Override
public void end() {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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) {
if (superClassMatch.booleanValue()) {
return true;
}
}
else {
// Need to read superclass to determine a match...
try {
if (match(superClassName, metadataReaderFactory)) {
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
return true;
}
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not read superclass [" + superClassName +
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
"] 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) {
if (interfaceMatch.booleanValue()) {
return true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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 (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
return (values != null && !values.isEmpty() ? 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<>(values.size()));
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new ArrayList<>(1));
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 (!CollectionUtils.isEmpty(values)) {
if (values != null && !values.isEmpty()) {
singleValueMap.put(key, values.get(0));
}
});
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,7 +781,6 @@ 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
@@ -830,7 +829,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
@@ -860,7 +859,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
@@ -1,112 +0,0 @@
/*
* 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() {
}
}
}
@@ -24,7 +24,6 @@ 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;
@@ -176,7 +175,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
}
@Test
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
@@ -241,44 +240,6 @@ 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) {
@@ -488,27 +449,4 @@ 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 {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,10 +18,7 @@ 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;
@@ -310,12 +307,6 @@ 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() {
@@ -416,7 +407,6 @@ class StandardEnvironmentTests {
EnvironmentTestUtils.getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
}
@Nested
class GetActiveProfiles {
@@ -466,7 +456,6 @@ class StandardEnvironmentTests {
}
}
@Nested
class AcceptsProfilesTests {
@@ -549,8 +538,8 @@ class StandardEnvironmentTests {
environment.addActiveProfile("p2");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
}
}
}
@Nested
class MatchesProfilesTests {
@@ -661,6 +650,7 @@ class StandardEnvironmentTests {
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,6 @@ class StreamUtilsTests {
private String string = "";
@BeforeEach
void setup() {
new Random().nextBytes(bytes);
@@ -54,7 +53,6 @@ class StreamUtilsTests {
}
}
@Test
void copyToByteArray() throws Exception {
InputStream inputStream = new ByteArrayInputStream(bytes);
@@ -129,5 +127,4 @@ class StreamUtilsTests {
ordered.verify(source).write(bytes, 1, 2);
ordered.verify(source, never()).close();
}
}
@@ -228,14 +228,14 @@ public abstract class JdbcUtils {
try {
return rs.getObject(index, requiredType);
}
catch (SQLFeatureNotSupportedException | AbstractMethodError ex) {
catch (AbstractMethodError err) {
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
}
catch (SQLFeatureNotSupportedException ex) {
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
}
catch (SQLException ex) {
if (logger.isDebugEnabled()) {
logger.debug("JDBC driver has limited support for 'getObject(int, Class)' with column type: " +
requiredType.getName(), ex);
}
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
}
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* The common implementation of Spring's {@link SqlRowSet} interface, wrapping a
* The default 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 pattern, but there are
* the column name. Most JDBC ResultSet implementations follow this new pattern but there are
* exceptions such as the {@code com.sun.rowset.CachedRowSetImpl} class which only uses
* the column name, ignoring any column labels. {@code ResultSetWrappingSqlRowSet}
* will translate column labels to the correct column index to provide better support for
* 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
* {@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 serialization should usually work.
* are actually serializable, so this should usually work out.
*
* @author Thomas Risberg
* @author Juergen Hoeller
@@ -67,18 +67,16 @@ 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 {@code ResultSetWrappingSqlRowSet} for the given {@link ResultSet}.
* Create a new ResultSetWrappingSqlRowSet for the given ResultSet.
* @param resultSet a disconnected ResultSet to wrap
* (usually a {@code javax.sql.rowset.CachedRowSet})
* @throws InvalidResultSetAccessException if extracting
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import 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;
@@ -37,168 +38,174 @@ import static org.mockito.Mockito.mock;
/**
* @author Thomas Risberg
*/
class ResultSetWrappingRowSetTests {
public class ResultSetWrappingRowSetTests {
private final ResultSet resultSet = mock(ResultSet.class);
private ResultSet resultSet;
private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
private ResultSetWrappingSqlRowSet rowSet;
@BeforeEach
public void setup() throws Exception {
resultSet = mock(ResultSet.class);
rowSet = new ResultSetWrappingSqlRowSet(resultSet);
}
@Test
void testGetBigDecimalInt() throws Exception {
public 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
void testGetBigDecimalString() throws Exception {
public 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
void testGetStringInt() throws Exception {
public 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
void testGetStringString() throws Exception {
public 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
void testGetTimestampInt() throws Exception {
public 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
void testGetTimestampString() throws Exception {
public 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
void testGetDateInt() throws Exception {
public 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
void testGetDateString() throws Exception {
public 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
void testGetTimeInt() throws Exception {
public 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
void testGetTimeString() throws Exception {
public 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
void testGetObjectInt() throws Exception {
public 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
void testGetObjectString() throws Exception {
public 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
void testGetIntInt() throws Exception {
public 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
void testGetIntString() throws Exception {
public 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
void testGetFloatInt() throws Exception {
public 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
void testGetFloatString() throws Exception {
public 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
void testGetDoubleInt() throws Exception {
public 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
void testGetDoubleString() throws Exception {
public 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
void testGetLongInt() throws Exception {
public 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
void testGetLongString() throws Exception {
public 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
void testGetBooleanInt() throws Exception {
public 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
void testGetBooleanString() throws Exception {
public 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);
@@ -208,9 +215,9 @@ 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-2024 the original author or authors.
* Copyright 2002-2017 the original author 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,6 +189,11 @@ 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-2024 the original author or authors.
* Copyright 2002-2017 the original author 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-2024 the original author or authors.
* Copyright 2002-2012 the original author 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 ex) {
catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException 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 ex) {
catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,9 +390,7 @@ public abstract class SharedEntityManagerCreator {
else if (targetClass.isInstance(proxy)) {
return proxy;
}
else {
return this.target.unwrap(targetClass);
}
break;
case "getOutputParameterValue":
if (this.entityManager == null) {
Object key = args[0];
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author 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;
/**
* Tests for {@link SharedEntityManagerCreator}.
* Unit tests for {@link SharedEntityManagerCreator}.
*
* @author Oliver Gierke
* @author Juergen Hoeller
*/
@ExtendWith(MockitoExtension.class)
class SharedEntityManagerCreatorTests {
public class SharedEntityManagerCreatorTests {
@Test
void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
EntityManagerFactory emf = mock(EntityManagerFactory.class,
withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
// EntityManagerFactoryInfo.getEntityManagerInterface returns null
@@ -55,7 +55,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnJoinTransaction() {
public void transactionRequiredExceptionOnJoinTransaction() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@@ -63,7 +63,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnFlush() {
public void transactionRequiredExceptionOnFlush() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@@ -71,7 +71,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnPersist() {
public void transactionRequiredExceptionOnPersist() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -79,7 +79,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnMerge() {
public void transactionRequiredExceptionOnMerge() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -87,7 +87,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnRemove() {
public void transactionRequiredExceptionOnRemove() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -95,7 +95,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnRefresh() {
public void transactionRequiredExceptionOnRefresh() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -103,98 +103,78 @@ class SharedEntityManagerCreatorTests {
}
@Test
void deferredQueryWithUpdate() {
public void deferredQueryWithUpdate() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
Query targetQuery = mock(Query.class);
Query query = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
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();
em.createQuery("x").executeUpdate();
verify(targetQuery).executeUpdate();
verify(query).executeUpdate();
verify(targetEm).close();
}
@Test
void deferredQueryWithSingleResult() {
public void deferredQueryWithSingleResult() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
Query targetQuery = mock(Query.class);
Query query = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
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();
em.createQuery("x").getSingleResult();
verify(targetQuery).getSingleResult();
verify(query).getSingleResult();
verify(targetEm).close();
}
@Test
void deferredQueryWithResultList() {
public void deferredQueryWithResultList() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
Query targetQuery = mock(Query.class);
Query query = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
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();
em.createQuery("x").getResultList();
verify(targetQuery).getResultList();
verify(query).getResultList();
verify(targetEm).close();
}
@Test
void deferredQueryWithResultStream() {
public void deferredQueryWithResultStream() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
Query targetQuery = mock(Query.class);
Query query = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
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();
em.createQuery("x").getResultStream();
verify(targetQuery).getResultStream();
verify(query).getResultStream();
verify(targetEm).close();
}
@Test
void deferredStoredProcedureQueryWithIndexedParameters() {
public void deferredStoredProcedureQueryWithIndexedParameters() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
StoredProcedureQuery query = mock(StoredProcedureQuery.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(targetQuery).getOutputParameterValue(0);
willReturn("z").given(targetQuery).getOutputParameterValue(2);
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
willReturn("y").given(query).getOutputParameterValue(0);
willReturn("z").given(query).getOutputParameterValue(2);
given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@@ -208,24 +188,24 @@ class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue(1));
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
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(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(targetEm).close();
verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(query);
verifyNoMoreInteractions(targetEm);
}
@Test
void deferredStoredProcedureQueryWithNamedParameters() {
public void deferredStoredProcedureQueryWithNamedParameters() {
EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class);
StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
StoredProcedureQuery query = mock(StoredProcedureQuery.class);
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(targetQuery).getOutputParameterValue("a");
willReturn("z").given(targetQuery).getOutputParameterValue("c");
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
willReturn("y").given(query).getOutputParameterValue("a");
willReturn("z").given(query).getOutputParameterValue("c");
given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@@ -239,12 +219,12 @@ class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue("b"));
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
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(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(targetEm).close();
verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(query);
verifyNoMoreInteractions(targetEm);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,6 +60,7 @@ 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;
@@ -989,7 +990,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
}
private static String encodeCookies(Cookie... cookies) {
private static String encodeCookies(@NonNull Cookie... cookies) {
return Arrays.stream(cookies)
.map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue()))
.collect(Collectors.joining("; "));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ 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;
@@ -279,6 +280,7 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
}
}
@NonNull
private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
/**
* Create a new {@code DefaultTransactionAttribute} with default settings.
* Create a new 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 {@code DefaultTransactionAttribute} with the given
* Create a new DefaultTransactionAttribute 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-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,13 +210,11 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status);
}
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
// regular participation in existing transaction.
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
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));
}
/**
@@ -332,7 +330,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) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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 savepoints. Never activates Spring synchronization.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
@@ -465,8 +465,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
}
}
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
// regular participation in existing transaction.
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2018 the original author 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 {@code DefaultTransactionDefinition} with default settings.
* Create a new 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 {@code DefaultTransactionDefinition} with the given
* Create a new 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-2024 the original author or authors.
* Copyright 2002-2022 the original author 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://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/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://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
*/
ResponseCookieBuilder httpOnly(boolean httpOnly);
@@ -16,12 +16,8 @@
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;
@@ -58,7 +54,16 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
this.statusCode = statusCode;
this.headers = headers;
this.cookies = cookies;
this.body = Flux.from(new SingleSubscriberPublisher<>(body));
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");
}
});
}
@@ -86,39 +91,4 @@ 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"));
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,10 +39,11 @@ 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
@@ -122,14 +123,16 @@ 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, MediaType.ALL_VALUE);
this.httpRequest.addHeader(HttpHeaders.ACCEPT, ALL_VALUE);
}
this.contentLength = headers.getContentLength();
}
@@ -140,6 +143,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
}
CookieStore cookieStore = this.context.getCookieStore();
getCookies().values()
.stream()
.flatMap(Collection::stream)
@@ -81,7 +81,6 @@ 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();
}
@@ -107,13 +106,6 @@ 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);
@@ -131,10 +123,15 @@ 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 {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,6 +51,22 @@ 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;
@@ -107,6 +123,7 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
}
@Override
@SuppressWarnings("unchecked")
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
@@ -114,33 +131,16 @@ 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(regions, outputMessage);
writeResourceRegionCollection((Collection<ResourceRegion>) object, 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");
@@ -73,19 +73,19 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");
private static final String SCHEME_PATTERN = "([^:/?#\\\\]+):";
private static final String SCHEME_PATTERN = "([^:/?#]+):";
private static final String HTTP_PATTERN = "(?i)(http|https):";
private static final String USERINFO_PATTERN = "([^/?#\\\\]*)";
private static final String USERINFO_PATTERN = "([^/?#]*)";
private static final String HOST_IPV4_PATTERN = "[^/?#:\\\\]*";
private static final String HOST_IPV4_PATTERN = "[^/?#:]*";
private static final String HOST_IPV6_PATTERN = "\\[[\\p{XDigit}:.]*[%\\p{Alnum}]*]";
private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
private static final String PORT_PATTERN = "(\\{[^}]+\\}?|[^/?#\\\\]*)";
private static final String PORT_PATTERN = "(\\{[^}]+\\}?|[^/?#]*)";
private static final String PATH_PATTERN = "([^?#]*)";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@ 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;
@@ -992,7 +993,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
}
private static String encodeCookies(Cookie... cookies) {
private static String encodeCookies(@NonNull Cookie... cookies) {
return Arrays.stream(cookies)
.map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue()))
.collect(Collectors.joining("; "));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -221,7 +221,7 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
@Nullable
private String decodeAndNormalizePath(@Nullable String path, HttpServletRequest request) {
if (StringUtils.hasLength(path)) {
if (path != null && !path.isEmpty()) {
path = getUrlPathHelper().decodeRequestString(request, path);
if (path.charAt(0) != '/') {
String requestUri = getUrlPathHelper().getRequestUri(request);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -248,31 +248,30 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
@Override
public void close(CloseStatus status) throws IOException {
if (this.closeLock.tryLock()) {
try {
if (this.closeInProgress) {
return;
}
if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) {
try {
checkSessionLimits();
}
catch (SessionLimitExceededException ex) {
// Ignore
}
if (this.limitExceeded) {
if (logger.isDebugEnabled()) {
logger.debug("Changing close status " + status + " to SESSION_NOT_RELIABLE.");
}
status = CloseStatus.SESSION_NOT_RELIABLE;
}
}
this.closeInProgress = true;
super.close(status);
this.closeLock.lock();
try {
if (this.closeInProgress) {
return;
}
finally {
this.closeLock.unlock();
if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) {
try {
checkSessionLimits();
}
catch (SessionLimitExceededException ex) {
// Ignore
}
if (this.limitExceeded) {
if (logger.isDebugEnabled()) {
logger.debug("Changing close status " + status + " to SESSION_NOT_RELIABLE.");
}
status = CloseStatus.SESSION_NOT_RELIABLE;
}
}
this.closeInProgress = true;
super.close(status);
}
finally {
this.closeLock.unlock();
}
}
+29 -7
View File
@@ -7670,13 +7670,35 @@ or reactive type such as Reactor `Mono`:
[[spring-mvc-test-vs-streaming-response]]
===== Streaming Responses
You can use `WebTestClient` to test <<testing/testing-webtestclient.adoc#webtestclient-stream,streaming responses>>
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
streams because there is no way to cancel the server stream from the client side.
To test infinite streams, you'll need to
<<testing/testing-webtestclient.adoc#webtestclient-server-config,bind to>> a running server,
or when using Spring Boot,
{doc-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
The best way to test streaming responses such as Server-Sent Events is through the
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
FluxExchangeResult<Person> exchangeResult = client.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
// Use StepVerifier from Project Reactor to test the streaming response
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
----
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can
{doc-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
[[spring-mvc-test-server-filters]]