Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds df041ba032 Release v5.3.33 2024-03-14 09:05:01 +00:00
96 changed files with 902 additions and 1180 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.36
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.
@@ -42,7 +42,6 @@ import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAwareMethodMatcher;
@@ -120,8 +119,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Nullable
private transient PointcutExpression pointcutExpression;
private transient boolean pointcutParsingFailed = false;
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
@@ -177,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);
@@ -277,13 +269,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
if (this.pointcutParsingFailed) {
return false;
}
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);
@@ -294,12 +283,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
}
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
this.pointcutParsingFailed = true;
if (logger.isDebugEnabled()) {
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
}
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
@@ -308,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
@@ -345,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-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.
@@ -50,7 +50,6 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils;
@@ -134,19 +133,17 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
// to getAdvisor(...) to represent the "current position" in the declared methods list.
// However, since Java 7 the "current position" is not valid since the JDK no longer
// returns declared methods in the order in which they are declared in the source code.
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
// discovered via reflection in order to support reliable advice ordering across JVM launches.
// Specifically, a value of 0 aligns with the default value used in
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
// to getAdvisor(...) to represent the "current position" in the declared methods list.
// However, since Java 7 the "current position" is not valid since the JDK no longer
// returns declared methods in the order in which they are declared in the source code.
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
// discovered via reflection in order to support reliable advice ordering across JVM launches.
// Specifically, a value of 0 aligns with the default value used in
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
@@ -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,13 +42,14 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* @author Rob Harrop
* @author Rod Johnson
* @author Chris Beams
* @author Juergen Hoeller
*/
public class AspectJExpressionPointcutTests {
@@ -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)";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
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-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,6 +37,7 @@ import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import test.aop.DefaultLockable;
import test.aop.Lockable;
@@ -75,24 +76,25 @@ abstract class AbstractAspectJAdvisorFactoryTests {
/**
* To be overridden by concrete test subclasses.
* @return the fixture
*/
protected abstract AspectJAdvisorFactory getFixture();
@Test
void rejectsPerCflowAspect() {
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getFixture().getAdvisors(
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
.withMessageContaining("PERCFLOW");
.withMessageContaining("PERCFLOW");
}
@Test
void rejectsPerCflowBelowAspect() {
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW");
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW");
}
@Test
@@ -383,7 +385,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(lockable.locked()).as("Already locked").isTrue();
lockable.lock();
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
lockable.unlock());
}
@Test
@@ -410,7 +413,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
lockable.locked();
}
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
@Test
@Disabled
void introductionWithArgumentBinding() {
TestBean target = new TestBean();
@@ -518,16 +523,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
@Test
void parentAspect() {
TestBean target = new TestBean("Jane", 42);
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
new IncrementingAspect(), "incrementingAspect");
ITestBean proxy = (ITestBean) createProxy(target,
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
}
@Test
void failureWithoutExplicitDeclarePrecedence() {
TestBean target = new TestBean();
@@ -652,7 +647,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
static class NamedPointcutAspectWithFQN {
@SuppressWarnings("unused")
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Pointcut("execution(* getAge())")
void getAge() {
@@ -772,31 +767,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
}
@Aspect
abstract static class DoublingAspect {
@Around("execution(* getAge())")
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
}
@Aspect
static class IncrementingAspect extends DoublingAspect {
@Override
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
@Around("execution(* getAge())")
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) + 1;
}
}
@Aspect
private static class InvocationTrackingAspect {
@@ -854,7 +824,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) {
return 42;
return 666;
}
}
@@ -874,7 +844,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) {
return 42;
return 666;
}
}
@@ -1096,7 +1066,7 @@ class PerThisAspect {
// Just to check that this doesn't cause problems with introduction processing
@SuppressWarnings("unused")
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Around("execution(int *.getAge())")
int returnCountAsAge() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,13 +22,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Adrian Colyer
* @author Juergen Hoeller
*/
public class AutoProxyWithCodeStyleAspectsTests {
@Test
@SuppressWarnings("resource")
public void noAutoProxyingOfAjcCompiledAspects() {
public void noAutoproxyingOfAjcCompiledAspects() {
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2015 the original author 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,10 +20,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Ramnivas Laddad
* @author Juergen Hoeller
*/
public class SpringConfiguredWithAutoProxyingTests {
@Test
@@ -2,21 +2,16 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:aspectj-autoproxy/>
<context:spring-configured/>
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
factory-method="aspectOf">
<property name="foo" value="bar"/>
</bean>
<bean id="otherBean" class="java.lang.Object"/>
<bean id="yetAnotherBean" class="java.lang.Object"/>
</beans>
@@ -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-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,10 +22,8 @@ import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.springframework.format.Formatter;
@@ -37,14 +35,9 @@ import org.springframework.util.StringUtils;
/**
* A formatter for {@link java.util.Date} types.
*
* <p>Supports the configuration of an explicit date time pattern, timezone,
* locale, and fallback date time patterns for lenient parsing.
*
* <p>Common ISO patterns for UTC instants are applied at millisecond precision.
* Note that {@link org.springframework.format.datetime.standard.InstantFormatter}
* is recommended for flexible UTC parsing into a {@link java.time.Instant} instead.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Phillip Webb
@@ -58,21 +51,12 @@ public class DateFormatter implements Formatter<Date> {
private static final Map<ISO, String> ISO_PATTERNS;
private static final Map<ISO, String> ISO_FALLBACK_PATTERNS;
static {
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
Map<ISO, String> formats = new EnumMap<>(ISO.class);
formats.put(ISO.DATE, "yyyy-MM-dd");
formats.put(ISO.TIME, "HH:mm:ss.SSSXXX");
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
ISO_PATTERNS = Collections.unmodifiableMap(formats);
// Fallback format for the time part without milliseconds.
Map<ISO, String> fallbackFormats = new EnumMap<>(ISO.class);
fallbackFormats.put(ISO.TIME, "HH:mm:ssXXX");
fallbackFormats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ssXXX");
ISO_FALLBACK_PATTERNS = Collections.unmodifiableMap(fallbackFormats);
}
@@ -217,16 +201,8 @@ public class DateFormatter implements Formatter<Date> {
return getDateFormat(locale).parse(text);
}
catch (ParseException ex) {
Set<String> fallbackPatterns = new LinkedHashSet<>();
String isoPattern = ISO_FALLBACK_PATTERNS.get(this.iso);
if (isoPattern != null) {
fallbackPatterns.add(isoPattern);
}
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
Collections.addAll(fallbackPatterns, this.fallbackPatterns);
}
if (!fallbackPatterns.isEmpty()) {
for (String pattern : fallbackPatterns) {
for (String pattern : this.fallbackPatterns) {
try {
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
// Align timezone for parsing format with printing format if ISO is set.
@@ -244,8 +220,8 @@ public class DateFormatter implements Formatter<Date> {
}
if (this.source != null) {
ParseException parseException = new ParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
parseException.initCause(ex);
throw parseException;
}
@@ -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.
@@ -41,12 +41,12 @@ public class InstantFormatter implements Formatter<Instant> {
@Override
public Instant parse(String text, Locale locale) throws ParseException {
if (!text.isEmpty() && Character.isAlphabetic(text.charAt(0))) {
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
}
else {
// assuming UTC instant a la "2007-12-03T10:15:30.000Z"
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
return Instant.parse(text);
}
}
@@ -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;
}
@@ -28,14 +28,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
* @author Adrian Colyer
* @author Chris Beams
* @author Juergen Hoeller
*/
class OverloadedAdviceTests {
@Test
@SuppressWarnings("resource")
void testConfigParsingWithMismatchedAdviceMethod() {
new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass());
void testExceptionOnConfigParsingWithMismatchedAdviceMethod() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass()))
.havingRootCause()
.isInstanceOf(IllegalArgumentException.class)
.as("invalidAbsoluteTypeName should be detected by AJ").withMessageContaining("invalidAbsoluteTypeName");
}
@Test
@@ -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-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.
@@ -23,6 +23,9 @@ import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.jupiter.api.Test;
import org.springframework.format.annotation.DateTimeFormat.ISO;
@@ -30,88 +33,83 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link DateFormatter}.
*
* @author Keith Donald
* @author Phillip Webb
* @author Juergen Hoeller
*/
class DateFormatterTests {
public class DateFormatterTests {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
@Test
void shouldPrintAndParseDefault() throws Exception {
public void shouldPrintAndParseDefault() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseFromPattern() throws ParseException {
public void shouldPrintAndParseFromPattern() throws ParseException {
DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
formatter.setTimeZone(UTC);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
assertThat(formatter.parse("2009-06-01", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseShort() throws Exception {
public void shouldPrintAndParseShort() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.SHORT);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("6/1/09");
assertThat(formatter.parse("6/1/09", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseMedium() throws Exception {
public void shouldPrintAndParseMedium() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.MEDIUM);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseLong() throws Exception {
public void shouldPrintAndParseLong() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.LONG);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("June 1, 2009");
assertThat(formatter.parse("June 1, 2009", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseFull() throws Exception {
public void shouldPrintAndParseFull() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.FULL);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Monday, June 1, 2009");
assertThat(formatter.parse("Monday, June 1, 2009", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseIsoDate() throws Exception {
public void shouldPrintAndParseISODate() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.DATE);
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
assertThat(formatter.parse("2009-6-01", Locale.US))
@@ -119,56 +117,79 @@ class DateFormatterTests {
}
@Test
void shouldPrintAndParseIsoTime() throws Exception {
public void shouldPrintAndParseISOTime() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.TIME);
Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.003Z");
assertThat(formatter.parse("14:23:05.003Z", Locale.US))
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3));
date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 0);
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.000Z");
assertThat(formatter.parse("14:23:05Z", Locale.US))
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 0).toInstant());
}
@Test
void shouldPrintAndParseIsoDateTime() throws Exception {
public void shouldPrintAndParseISODateTime() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.DATE_TIME);
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.003Z");
assertThat(formatter.parse("2009-06-01T14:23:05.003Z", Locale.US)).isEqualTo(date);
date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 0);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.000Z");
assertThat(formatter.parse("2009-06-01T14:23:05Z", Locale.US)).isEqualTo(date.toInstant());
}
@Test
void shouldThrowOnUnsupportedStylePattern() {
public void shouldSupportJodaStylePatterns() throws Exception {
String[] chars = { "S", "M", "-" };
for (String d : chars) {
for (String t : chars) {
String style = d + t;
if (!style.equals("--")) {
Date date = getDate(2009, Calendar.JUNE, 10, 14, 23, 0, 0);
if (t.equals("-")) {
date = getDate(2009, Calendar.JUNE, 10);
}
else if (d.equals("-")) {
date = getDate(1970, Calendar.JANUARY, 1, 14, 23, 0, 0);
}
testJodaStylePatterns(style, Locale.US, date);
}
}
}
}
private void testJodaStylePatterns(String style, Locale locale, Date date) throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStylePattern(style);
DateTimeFormatter jodaFormatter = DateTimeFormat.forStyle(style).withLocale(locale).withZone(DateTimeZone.UTC);
String jodaPrinted = jodaFormatter.print(date.getTime());
assertThat(formatter.print(date, locale))
.as("Unable to print style pattern " + style)
.isEqualTo(jodaPrinted);
assertThat(formatter.parse(jodaPrinted, locale))
.as("Unable to parse style pattern " + style)
.isEqualTo(date);
}
@Test
public void shouldThrowOnUnsupportedStylePattern() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setStylePattern("OO");
assertThatIllegalStateException().isThrownBy(() -> formatter.parse("2009", Locale.US))
.withMessageContaining("Unsupported style pattern 'OO'");
assertThatIllegalStateException().isThrownBy(() ->
formatter.parse("2009", Locale.US))
.withMessageContaining("Unsupported style pattern 'OO'");
}
@Test
void shouldUseCorrectOrder() {
public void shouldUseCorrectOrder() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.SHORT);
formatter.setStylePattern("L-");
formatter.setIso(ISO.DATE_TIME);
formatter.setPattern("yyyy");
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).as("uses pattern").isEqualTo("2009");
formatter.setPattern("");
@@ -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,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Juergen Hoeller
* @author Sam Brannen
*/
class DateFormattingTests {
public class DateFormattingTests {
private final FormattingConversionService conversionService = new FormattingConversionService();
@@ -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.
@@ -105,7 +105,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", "10/31/09");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
}
@@ -117,7 +117,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", "October 31, 2009");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("October 31, 2009");
}
@@ -129,7 +129,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", "20091031");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("20091031");
}
@@ -138,7 +138,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", new String[] {"10/31/09"});
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
}
@Test
@@ -146,7 +146,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("styleLocalDate", "Oct 31, 2009");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
}
@@ -164,7 +164,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("children[0].styleLocalDate", "Oct 31, 2009");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("children[0].styleLocalDate")).isEqualTo("Oct 31, 2009");
}
@@ -174,7 +174,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("styleLocalDate", "Oct 31, 2009");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
}
@@ -193,7 +193,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
}
@@ -202,7 +202,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localTime", "12:00 PM");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
}
@@ -214,7 +214,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localTime", "12:00:00 PM");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00:00 PM");
}
@@ -226,7 +226,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localTime", "130000");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("130000");
}
@@ -235,7 +235,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("styleLocalTime", "12:00:00 PM");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("styleLocalTime")).isEqualTo("12:00:00 PM");
}
@@ -244,7 +244,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
}
@@ -253,9 +253,10 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
assertThat(value.startsWith("10/31/09")).isTrue();
assertThat(value.endsWith("12:00 PM")).isTrue();
}
@Test
@@ -263,9 +264,10 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("styleLocalDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
assertThat(value.endsWith("12:00:00 PM")).isTrue();
}
@Test
@@ -273,9 +275,10 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
assertThat(value.startsWith("10/31/09")).isTrue();
assertThat(value.endsWith("12:00 PM")).isTrue();
}
@Test
@@ -286,9 +289,10 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
assertThat(value.endsWith("12:00:00 PM")).isTrue();
}
@Test
@@ -296,7 +300,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("patternLocalDateTime", "10/31/09 12:00 PM");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("patternLocalDateTime")).isEqualTo("10/31/09 12:00 PM");
}
@@ -313,7 +317,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("isoLocalDate", "2009-10-31");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
}
@@ -352,7 +356,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("isoLocalTime", "12:00:00");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
}
@@ -361,7 +365,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("isoLocalTime", "12:00:00.000-05:00");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
}
@@ -370,7 +374,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
}
@@ -379,7 +383,7 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00.000Z");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
}
@@ -388,8 +392,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("instant", "2009-10-31T12:00:00.000Z");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31T12:00");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00")).isTrue();
}
@Test
@@ -401,8 +405,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("instant", new Date(109, 9, 31, 12, 0));
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31")).isTrue();
}
finally {
TimeZone.setDefault(defaultZone);
@@ -414,8 +418,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("period", "P6Y3M1D");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("period").toString()).isEqualTo("P6Y3M1D");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("period").toString().equals("P6Y3M1D")).isTrue();
}
@Test
@@ -423,8 +427,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("duration", "PT8H6M12.345S");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("duration").toString()).isEqualTo("PT8H6M12.345S");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("duration").toString().equals("PT8H6M12.345S")).isTrue();
}
@Test
@@ -432,8 +436,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("year", "2007");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("year").toString()).isEqualTo("2007");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("year").toString().equals("2007")).isTrue();
}
@Test
@@ -441,8 +445,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("month", "JULY");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
}
@Test
@@ -450,8 +454,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("month", "July");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
}
@Test
@@ -459,8 +463,8 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("yearMonth", "2007-12");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString()).isEqualTo("2007-12");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")).isTrue();
}
@Test
@@ -468,11 +472,10 @@ class DateTimeFormattingTests {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("monthDay", "--12-03");
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString()).isEqualTo("--12-03");
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")).isTrue();
}
@Nested
class FallbackPatternTests {
@@ -484,7 +487,7 @@ class DateTimeFormattingTests {
propertyValues.add(propertyName, propertyValue);
binder.bind(propertyValues);
BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getErrorCount()).isZero();
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
}
@@ -496,12 +499,11 @@ class DateTimeFormattingTests {
propertyValues.add(propertyName, propertyValue);
binder.bind(propertyValues);
BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getErrorCount()).isZero();
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
}
@ParameterizedTest(name = "input date: {0}")
// @ValueSource(strings = {"12:00:00\u202FPM", "12:00:00", "12:00"})
@ValueSource(strings = {"12:00:00 PM", "12:00:00", "12:00"})
void styleLocalTime(String propertyValue) {
String propertyName = "styleLocalTimeWithFallbackPatterns";
@@ -509,8 +511,7 @@ class DateTimeFormattingTests {
propertyValues.add(propertyName, propertyValue);
binder.bind(propertyValues);
BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getErrorCount()).isZero();
// assertThat(bindingResult.getFieldValue(propertyName)).asString().matches("12:00:00\\SPM");
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("12:00:00 PM");
}
@@ -522,7 +523,7 @@ class DateTimeFormattingTests {
propertyValues.add(propertyName, propertyValue);
binder.bind(propertyValues);
BindingResult bindingResult = binder.getBindingResult();
assertThat(bindingResult.getErrorCount()).isZero();
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02T12:00:00");
}
@@ -564,10 +565,10 @@ class DateTimeFormattingTests {
@DateTimeFormat(style = "M-")
private LocalDate styleLocalDate;
@DateTimeFormat(style = "S-", fallbackPatterns = {"yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd"})
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
private LocalDate styleLocalDateWithFallbackPatterns;
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = {"M/d/yy", "yyyyMMdd", "yyyy.MM.dd"})
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
private LocalDate patternLocalDateWithFallbackPatterns;
private LocalTime localTime;
@@ -575,7 +576,7 @@ class DateTimeFormattingTests {
@DateTimeFormat(style = "-M")
private LocalTime styleLocalTime;
@DateTimeFormat(style = "-M", fallbackPatterns = {"HH:mm:ss", "HH:mm"})
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
private LocalTime styleLocalTimeWithFallbackPatterns;
private LocalDateTime localDateTime;
@@ -595,7 +596,7 @@ class DateTimeFormattingTests {
@DateTimeFormat(iso = ISO.DATE_TIME)
private LocalDateTime isoLocalDateTime;
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = {"yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
private Instant instant;
@@ -614,6 +615,7 @@ class DateTimeFormattingTests {
private final List<DateTimeBean> children = new ArrayList<>();
public LocalDate getLocalDate() {
return this.localDate;
}
@@ -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.
@@ -19,7 +19,6 @@ package org.springframework.format.datetime.standard;
import java.text.ParseException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Random;
import java.util.stream.Stream;
@@ -50,12 +49,13 @@ class InstantFormatterTests {
private final InstantFormatter instantFormatter = new InstantFormatter();
@ParameterizedTest
@ArgumentsSource(ISOSerializedInstantProvider.class)
void should_parse_an_ISO_formatted_string_representation_of_an_Instant(String input) throws ParseException {
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, Locale.US);
Instant actual = instantFormatter.parse(input, null);
assertThat(actual).isEqualTo(expected);
}
@@ -63,7 +63,9 @@ class InstantFormatterTests {
@ArgumentsSource(RFC1123SerializedInstantProvider.class)
void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant(String input) throws ParseException {
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, Locale.US);
Instant actual = instantFormatter.parse(input, null);
assertThat(actual).isEqualTo(expected);
}
@@ -71,11 +73,12 @@ class InstantFormatterTests {
@ArgumentsSource(RandomInstantProvider.class)
void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale(Instant input) {
String expected = DateTimeFormatter.ISO_INSTANT.format(input);
String actual = instantFormatter.print(input, Locale.US);
String actual = instantFormatter.print(input, null);
assertThat(actual).isEqualTo(expected);
}
private static class RandomInstantProvider implements ArgumentsProvider {
private static final long DATA_SET_SIZE = 10;
@@ -97,7 +100,6 @@ class InstantFormatterTests {
}
}
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
@Override
@@ -106,7 +108,6 @@ class InstantFormatterTests {
}
}
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
// RFC-1123 supports only 4-digit years
@@ -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);
}
@@ -18,6 +18,4 @@
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
<bean id="testBean2" class="org.springframework.beans.testfixture.beans.TestBean"/>
</beans>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-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-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.
@@ -154,7 +154,7 @@ final class PersistenceUnitReader {
/**
* Validate the given stream and return a valid DOM document for parsing.
*/
Document buildDocument(ErrorHandler handler, InputStream stream)
protected Document buildDocument(ErrorHandler handler, InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
@@ -168,7 +168,9 @@ final class PersistenceUnitReader {
/**
* Parse the validated document and add entries to the given unit info list.
*/
void parseDocument(Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
protected List<SpringPersistenceUnitInfo> parseDocument(
Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
Element persistence = document.getDocumentElement();
String version = persistence.getAttribute(PERSISTENCE_VERSION);
URL rootUrl = determinePersistenceUnitRootUrl(resource);
@@ -177,12 +179,14 @@ final class PersistenceUnitReader {
for (Element unit : units) {
infos.add(parsePersistenceUnitInfo(unit, version, rootUrl));
}
return infos;
}
/**
* Parse the unit info DOM element.
*/
SpringPersistenceUnitInfo parsePersistenceUnitInfo(
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(
Element persistenceUnit, String version, @Nullable URL rootUrl) throws IOException {
SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
@@ -249,7 +253,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code property} XML elements.
*/
void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
Element propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES);
if (propRoot == null) {
return;
@@ -265,7 +269,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code class} XML elements.
*/
void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
List<Element> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME);
for (Element element : classes) {
String value = DomUtils.getTextValue(element).trim();
@@ -278,7 +282,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code mapping-file} XML elements.
*/
void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
List<Element> files = DomUtils.getChildElementsByTagName(persistenceUnit, MAPPING_FILE_NAME);
for (Element element : files) {
String value = DomUtils.getTextValue(element).trim();
@@ -291,7 +295,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code jar-file} XML elements.
*/
void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
protected void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
List<Element> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
for (Element element : jars) {
String value = DomUtils.getTextValue(element).trim();
@@ -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");
@@ -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.
@@ -93,7 +93,7 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
/**
* Sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes}
* to {@code text/xml} and {@code application/xml}, and {@code application/*+xml}.
* to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}.
*/
public SourceHttpMessageConverter() {
super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml"));
@@ -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]]