mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ea8ba1b96 |
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
@@ -24,11 +24,9 @@ buildSrc/build
|
||||
/spring-core/graalvm/build
|
||||
/spring-core/kotlin-coroutines/build
|
||||
/framework-bom/build
|
||||
/framework-docs/build
|
||||
/integration-tests/build
|
||||
/src/asciidoc/build
|
||||
target/
|
||||
/target/
|
||||
|
||||
# Eclipse artifacts, including WTP generated manifests
|
||||
.classpath
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=8.0.362-librca
|
||||
java=8.0.345-librca
|
||||
|
||||
+28
-31
@@ -1,17 +1,17 @@
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
|
||||
id 'io.spring.nohttp' version '0.0.10'
|
||||
id 'io.freefair.aspectj' version '6.2.0' apply false
|
||||
id "io.freefair.aspectj" version '6.2.0' apply false
|
||||
id 'org.jetbrains.dokka' version '1.6.10' apply false
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.32" apply false
|
||||
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '3.3.2'
|
||||
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.39.0'
|
||||
id 'com.github.johnrengelman.shadow' version '7.0.0' apply false
|
||||
id "org.unbroken-dome.xjc" version '2.0.0' apply false
|
||||
id "com.github.ben-manes.versions" version '0.39.0'
|
||||
id "com.github.johnrengelman.shadow" version '7.0.0' apply false
|
||||
id 'de.undercouch.download' version '4.1.2'
|
||||
id 'me.champeau.jmh' version "0.6.6" apply false
|
||||
id "me.champeau.jmh" version "0.6.6" apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.91.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.31"
|
||||
mavenBom "io.netty:netty-bom:4.1.82.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.23"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.48.v20220622"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
|
||||
mavenBom "org.junit:junit-bom:5.8.2"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.20.0') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.18.0') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
@@ -68,7 +68,7 @@ configure(allprojects) { project ->
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
|
||||
dependency "io.smallrye.reactive:mutiny:1.8.0"
|
||||
dependency "io.smallrye.reactive:mutiny:1.7.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
@@ -92,11 +92,11 @@ configure(allprojects) { project ->
|
||||
entry 'jibx-run'
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.33"
|
||||
dependency "org.yaml:snakeyaml:1.30"
|
||||
|
||||
dependency "com.h2database:h2:2.1.214"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
|
||||
dependency "com.github.librepdf:openpdf:1.3.30"
|
||||
dependency "com.github.librepdf:openpdf:1.3.29"
|
||||
dependency "com.rometools:rome:1.18.0"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.4"
|
||||
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
|
||||
dependency "org.webjars:webjars-locator-core:0.48"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.73') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.65') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.73') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.65') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.23.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.19.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-servlet') {
|
||||
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
|
||||
@@ -150,7 +150,7 @@ configure(allprojects) { project ->
|
||||
}
|
||||
}
|
||||
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.13"
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.12"
|
||||
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
|
||||
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3'
|
||||
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
|
||||
@@ -191,14 +191,14 @@ configure(allprojects) { project ->
|
||||
dependency "org.junit.support:testng-engine:1.0.4"
|
||||
dependency "org.hamcrest:hamcrest:2.2"
|
||||
dependency "org.awaitility:awaitility:3.1.6"
|
||||
dependency "org.assertj:assertj-core:3.24.2"
|
||||
dependency "org.assertj:assertj-core:3.23.0"
|
||||
dependencySet(group: 'org.xmlunit', version: '2.9.0') {
|
||||
entry 'xmlunit-assertj'
|
||||
entry('xmlunit-matchers') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.mockito', version: '4.9.0') { // spring-beans tests fail with 4.10+
|
||||
dependencySet(group: 'org.mockito', version: '4.6.1') {
|
||||
entry('mockito-core') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "io.mockk:mockk:1.12.1"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.70.0") {
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.64.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.70.0") {
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.64.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
|
||||
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.9.3"
|
||||
toolVersion = "10.3.3"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -377,9 +377,9 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
@@ -413,9 +413,11 @@ configure(rootProject) {
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
source.exclude "spring-core/graalvm/build/**" // only available on `main`
|
||||
source.exclude "spring-core-test/build/**" // only available on `main`
|
||||
allowlistFile = project.file("src/nohttp/allowlist.lines")
|
||||
def rootPath = file(rootDir).toPath()
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/spring-core-test" + "${rootDir}/buildSrc" + "${rootDir}/framework-docs"
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
|
||||
projectDirs.forEach { dir ->
|
||||
[ 'bin', 'build', 'out', '.settings' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
@@ -426,10 +428,6 @@ configure(rootProject) {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("checkstyleNohttp").configure {
|
||||
maxHeapSize = "1g"
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
@@ -439,5 +437,4 @@ configure(rootProject) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,3 @@ changelog:
|
||||
sort: "title"
|
||||
labels:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
FROM ubuntu:focal-20220922
|
||||
FROM ubuntu:focal-20220826
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java8
|
||||
ENV JDK11 /opt/openjdk/java11
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
|
||||
@@ -5,9 +5,15 @@ case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
|
||||
;;
|
||||
java11)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.16+8/bellsoft-jdk11.0.16+8-linux-amd64.tar.gz"
|
||||
;;
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.4+8/bellsoft-jdk17.0.4+8-linux-amd64.tar.gz"
|
||||
;;
|
||||
java18)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2+10/bellsoft-jdk18.0.2+10-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java8 java17
|
||||
for jdk in java8 java11 java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
@@ -8,4 +8,7 @@ milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
registry-mirror-host: docker.repo.spring.io
|
||||
registry-mirror-username: ((artifactory-username))
|
||||
registry-mirror-password: ((artifactory-password))
|
||||
task-timeout: 1h00m
|
||||
|
||||
+47
-13
@@ -23,6 +23,11 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: ((milestone))
|
||||
registry-mirror-vars: ®istry-mirror-vars
|
||||
registry-mirror-host: ((registry-mirror-host))
|
||||
registry-mirror-username: ((registry-mirror-username))
|
||||
registry-mirror-password: ((registry-mirror-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!>
|
||||
@@ -43,37 +48,31 @@ resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.7.1
|
||||
tag: 1.5.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.18
|
||||
tag: 0.0.17
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.8.0
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: pull-request
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: teliaoss/github-pr-resource
|
||||
tag: v0.23.0
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
@@ -102,7 +101,6 @@ resources:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -127,6 +125,14 @@ resources:
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk11-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk11-build
|
||||
- name: repo-status-jdk17-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
@@ -170,7 +176,7 @@ jobs:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *docker-resource-source
|
||||
<<: *registry-mirror-vars
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
@@ -231,6 +237,34 @@ jobs:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk11-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk11-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: 11
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk17-build
|
||||
serial: true
|
||||
public: true
|
||||
@@ -248,7 +282,7 @@ jobs:
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 17
|
||||
TEST_TOOLCHAIN: 15
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
@@ -437,7 +471,7 @@ jobs:
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk17-build"]
|
||||
jobs: ["build", "jdk11-build", "jdk17-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -4,6 +4,6 @@ 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 \
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
|
||||
@@ -4,9 +4,11 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.10.0
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 0.9.1
|
||||
registry_mirror:
|
||||
host: ((registry-mirror-host))
|
||||
username: ((registry-mirror-username))
|
||||
password: ((registry-mirror-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.27
|
||||
version=5.3.23
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -81,10 +81,6 @@ plugins.withType(JavaPlugin) {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
if(testLanguageVersion == JavaLanguageVersion.of(17)) {
|
||||
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,6 +130,21 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
|
||||
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.12.1"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
id "com.gradle.enterprise" version "3.10.2"
|
||||
id "io.spring.ge.conventions" version "0.0.10"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2016 the original author 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,10 @@ import org.springframework.lang.Nullable;
|
||||
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
|
||||
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
|
||||
*
|
||||
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
|
||||
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
|
||||
* we need to split out this subinterface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see AspectMetadata
|
||||
@@ -31,13 +35,13 @@ import org.springframework.lang.Nullable;
|
||||
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
|
||||
|
||||
/**
|
||||
* Get the AspectJ AspectMetadata for this factory's aspect.
|
||||
* Return the AspectJ AspectMetadata for this factory's aspect.
|
||||
* @return the aspect metadata
|
||||
*/
|
||||
AspectMetadata getAspectMetadata();
|
||||
|
||||
/**
|
||||
* Get the best possible creation mutex for this factory.
|
||||
* Return the best possible creation mutex for this factory.
|
||||
* @return the mutex object (may be {@code null} for no mutex to use)
|
||||
* @since 4.3
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -123,11 +123,6 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
|
||||
}
|
||||
if (classLoader == null || classLoader.getParent() == null) {
|
||||
// JDK bootstrap loader or platform loader suggested ->
|
||||
// use higher-level loader which can see Spring infrastructure classes
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
|
||||
}
|
||||
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Various {@link org.springframework.aop.framework.autoproxy.TargetSourceCreator}
|
||||
* implementations for use with Spring's AOP auto-proxying support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.aop.framework.autoproxy.target;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.aop.framework;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.accessibility.Accessible;
|
||||
@@ -383,40 +380,6 @@ public class ProxyFactoryTests {
|
||||
assertThat(proxy.getName()).isEqualTo("tb");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCharSequenceProxy() {
|
||||
CharSequence target = "test";
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateProxy() {
|
||||
Date target = new Date();
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
pf.setProxyTargetClass(true);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcSavepointProxy() throws SQLException {
|
||||
Savepoint target = new Savepoint() {
|
||||
@Override
|
||||
public int getSavepointId() throws SQLException {
|
||||
return 1;
|
||||
}
|
||||
@Override
|
||||
public String getSavepointName() throws SQLException {
|
||||
return "sp";
|
||||
}
|
||||
};
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = Savepoint.class.getClassLoader();
|
||||
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
|
||||
}
|
||||
|
||||
|
||||
@Order(2)
|
||||
public static class A implements Runnable {
|
||||
@@ -428,7 +391,7 @@ public class ProxyFactoryTests {
|
||||
|
||||
|
||||
@Order(1)
|
||||
public static class B implements Runnable {
|
||||
public static class B implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.StringUtils;
|
||||
* <p>Mainly for internal use within the framework, but to some degree also
|
||||
* useful for application classes. Consider
|
||||
* <a href="https://commons.apache.org/proper/commons-beanutils/">Apache Commons BeanUtils</a>,
|
||||
* <a href="https://github.com/ExpediaGroup/bull">BULL - Bean Utils Light Library</a>,
|
||||
* <a href="https://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
|
||||
* or similar third-party frameworks for more comprehensive bean utilities.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
@@ -247,8 +247,7 @@ public abstract class BeanUtils {
|
||||
// A single public constructor
|
||||
return (Constructor<T>) ctors[0];
|
||||
}
|
||||
else if (ctors.length == 0) {
|
||||
// No public constructors -> check non-public
|
||||
else if (ctors.length == 0){
|
||||
ctors = clazz.getDeclaredConstructors();
|
||||
if (ctors.length == 1) {
|
||||
// A single non-public constructor, e.g. from a non-public record type
|
||||
|
||||
+10
-10
@@ -97,6 +97,8 @@ public final class CachedIntrospectionResults {
|
||||
*/
|
||||
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
|
||||
|
||||
private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
|
||||
|
||||
|
||||
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
|
||||
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
|
||||
@@ -295,7 +297,7 @@ public final class CachedIntrospectionResults {
|
||||
// Only allow URL attribute introspection, not content resolution
|
||||
continue;
|
||||
}
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
@@ -345,8 +347,7 @@ public final class CachedIntrospectionResults {
|
||||
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
|
||||
// against a declared read method, so we prefer read method descriptors here.
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
if (pd.getWriteMethod() == null &&
|
||||
isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
@@ -379,7 +380,7 @@ public final class CachedIntrospectionResults {
|
||||
if (Modifier.isStatic(method.getModifiers()) ||
|
||||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
|
||||
method.getParameterCount() > 0 || method.getReturnType() == void.class ||
|
||||
isInvalidReadOnlyPropertyType(method.getReturnType(), method.getDeclaringClass())) {
|
||||
isInvalidReadOnlyPropertyType(method.getReturnType())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
@@ -392,11 +393,10 @@ public final class CachedIntrospectionResults {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType, Class<?> beanClass) {
|
||||
return (returnType != null && (ClassLoader.class.isAssignableFrom(returnType) ||
|
||||
ProtectionDomain.class.isAssignableFrom(returnType) ||
|
||||
(AutoCloseable.class.isAssignableFrom(returnType) &&
|
||||
!AutoCloseable.class.isAssignableFrom(beanClass))));
|
||||
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
|
||||
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
|
||||
ClassLoader.class.isAssignableFrom(returnType) ||
|
||||
ProtectionDomain.class.isAssignableFrom(returnType)));
|
||||
}
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ public final class CachedIntrospectionResults {
|
||||
}
|
||||
|
||||
PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.values().toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
}
|
||||
|
||||
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -17,15 +17,9 @@
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -38,80 +32,6 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
abstract class PropertyDescriptorUtils {
|
||||
|
||||
public static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
|
||||
|
||||
|
||||
/**
|
||||
* Simple introspection algorithm for basic set/get/is accessor methods,
|
||||
* building corresponding JavaBeans property descriptors for them.
|
||||
* <p>This just supports the basic JavaBeans conventions, without indexed
|
||||
* properties or any customizers, and without other BeanInfo metadata.
|
||||
* For standard JavaBeans introspection, use the JavaBeans Introspector.
|
||||
* @param beanClass the target class to introspect
|
||||
* @return a collection of property descriptors
|
||||
* @throws IntrospectionException from introspecting the given bean class
|
||||
* @since 5.3.24
|
||||
* @see SimpleBeanInfoFactory
|
||||
* @see java.beans.Introspector#getBeanInfo(Class)
|
||||
*/
|
||||
public static Collection<? extends PropertyDescriptor> determineBasicProperties(Class<?> beanClass)
|
||||
throws IntrospectionException {
|
||||
|
||||
Map<String, BasicPropertyDescriptor> pdMap = new TreeMap<>();
|
||||
|
||||
for (Method method : beanClass.getMethods()) {
|
||||
String methodName = method.getName();
|
||||
|
||||
boolean setter;
|
||||
int nameIndex;
|
||||
if (methodName.startsWith("set") && method.getParameterCount() == 1) {
|
||||
setter = true;
|
||||
nameIndex = 3;
|
||||
}
|
||||
else if (methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE) {
|
||||
setter = false;
|
||||
nameIndex = 3;
|
||||
}
|
||||
else if (methodName.startsWith("is") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
|
||||
setter = false;
|
||||
nameIndex = 2;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
String propertyName = Introspector.decapitalize(methodName.substring(nameIndex));
|
||||
if (propertyName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicPropertyDescriptor pd = pdMap.get(propertyName);
|
||||
if (pd != null) {
|
||||
if (setter) {
|
||||
if (pd.getWriteMethod() == null ||
|
||||
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
pd.setWriteMethod(method);
|
||||
}
|
||||
else {
|
||||
pd.addWriteMethod(method);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pd.getReadMethod() == null ||
|
||||
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
|
||||
pd.setReadMethod(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
pd = new BasicPropertyDescriptor(propertyName, (!setter ? method : null), (setter ? method : null));
|
||||
pdMap.put(propertyName, pd);
|
||||
}
|
||||
}
|
||||
|
||||
return pdMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link java.beans.FeatureDescriptor}.
|
||||
*/
|
||||
@@ -253,72 +173,4 @@ abstract class PropertyDescriptorUtils {
|
||||
pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PropertyDescriptor for {@link #determineBasicProperties(Class)},
|
||||
* not performing any early type determination for
|
||||
* {@link #setReadMethod}/{@link #setWriteMethod}.
|
||||
* @since 5.3.24
|
||||
*/
|
||||
private static class BasicPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
@Nullable
|
||||
private Method readMethod;
|
||||
|
||||
@Nullable
|
||||
private Method writeMethod;
|
||||
|
||||
private final List<Method> alternativeWriteMethods = new ArrayList<>();
|
||||
|
||||
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
super(propertyName, readMethod, writeMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(@Nullable Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(@Nullable Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
public void addWriteMethod(Method writeMethod) {
|
||||
if (this.writeMethod != null) {
|
||||
this.alternativeWriteMethods.add(this.writeMethod);
|
||||
this.writeMethod = null;
|
||||
}
|
||||
this.alternativeWriteMethods.add(writeMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getWriteMethod() {
|
||||
if (this.writeMethod == null && !this.alternativeWriteMethods.isEmpty()) {
|
||||
if (this.readMethod == null) {
|
||||
return this.alternativeWriteMethods.get(0);
|
||||
}
|
||||
else {
|
||||
for (Method method : this.alternativeWriteMethods) {
|
||||
if (this.readMethod.getReturnType().isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
this.writeMethod = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.writeMethod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.beans;
|
||||
|
||||
import java.beans.BeanDescriptor;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.beans.SimpleBeanInfo;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* {@link BeanInfoFactory} implementation that bypasses the standard {@link java.beans.Introspector}
|
||||
* for faster introspection, reduced to basic property determination (as commonly needed in Spring).
|
||||
*
|
||||
* <p>To be configured via a {@code META-INF/spring.factories} file with the following content,
|
||||
* overriding other custom {@code org.springframework.beans.BeanInfoFactory} declarations:
|
||||
* {@code org.springframework.beans.BeanInfoFactory=org.springframework.beans.SimpleBeanInfoFactory}
|
||||
*
|
||||
* <p>Ordered at {@code Ordered.LOWEST_PRECEDENCE - 1} to override {@link ExtendedBeanInfoFactory}
|
||||
* (registered by default in 5.3) if necessary while still allowing other user-defined
|
||||
* {@link BeanInfoFactory} types to take precedence.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3.24
|
||||
* @see ExtendedBeanInfoFactory
|
||||
* @see CachedIntrospectionResults
|
||||
*/
|
||||
public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
|
||||
Collection<? extends PropertyDescriptor> pds =
|
||||
PropertyDescriptorUtils.determineBasicProperties(beanClass);
|
||||
|
||||
return new SimpleBeanInfo() {
|
||||
@Override
|
||||
public BeanDescriptor getBeanDescriptor() {
|
||||
return new BeanDescriptor(beanClass);
|
||||
}
|
||||
@Override
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return pds.toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -438,6 +438,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
// Fall back to class name as cache key, for backwards compatibility with custom callers.
|
||||
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
|
||||
@@ -562,9 +563,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* @param type the type of the bean
|
||||
* @return the target beans, or an empty Collection if no bean of this type is found
|
||||
* @throws BeansException if bean retrieval failed
|
||||
* @deprecated since 5.3.24 since it is unused in the meantime
|
||||
*/
|
||||
@Deprecated
|
||||
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("No BeanFactory configured - " +
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -25,7 +25,6 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
@@ -241,11 +240,10 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
}
|
||||
}
|
||||
if (targetAnnotation == null) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
// Look for matching annotation on the target class
|
||||
if (beanFactory != null) {
|
||||
if (getBeanFactory() != null) {
|
||||
try {
|
||||
Class<?> beanType = beanFactory.getType(bdHolder.getBeanName());
|
||||
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
|
||||
if (beanType != null) {
|
||||
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -184,9 +184,8 @@ public abstract class YamlProcessor {
|
||||
protected Yaml createYaml() {
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setAllowDuplicateKeys(false);
|
||||
DumperOptions dumperOptions = new DumperOptions();
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(dumperOptions),
|
||||
dumperOptions, loaderOptions);
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
|
||||
new DumperOptions(), loaderOptions);
|
||||
}
|
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
|
||||
|
||||
+35
-49
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -161,7 +161,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
/** Cache of pre-filtered post-processors. */
|
||||
@Nullable
|
||||
private BeanPostProcessorCache beanPostProcessorCache;
|
||||
private volatile BeanPostProcessorCache beanPostProcessorCache;
|
||||
|
||||
/** Map from scope identifier String to corresponding Scope. */
|
||||
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
|
||||
@@ -944,12 +944,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
|
||||
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
|
||||
synchronized (this.beanPostProcessors) {
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.remove(beanPostProcessor);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.add(beanPostProcessor);
|
||||
}
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.remove(beanPostProcessor);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.add(beanPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -959,12 +957,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @see #addBeanPostProcessor
|
||||
*/
|
||||
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.removeAll(beanPostProcessors);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.addAll(beanPostProcessors);
|
||||
}
|
||||
this.beanPostProcessors.removeAll(beanPostProcessors);
|
||||
this.beanPostProcessors.addAll(beanPostProcessors);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -986,34 +980,26 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @since 5.3
|
||||
*/
|
||||
BeanPostProcessorCache getBeanPostProcessorCache() {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
BeanPostProcessorCache bppCache = this.beanPostProcessorCache;
|
||||
if (bppCache == null) {
|
||||
bppCache = new BeanPostProcessorCache();
|
||||
for (BeanPostProcessor bpp : this.beanPostProcessors) {
|
||||
if (bpp instanceof InstantiationAwareBeanPostProcessor) {
|
||||
bppCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bpp);
|
||||
if (bpp instanceof SmartInstantiationAwareBeanPostProcessor) {
|
||||
bppCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bpp);
|
||||
}
|
||||
}
|
||||
if (bpp instanceof DestructionAwareBeanPostProcessor) {
|
||||
bppCache.destructionAware.add((DestructionAwareBeanPostProcessor) bpp);
|
||||
}
|
||||
if (bpp instanceof MergedBeanDefinitionPostProcessor) {
|
||||
bppCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bpp);
|
||||
BeanPostProcessorCache bpCache = this.beanPostProcessorCache;
|
||||
if (bpCache == null) {
|
||||
bpCache = new BeanPostProcessorCache();
|
||||
for (BeanPostProcessor bp : this.beanPostProcessors) {
|
||||
if (bp instanceof InstantiationAwareBeanPostProcessor) {
|
||||
bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);
|
||||
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
|
||||
bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);
|
||||
}
|
||||
}
|
||||
this.beanPostProcessorCache = bppCache;
|
||||
if (bp instanceof DestructionAwareBeanPostProcessor) {
|
||||
bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);
|
||||
}
|
||||
if (bp instanceof MergedBeanDefinitionPostProcessor) {
|
||||
bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);
|
||||
}
|
||||
}
|
||||
return bppCache;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetBeanPostProcessorCache() {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
this.beanPostProcessorCache = null;
|
||||
this.beanPostProcessorCache = bpCache;
|
||||
}
|
||||
return bpCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2028,27 +2014,27 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public BeanPostProcessor set(int index, BeanPostProcessor element) {
|
||||
BeanPostProcessor result = super.set(index, element);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(BeanPostProcessor o) {
|
||||
boolean success = super.add(o);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, BeanPostProcessor element) {
|
||||
super.add(index, element);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanPostProcessor remove(int index) {
|
||||
BeanPostProcessor result = super.remove(index);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2056,7 +2042,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean remove(Object o) {
|
||||
boolean success = super.remove(o);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2065,7 +2051,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
boolean success = super.removeAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2074,7 +2060,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
boolean success = super.retainAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2083,7 +2069,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean addAll(Collection<? extends BeanPostProcessor> c) {
|
||||
boolean success = super.addAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2092,7 +2078,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean addAll(int index, Collection<? extends BeanPostProcessor> c) {
|
||||
boolean success = super.addAll(index, c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2101,7 +2087,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean removeIf(Predicate<? super BeanPostProcessor> filter) {
|
||||
boolean success = super.removeIf(filter);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2109,7 +2095,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public void replaceAll(UnaryOperator<BeanPostProcessor> operator) {
|
||||
super.replaceAll(operator);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -214,15 +214,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,15 +240,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,23 +320,18 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -547,8 +547,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
||||
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
|
||||
*/
|
||||
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
|
||||
ResourceLoader resourceLoader = getResourceLoader();
|
||||
ClassLoader cl = (resourceLoader != null ? resourceLoader.getClassLoader() : getBeanClassLoader());
|
||||
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
|
||||
return new DefaultNamespaceHandlerResolver(cl);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -77,21 +77,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceWrappedInstance() {
|
||||
GetterBean target = new GetterBean();
|
||||
BeanWrapperImpl accessor = createAccessor(target);
|
||||
accessor.setPropertyValue("name", "tom");
|
||||
assertThat(target.getAliasedName()).isEqualTo("tom");
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
|
||||
target = new GetterBean();
|
||||
accessor.setWrappedInstance(target);
|
||||
accessor.setPropertyValue("name", "tom");
|
||||
assertThat(target.getAliasedName()).isEqualTo("tom");
|
||||
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setValidAndInvalidPropertyValuesShouldContainExceptionDetails() {
|
||||
TestBean target = new TestBean();
|
||||
@@ -168,16 +153,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setterOverload() {
|
||||
SetterOverload target = new SetterOverload();
|
||||
BeanWrapper accessor = createAccessor(target);
|
||||
accessor.setPropertyValue("object", "a String");
|
||||
assertThat(target.value).isEqualTo("a String");
|
||||
assertThat(target.getObject()).isEqualTo("a String");
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo("a String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyDescriptors() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
@@ -231,10 +206,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
assertThat(accessor.isReadableProperty("inputStream")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("filename")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("description")).isTrue();
|
||||
|
||||
accessor = createAccessor(new ActiveResource());
|
||||
|
||||
assertThat(accessor.isReadableProperty("resource")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -377,37 +348,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class SetterOverload {
|
||||
|
||||
public String value;
|
||||
|
||||
public void setObject(Integer length) {
|
||||
this.value = length.toString();
|
||||
}
|
||||
|
||||
public void setObject(String object) {
|
||||
this.value = object;
|
||||
}
|
||||
|
||||
public String getObject() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("try")
|
||||
public static class ActiveResource implements AutoCloseable {
|
||||
|
||||
public ActiveResource getResource() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class GetterWithOptional {
|
||||
|
||||
public TestBean value;
|
||||
|
||||
+17
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -436,6 +436,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
@Test
|
||||
void empty() {
|
||||
ListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
assertThat(lbf.getBeanDefinitionNames() != null).as("No beans defined --> array != null").isTrue();
|
||||
assertThat(lbf.getBeanDefinitionNames().length == 0).as("No beans defined after no arg constructor").isTrue();
|
||||
assertThat(lbf.getBeanDefinitionCount() == 0).as("No beans defined after no arg constructor").isTrue();
|
||||
@@ -777,6 +778,21 @@ class DefaultListableBeanFactoryTests {
|
||||
assertThat(factory.getType("child")).isEqualTo(DerivedTestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameAlreadyBound() {
|
||||
Properties p = new Properties();
|
||||
p.setProperty("kerry.(class)", TestBean.class.getName());
|
||||
p.setProperty("kerry.age", "35");
|
||||
registerBeanDefinitions(p);
|
||||
try {
|
||||
registerBeanDefinitions(p);
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
assertThat(ex.getBeanName()).isEqualTo("kerry");
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
private void singleTestBean(ListableBeanFactory lbf) {
|
||||
assertThat(lbf.getBeanDefinitionCount() == 1).as("1 beans defined").isTrue();
|
||||
String[] names = lbf.getBeanDefinitionNames();
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -148,7 +148,7 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
lbf.registerBeanDefinition(MARK, person2);
|
||||
MethodParameter param = new MethodParameter(QualifiedTestBean.class.getDeclaredConstructor(Person.class), 0);
|
||||
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(param, false);
|
||||
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
param.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
assertThat(param.getParameterName()).isEqualTo("tpb");
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
|
||||
@@ -174,9 +174,9 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
new MethodParameter(QualifiedTestBean.class.getDeclaredMethod("autowireNonqualified", Person.class), 0);
|
||||
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(qualifiedParam, false);
|
||||
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(nonqualifiedParam, false);
|
||||
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
qualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
nonqualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||
assertThat(nonqualifiedParam.getParameterName()).isEqualTo("tpb");
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
|
||||
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -502,7 +502,7 @@ class CustomEditorTests {
|
||||
CharBean cb = new CharBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(cb);
|
||||
|
||||
bw.setPropertyValue("myChar", 'c');
|
||||
bw.setPropertyValue("myChar", new Character('c'));
|
||||
assertThat(cb.getMyChar()).isEqualTo('c');
|
||||
|
||||
bw.setPropertyValue("myChar", "c");
|
||||
@@ -526,16 +526,16 @@ class CustomEditorTests {
|
||||
bw.registerCustomEditor(Character.class, new CharacterEditor(true));
|
||||
|
||||
bw.setPropertyValue("myCharacter", 'c');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('c');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
|
||||
|
||||
bw.setPropertyValue("myCharacter", "c");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('c');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
|
||||
|
||||
bw.setPropertyValue("myCharacter", "\u0041");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo('A');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('A'));
|
||||
|
||||
bw.setPropertyValue("myCharacter", " ");
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(' ');
|
||||
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf(' '));
|
||||
|
||||
bw.setPropertyValue("myCharacter", "");
|
||||
assertThat(cb.getMyCharacter()).isNull();
|
||||
@@ -722,7 +722,7 @@ class CustomEditorTests {
|
||||
|
||||
@Test
|
||||
void testCustomDateEditor() {
|
||||
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), false);
|
||||
CustomDateEditor editor = new CustomDateEditor(null, false);
|
||||
editor.setValue(null);
|
||||
assertThat(editor.getValue()).isNull();
|
||||
assertThat(editor.getAsText()).isEmpty();
|
||||
@@ -730,7 +730,7 @@ class CustomEditorTests {
|
||||
|
||||
@Test
|
||||
void testCustomDateEditorWithEmptyAsNull() {
|
||||
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true);
|
||||
CustomDateEditor editor = new CustomDateEditor(null, true);
|
||||
editor.setValue(null);
|
||||
assertThat(editor.getValue()).isNull();
|
||||
assertThat(editor.getAsText()).isEmpty();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
||||
Vendored
+3
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -51,7 +51,6 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @author Brian Clozel
|
||||
* @since 4.3
|
||||
* @see CaffeineCache
|
||||
*/
|
||||
@@ -189,13 +188,8 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
if (this.dynamic) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
return (cache != null) ? cache : this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
else {
|
||||
return this.cacheMap.get(name);
|
||||
}
|
||||
return this.cacheMap.computeIfAbsent(name, cacheName ->
|
||||
this.dynamic ? createCaffeineCache(cacheName) : null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -93,7 +93,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
|
||||
|
||||
@Override
|
||||
public void setFrom(@Nullable String from) {
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReplyTo(@Nullable String replyTo) {
|
||||
public void setReplyTo(String replyTo) {
|
||||
this.replyTo = replyTo;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTo(@Nullable String to) {
|
||||
public void setTo(String to) {
|
||||
this.to = new String[] {to};
|
||||
}
|
||||
|
||||
@@ -128,12 +128,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCc(@Nullable String cc) {
|
||||
public void setCc(String cc) {
|
||||
this.cc = new String[] {cc};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCc(@Nullable String... cc) {
|
||||
public void setCc(String... cc) {
|
||||
this.cc = cc;
|
||||
}
|
||||
|
||||
@@ -143,12 +143,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBcc(@Nullable String bcc) {
|
||||
public void setBcc(String bcc) {
|
||||
this.bcc = new String[] {bcc};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBcc(@Nullable String... bcc) {
|
||||
public void setBcc(String... bcc) {
|
||||
this.bcc = bcc;
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSentDate(@Nullable Date sentDate) {
|
||||
public void setSentDate(Date sentDate) {
|
||||
this.sentDate = sentDate;
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubject(@Nullable String subject) {
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(@Nullable String text) {
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -132,10 +132,10 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow {@code Map} access to the JavaMail properties of this sender,
|
||||
* Allow {code Map} access to the JavaMail properties of this sender,
|
||||
* with the option to add or override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via
|
||||
* {@code javaMailProperties[mail.smtp.auth]}.
|
||||
* {code javaMailProperties[mail.smtp.auth]}.
|
||||
*/
|
||||
public Properties getJavaMailProperties() {
|
||||
return this.javaMailProperties;
|
||||
|
||||
Vendored
-17
@@ -77,23 +77,6 @@ public class LoggingCacheErrorHandler implements CacheErrorHandler {
|
||||
this.logStackTraces = logStackTraces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code LoggingCacheErrorHandler} that uses the supplied
|
||||
* {@code loggerName} and {@code logStackTraces} flag.
|
||||
* @param loggerName the name of the logger to use. The name will be passed
|
||||
* to the underlying logger implementation through Commons Logging, getting
|
||||
* interpreted as log category according to the logger's configuration.
|
||||
* @param logStackTraces whether to log stack traces
|
||||
* @since 5.3.24
|
||||
* @see org.apache.commons.logging.LogFactory#getLog(String)
|
||||
* @see java.util.logging.Logger#getLogger(String)
|
||||
*/
|
||||
public LoggingCacheErrorHandler(String loggerName, boolean logStackTraces) {
|
||||
Assert.notNull(loggerName, "'loggerName' must not be null");
|
||||
this.logger = LogFactory.getLog(loggerName);
|
||||
this.logStackTraces = logStackTraces;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
|
||||
|
||||
+4
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -729,11 +729,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
/**
|
||||
* Modify the application context's internal bean factory after its standard
|
||||
* initialization. The initial definition resources will have been loaded but no
|
||||
* post-processors will have run and no derived bean definitions will have been
|
||||
* registered, and most importantly, no beans will have been instantiated yet.
|
||||
* <p>This template method allows for registering special BeanPostProcessors
|
||||
* etc in certain AbstractApplicationContext subclasses.
|
||||
* initialization. All bean definitions will have been loaded, but no beans
|
||||
* will have been instantiated yet. This allows for registering special
|
||||
* BeanPostProcessors etc in certain ApplicationContext implementations.
|
||||
* @param beanFactory the bean factory used by the application context
|
||||
*/
|
||||
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -96,8 +96,6 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
|
||||
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
|
||||
|
||||
List<String> resolvedFallbackPatterns = new ArrayList<>();
|
||||
for (String fallbackPattern : annotation.fallbackPatterns()) {
|
||||
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
|
||||
@@ -106,6 +104,7 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
|
||||
}
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
|
||||
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType,
|
||||
formatter, resolvedFallbackPatterns.toArray(new String[0]), annotation);
|
||||
}
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -71,8 +71,7 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
|
||||
}
|
||||
|
||||
TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter,
|
||||
@Nullable String[] fallbackPatterns, @Nullable Object source) {
|
||||
|
||||
@Nullable String[] fallbackPatterns, @Nullable Object source) {
|
||||
this.temporalAccessorType = temporalAccessorType;
|
||||
this.formatter = formatter;
|
||||
this.fallbackPatterns = fallbackPatterns;
|
||||
@@ -101,8 +100,8 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
|
||||
}
|
||||
if (this.source != null) {
|
||||
throw new DateTimeParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
text, ex.getErrorIndex(), ex);
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
text, ex.getErrorIndex(), ex);
|
||||
}
|
||||
// else rethrow original exception
|
||||
throw ex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -125,7 +125,7 @@ public class FieldError extends ObjectError {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Field error in object '" + getObjectName() + "' on field '" + this.field +
|
||||
"': rejected value [" + ObjectUtils.nullSafeConciseToString(this.rejectedValue) + "]; " +
|
||||
"': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
|
||||
resolvableToString();
|
||||
}
|
||||
|
||||
|
||||
-7
@@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.NoOpCache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -85,10 +84,4 @@ class LoggingCacheErrorHandlerTests {
|
||||
verify(this.logger).warn("Cache 'NOOP' failed to get entry with key 'enigma'", exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWithLoggerName() {
|
||||
assertThatCode(() -> new LoggingCacheErrorHandler("org.apache.commons.logging.Log", true))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-3
@@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class Gh29105Tests {
|
||||
public class Gh29105Tests {
|
||||
|
||||
@Test
|
||||
void beanProviderWithParentContextReuseOrder() {
|
||||
@@ -47,7 +47,6 @@ class Gh29105Tests {
|
||||
List<Class<?>> orderedTypes = child.getBeanProvider(MyService.class)
|
||||
.orderedStream().map(Object::getClass).collect(Collectors.toList());
|
||||
assertThat(orderedTypes).containsExactly(CustomService.class, DefaultService.class);
|
||||
child.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,5 +78,4 @@ class Gh29105Tests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -133,7 +133,7 @@ public class DateFormattingTests {
|
||||
assertThat(exception)
|
||||
.hasMessageContaining("for property 'styleDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [99/01/01]")
|
||||
.hasMessageContaining("for value '99/01/01'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [99/01/01]")
|
||||
.hasCauseInstanceOf(ParseException.class).cause()
|
||||
@@ -353,7 +353,7 @@ public class DateFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [210302]")
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(ParseException.class).cause()
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -333,7 +333,7 @@ class DateTimeFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'isoLocalDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [2009-31-10]")
|
||||
.hasMessageContaining("for value '2009-31-10'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [2009-31-10]")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).cause()
|
||||
@@ -540,7 +540,7 @@ class DateTimeFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternLocalDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [210302]")
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).cause()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -20,6 +20,7 @@ import java.beans.PropertyDescriptor;
|
||||
|
||||
import javax.management.DynamicMBean;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.ObjectName;
|
||||
@@ -31,7 +32,6 @@ import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.jmx.IJmxTestBean;
|
||||
import org.springframework.jmx.JmxTestBean;
|
||||
import org.springframework.jmx.export.TestDynamicMBean;
|
||||
import org.springframework.util.MBeanTestUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -41,7 +41,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class JmxUtilsTests {
|
||||
|
||||
@@ -132,11 +131,10 @@ class JmxUtilsTests {
|
||||
MBeanServer server = null;
|
||||
try {
|
||||
server = JmxUtils.locateMBeanServer();
|
||||
assertThat(server).isNotNull();
|
||||
}
|
||||
finally {
|
||||
if (server != null) {
|
||||
MBeanTestUtils.releaseMBeanServer(server);
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,25 +21,21 @@ import java.lang.reflect.Field;
|
||||
import javax.naming.spi.NamingManager;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledForJreRange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.condition.JRE.JAVA_16;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link JndiLocatorDelegate}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@DisabledForJreRange(
|
||||
min = JAVA_16,
|
||||
disabledReason = "Cannot use reflection to set private static field in javax.naming.spi.NamingManager")
|
||||
class JndiLocatorDelegateTests {
|
||||
public class JndiLocatorDelegateTests {
|
||||
|
||||
@Test
|
||||
void isDefaultJndiEnvironmentAvailableFalse() throws Exception {
|
||||
public void isDefaultJndiEnvironmentAvailableFalse() throws Exception {
|
||||
Field builderField = NamingManager.class.getDeclaredField("initctx_factory_builder");
|
||||
builderField.setAccessible(true);
|
||||
Object oldBuilder = builderField.get(null);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -18,59 +18,37 @@ package org.springframework.util;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
/**
|
||||
* Utilities for MBean tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class MBeanTestUtils {
|
||||
|
||||
/**
|
||||
* Reset the {@link MBeanServerFactory} to a known consistent state. This involves
|
||||
* {@linkplain #releaseMBeanServer(MBeanServer) releasing} all currently registered
|
||||
* MBeanServers.
|
||||
* <p>On JDK 8 - JDK 16, this method also resets the platformMBeanServer field
|
||||
* in {@link ManagementFactory} to {@code null}.
|
||||
* Resets MBeanServerFactory and ManagementFactory to a known consistent state.
|
||||
* <p>This involves releasing all currently registered MBeanServers and resetting
|
||||
* the platformMBeanServer to null.
|
||||
*/
|
||||
public static synchronized void resetMBeanServers() throws Exception {
|
||||
for (MBeanServer server : MBeanServerFactory.findMBeanServer(null)) {
|
||||
releaseMBeanServer(server);
|
||||
}
|
||||
|
||||
if (!isCurrentJreWithinRange(JRE.JAVA_16, JRE.OTHER)) {
|
||||
Field field = ManagementFactory.class.getDeclaredField("platformMBeanServer");
|
||||
field.setAccessible(true);
|
||||
field.set(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to release the supplied {@link MBeanServer}.
|
||||
* <p>Ignores any {@link IllegalArgumentException} thrown by
|
||||
* {@link MBeanServerFactory#releaseMBeanServer(MBeanServer)} whose error
|
||||
* message contains the text "not in list".
|
||||
*/
|
||||
public static void releaseMBeanServer(MBeanServer server) {
|
||||
try {
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (!ex.getMessage().contains("not in list")) {
|
||||
throw ex;
|
||||
try {
|
||||
MBeanServerFactory.releaseMBeanServer(server);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (!ex.getMessage().contains("not in list")) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isCurrentJreWithinRange(JRE min, JRE max) {
|
||||
return EnumSet.range(min, max).contains(JRE.currentVersion());
|
||||
Field field = ManagementFactory.class.getDeclaredField("platformMBeanServer");
|
||||
field.setAccessible(true);
|
||||
field.set(null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,17 +82,6 @@ public abstract class AnnotationVisitor {
|
||||
this.av = annotationVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The annotation visitor to which this visitor must delegate method calls. May be {@literal
|
||||
* null}.
|
||||
*
|
||||
* @return the annotation visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
*/
|
||||
public AnnotationVisitor getDelegate() {
|
||||
return av;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a primitive value of the annotation.
|
||||
*
|
||||
|
||||
@@ -194,7 +194,7 @@ public class ClassReader {
|
||||
this.b = classFileBuffer;
|
||||
// Check the class' major_version. This field is after the magic and minor_version fields, which
|
||||
// use 4 and 2 bytes respectively.
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V21) {
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V19) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
}
|
||||
@@ -308,7 +308,6 @@ public class ClassReader {
|
||||
* @return the content of the given input stream.
|
||||
* @throws IOException if a problem occurs during reading.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseTryWithResources")
|
||||
private static byte[] readStream(final InputStream inputStream, final boolean close)
|
||||
throws IOException {
|
||||
if (inputStream == null) {
|
||||
@@ -377,7 +376,7 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal name of the super class (see {@link Type#getInternalName()}). For
|
||||
* Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
|
||||
* interfaces, the super class is {@link Object}.
|
||||
*
|
||||
* @return the internal name of the super class, or {@literal null} for {@link Object} class.
|
||||
|
||||
@@ -42,8 +42,7 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Constructs a new {@link ClassTooLargeException}.
|
||||
*
|
||||
* @param className the internal name of the class (see {@link
|
||||
* org.objectweb.asm.Type#getInternalName()}).
|
||||
* @param className the internal name of the class.
|
||||
* @param constantPoolCount the number of constant pool items of the class.
|
||||
*/
|
||||
public ClassTooLargeException(final String className, final int constantPoolCount) {
|
||||
@@ -53,7 +52,7 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal name of the class (see {@link org.objectweb.asm.Type#getInternalName()}).
|
||||
* Returns the internal name of the class.
|
||||
*
|
||||
* @return the internal name of the class.
|
||||
*/
|
||||
|
||||
@@ -81,15 +81,6 @@ public abstract class ClassVisitor {
|
||||
this.cv = classVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the class visitor to which this visitor must delegate method calls, or {@literal null}.
|
||||
*/
|
||||
public ClassVisitor getDelegate() {
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the header of the class.
|
||||
*
|
||||
@@ -164,8 +155,7 @@ public abstract class ClassVisitor {
|
||||
* implicitly its own nest, so it's invalid to call this method with the visited class name as
|
||||
* argument.
|
||||
*
|
||||
* @param nestHost the internal name of the host class of the nest (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param nestHost the internal name of the host class of the nest.
|
||||
*/
|
||||
public void visitNestHost(final String nestHost) {
|
||||
if (api < Opcodes.ASM7) {
|
||||
@@ -177,19 +167,14 @@ public abstract class ClassVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the enclosing class of the class. This method must be called only if this class is a
|
||||
* local or anonymous class. See the JVMS 4.7.7 section for more details.
|
||||
* Visits the enclosing class of the class. This method must be called only if the class has an
|
||||
* enclosing class.
|
||||
*
|
||||
* @param owner internal name of the enclosing class of the class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param owner internal name of the enclosing class of the class.
|
||||
* @param name the name of the method that contains the class, or {@literal null} if the class is
|
||||
* not enclosed in a method or constructor of its enclosing class (e.g. if it is enclosed in
|
||||
* an instance initializer, static initializer, instance variable initializer, or class
|
||||
* variable initializer).
|
||||
* not enclosed in a method of its enclosing class.
|
||||
* @param descriptor the descriptor of the method that contains the class, or {@literal null} if
|
||||
* the class is not enclosed in a method or constructor of its enclosing class (e.g. if it is
|
||||
* enclosed in an instance initializer, static initializer, instance variable initializer, or
|
||||
* class variable initializer).
|
||||
* the class is not enclosed in a method of its enclosing class.
|
||||
*/
|
||||
public void visitOuterClass(final String owner, final String name, final String descriptor) {
|
||||
if (cv != null) {
|
||||
@@ -256,7 +241,7 @@ public abstract class ClassVisitor {
|
||||
* the visited class is the host of a nest. A nest host is implicitly a member of its own nest, so
|
||||
* it's invalid to call this method with the visited class name as argument.
|
||||
*
|
||||
* @param nestMember the internal name of a nest member (see {@link Type#getInternalName()}).
|
||||
* @param nestMember the internal name of a nest member.
|
||||
*/
|
||||
public void visitNestMember(final String nestMember) {
|
||||
if (api < Opcodes.ASM7) {
|
||||
@@ -271,8 +256,7 @@ public abstract class ClassVisitor {
|
||||
* Visits a permitted subclasses. A permitted subclass is one of the allowed subclasses of the
|
||||
* current class.
|
||||
*
|
||||
* @param permittedSubclass the internal name of a permitted subclass (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param permittedSubclass the internal name of a permitted subclass.
|
||||
*/
|
||||
public void visitPermittedSubclass(final String permittedSubclass) {
|
||||
if (api < Opcodes.ASM9) {
|
||||
@@ -285,18 +269,15 @@ public abstract class ClassVisitor {
|
||||
|
||||
/**
|
||||
* Visits information about an inner class. This inner class is not necessarily a member of the
|
||||
* class being visited. More precisely, every class or interface C which is referenced by this
|
||||
* class and which is not a package member must be visited with this method. This class must
|
||||
* reference its nested class or interface members, and its enclosing class, if any. See the JVMS
|
||||
* 4.7.6 section for more details.
|
||||
* class being visited.
|
||||
*
|
||||
* @param name the internal name of C (see {@link Type#getInternalName()}).
|
||||
* @param outerName the internal name of the class or interface C is a member of (see {@link
|
||||
* Type#getInternalName()}). Must be {@literal null} if C is not the member of a class or
|
||||
* interface (e.g. for local or anonymous classes).
|
||||
* @param innerName the (simple) name of C. Must be {@literal null} for anonymous inner classes.
|
||||
* @param access the access flags of C originally declared in the source code from which this
|
||||
* class was compiled.
|
||||
* @param name the internal name of an inner class (see {@link Type#getInternalName()}).
|
||||
* @param outerName the internal name of the class to which the inner class belongs (see {@link
|
||||
* Type#getInternalName()}). May be {@literal null} for not member classes.
|
||||
* @param innerName the (simple) name of the inner class inside its enclosing class. May be
|
||||
* {@literal null} for anonymous inner classes.
|
||||
* @param access the access flags of the inner class as originally declared in the enclosing
|
||||
* class.
|
||||
*/
|
||||
public void visitInnerClass(
|
||||
final String name, final String outerName, final String innerName, final int access) {
|
||||
|
||||
@@ -842,7 +842,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param value the internal name of the class (see {@link Type#getInternalName()}).
|
||||
* @param value the internal name of the class.
|
||||
* @return the index of a new or already existing class reference item.
|
||||
*/
|
||||
public int newClass(final String value) {
|
||||
@@ -894,8 +894,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||||
* Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the field or method owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param owner the internal name of the field or method owner class.
|
||||
* @param name the name of the field or method.
|
||||
* @param descriptor the descriptor of the field or method.
|
||||
* @return the index of a new or already existing method type reference item.
|
||||
@@ -917,8 +916,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||||
* Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the field or method owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param owner the internal name of the field or method owner class.
|
||||
* @param name the name of the field or method.
|
||||
* @param descriptor the descriptor of the field or method.
|
||||
* @param isInterface true if the owner is an interface.
|
||||
@@ -980,7 +978,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param owner the internal name of the field's owner class (see {@link Type#getInternalName()}).
|
||||
* @param owner the internal name of the field's owner class.
|
||||
* @param name the field's name.
|
||||
* @param descriptor the field's descriptor.
|
||||
* @return the index of a new or already existing field reference item.
|
||||
@@ -994,8 +992,7 @@ public class ClassWriter extends ClassVisitor {
|
||||
* constant pool already contains a similar item. <i>This method is intended for {@link Attribute}
|
||||
* sub classes, and is normally not needed by class generators or adapters.</i>
|
||||
*
|
||||
* @param owner the internal name of the method's owner class (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param owner the internal name of the method's owner class.
|
||||
* @param name the method's name.
|
||||
* @param descriptor the method's descriptor.
|
||||
* @param isInterface {@literal true} if {@code owner} is an interface.
|
||||
@@ -1031,10 +1028,9 @@ public class ClassWriter extends ClassVisitor {
|
||||
* currently being generated by this ClassWriter, which can of course not be loaded since it is
|
||||
* under construction.
|
||||
*
|
||||
* @param type1 the internal name of a class (see {@link Type#getInternalName()}).
|
||||
* @param type2 the internal name of another class (see {@link Type#getInternalName()}).
|
||||
* @return the internal name of the common super class of the two given classes (see {@link
|
||||
* Type#getInternalName()}).
|
||||
* @param type1 the internal name of a class.
|
||||
* @param type2 the internal name of another class.
|
||||
* @return the internal name of the common super class of the two given classes.
|
||||
*/
|
||||
protected String getCommonSuperClass(final String type1, final String type2) {
|
||||
ClassLoader classLoader = getClassLoader();
|
||||
|
||||
@@ -78,15 +78,6 @@ public abstract class FieldVisitor {
|
||||
this.fv = fieldVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The field visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the field visitor to which this visitor must delegate method calls, or {@literal null}.
|
||||
*/
|
||||
public FieldVisitor getDelegate() {
|
||||
return fv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an annotation of the field.
|
||||
*
|
||||
|
||||
@@ -367,12 +367,11 @@ class Frame {
|
||||
typeValue = REFERENCE_KIND | symbolTable.addType(internalName);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid descriptor fragment: " + buffer.substring(elementDescriptorOffset));
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return ((elementDescriptorOffset - offset) << DIM_SHIFT) | typeValue;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid descriptor: " + buffer.substring(offset));
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class Handle {
|
||||
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||
* Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the class that owns the field or method designated by this
|
||||
* handle (see {@link Type#getInternalName()}).
|
||||
* handle.
|
||||
* @param name the name of the field or method designated by this handle.
|
||||
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||
* @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String,
|
||||
@@ -85,7 +85,7 @@ public final class Handle {
|
||||
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||
* Opcodes#H_INVOKEINTERFACE}.
|
||||
* @param owner the internal name of the class that owns the field or method designated by this
|
||||
* handle (see {@link Type#getInternalName()}).
|
||||
* handle.
|
||||
* @param name the name of the field or method designated by this handle.
|
||||
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||
* @param isInterface whether the owner is an interface or not.
|
||||
@@ -118,8 +118,7 @@ public final class Handle {
|
||||
/**
|
||||
* Returns the internal name of the class that owns the field or method designated by this handle.
|
||||
*
|
||||
* @return the internal name of the class that owns the field or method designated by this handle
|
||||
* (see {@link Type#getInternalName()}).
|
||||
* @return the internal name of the class that owns the field or method designated by this handle.
|
||||
*/
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Constructs a new {@link MethodTooLargeException}.
|
||||
*
|
||||
* @param className the internal name of the owner class (see {@link Type#getInternalName()}).
|
||||
* @param className the internal name of the owner class.
|
||||
* @param methodName the name of the method.
|
||||
* @param descriptor the descriptor of the method.
|
||||
* @param codeSize the size of the method's Code attribute, in bytes.
|
||||
@@ -64,7 +64,7 @@ public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||
/**
|
||||
* Returns the internal name of the owner class.
|
||||
*
|
||||
* @return the internal name of the owner class (see {@link Type#getInternalName()}).
|
||||
* @return the internal name of the owner class.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return className;
|
||||
|
||||
@@ -94,16 +94,6 @@ public abstract class MethodVisitor {
|
||||
this.mv = methodVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the method visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
*/
|
||||
public MethodVisitor getDelegate() {
|
||||
return mv;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// Parameters, annotations and non standard attributes
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
@@ -130,7 +120,7 @@ public abstract class MethodVisitor {
|
||||
* @return a visitor to the visit the actual default value of this annotation interface method, or
|
||||
* {@literal null} if this visitor is not interested in visiting this default value. The
|
||||
* 'name' parameters passed to the methods of this annotation visitor are ignored. Moreover,
|
||||
* exactly one visit method must be called on this annotation visitor, followed by visitEnd.
|
||||
* exacly one visit method must be called on this annotation visitor, followed by visitEnd.
|
||||
*/
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
if (mv != null) {
|
||||
@@ -283,17 +273,15 @@ public abstract class MethodVisitor {
|
||||
* @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
|
||||
* frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
|
||||
* Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames.
|
||||
* @param numLocal the number of local variables in the visited frame. Long and double values
|
||||
* count for one variable.
|
||||
* @param numLocal the number of local variables in the visited frame.
|
||||
* @param local the local variable types in this frame. This array must not be modified. Primitive
|
||||
* types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
|
||||
* Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or
|
||||
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a single element).
|
||||
* Reference types are represented by String objects (representing internal names, see {@link
|
||||
* Type#getInternalName()}), and uninitialized types by Label objects (this label designates
|
||||
* the NEW instruction that created this uninitialized value).
|
||||
* @param numStack the number of operand stack elements in the visited frame. Long and double
|
||||
* values count for one stack element.
|
||||
* Reference types are represented by String objects (representing internal names), and
|
||||
* uninitialized types by Label objects (this label designates the NEW instruction that
|
||||
* created this uninitialized value).
|
||||
* @param numStack the number of operand stack elements in the visited frame.
|
||||
* @param stack the operand stack types in this frame. This array must not be modified. Its
|
||||
* content has the same format as the "local" array.
|
||||
* @throws IllegalStateException if a frame is visited just after another one, without any
|
||||
@@ -372,7 +360,7 @@ public abstract class MethodVisitor {
|
||||
|
||||
/**
|
||||
* Visits a type instruction. A type instruction is an instruction that takes the internal name of
|
||||
* a class as parameter (see {@link Type#getInternalName()}).
|
||||
* a class as parameter.
|
||||
*
|
||||
* @param opcode the opcode of the type instruction to be visited. This opcode is either NEW,
|
||||
* ANEWARRAY, CHECKCAST or INSTANCEOF.
|
||||
@@ -564,12 +552,12 @@ public abstract class MethodVisitor {
|
||||
/**
|
||||
* Visits an IINC instruction.
|
||||
*
|
||||
* @param varIndex index of the local variable to be incremented.
|
||||
* @param var index of the local variable to be incremented.
|
||||
* @param increment amount to increment the local variable by.
|
||||
*/
|
||||
public void visitIincInsn(final int varIndex, final int increment) {
|
||||
public void visitIincInsn(final int var, final int increment) {
|
||||
if (mv != null) {
|
||||
mv.visitIincInsn(varIndex, increment);
|
||||
mv.visitIincInsn(var, increment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,9 +643,8 @@ public abstract class MethodVisitor {
|
||||
* @param start the beginning of the exception handler's scope (inclusive).
|
||||
* @param end the end of the exception handler's scope (exclusive).
|
||||
* @param handler the beginning of the exception handler's code.
|
||||
* @param type the internal name of the type of exceptions handled by the handler (see {@link
|
||||
* Type#getInternalName()}), or {@literal null} to catch any exceptions (for "finally"
|
||||
* blocks).
|
||||
* @param type the internal name of the type of exceptions handled by the handler, or {@literal
|
||||
* null} to catch any exceptions (for "finally" blocks).
|
||||
* @throws IllegalArgumentException if one of the labels has already been visited by this visitor
|
||||
* (by the {@link #visitLabel} method).
|
||||
*/
|
||||
|
||||
@@ -284,8 +284,6 @@ public interface Opcodes {
|
||||
int V17 = 0 << 16 | 61;
|
||||
int V18 = 0 << 16 | 62;
|
||||
int V19 = 0 << 16 | 63;
|
||||
int V20 = 0 << 16 | 64;
|
||||
int V21 = 0 << 16 | 65;
|
||||
|
||||
/**
|
||||
* Version flag indicating that the class is using 'preview' features.
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class RecordComponentVisitor {
|
||||
/**
|
||||
* The record visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*/
|
||||
protected RecordComponentVisitor delegate;
|
||||
/*package-private*/ RecordComponentVisitor delegate;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link RecordComponentVisitor}.
|
||||
@@ -83,8 +83,7 @@ public abstract class RecordComponentVisitor {
|
||||
/**
|
||||
* The record visitor to which this visitor must delegate method calls. May be {@literal null}.
|
||||
*
|
||||
* @return the record visitor to which this visitor must delegate method calls, or {@literal
|
||||
* null}.
|
||||
* @return the record visitor to which this visitor must delegate method calls or {@literal null}.
|
||||
*/
|
||||
public RecordComponentVisitor getDelegate() {
|
||||
return delegate;
|
||||
|
||||
@@ -37,7 +37,7 @@ final class RecordComponentWriter extends RecordComponentVisitor {
|
||||
/** The name_index field of the Record attribute. */
|
||||
private final int nameIndex;
|
||||
|
||||
/** The descriptor_index field of the Record attribute. */
|
||||
/** The descriptor_index field of the the Record attribute. */
|
||||
private final int descriptorIndex;
|
||||
|
||||
/**
|
||||
|
||||
@@ -245,7 +245,7 @@ public final class Type {
|
||||
/**
|
||||
* Returns the {@link Type} corresponding to the given internal name.
|
||||
*
|
||||
* @param internalName an internal name (see {@link Type#getInternalName()}).
|
||||
* @param internalName an internal name.
|
||||
* @return the {@link Type} corresponding to the given internal name.
|
||||
*/
|
||||
public static Type getObjectType(final String internalName) {
|
||||
@@ -708,8 +708,8 @@ public final class Type {
|
||||
*
|
||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
*/
|
||||
public int getArgumentsAndReturnSizes() {
|
||||
return getArgumentsAndReturnSizes(getDescriptor());
|
||||
@@ -721,8 +721,8 @@ public final class Type {
|
||||
* @param methodDescriptor a method descriptor.
|
||||
* @return the size of the arguments of the method (plus one for the implicit this argument),
|
||||
* argumentsSize, and the size of its return value, returnSize, packed into a single int i =
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code
|
||||
* i >> 2}, and returnSize to {@code i & 0x03}).
|
||||
*/
|
||||
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
|
||||
int argumentsSize = 1;
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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,9 +18,9 @@ package org.springframework.core;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
|
||||
* using the Java 8 standard reflection mechanism, and falling back to the ASM-based
|
||||
* {@link LocalVariableTableParameterNameDiscoverer} for checking debug information
|
||||
* in the class file (e.g. for classes compiled with earlier Java versions).
|
||||
* using the Java 8 standard reflection mechanism (if available), and falling back
|
||||
* to the ASM-based {@link LocalVariableTableParameterNameDiscoverer} for checking
|
||||
* debug information in the class file.
|
||||
*
|
||||
* <p>If a Kotlin reflection implementation is present,
|
||||
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and
|
||||
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -31,13 +31,11 @@ import org.springframework.lang.Nullable;
|
||||
* {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities
|
||||
* for introspecting parameter names.
|
||||
*
|
||||
* <p>Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
|
||||
* Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
|
||||
* determine interface parameter names without requiring Java 8 -parameters compiler flag.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
* @see StandardReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
-6
@@ -47,18 +47,12 @@ import org.springframework.util.ClassUtils;
|
||||
* caches the ASM discovered information for each introspected Class, in a thread-safe
|
||||
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
|
||||
*
|
||||
* <p>This discoverer variant is effectively superseded by the Java 8 based
|
||||
* {@link StandardReflectionParameterNameDiscoverer} but included as a fallback still
|
||||
* (for code not compiled with the standard "-parameters" compiler flag).
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see StandardReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
+1
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -26,15 +26,10 @@ import org.springframework.lang.Nullable;
|
||||
* {@link ParameterNameDiscoverer} implementation which uses JDK 8's reflection facilities
|
||||
* for introspecting parameter names (based on the "-parameters" compiler flag).
|
||||
*
|
||||
* <p>This is a key element of {@link DefaultParameterNameDiscoverer} where it is being
|
||||
* combined with {@link KotlinReflectionParameterNameDiscoverer} if Kotlin is present.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see java.lang.reflect.Method#getParameters()
|
||||
* @see java.lang.reflect.Parameter#getName()
|
||||
* @see KotlinReflectionParameterNameDiscoverer
|
||||
* @see DefaultParameterNameDiscoverer
|
||||
*/
|
||||
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
|
||||
|
||||
|
||||
+8
-58
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -433,16 +433,6 @@ public abstract class AnnotatedElementUtils {
|
||||
* single annotation and within annotation hierarchies.
|
||||
* <p>This method follows <em>get semantics</em> as described in the
|
||||
* {@linkplain AnnotatedElementUtils class-level javadoc}.
|
||||
* <p><strong>WARNING</strong>: if the supplied {@code containerType} is not
|
||||
* {@code null}, the search will be restricted to supporting only repeatable
|
||||
* annotations whose container is the supplied {@code containerType}. This
|
||||
* prevents the search from finding repeatable annotations declared as
|
||||
* meta-annotations on other types of repeatable annotations. If you need to
|
||||
* support such a use case, favor {@link #getMergedRepeatableAnnotations(AnnotatedElement, Class)}
|
||||
* over this method or alternatively use the {@link MergedAnnotations} API
|
||||
* directly in conjunction with {@link RepeatableContainers} that are
|
||||
* {@linkplain RepeatableContainers#and(Class, Class) composed} to support
|
||||
* multiple repeatable annotation types.
|
||||
* @param element the annotated element (never {@code null})
|
||||
* @param annotationType the annotation type to find (never {@code null})
|
||||
* @param containerType the type of the container that holds the annotations;
|
||||
@@ -735,16 +725,6 @@ public abstract class AnnotatedElementUtils {
|
||||
* single annotation and within annotation hierarchies.
|
||||
* <p>This method follows <em>find semantics</em> as described in the
|
||||
* {@linkplain AnnotatedElementUtils class-level javadoc}.
|
||||
* <p><strong>WARNING</strong>: if the supplied {@code containerType} is not
|
||||
* {@code null}, the search will be restricted to supporting only repeatable
|
||||
* annotations whose container is the supplied {@code containerType}. This
|
||||
* prevents the search from finding repeatable annotations declared as
|
||||
* meta-annotations on other types of repeatable annotations. If you need to
|
||||
* support such a use case, favor {@link #findMergedRepeatableAnnotations(AnnotatedElement, Class)}
|
||||
* over this method or alternatively use the {@link MergedAnnotations} API
|
||||
* directly in conjunction with {@link RepeatableContainers} that are
|
||||
* {@linkplain RepeatableContainers#and(Class, Class) composed} to support
|
||||
* multiple repeatable annotation types.
|
||||
* @param element the annotated element (never {@code null})
|
||||
* @param annotationType the annotation type to find (never {@code null})
|
||||
* @param containerType the type of the container that holds the annotations;
|
||||
@@ -776,23 +756,7 @@ public abstract class AnnotatedElementUtils {
|
||||
private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element,
|
||||
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers;
|
||||
if (containerType == null) {
|
||||
// Invoke RepeatableContainers.of() in order to adhere to the contract of
|
||||
// getMergedRepeatableAnnotations() which states that an IllegalArgumentException
|
||||
// will be thrown if the container cannot be resolved.
|
||||
//
|
||||
// In any case, we use standardRepeatables() in order to support repeatable
|
||||
// annotations on other types of repeatable annotations (i.e., nested repeatable
|
||||
// annotation types).
|
||||
//
|
||||
// See https://github.com/spring-projects/spring-framework/issues/20279
|
||||
RepeatableContainers.of(annotationType, null);
|
||||
repeatableContainers = RepeatableContainers.standardRepeatables();
|
||||
}
|
||||
else {
|
||||
repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
}
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers);
|
||||
}
|
||||
|
||||
@@ -803,23 +767,7 @@ public abstract class AnnotatedElementUtils {
|
||||
private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element,
|
||||
@Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) {
|
||||
|
||||
RepeatableContainers repeatableContainers;
|
||||
if (containerType == null) {
|
||||
// Invoke RepeatableContainers.of() in order to adhere to the contract of
|
||||
// findMergedRepeatableAnnotations() which states that an IllegalArgumentException
|
||||
// will be thrown if the container cannot be resolved.
|
||||
//
|
||||
// In any case, we use standardRepeatables() in order to support repeatable
|
||||
// annotations on other types of repeatable annotations (i.e., nested repeatable
|
||||
// annotation types).
|
||||
//
|
||||
// See https://github.com/spring-projects/spring-framework/issues/20279
|
||||
RepeatableContainers.of(annotationType, null);
|
||||
repeatableContainers = RepeatableContainers.standardRepeatables();
|
||||
}
|
||||
else {
|
||||
repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
}
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
|
||||
return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers);
|
||||
}
|
||||
|
||||
@@ -829,7 +777,8 @@ public abstract class AnnotatedElementUtils {
|
||||
}
|
||||
|
||||
private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() {
|
||||
return Comparator.<MergedAnnotation<A>> comparingInt(MergedAnnotation::getAggregateIndex).reversed();
|
||||
return Comparator.<MergedAnnotation<A>> comparingInt(
|
||||
MergedAnnotation::getAggregateIndex).reversed();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -839,12 +788,13 @@ public abstract class AnnotatedElementUtils {
|
||||
if (!annotation.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
|
||||
return annotation.asAnnotationAttributes(
|
||||
Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapted {@link AnnotatedElement} that holds specific annotations.
|
||||
* Adapted {@link AnnotatedElement} that hold specific annotations.
|
||||
*/
|
||||
private static class AnnotatedElementForAnnotations implements AnnotatedElement {
|
||||
|
||||
|
||||
+4
-3
@@ -32,6 +32,7 @@ import java.util.Set;
|
||||
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -240,7 +241,7 @@ final class AnnotationTypeMapping {
|
||||
mapping.claimedAliases.addAll(aliases);
|
||||
if (mapping.annotation != null) {
|
||||
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
|
||||
mapping.annotation, AnnotationUtils::invokeAnnotationMethod);
|
||||
mapping.annotation, ReflectionUtils::invokeMethod);
|
||||
for (int i = 0; i < mapping.attributes.size(); i++) {
|
||||
if (aliases.contains(mapping.attributes.get(i))) {
|
||||
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
|
||||
@@ -504,7 +505,7 @@ final class AnnotationTypeMapping {
|
||||
if (source == this && metaAnnotationsOnly) {
|
||||
return null;
|
||||
}
|
||||
return AnnotationUtils.invokeAnnotationMethod(source.attributes.get(mappedIndex), source.annotation);
|
||||
return ReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -593,7 +594,7 @@ final class AnnotationTypeMapping {
|
||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
Method attribute = attributes.get(i);
|
||||
Object value1 = AnnotationUtils.invokeAnnotationMethod(attribute, annotation);
|
||||
Object value1 = ReflectionUtils.invokeMethod(attribute, annotation);
|
||||
Object value2;
|
||||
if (extractedValue instanceof TypeMappedAnnotation) {
|
||||
value2 = ((TypeMappedAnnotation<?>) extractedValue).getValue(attribute.getName()).orElse(null);
|
||||
|
||||
@@ -19,10 +19,9 @@ package org.springframework.core.annotation;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -1053,42 +1052,23 @@ public abstract class AnnotationUtils {
|
||||
}
|
||||
try {
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
rethrowAnnotationConfigurationException(ex.getTargetException());
|
||||
throw new IllegalStateException("Could not obtain value for annotation attribute '" +
|
||||
attributeName + "' in " + annotation, ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the supplied annotation attribute {@link Method} on the supplied
|
||||
* {@link Annotation}.
|
||||
* <p>An attempt will first be made to invoke the method via the annotation's
|
||||
* {@link InvocationHandler} (if the annotation instance is a JDK dynamic proxy).
|
||||
* If that fails, an attempt will be made to invoke the method via reflection.
|
||||
* @param method the method to invoke
|
||||
* @param annotation the annotation on which to invoke the method
|
||||
* @return the value returned from the method invocation
|
||||
* @since 5.3.24
|
||||
*/
|
||||
static Object invokeAnnotationMethod(Method method, Object annotation) {
|
||||
if (Proxy.isProxyClass(annotation.getClass())) {
|
||||
try {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
||||
return handler.invoke(annotation, method, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore and fall back to reflection below
|
||||
}
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the supplied throwable is an {@link AnnotationConfigurationException},
|
||||
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
|
||||
|
||||
+13
-2
@@ -86,6 +86,17 @@ final class AttributeMethods {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this instance only contains a single attribute named
|
||||
* {@code value}.
|
||||
* @return {@code true} if there is only a value attribute
|
||||
*/
|
||||
boolean hasOnlyValueAttribute() {
|
||||
return (this.attributeMethods.length == 1 &&
|
||||
MergedAnnotation.VALUE.equals(this.attributeMethods[0].getName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
@@ -98,7 +109,7 @@ final class AttributeMethods {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
get(i).invoke(annotation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
@@ -123,7 +134,7 @@ final class AttributeMethods {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
get(i).invoke(annotation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||
|
||||
+10
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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 org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Strategy used to determine annotations that act as containers for other
|
||||
@@ -38,7 +39,6 @@ import org.springframework.util.ObjectUtils;
|
||||
* <p>To completely disable repeatable support use {@link #none()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2
|
||||
*/
|
||||
public abstract class RepeatableContainers {
|
||||
@@ -101,19 +101,15 @@ public abstract class RepeatableContainers {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RepeatableContainers} instance that uses predefined
|
||||
* repeatable and container types.
|
||||
* @param repeatable the contained repeatable annotation type
|
||||
* @param container the container annotation type or {@code null}. If specified,
|
||||
* Create a {@link RepeatableContainers} instance that uses a defined
|
||||
* container and repeatable type.
|
||||
* @param repeatable the contained repeatable annotation
|
||||
* @param container the container annotation or {@code null}. If specified,
|
||||
* this annotation must declare a {@code value} attribute returning an array
|
||||
* of repeatable annotations. If not specified, the container will be
|
||||
* deduced by inspecting the {@code @Repeatable} annotation on
|
||||
* {@code repeatable}.
|
||||
* @return a {@link RepeatableContainers} instance
|
||||
* @throws IllegalArgumentException if the supplied container type is
|
||||
* {@code null} and the annotation type is not a repeatable annotation
|
||||
* @throws AnnotationConfigurationException if the supplied container type
|
||||
* is not a properly configured container for a repeatable annotation
|
||||
*/
|
||||
public static RepeatableContainers of(
|
||||
Class<? extends Annotation> repeatable, @Nullable Class<? extends Annotation> container) {
|
||||
@@ -152,7 +148,7 @@ public abstract class RepeatableContainers {
|
||||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
|
||||
if (method != null) {
|
||||
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(method, annotation);
|
||||
return (Annotation[]) ReflectionUtils.invokeMethod(method, annotation);
|
||||
}
|
||||
return super.findRepeatedAnnotations(annotation);
|
||||
}
|
||||
@@ -166,8 +162,8 @@ public abstract class RepeatableContainers {
|
||||
|
||||
private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
|
||||
Method method = methods.get(MergedAnnotation.VALUE);
|
||||
if (method != null) {
|
||||
if (methods.hasOnlyValueAttribute()) {
|
||||
Method method = methods.get(0);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType.isArray()) {
|
||||
Class<?> componentType = returnType.getComponentType();
|
||||
@@ -239,7 +235,7 @@ public abstract class RepeatableContainers {
|
||||
@Nullable
|
||||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||
if (this.container.isAssignableFrom(annotation.annotationType())) {
|
||||
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(this.valueMethod, annotation);
|
||||
return (Annotation[]) ReflectionUtils.invokeMethod(this.valueMethod, annotation);
|
||||
}
|
||||
return super.findRepeatedAnnotations(annotation);
|
||||
}
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
|
||||
for (int i = 0; i < this.attributes.size(); i++) {
|
||||
Method attribute = this.attributes.get(i);
|
||||
Object thisValue = getAttributeValue(attribute);
|
||||
Object otherValue = AnnotationUtils.invokeAnnotationMethod(attribute, other);
|
||||
Object otherValue = ReflectionUtils.invokeMethod(attribute, other);
|
||||
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+8
-8
@@ -33,6 +33,7 @@ import java.util.function.Predicate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link MergedAnnotation} that adapts attributes from a root annotation by
|
||||
@@ -42,9 +43,9 @@ import org.springframework.util.ClassUtils;
|
||||
* {@code BiFunction}. This allows various different annotation models to be
|
||||
* supported by the same class. For example, the attributes source might be an
|
||||
* actual {@link Annotation} instance where methods on the annotation instance
|
||||
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
|
||||
* to extract values. Similarly, the source could be a simple {@link Map} with
|
||||
* values extracted using {@link Map#get(Object)}.
|
||||
* are {@linkplain ReflectionUtils#invokeMethod(Method, Object) invoked} to extract
|
||||
* values. Equally, the source could be a simple {@link Map} with values
|
||||
* extracted using {@link Map#get(Object)}.
|
||||
*
|
||||
* <p>Extracted root attribute values must be compatible with the attribute
|
||||
* return type, namely:
|
||||
@@ -433,7 +434,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
}
|
||||
if (value == null) {
|
||||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||
value = AnnotationUtils.invokeAnnotationMethod(attribute, this.mapping.getAnnotation());
|
||||
value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -554,7 +555,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
|
||||
private ValueExtractor getValueExtractor(Object value) {
|
||||
if (value instanceof Annotation) {
|
||||
return AnnotationUtils::invokeAnnotationMethod;
|
||||
return ReflectionUtils::invokeMethod;
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
return TypeMappedAnnotation::extractFromMap;
|
||||
@@ -614,8 +615,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
|
||||
return new TypeMappedAnnotation<>(
|
||||
mappings.get(0), null, source, annotation, AnnotationUtils::invokeAnnotationMethod, 0);
|
||||
return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
|
||||
}
|
||||
|
||||
static <A extends Annotation> MergedAnnotation<A> of(
|
||||
@@ -649,7 +649,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
int aggregateIndex, IntrospectionFailureLogger logger) {
|
||||
|
||||
return createIfPossible(mapping, source, annotation,
|
||||
AnnotationUtils::invokeAnnotationMethod, aggregateIndex, logger);
|
||||
ReflectionUtils::invokeMethod, aggregateIndex, logger);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -49,7 +49,7 @@ public class ConversionFailedException extends ConversionException {
|
||||
@Nullable Object value, Throwable cause) {
|
||||
|
||||
super("Failed to convert from type [" + sourceType + "] to type [" + targetType +
|
||||
"] for value [" + ObjectUtils.nullSafeConciseToString(value) + "]", cause);
|
||||
"] for value '" + ObjectUtils.nullSafeToString(value) + "'", cause);
|
||||
this.sourceType = sourceType;
|
||||
this.targetType = targetType;
|
||||
this.value = value;
|
||||
|
||||
@@ -177,7 +177,7 @@ public abstract class DataBufferUtils {
|
||||
if (options.length > 0) {
|
||||
for (OpenOption option : options) {
|
||||
Assert.isTrue(!(option == StandardOpenOption.APPEND || option == StandardOpenOption.WRITE),
|
||||
() -> "'" + option + "' not allowed");
|
||||
"'" + option + "' not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ public abstract class DataBufferUtils {
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
Assert.state(this.longestDelimiter != NO_DELIMITER, "'delimiter' not set");
|
||||
Assert.state(this.longestDelimiter != NO_DELIMITER, "Illegal state!");
|
||||
return this.longestDelimiter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for formatting and logging messages.
|
||||
@@ -79,7 +78,7 @@ public abstract class LogFormatUtils {
|
||||
result = ObjectUtils.nullSafeToString(ex);
|
||||
}
|
||||
if (maxLength != -1) {
|
||||
result = StringUtils.truncate(result, maxLength);
|
||||
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
|
||||
}
|
||||
if (replaceNewlinesAndControlCharacters) {
|
||||
result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -22,13 +22,12 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple log message type for use with Commons Logging, allowing for convenient
|
||||
* lazy resolution of a given {@link Supplier} instance (typically bound to a lambda
|
||||
* expression) or a printf-style format string ({@link String#format}) in its
|
||||
* {@link #toString()}.
|
||||
* A simple log message type for use with Commons Logging, allowing
|
||||
* for convenient lazy resolution of a given {@link Supplier} instance
|
||||
* (typically bound to a Java 8 lambda expression) or a printf-style
|
||||
* format string ({@link String#format}) in its {@link #toString()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.2
|
||||
* @see #of(Supplier)
|
||||
* @see #format(String, Object)
|
||||
@@ -78,7 +77,7 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
/**
|
||||
* Build a lazily resolving message from the given supplier.
|
||||
* @param supplier the supplier (typically bound to a lambda expression)
|
||||
* @param supplier the supplier (typically bound to a Java 8 lambda expression)
|
||||
* @see #toString()
|
||||
*/
|
||||
public static LogMessage of(Supplier<? extends CharSequence> supplier) {
|
||||
@@ -88,68 +87,63 @@ public abstract class LogMessage implements CharSequence {
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and argument.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the argument (can be {@code null})
|
||||
* @param arg1 the argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, @Nullable Object arg1) {
|
||||
public static LogMessage format(String format, Object arg1) {
|
||||
return new FormatMessage1(format, arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2) {
|
||||
public static LogMessage format(String format, Object arg1, Object arg2) {
|
||||
return new FormatMessage2(format, arg1, arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @param arg3 the third argument (can be {@code null})
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3) {
|
||||
return new FormatMessage3(format, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument (can be {@code null})
|
||||
* @param arg2 the second argument (can be {@code null})
|
||||
* @param arg3 the third argument (can be {@code null})
|
||||
* @param arg4 the fourth argument (can be {@code null})
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @param arg4 the fourth argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3,
|
||||
@Nullable Object arg4) {
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
return new FormatMessage4(format, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a lazily formatted message from the given format string and varargs.
|
||||
* <p>This varargs {@code format()} variant may be costly. You should therefore
|
||||
* use the individual argument variants whenever possible:
|
||||
* {@link #format(String, Object)}, {@link #format(String, Object, Object)}, etc.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param args the varargs array (can be {@code null} and can contain {@code null}
|
||||
* elements)
|
||||
* @param args the varargs array (costly, prefer individual arguments)
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, @Nullable Object... args) {
|
||||
public static LogMessage format(String format, Object... args) {
|
||||
return new FormatMessageX(format, args);
|
||||
}
|
||||
|
||||
|
||||
private static final class SupplierMessage extends LogMessage {
|
||||
|
||||
private final Supplier<? extends CharSequence> supplier;
|
||||
private Supplier<? extends CharSequence> supplier;
|
||||
|
||||
SupplierMessage(Supplier<? extends CharSequence> supplier) {
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
@@ -176,10 +170,9 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage1 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
FormatMessage1(String format, @Nullable Object arg1) {
|
||||
FormatMessage1(String format, Object arg1) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
}
|
||||
@@ -193,13 +186,11 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage2 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
FormatMessage2(String format, @Nullable Object arg1, @Nullable Object arg2) {
|
||||
FormatMessage2(String format, Object arg1, Object arg2) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -214,16 +205,13 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage3 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
@Nullable
|
||||
private final Object arg3;
|
||||
|
||||
FormatMessage3(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
|
||||
FormatMessage3(String format, Object arg1, Object arg2, Object arg3) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -239,20 +227,15 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessage4 extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object arg1;
|
||||
|
||||
@Nullable
|
||||
private final Object arg2;
|
||||
|
||||
@Nullable
|
||||
private final Object arg3;
|
||||
|
||||
@Nullable
|
||||
private final Object arg4;
|
||||
|
||||
FormatMessage4(String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3,
|
||||
@Nullable Object arg4) {
|
||||
FormatMessage4(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
@@ -269,10 +252,9 @@ public abstract class LogMessage implements CharSequence {
|
||||
|
||||
private static final class FormatMessageX extends FormatMessage {
|
||||
|
||||
@Nullable
|
||||
private final Object[] args;
|
||||
|
||||
FormatMessageX(String format, @Nullable Object... args) {
|
||||
FormatMessageX(String format, Object... args) {
|
||||
super(format);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
+1
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -90,9 +90,4 @@ public class ExecutorServiceAdapter extends AbstractExecutorService {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @Override on JDK 19
|
||||
public void close() {
|
||||
// no-op in order to avoid container-triggered shutdown call which would lead to exception logging
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -85,7 +85,7 @@ public abstract class CollectionUtils {
|
||||
* @see #newLinkedHashMap(int)
|
||||
*/
|
||||
public static <K, V> HashMap<K, V> newHashMap(int expectedSize) {
|
||||
return new HashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
|
||||
return new HashMap<>((int) (expectedSize / DEFAULT_LOAD_FACTOR), DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,11 +102,7 @@ public abstract class CollectionUtils {
|
||||
* @see #newHashMap(int)
|
||||
*/
|
||||
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) {
|
||||
return new LinkedHashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
private static int computeMapInitialCapacity(int expectedSize) {
|
||||
return (int) Math.ceil(expectedSize / (double) DEFAULT_LOAD_FACTOR);
|
||||
return new LinkedHashMap<>((int) (expectedSize / DEFAULT_LOAD_FACTOR), DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -64,7 +64,7 @@ public class InstanceFilter<T> {
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the specified {@code instance} matches this filter.
|
||||
* Determine if the specified {code instance} matches this filter.
|
||||
*/
|
||||
public boolean match(T instance) {
|
||||
Assert.notNull(instance, "Instance to match must not be null");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,8 @@
|
||||
package org.springframework.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
@@ -658,7 +653,6 @@ public abstract class ObjectUtils {
|
||||
* Returns a {@code "null"} String if {@code obj} is {@code null}.
|
||||
* @param obj the object to build a String representation for
|
||||
* @return a String representation of {@code obj}
|
||||
* @see #nullSafeConciseToString(Object)
|
||||
*/
|
||||
public static String nullSafeToString(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
@@ -914,73 +908,4 @@ public abstract class ObjectUtils {
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a null-safe, concise string representation of the supplied object
|
||||
* as described below.
|
||||
* <p>Favor this method over {@link #nullSafeToString(Object)} when you need
|
||||
* the length of the generated string to be limited.
|
||||
* <p>Returns:
|
||||
* <ul>
|
||||
* <li>{@code "null"} if {@code obj} is {@code null}</li>
|
||||
* <li>{@linkplain Class#getName() Class name} if {@code obj} is a {@link Class}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a {@link String} or {@link CharSequence}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a <em>simple type</em> whose {@code toString()} method returns
|
||||
* a non-null value.</li>
|
||||
* <li>Otherwise, a string representation of the object's type name concatenated
|
||||
* with {@code @} and a hex string form of the object's identity hash code</li>
|
||||
* </ul>
|
||||
* <p>In the context of this method, a <em>simple type</em> is any of the following:
|
||||
* a primitive wrapper (excluding {@link Void}), an {@link Enum}, a {@link Number},
|
||||
* a {@link Date}, a {@link Temporal}, a {@link URI}, a {@link URL}, or a {@link Locale}.
|
||||
* @param obj the object to build a string representation for
|
||||
* @return a concise string representation of the supplied object
|
||||
* @since 5.3.27
|
||||
* @see #nullSafeToString(Object)
|
||||
* @see StringUtils#truncate(CharSequence)
|
||||
*/
|
||||
public static String nullSafeConciseToString(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
return "null";
|
||||
}
|
||||
if (obj instanceof Class<?>) {
|
||||
return ((Class<?>) obj).getName();
|
||||
}
|
||||
if (obj instanceof CharSequence) {
|
||||
return StringUtils.truncate((CharSequence) obj);
|
||||
}
|
||||
Class<?> type = obj.getClass();
|
||||
if (isSimpleValueType(type)) {
|
||||
String str = obj.toString();
|
||||
if (str != null) {
|
||||
return StringUtils.truncate(str);
|
||||
}
|
||||
}
|
||||
return type.getTypeName() + "@" + getIdentityHexString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of {@link org.springframework.beans.BeanUtils#isSimpleValueType(Class)}.
|
||||
* <p>Check if the given type represents a "simple" value type: a primitive or
|
||||
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
|
||||
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
|
||||
* <p>{@code Void} and {@code void} are not considered simple value types.
|
||||
* @param type the type to check
|
||||
* @return whether the given type represents a "simple" value type
|
||||
*/
|
||||
private static boolean isSimpleValueType(Class<?> type) {
|
||||
return (Void.class != type && void.class != type &&
|
||||
(ClassUtils.isPrimitiveOrWrapper(type) ||
|
||||
Enum.class.isAssignableFrom(type) ||
|
||||
CharSequence.class.isAssignableFrom(type) ||
|
||||
Number.class.isAssignableFrom(type) ||
|
||||
Date.class.isAssignableFrom(type) ||
|
||||
Temporal.class.isAssignableFrom(type) ||
|
||||
URI.class == type ||
|
||||
URL.class == type ||
|
||||
Locale.class == type ||
|
||||
Class.class == type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -75,10 +75,6 @@ public abstract class StringUtils {
|
||||
|
||||
private static final char EXTENSION_SEPARATOR = '.';
|
||||
|
||||
private static final int DEFAULT_TRUNCATION_THRESHOLD = 100;
|
||||
|
||||
private static final String TRUNCATION_SUFFIX = " (truncated)...";
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// General convenience methods for working with Strings
|
||||
@@ -1392,40 +1388,4 @@ public abstract class StringUtils {
|
||||
return arrayToDelimitedString(arr, ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the supplied {@link CharSequence}.
|
||||
* <p>Delegates to {@link #truncate(CharSequence, int)}, supplying {@code 100}
|
||||
* as the threshold.
|
||||
* @param charSequence the {@code CharSequence} to truncate
|
||||
* @return a truncated string, or a string representation of the original
|
||||
* {@code CharSequence} if its length does not exceed the threshold
|
||||
* @since 5.3.27
|
||||
*/
|
||||
public static String truncate(CharSequence charSequence) {
|
||||
return truncate(charSequence, DEFAULT_TRUNCATION_THRESHOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the supplied {@link CharSequence}.
|
||||
* <p>If the length of the {@code CharSequence} is greater than the threshold,
|
||||
* this method returns a {@linkplain CharSequence#subSequence(int, int)
|
||||
* subsequence} of the {@code CharSequence} (up to the threshold) appended
|
||||
* with the suffix {@code " (truncated)..."}. Otherwise, this method returns
|
||||
* {@code charSequence.toString()}.
|
||||
* @param charSequence the {@code CharSequence} to truncate
|
||||
* @param threshold the maximum length after which to truncate; must be a
|
||||
* positive number
|
||||
* @return a truncated string, or a string representation of the original
|
||||
* {@code CharSequence} if its length does not exceed the threshold
|
||||
* @since 5.3.27
|
||||
*/
|
||||
public static String truncate(CharSequence charSequence, int threshold) {
|
||||
Assert.isTrue(threshold > 0,
|
||||
() -> "Truncation threshold must be a positive number: " + threshold);
|
||||
if (charSequence.length() > threshold) {
|
||||
return charSequence.subSequence(0, threshold) + TRUNCATION_SUFFIX;
|
||||
}
|
||||
return charSequence.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import org.junit.platform.suite.api.IncludeClassNamePatterns;
|
||||
import org.junit.platform.suite.api.SelectPackages;
|
||||
import org.junit.platform.suite.api.Suite;
|
||||
|
||||
/**
|
||||
* JUnit Platform based test suite for tests in the spring-core module.
|
||||
*
|
||||
* <p><strong>This suite is only intended to be used manually within an IDE.</strong>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@Suite
|
||||
@SelectPackages({"org.springframework.core", "org.springframework.util"})
|
||||
@IncludeClassNamePatterns(".*Tests?$")
|
||||
class SpringCoreTestSuite {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -34,6 +34,7 @@ import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
@@ -155,7 +156,7 @@ class CollectionFactoryTests {
|
||||
@Test
|
||||
void createApproximateCollectionFromEmptyHashSet() {
|
||||
Collection<String> set = createApproximateCollection(new HashSet<String>(), 2);
|
||||
assertThat(set).isEmpty();
|
||||
Assertions.assertThat(set).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
private final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
@Test
|
||||
@@ -162,23 +162,23 @@ class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
Constructor<?> ctor = clazz.getDeclaredConstructor(Object.class);
|
||||
String[] names = discoverer.getParameterNames(ctor);
|
||||
assertThat(names).hasSize(1);
|
||||
assertThat(names.length).isEqualTo(1);
|
||||
assertThat(names[0]).isEqualTo("key");
|
||||
|
||||
ctor = clazz.getDeclaredConstructor(Object.class, Object.class);
|
||||
names = discoverer.getParameterNames(ctor);
|
||||
assertThat(names).hasSize(2);
|
||||
assertThat(names.length).isEqualTo(2);
|
||||
assertThat(names[0]).isEqualTo("key");
|
||||
assertThat(names[1]).isEqualTo("value");
|
||||
|
||||
Method m = clazz.getMethod("generifiedStaticMethod", Object.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names).hasSize(1);
|
||||
assertThat(names.length).isEqualTo(1);
|
||||
assertThat(names[0]).isEqualTo("param");
|
||||
|
||||
m = clazz.getMethod("generifiedMethod", Object.class, long.class, Object.class, Object.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names).hasSize(4);
|
||||
assertThat(names.length).isEqualTo(4);
|
||||
assertThat(names[0]).isEqualTo("param");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("key");
|
||||
@@ -186,21 +186,21 @@ class LocalVariableTableParameterNameDiscovererTests {
|
||||
|
||||
m = clazz.getMethod("voidStaticMethod", Object.class, long.class, int.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names).hasSize(3);
|
||||
assertThat(names.length).isEqualTo(3);
|
||||
assertThat(names[0]).isEqualTo("obj");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("i");
|
||||
|
||||
m = clazz.getMethod("nonVoidStaticMethod", Object.class, long.class, int.class);
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names).hasSize(3);
|
||||
assertThat(names.length).isEqualTo(3);
|
||||
assertThat(names[0]).isEqualTo("obj");
|
||||
assertThat(names[1]).isEqualTo("x");
|
||||
assertThat(names[2]).isEqualTo("i");
|
||||
|
||||
m = clazz.getMethod("getDate");
|
||||
names = discoverer.getParameterNames(m);
|
||||
assertThat(names).isEmpty();
|
||||
assertThat(names.length).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Disabled("Ignored because Ubuntu packages OpenJDK with debug symbols enabled. See SPR-8078.")
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -43,10 +43,11 @@ import static org.junit.jupiter.api.condition.JRE.JAVA_14;
|
||||
@DisabledForJreRange(min = JAVA_14)
|
||||
public class SpringCoreBlockHoundIntegrationTests {
|
||||
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
static void setUp() {
|
||||
BlockHound.builder()
|
||||
.with(new ReactorBlockHoundIntegration()) // Reactor non-blocking thread predicate
|
||||
.with(new ReactorBlockHoundIntegration()) // Reactor non-blocking thread predicate
|
||||
.with(new ReactiveAdapterRegistry.SpringCoreBlockHoundIntegration())
|
||||
.install();
|
||||
}
|
||||
|
||||
-47
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -78,7 +77,6 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.asArray;
|
||||
* @see AnnotationUtilsTests
|
||||
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
|
||||
* @see ComposedRepeatableAnnotationsTests
|
||||
* @see NestedRepeatableAnnotationsTests
|
||||
*/
|
||||
class AnnotatedElementUtilsTests {
|
||||
|
||||
@@ -910,31 +908,6 @@ class AnnotatedElementUtilsTests {
|
||||
assertThat(annotation.value()).containsExactly("FromValueAttributeMeta");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.3.25
|
||||
*/
|
||||
@Test // gh-29685
|
||||
void getMergedRepeatableAnnotationsWithContainerWithMultipleAttributes() {
|
||||
Set<StandardRepeatableWithContainerWithMultipleAttributes> repeatableAnnotations =
|
||||
AnnotatedElementUtils.getMergedRepeatableAnnotations(
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardRepeatableWithContainerWithMultipleAttributes.class);
|
||||
assertThat(repeatableAnnotations).map(StandardRepeatableWithContainerWithMultipleAttributes::value)
|
||||
.containsExactly("a", "b");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.3.25
|
||||
*/
|
||||
@Test // gh-29685
|
||||
void findMergedRepeatableAnnotationsWithContainerWithMultipleAttributes() {
|
||||
Set<StandardRepeatableWithContainerWithMultipleAttributes> repeatableAnnotations =
|
||||
AnnotatedElementUtils.findMergedRepeatableAnnotations(
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardRepeatableWithContainerWithMultipleAttributes.class);
|
||||
assertThat(repeatableAnnotations).map(StandardRepeatableWithContainerWithMultipleAttributes::value)
|
||||
.containsExactly("a", "b");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -1584,24 +1557,4 @@ class AnnotatedElementUtilsTests {
|
||||
static class ValueAttributeMetaMetaClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainerWithMultipleAttributes {
|
||||
|
||||
StandardRepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainerWithMultipleAttributes.class)
|
||||
@interface StandardRepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("a")
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("b")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -58,6 +58,24 @@ class AttributeMethodsTests {
|
||||
assertThat(getAll(methods)).flatExtracting(Method::getName).containsExactly("intValue", "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlyValueAttributeReturnsTrue() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(ValueOnly.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlySingleNonValueAttributeReturnsFalse() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(NonValueOnly.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasOnlyValueAttributeWhenHasOnlyMultipleAttributesIncludingValueReturnsFalse() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||
assertThat(methods.hasOnlyValueAttribute()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexOfNameReturnsIndex() {
|
||||
AttributeMethods methods = AttributeMethods.forAnnotationType(MultipleAttributes.class);
|
||||
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for various ways to search for repeatable annotations that are
|
||||
* nested (i.e., repeatable annotations used as meta-annotations on other
|
||||
* repeatable annotations).
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.3.24
|
||||
* @see https://github.com/spring-projects/spring-framework/issues/20279
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class NestedRepeatableAnnotationsTests {
|
||||
|
||||
@Nested
|
||||
class SingleRepeatableAnnotationTests {
|
||||
|
||||
private final Method method = ReflectionUtils.findMethod(getClass(), "annotatedMethod");
|
||||
|
||||
@Test
|
||||
void streamRepeatableAnnotations_MergedAnnotationsApi() {
|
||||
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY)
|
||||
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Merged, so we expect to find @A once with its value coming from @B(5).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findMergedRepeatableAnnotations_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class);
|
||||
// Merged, so we expect to find @A once with its value coming from @B(5).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
|
||||
// Merged, so we expect to find @A once with its value coming from @B(5).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
|
||||
// Merged, so we expect to find @A once with its value coming from @B(5).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void getRepeatableAnnotations_AnnotationUtils() {
|
||||
Set<A> annotations = AnnotationUtils.getRepeatableAnnotations(method, A.class);
|
||||
// Not merged, so we expect to find @A once with the default value of 0.
|
||||
// @A will actually be found twice, but we have Set semantics here.
|
||||
assertThat(annotations).extracting(A::value).containsExactly(0);
|
||||
}
|
||||
|
||||
@B(5)
|
||||
void annotatedMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MultipleRepeatableAnnotationsTests {
|
||||
|
||||
private final Method method = ReflectionUtils.findMethod(getClass(), "annotatedMethod");
|
||||
|
||||
@Test
|
||||
void streamRepeatableAnnotationsWithStandardRepeatables_MergedAnnotationsApi() {
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables();
|
||||
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers)
|
||||
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamRepeatableAnnotationsWithExplicitRepeatables_MergedAnnotationsApi() {
|
||||
RepeatableContainers repeatableContainers =
|
||||
RepeatableContainers.of(A.class, A.Container.class).and(B.Container.class, B.class);
|
||||
Set<A> annotations = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers)
|
||||
.stream(A.class).collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class);
|
||||
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, A.class, A.Container.class);
|
||||
// When findMergedRepeatableAnnotations(...) is invoked with an explicit container
|
||||
// type, it uses RepeatableContainers.of(...) which limits the repeatable annotation
|
||||
// support to a single container type.
|
||||
//
|
||||
// In this test case, we are therefore limiting the support to @A.Container, which
|
||||
// means that @B.Container is unsupported and effectively ignored as a repeatable
|
||||
// container type.
|
||||
//
|
||||
// Long story, short: the search doesn't find anything.
|
||||
assertThat(annotations).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedRepeatableAnnotationsWithStandardRepeatables_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class);
|
||||
// Merged, so we expect to find @A twice with values coming from @B(5) and @B(10).
|
||||
assertThat(annotations).extracting(A::value).containsExactly(5, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedRepeatableAnnotationsWithExplicitContainer_AnnotatedElementUtils() {
|
||||
Set<A> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, A.class, A.Container.class);
|
||||
// When getMergedRepeatableAnnotations(...) is invoked with an explicit container
|
||||
// type, it uses RepeatableContainers.of(...) which limits the repeatable annotation
|
||||
// support to a single container type.
|
||||
//
|
||||
// In this test case, we are therefore limiting the support to @A.Container, which
|
||||
// means that @B.Container is unsupported and effectively ignored as a repeatable
|
||||
// container type.
|
||||
//
|
||||
// Long story, short: the search doesn't find anything.
|
||||
assertThat(annotations).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void getRepeatableAnnotations_AnnotationUtils() {
|
||||
Set<A> annotations = AnnotationUtils.getRepeatableAnnotations(method, A.class);
|
||||
// Not merged, so we expect to find a single @A with default value of 0.
|
||||
// @A will actually be found twice, but we have Set semantics here.
|
||||
assertThat(annotations).extracting(A::value).containsExactly(0);
|
||||
}
|
||||
|
||||
@B(5)
|
||||
@B(10)
|
||||
void annotatedMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Repeatable(A.Container.class)
|
||||
@interface A {
|
||||
|
||||
int value() default 0;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@interface Container {
|
||||
A[] value();
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Repeatable(B.Container.class)
|
||||
@A
|
||||
@A
|
||||
@interface B {
|
||||
|
||||
@AliasFor(annotation = A.class)
|
||||
int value();
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@interface Container {
|
||||
B[] value();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+146
-157
@@ -20,9 +20,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -33,175 +31,193 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link RepeatableContainers}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class RepeatableContainersTests {
|
||||
|
||||
@Nested
|
||||
class StandardRepeatableContainersTests {
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
NonRepeatableTestCase.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenSingleReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
SingleStandardRepeatableTestCase.class, StandardRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
ExplicitRepeatablesTestCase.class, ExplicitContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWithContainerWithMultipleAttributes() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.standardRepeatables(),
|
||||
StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class,
|
||||
StandardContainerWithMultipleAttributes.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardRepeatablesWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithNonRepeatable.class,
|
||||
NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ExplicitRepeatableContainerTests {
|
||||
@Test
|
||||
void standardRepeatablesWhenSingleReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(),
|
||||
WithSingleStandardRepeatable.class, StandardRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
NonRepeatableTestCase.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithStandardRepeatables.class,
|
||||
StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenStandardRepeatableContainerReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
@Test
|
||||
void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.standardRepeatables(), WithExplicitRepeatables.class,
|
||||
ExplicitContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
|
||||
ExplicitRepeatablesTestCase.class, ExplicitContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenNonRepeatableReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithNonRepeatable.class, NonRepeatable.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullDeducesContainer() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.of(StandardRepeatable.class, null),
|
||||
StandardRepeatablesTestCase.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenStandardRepeatableContainerReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithStandardRepeatables.class, StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenHasNoValueThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class))
|
||||
.withMessageContaining("Invalid declaration of container type [%s] for repeatable annotation [%s]",
|
||||
InvalidNoValue.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenContainerReturnsRepeats() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class),
|
||||
WithExplicitRepeatables.class, ExplicitContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenValueIsNotArrayThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class))
|
||||
.withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]",
|
||||
InvalidNotArray.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenHasNoValueThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidNoValue.class))
|
||||
.withMessageContaining("Invalid declaration of container type ["
|
||||
+ InvalidNoValue.class.getName()
|
||||
+ "] for repeatable annotation ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class)
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class))
|
||||
.withMessage("Container type [%s] must declare a 'value' attribute for an array of type [%s]",
|
||||
InvalidWrongArrayType.class.getName(), ExplicitRepeatable.class.getName());
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenValueIsNotArrayThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidNotArray.class))
|
||||
.withMessage("Container type ["
|
||||
+ InvalidNotArray.class.getName()
|
||||
+ "] must declare a 'value' attribute for an array of type ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenAnnotationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RepeatableContainers.of(null, null))
|
||||
.withMessage("Repeatable must not be null");
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenValueIsArrayOfWrongTypeThrowsException() {
|
||||
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, InvalidWrongArrayType.class))
|
||||
.withMessage("Container type ["
|
||||
+ InvalidWrongArrayType.class.getName()
|
||||
+ "] must declare a 'value' attribute for an array of type ["
|
||||
+ ExplicitRepeatable.class.getName() + "]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RepeatableContainers.of(ExplicitRepeatable.class, null))
|
||||
.withMessage("Annotation type must be a repeatable annotation: failed to resolve container type for %s",
|
||||
ExplicitRepeatable.class.getName());
|
||||
}
|
||||
@Test
|
||||
void ofExplicitWhenAnnotationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
RepeatableContainers.of(null, null))
|
||||
.withMessage("Repeatable must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullDeducesContainer() {
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.of(StandardRepeatable.class, null),
|
||||
WithStandardRepeatables.class, StandardContainer.class);
|
||||
assertThat(values).containsExactly("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofExplicitWhenContainerIsNullAndNotRepeatableThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
RepeatableContainers.of(ExplicitRepeatable.class, null))
|
||||
.withMessage("Annotation type must be a repeatable annotation: " +
|
||||
"failed to resolve container type for " +
|
||||
ExplicitRepeatable.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardAndExplicitReturnsRepeats() {
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables()
|
||||
.and(ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers, StandardRepeatablesTestCase.class, StandardContainer.class))
|
||||
.containsExactly("a", "b");
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers, ExplicitRepeatablesTestCase.class, ExplicitContainer.class))
|
||||
.containsExactly("a", "b");
|
||||
RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables().and(
|
||||
ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers,
|
||||
WithStandardRepeatables.class, StandardContainer.class)).containsExactly(
|
||||
"a", "b");
|
||||
assertThat(findRepeatedAnnotationValues(repeatableContainers,
|
||||
WithExplicitRepeatables.class, ExplicitContainer.class)).containsExactly(
|
||||
"a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noneAlwaysReturnsNull() {
|
||||
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.none(), StandardRepeatablesTestCase.class,
|
||||
StandardContainer.class);
|
||||
Object[] values = findRepeatedAnnotationValues(
|
||||
RepeatableContainers.none(), WithStandardRepeatables.class,
|
||||
StandardContainer.class);
|
||||
assertThat(values).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashcode() {
|
||||
RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class);
|
||||
RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class);
|
||||
RepeatableContainers c1 = RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class);
|
||||
RepeatableContainers c2 = RepeatableContainers.of(ExplicitRepeatable.class,
|
||||
ExplicitContainer.class);
|
||||
RepeatableContainers c3 = RepeatableContainers.standardRepeatables();
|
||||
RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(c1).hasSameHashCodeAs(c2);
|
||||
RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(
|
||||
ExplicitContainer.class, ExplicitRepeatable.class);
|
||||
assertThat(c1.hashCode()).isEqualTo(c2.hashCode());
|
||||
assertThat(c1).isEqualTo(c1).isEqualTo(c2);
|
||||
assertThat(c1).isNotEqualTo(c3).isNotEqualTo(c4);
|
||||
}
|
||||
|
||||
|
||||
private static Object[] findRepeatedAnnotationValues(RepeatableContainers containers,
|
||||
private Object[] findRepeatedAnnotationValues(RepeatableContainers containers,
|
||||
Class<?> element, Class<? extends Annotation> annotationType) {
|
||||
Annotation[] annotations = containers.findRepeatedAnnotations(element.getAnnotation(annotationType));
|
||||
Annotation[] annotations = containers.findRepeatedAnnotations(
|
||||
element.getAnnotation(annotationType));
|
||||
return extractValues(annotations);
|
||||
}
|
||||
|
||||
private static Object[] extractValues(Annotation[] annotations) {
|
||||
if (annotations == null) {
|
||||
return null;
|
||||
private Object[] extractValues(Annotation[] annotations) {
|
||||
try {
|
||||
if (annotations == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] result = new String[annotations.length];
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
result[i] = annotations[i].annotationType().getMethod("value").invoke(
|
||||
annotations[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return Arrays.stream(annotations).map(AnnotationUtils::getValue).toArray(Object[]::new);
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface NonRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainer.class)
|
||||
@interface StandardRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainer {
|
||||
|
||||
@@ -209,8 +225,7 @@ class RepeatableContainersTests {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainer.class)
|
||||
@interface StandardRepeatable {
|
||||
@interface ExplicitRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
@@ -221,12 +236,6 @@ class RepeatableContainersTests {
|
||||
ExplicitRepeatable[] value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ExplicitRepeatable {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface InvalidNoValue {
|
||||
}
|
||||
@@ -244,40 +253,20 @@ class RepeatableContainersTests {
|
||||
}
|
||||
|
||||
@NonRepeatable("a")
|
||||
static class NonRepeatableTestCase {
|
||||
static class WithNonRepeatable {
|
||||
}
|
||||
|
||||
@StandardRepeatable("a")
|
||||
static class SingleStandardRepeatableTestCase {
|
||||
static class WithSingleStandardRepeatable {
|
||||
}
|
||||
|
||||
@StandardRepeatable("a")
|
||||
@StandardRepeatable("b")
|
||||
static class StandardRepeatablesTestCase {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface StandardContainerWithMultipleAttributes {
|
||||
|
||||
StandardRepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(StandardContainerWithMultipleAttributes.class)
|
||||
@interface StandardRepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("a")
|
||||
@StandardRepeatableWithContainerWithMultipleAttributes("b")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
static class WithStandardRepeatables {
|
||||
}
|
||||
|
||||
@ExplicitContainer({ @ExplicitRepeatable("a"), @ExplicitRepeatable("b") })
|
||||
static class ExplicitRepeatablesTestCase {
|
||||
static class WithExplicitRepeatables {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -63,7 +63,7 @@ class ResourceRegionEncoderTests extends AbstractLeakCheckingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncodeResourceRegionFileResource() {
|
||||
void shouldEncodeResourceRegionFileResource() throws Exception {
|
||||
ResourceRegion region = new ResourceRegion(
|
||||
new ClassPathResource("ResourceRegionEncoderTests.txt", getClass()), 0, 6);
|
||||
Flux<DataBuffer> result = this.encoder.encode(Mono.just(region), this.bufferFactory,
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -35,7 +35,8 @@ class LeakAwareDataBufferFactoryTests {
|
||||
void leak() {
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer();
|
||||
try {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(this.bufferFactory::checkForLeaks);
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
this.bufferFactory::checkForLeaks);
|
||||
}
|
||||
finally {
|
||||
release(dataBuffer);
|
||||
|
||||
+108
-249
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -16,19 +16,16 @@
|
||||
|
||||
package org.springframework.core.io.support;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -36,9 +33,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link PathMatchingResourcePatternResolver}.
|
||||
*
|
||||
* <p>If tests fail, uncomment the diagnostics in {@link #assertFilenames(String, boolean, String...)}.
|
||||
* If this test case fails, uncomment diagnostics in the
|
||||
* {@link #assertProtocolAndFilenames} method.
|
||||
*
|
||||
* @author Oliver Hutchison
|
||||
* @author Juergen Hoeller
|
||||
@@ -48,261 +44,124 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
class PathMatchingResourcePatternResolverTests {
|
||||
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
|
||||
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
|
||||
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
|
||||
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT =
|
||||
new String[] {"EncodedResource.class", "LocalizedResourceHelper.class",
|
||||
"PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
|
||||
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class",
|
||||
"ResourcePatternResolver.class", "ResourcePatternUtils.class"};
|
||||
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT =
|
||||
new String[] {"PathMatchingResourcePatternResolverTests.class"};
|
||||
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATIONS =
|
||||
new String[] {"NonNull.class", "NonNullApi.class", "Nullable.class"};
|
||||
|
||||
private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
@Test
|
||||
void invalidPrefixWithPatternElementInIt() throws IOException {
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
|
||||
resolver.getResources("xx**:**/*.xy"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleResourceOnFileSystem() throws IOException {
|
||||
Resource[] resources =
|
||||
resolver.getResources("org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class");
|
||||
assertThat(resources.length).isEqualTo(1);
|
||||
assertProtocolAndFilenames(resources, "file", "PathMatchingResourcePatternResolverTests.class");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class InvalidPatterns {
|
||||
@Test
|
||||
void singleResourceInJar() throws IOException {
|
||||
Resource[] resources = resolver.getResources("org/reactivestreams/Publisher.class");
|
||||
assertThat(resources.length).isEqualTo(1);
|
||||
assertProtocolAndFilenames(resources, "jar", "Publisher.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidPrefixWithPatternElementInItThrowsException() {
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> resolver.getResources("xx**:**/*.xy"));
|
||||
@Disabled
|
||||
@Test
|
||||
void classpathStarWithPatternOnFileSystem() throws IOException {
|
||||
Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/sup*/*.class");
|
||||
// Have to exclude Clover-generated class files here,
|
||||
// as we might be running as part of a Clover test run.
|
||||
List<Resource> noCloverResources = new ArrayList<>();
|
||||
for (Resource resource : resources) {
|
||||
if (!resource.getFilename().contains("$__CLOVER_")) {
|
||||
noCloverResources.add(resource);
|
||||
}
|
||||
}
|
||||
resources = noCloverResources.toArray(new Resource[0]);
|
||||
assertProtocolAndFilenames(resources, "file",
|
||||
StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourcesOnFileSystemContainingHashtagsInTheirFileNames() throws IOException {
|
||||
Resource[] resources = resolver.getResources("classpath*:org/springframework/core/io/**/resource#test*.txt");
|
||||
assertThat(resources).extracting(Resource::getFile).extracting(File::getName)
|
||||
.containsExactlyInAnyOrder("resource#test1.txt", "resource#test2.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void classpathWithPatternInJar() throws IOException {
|
||||
Resource[] resources = resolver.getResources("classpath:reactor/util/annotation/*.class");
|
||||
assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classpathStarWithPatternInJar() throws IOException {
|
||||
Resource[] resources = resolver.getResources("classpath*:reactor/util/annotation/*.class");
|
||||
assertProtocolAndFilenames(resources, "jar", CLASSES_IN_REACTOR_UTIL_ANNOTATIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rootPatternRetrievalInJarFiles() throws IOException {
|
||||
Resource[] resources = resolver.getResources("classpath*:*.dtd");
|
||||
boolean found = false;
|
||||
for (Resource resource : resources) {
|
||||
if (resource.getFilename().equals("aspectj_1_5_0.dtd")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(found).as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar").isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FileSystemResources {
|
||||
private void assertProtocolAndFilenames(Resource[] resources, String protocol, String... filenames)
|
||||
throws IOException {
|
||||
|
||||
@Test
|
||||
void singleResourceOnFileSystem() {
|
||||
String pattern = "org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.class";
|
||||
assertExactFilenames(pattern, "PathMatchingResourcePatternResolverTests.class");
|
||||
}
|
||||
// Uncomment the following if you encounter problems with matching against the file system
|
||||
// It shows file locations.
|
||||
// String[] actualNames = new String[resources.length];
|
||||
// for (int i = 0; i < resources.length; i++) {
|
||||
// actualNames[i] = resources[i].getFilename();
|
||||
// }
|
||||
// List sortedActualNames = new LinkedList(Arrays.asList(actualNames));
|
||||
// List expectedNames = new LinkedList(Arrays.asList(fileNames));
|
||||
// Collections.sort(sortedActualNames);
|
||||
// Collections.sort(expectedNames);
|
||||
//
|
||||
// System.out.println("-----------");
|
||||
// System.out.println("Expected: " + StringUtils.collectionToCommaDelimitedString(expectedNames));
|
||||
// System.out.println("Actual: " + StringUtils.collectionToCommaDelimitedString(sortedActualNames));
|
||||
// for (int i = 0; i < resources.length; i++) {
|
||||
// System.out.println(resources[i]);
|
||||
// }
|
||||
|
||||
@Test
|
||||
void classpathStarWithPatternOnFileSystem() {
|
||||
String pattern = "classpath*:org/springframework/core/io/sup*/*.class";
|
||||
String[] expectedFilenames = StringUtils.concatenateStringArrays(CLASSES_IN_CORE_IO_SUPPORT, TEST_CLASSES_IN_CORE_IO_SUPPORT);
|
||||
assertFilenames(pattern, expectedFilenames);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithHashtagsInTheirFileNames {
|
||||
|
||||
@Test
|
||||
void usingClasspathStarProtocol() {
|
||||
String pattern = "classpath*:org/springframework/core/io/**/resource#test*.txt";
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt");
|
||||
assertExactSubPaths(pattern, pathPrefix, "support/resource#test1.txt", "support/resource#test2.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingClasspathStarProtocolWithWildcardInPatternAndNotEndingInSlash() throws Exception {
|
||||
String pattern = "classpath*:org/springframework/core/io/sup*";
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
|
||||
|
||||
// We DO find "support" if the pattern does NOT end with a slash.
|
||||
assertThat(actualSubPaths).containsExactly("support");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithWildcardInPatternAndNotEndingInSlash() throws Exception {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/org/springframework/core/io/sup*", testResourcesDir);
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
|
||||
|
||||
// We DO find "support" if the pattern does NOT end with a slash.
|
||||
assertThat(actualSubPaths).containsExactly("support");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingClasspathStarProtocolWithWildcardInPatternAndEndingInSlash() throws Exception {
|
||||
String pattern = "classpath*:org/springframework/core/io/sup*/";
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
|
||||
|
||||
// We do NOT find "support" if the pattern ENDS with a slash.
|
||||
assertThat(actualSubPaths).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithWildcardInPatternAndEndingInSlash() throws Exception {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/org/springframework/core/io/sup*/", testResourcesDir);
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
|
||||
|
||||
// We do NOT find "support" if the pattern ENDS with a slash.
|
||||
assertThat(actualSubPaths).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingClasspathStarProtocolWithWildcardInPatternAndEndingWithSuffixPattern() throws Exception {
|
||||
String pattern = "classpath*:org/springframework/core/io/sup*/*.txt";
|
||||
String pathPrefix = ".+org/springframework/core/io/";
|
||||
|
||||
List<String> actualSubPaths = getSubPathsIgnoringClassFiles(pattern, pathPrefix);
|
||||
|
||||
assertThat(actualSubPaths)
|
||||
.containsExactlyInAnyOrder("support/resource#test1.txt", "support/resource#test2.txt");
|
||||
}
|
||||
|
||||
private List<String> getSubPathsIgnoringClassFiles(String pattern, String pathPrefix) throws IOException {
|
||||
return Arrays.stream(resolver.getResources(pattern))
|
||||
.map(resource -> getPath(resource).replaceFirst(pathPrefix, ""))
|
||||
.filter(name -> !name.endsWith(".class"))
|
||||
.distinct()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
|
||||
String pathPrefix = ".+?resources/";
|
||||
|
||||
// We do NOT find "scanned-resources" if the pattern ENDS with "/**" AND does NOT otherwise contain a wildcard.
|
||||
assertExactFilenames(pattern, "resource#test1.txt", "resource#test2.txt");
|
||||
assertExactSubPaths(pattern, pathPrefix, "scanned-resources/resource#test1.txt",
|
||||
"scanned-resources/resource#test2.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithWildcardInPatternAndEndingInSlashStarStar() {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/scanned*resources/**", testResourcesDir);
|
||||
String pathPrefix = ".+?resources/";
|
||||
|
||||
// We DO find "scanned-resources" if the pattern ENDS with "/**" AND DOES otherwise contain a wildcard.
|
||||
assertExactFilenames(pattern, "scanned-resources", "resource#test1.txt", "resource#test2.txt");
|
||||
assertExactSubPaths(pattern, pathPrefix, "scanned-resources", "scanned-resources/resource#test1.txt",
|
||||
"scanned-resources/resource#test2.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolAndAssertingUrlAndUriSyntax() throws Exception {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/scanned-resources/**/resource#test1.txt", testResourcesDir);
|
||||
Resource[] resources = resolver.getResources(pattern);
|
||||
assertThat(resources).hasSize(1);
|
||||
Resource resource = resources[0];
|
||||
assertThat(resource.getFilename()).isEqualTo("resource#test1.txt");
|
||||
// The following assertions serve as regression tests for the lack of the
|
||||
// "authority component" (//) in the returned URI/URL. For example, we are
|
||||
// expecting file:/my/path (or file:/C:/My/Path) instead of file:///my/path.
|
||||
assertThat(resource.getURL().toString()).matches("^file:\\/[^\\/].+test1\\.txt$");
|
||||
assertThat(resource.getURI().toString()).matches("^file:\\/[^\\/].+test1\\.txt$");
|
||||
}
|
||||
assertThat(resources.length).as("Correct number of files found").isEqualTo(filenames.length);
|
||||
for (Resource resource : resources) {
|
||||
String actualProtocol = resource.getURL().getProtocol();
|
||||
assertThat(actualProtocol).isEqualTo(protocol);
|
||||
assertFilenameIn(resource, filenames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class JarResources {
|
||||
|
||||
@Test
|
||||
void singleResourceInJar() {
|
||||
String pattern = "org/reactivestreams/Publisher.class";
|
||||
assertExactFilenames(pattern, "Publisher.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleResourceInRootOfJar() {
|
||||
String pattern = "aspectj_1_5_0.dtd";
|
||||
assertExactFilenames(pattern, "aspectj_1_5_0.dtd");
|
||||
}
|
||||
|
||||
@Test
|
||||
void classpathWithPatternInJar() {
|
||||
String pattern = "classpath:reactor/util/annotation/*.class";
|
||||
assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classpathStarWithPatternInJar() {
|
||||
String pattern = "classpath*:reactor/util/annotation/*.class";
|
||||
assertExactFilenames(pattern, CLASSES_IN_REACTOR_UTIL_ANNOTATION);
|
||||
}
|
||||
|
||||
// Fails in a native image -- https://github.com/oracle/graal/issues/5020
|
||||
@Test
|
||||
void rootPatternRetrievalInJarFiles() throws IOException {
|
||||
assertThat(resolver.getResources("classpath*:aspectj*.dtd")).extracting(Resource::getFilename)
|
||||
.as("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar")
|
||||
.containsExactly("aspectj_1_5_0.dtd");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void assertFilenames(String pattern, String... filenames) {
|
||||
assertFilenames(pattern, false, filenames);
|
||||
}
|
||||
|
||||
private void assertExactFilenames(String pattern, String... filenames) {
|
||||
assertFilenames(pattern, true, filenames);
|
||||
}
|
||||
|
||||
private void assertFilenames(String pattern, boolean exactly, String... filenames) {
|
||||
try {
|
||||
Resource[] resources = resolver.getResources(pattern);
|
||||
List<String> actualNames = Arrays.stream(resources)
|
||||
.map(Resource::getFilename)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Uncomment the following if you encounter problems with matching against the file system.
|
||||
// List<String> expectedNames = Arrays.stream(filenames).sorted().toList();
|
||||
// System.out.println("----------------------------------------------------------------------");
|
||||
// System.out.println("Expected: " + expectedNames);
|
||||
// System.out.println("Actual: " + actualNames);
|
||||
// Arrays.stream(resources).forEach(System.out::println);
|
||||
|
||||
if (exactly) {
|
||||
assertThat(actualNames).as("subset of files found").containsExactlyInAnyOrder(filenames);
|
||||
}
|
||||
else {
|
||||
assertThat(actualNames).as("subset of files found").contains(filenames);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertExactSubPaths(String pattern, String pathPrefix, String... subPaths) {
|
||||
try {
|
||||
Resource[] resources = resolver.getResources(pattern);
|
||||
List<String> actualSubPaths = Arrays.stream(resources)
|
||||
.map(resource -> getPath(resource).replaceFirst(pathPrefix, ""))
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
assertThat(actualSubPaths).containsExactlyInAnyOrder(subPaths);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPath(Resource resource) {
|
||||
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
|
||||
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
|
||||
//
|
||||
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
|
||||
// we use FileSystemResource#getPath since this test class is sometimes run within a
|
||||
// GraalVM native image which cannot support Path#toFile.
|
||||
//
|
||||
// See: https://github.com/spring-projects/spring-framework/issues/29243
|
||||
return ((FileSystemResource) resource).getPath();
|
||||
private void assertFilenameIn(Resource resource, String... filenames) {
|
||||
String filename = resource.getFilename();
|
||||
assertThat(Arrays.stream(filenames).anyMatch(filename::endsWith)).as(resource + " does not have a filename that matches any of the specified names").isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,25 +17,15 @@
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
@@ -261,28 +251,32 @@ class ObjectUtilsTests {
|
||||
@Deprecated
|
||||
void hashCodeWithDouble() {
|
||||
double dbl = 9830.43;
|
||||
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(Double.hashCode(dbl));
|
||||
int expected = (new Double(dbl)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void hashCodeWithFloat() {
|
||||
float flt = 34.8f;
|
||||
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(Float.hashCode(flt));
|
||||
int expected = (Float.valueOf(flt)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
void hashCodeWithLong() {
|
||||
long lng = 883L;
|
||||
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(Long.hashCode(lng));
|
||||
int expected = (Long.valueOf(lng)).hashCode();
|
||||
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityToString() {
|
||||
Object obj = new Object();
|
||||
String expected = obj.getClass().getName() + "@" + ObjectUtils.getIdentityHexString(obj);
|
||||
assertThat(ObjectUtils.identityToString(obj)).isEqualTo(expected);
|
||||
String actual = ObjectUtils.identityToString(obj);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -738,7 +732,7 @@ class ObjectUtilsTests {
|
||||
|
||||
@Test
|
||||
void nullSafeToStringWithObjectArray() {
|
||||
Object[] array = {"Han", 43};
|
||||
Object[] array = {"Han", Long.valueOf(43)};
|
||||
assertThat(ObjectUtils.nullSafeToString(array)).isEqualTo("{Han, 43}");
|
||||
}
|
||||
|
||||
@@ -826,144 +820,7 @@ class ObjectUtilsTests {
|
||||
.withMessage("Constant [bogus] does not exist in enum type org.springframework.util.ObjectUtilsTests$Tropes");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class NullSafeConciseToStringTests {
|
||||
|
||||
private final String truncated = " (truncated)...";
|
||||
private final int truncatedLength = 100 + truncated.length();
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNull() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForClass() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(String.class)).isEqualTo("java.lang.String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForStrings() {
|
||||
String repeat100 = repeat("X", 100);
|
||||
String repeat101 = repeat("X", 101);
|
||||
|
||||
assertThat(ObjectUtils.nullSafeConciseToString("")).isEqualTo("");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString("foo")).isEqualTo("foo");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(repeat100)).isEqualTo(repeat100);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(repeat101)).hasSize(truncatedLength).endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForStringBuilders() {
|
||||
String repeat100 = repeat("X", 100);
|
||||
String repeat101 = repeat("X", 101);
|
||||
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder("foo"))).isEqualTo("foo");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder(repeat100))).isEqualTo(repeat100);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder(repeat101))).hasSize(truncatedLength).endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForEnum() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Tropes.FOO)).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNumber() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(42L)).isEqualTo("42");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(99.1234D)).isEqualTo("99.1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForDate() {
|
||||
Date date = new Date();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(date)).isEqualTo(date.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForTemporal() {
|
||||
LocalDate localDate = LocalDate.now();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(localDate)).isEqualTo(localDate.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForUri() {
|
||||
String uri = "https://www.example.com/?foo=1&bar=2&baz=3";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(URI.create(uri))).isEqualTo(uri);
|
||||
|
||||
uri += "&qux=" + repeat("4", 60);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(URI.create(uri)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(uri.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForUrl() throws Exception {
|
||||
String url = "https://www.example.com/?foo=1&bar=2&baz=3";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new URL(url))).isEqualTo(url);
|
||||
|
||||
url += "&qux=" + repeat("4", 60);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new URL(url)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(url.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForLocale() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Locale.GERMANY)).isEqualTo("de_DE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForArraysAndCollections() {
|
||||
List<String> list = Arrays.asList("a", "b", "c");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1, 2}, {3, 4}})).startsWith(prefix(int[][].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new Object[0]))).startsWith(prefix(Object[].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new String[0]))).startsWith(prefix(String[].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new ArrayList<>(list))).startsWith(prefix(ArrayList.class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new HashSet<>(list))).startsWith(prefix(HashSet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForCustomTypes() {
|
||||
class ExplosiveType {
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new UnsupportedOperationException("no-go");
|
||||
}
|
||||
}
|
||||
ExplosiveType explosiveType = new ExplosiveType();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(explosiveType::toString);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(explosiveType)).startsWith(prefix(ExplosiveType.class));
|
||||
|
||||
class WordyType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return repeat("blah blah", 20);
|
||||
}
|
||||
}
|
||||
WordyType wordyType = new WordyType();
|
||||
assertThat(wordyType).asString().hasSizeGreaterThanOrEqualTo(180 /* 9x20 */);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(wordyType)).startsWith(prefix(WordyType.class));
|
||||
}
|
||||
|
||||
private String repeat(String str, int count) {
|
||||
String result = "";
|
||||
for (int i = 0; i < count; i++) {
|
||||
result += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String prefix(Class<?> clazz) {
|
||||
return clazz.getTypeName() + "@";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void assertEqualHashCodes(int expected, Object array) {
|
||||
private void assertEqualHashCodes(int expected, Object array) {
|
||||
int actual = ObjectUtils.nullSafeHashCode(array);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
assertThat(array.hashCode() != actual).isTrue();
|
||||
|
||||
@@ -50,14 +50,14 @@ class SocketUtilsTests {
|
||||
|
||||
@Test
|
||||
void findAvailableTcpPortWithZeroMinPort() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(0));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableTcpPort(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAvailableTcpPortWithNegativeMinPort() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(-500));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableTcpPort(-500));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,8 +80,8 @@ class SocketUtilsTests {
|
||||
try (ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"))) {
|
||||
assertThat(socket).isNotNull();
|
||||
// will only look for the exact port
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableTcpPort(port, port))
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableTcpPort(port, port))
|
||||
.withMessageStartingWith("Could not find an available TCP port")
|
||||
.withMessageEndingWith("after 1 attempts");
|
||||
}
|
||||
@@ -123,7 +123,8 @@ class SocketUtilsTests {
|
||||
|
||||
@Test
|
||||
void findAvailableTcpPortsWithRequestedNumberGreaterThanSizeOfRange() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> findAvailableTcpPorts(50, 45000, 45010));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
findAvailableTcpPorts(50, 45000, 45010));
|
||||
}
|
||||
|
||||
|
||||
@@ -131,14 +132,14 @@ class SocketUtilsTests {
|
||||
|
||||
@Test
|
||||
void findAvailableUdpPortWithZeroMinPort() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(0));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableUdpPort(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAvailableUdpPortWithNegativeMinPort() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(-500));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableUdpPort(-500));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,8 +155,8 @@ class SocketUtilsTests {
|
||||
try (DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost"))) {
|
||||
assertThat(socket).isNotNull();
|
||||
// will only look for the exact port
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> org.springframework.util.SocketUtils.findAvailableUdpPort(port, port))
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
org.springframework.util.SocketUtils.findAvailableUdpPort(port, port))
|
||||
.withMessageStartingWith("Could not find an available UDP port")
|
||||
.withMessageEndingWith("after 1 attempts");
|
||||
}
|
||||
@@ -197,7 +198,8 @@ class SocketUtilsTests {
|
||||
|
||||
@Test
|
||||
void findAvailableUdpPortsWithRequestedNumberGreaterThanSizeOfRange() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> findAvailableUdpPorts(50, 45000, 45010));
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
findAvailableUdpPorts(50, 45000, 45010));
|
||||
}
|
||||
|
||||
|
||||
@@ -224,13 +226,13 @@ class SocketUtilsTests {
|
||||
SortedSet<Integer> ports = org.springframework.util.SocketUtils.findAvailableUdpPorts(numRequested, minPort, maxPort);
|
||||
assertAvailablePorts(ports, numRequested, minPort, maxPort);
|
||||
}
|
||||
|
||||
private void assertPortInRange(int port, int minPort, int maxPort) {
|
||||
assertThat(port).as("port").isBetween(minPort, maxPort);
|
||||
assertThat(port >= minPort).as("port [" + port + "] >= " + minPort).isTrue();
|
||||
assertThat(port <= maxPort).as("port [" + port + "] <= " + maxPort).isTrue();
|
||||
}
|
||||
|
||||
private void assertAvailablePorts(SortedSet<Integer> ports, int numRequested, int minPort, int maxPort) {
|
||||
assertThat(ports).as("number of ports requested").hasSize(numRequested);
|
||||
assertThat(ports.size()).as("number of ports requested").isEqualTo(numRequested);
|
||||
for (int port : ports) {
|
||||
assertPortInRange(port, minPort, maxPort);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user