Compare commits

...

45 Commits

Author SHA1 Message Date
Spring Builds 2aeddb4fcb Release v5.3.25 2023-01-11 11:05:48 +00:00
Juergen Hoeller 26cd33cb2b Upgrade to Reactor 2020.0.27
Includes HtmlUnit 2.69, AssertJ 3.24.1, Checkstyle 10.6

Closes gh-29798
2023-01-11 00:23:38 +01:00
Juergen Hoeller 2ee393ae71 Upgrade to Tomcat 9.0.70, Jetty 9.4.50, Netty 4.1.86, Undertow 2.2.22, HtmlUnit 2.67, Mockito 4.9, AssertJ 3.23.1, Checkstyle 10.5 2022-12-23 15:52:09 +01:00
Juergen Hoeller 0815d29e45 Defensive check for null returned from createConnection()
Closes gh-29706
2022-12-23 15:51:37 +01:00
Brian Clozel 777f01d786 Fix path within mapping when pattern contains ".*"
Prior to this commit, extracting the path within handler mapping would
result in "" if the matching path element would be a Regex and contain
".*". This could cause issues with resource handling if the handler
mapping pattern was similar to `"/folder/file.*.extension"`.

This commit introduces a new `isLiteral()` method in the `PathElement`
abstract class that expresses whether the path element can be compared
as a String for path matching or if it requires a more elaborate
matching process.

Using this method for extracting the path within handler mapping avoids
relying on wildcard count or other properties.

See gh-29712
Fixes gh-29716
2022-12-19 10:53:02 +01:00
Sam Brannen f8fea013fc Update Jakarta Mail info in ref docs
Closes gh-29708
2022-12-17 14:18:42 +01:00
Sam Brannen 916539178b Improve documentation for literals in SpEL expressions
Closes gh-29701
2022-12-16 14:51:48 +01:00
Sam Brannen fdf3bcc9d9 Remove obsolete AttributeMethods.hasOnlyValueAttribute() method
See gh-29685
2022-12-13 15:52:01 +01:00
Sam Brannen 5ddc984192 Support repeatable annotation containers with multiple attributes
Prior to this commit, there was a bug in the implementation of
StandardRepeatableContainers.computeRepeatedAnnotationsMethod() which
has existed since Spring Framework 5.2 (when
StandardRepeatableContainers was introduced). Specifically,
StandardRepeatableContainers ignored any repeatable container
annotation if it declared attributes other than `value()`. However,
Java permits any number of attributes in a repeatable container
annotation.

In addition, the changes made in conjunction with gh-20279 made the bug
in StandardRepeatableContainers apparent when using the
getMergedRepeatableAnnotations() or findMergedRepeatableAnnotations()
method in AnnotatedElementUtils, resulting in regressions for the
behavior of those two methods.

This commit fixes the regressions and bug by altering the logic in
StandardRepeatableContainers.computeRepeatedAnnotationsMethod() so that
it explicitly looks for the `value()` method and ignores any other
methods declared in a repeatable container annotation candidate.

See gh-29685
Closes gh-29686
2022-12-13 15:47:13 +01:00
Sam Brannen b2ce54e7f1 Revise RepeatableContainersTests 2022-12-13 15:47:13 +01:00
Juergen Hoeller 937ab5f4b2 Polishing (aligned with main) 2022-12-13 12:06:09 +01:00
Juergen Hoeller 064c618050 Drop SQLExceptionSubclassFactory and unify SQLStateSQLExceptionTranslator tests 2022-12-13 12:05:35 +01:00
Juergen Hoeller 8c80ec1138 Avoid NPE on BeanDescriptor access with SimpleBeanInfoFactory
Closes gh-29681

(cherry picked from commit d74191427e)
2022-12-13 11:43:43 +01:00
rstoyanchev 912fa7602a Improve invalid Content-Type handling in WebFlux
Closes gh-29565
2022-12-09 11:53:16 +00:00
rstoyanchev 525fc7a27e Optimize object creation PartialMatchHelper
Closes gh-29667
2022-12-09 11:27:03 +00:00
Sam Brannen 72285034ba Support arrays in AST string representations of SpEL expressions
Prior to this commit, SpEL's ConstructorReference did not provide
support for arrays when generating a string representation of the
internal AST. For example, 'new String[3]' was represented as 'new
String()' instead of 'new String[3]'.

This commit introduces support for standard array construction and array
construction with initializers in ConstructorReference's toStringAST()
implementation.

Closes gh-29666
2022-12-08 23:23:01 -05:00
Sam Brannen 933474000b Polishing 2022-12-08 18:45:02 -05:00
Sam Brannen 4e8aebcde7 Fix SpEL support for quotes within String literals
Prior to this commit, there were two bugs in the support for quotes
within String literals in SpEL expressions.

- Two double quotes ("") or two single quotes ('') were always replaced
  with one double quote or one single quote, respectively, regardless
  of which quote character was used to enclose the original String
  literal. This resulted in the loss of one of the double quotes when
  the String literal was enclosed in single quotes, and vice versa. For
  example, 'x "" y' became 'x " y'.

- A single quote which was properly escaped in a String literal
  enclosed within single quotes was not escaped in the AST string
  representation of the expression. For example, 'x '' y' became 'x ' y'.

This commit fixes both of these related issues in StringLiteral and
overhauls the structure of ParsingTests.

Closes gh-29604
Closes gh-28356
2022-12-07 16:03:49 -05:00
Sam Brannen dff5b1ff8e Polishing 2022-12-07 15:59:08 -05:00
Sam Brannen fa0a4a0a46 Improve Javadoc for SqlLobValue 2022-12-07 15:58:56 -05:00
Sam Brannen 41a6b7ec20 Update copyright headers for source code changed since August 2022
The changes in this commit were performed using the newly introduced
update_copyright_headers.sh script.
2022-12-03 17:23:21 -05:00
Sam Brannen cfaa4ba89c Apply update_copyright_headers.sh to staged files as well 2022-12-03 17:23:21 -05:00
Sam Brannen 598e9d586b Introduce update_copyright_headers.sh shell script
In order to automate maintenance of copyright headers in our source
code (especially when merging PRs from external contributors), this
commit introduces an update_copyright_headers.sh script (tested on
mac OS) that will update the copyright headers of all Java, Kotlin,
and Groovy source files that have been added or modified this year
(or at least as far back as the git log history supports it).

For example, running this script currently outputs the following.

Updating copyright headers in Java, Kotlin, and Groovy source code for year 2022
warning: log for 'main' only goes back to Tue, 16 Aug 2022 16:24:55 +0200
2022-12-03 17:23:21 -05:00
Sam Brannen 750a8b359f Stop using Mockito to spy() on JDK I/O streams
When running on JDK 16+, we are not able to spy() on JDK types. To
address this, this commit stops using Mockito to spy on JDK I/O streams
(such as ByteArrayInputStream and ByteArrayOutputStream).
2022-12-03 17:23:21 -05:00
Sam Brannen c838bcff3a Fix/Disable JMX & JNDI tests on JDK 16+ 2022-12-03 17:23:21 -05:00
Sam Brannen 109b00d24d Avoid use of deprecated Character/Double constructors in tests 2022-12-03 17:23:21 -05:00
Sam Brannen b6abf45a56 Introduce @Suite classes for individual modules 2022-12-03 16:32:27 -05:00
Juergen Hoeller 2d94b43faa Polishing 2022-12-02 11:15:03 +01:00
Sam Brannen f7f73439ab Modify MBeanTestUtils to work on JDK 17+ 2022-11-30 14:55:15 +01:00
Sam Brannen 5e55558a93 Fix SockJsServiceTests
See gh-29594
2022-11-28 17:12:08 +01:00
Sam Brannen eee50d3b8a Polishing 2022-11-28 17:06:22 +01:00
Sam Brannen 92af390ed2 Polish contribution
See gh-29594
2022-11-28 17:02:53 +01:00
Aashay Chapatwala 332d2a36bb Add title to SockJS iFrames for accessibility compliance
Closes gh-29594
2022-11-28 17:02:01 +01:00
Brian Clozel 92b2b828f5 Fix Java 17 test CI and remove Java 11 variant
This commit fixes configuration and runtime issues with the Java 17 test
CI variant and removes the Java 11 one, now that Spring Framework 5.3.x
is in maintenance mode.
2022-11-28 10:17:56 +01:00
Juergen Hoeller a11222f681 Polishing 2022-11-25 17:58:59 +01:00
Juergen Hoeller 7de8d81932 Explicit documentation notes on standard Java reflection
See gh-29531
2022-11-24 14:25:43 +01:00
Juergen Hoeller 0e33537d9d Consistent use of DefaultParameterNameResolver in tests 2022-11-23 11:17:58 +01:00
Sébastien Deleuze 00da70e26b Fix some typos in Kotlin WebClient example code
Closes gh-29542
2022-11-22 08:35:50 +01:00
Sam Brannen 72b44cebea Catch Error for SourceHttpMessageConverter in WebMvcConfigurationSupport
Prior to this commit, the addDefaultHttpMessageConverters() method in
WebMvcConfigurationSupport caught Throwable for SourceHttpMessageConverter
instantiation; whereas, the rest of the code base correctly catches Error
for SourceHttpMessageConverter instantiation (to handle errors such as
NoClassDefFoundError).

Throwable should not be caught since it can mask other categories of
failures (such as configuration errors).

This commit therefore switches to catching Error for SourceHttpMessageConverter
instantiation in WebMvcConfigurationSupport.

Closes gh-29537
2022-11-21 17:37:22 +01:00
Marten Deinum 1ee3777ac7 Fix link to Bean Utils Light Library in BeanUtils Javadoc
The URL for the BULL library has changed (not sure when, probably way back).

This updates it to the correct location.

Closes gh-29534, gh-29536
2022-11-21 17:25:50 +01:00
divcon cad6e8756d Fix link to WebFlux section in reference manual
Closes gh-29526
2022-11-19 14:51:03 +01:00
Sam Brannen c7bd685c54 Polishing 2022-11-19 14:49:26 +01:00
Sam Brannen 4f472d2ac4 Remove TODOs in WebFlux ref docs 2022-11-19 14:49:07 +01:00
Stephane Nicoll a18842b72b Fix link to WebFlux section
Closes gh-29517
2022-11-18 10:48:05 +01:00
Spring Builds a41f97bc2b Next development version (v5.3.25-SNAPSHOT) 2022-11-16 08:07:01 +00:00
166 changed files with 1701 additions and 1575 deletions
+11 -11
View File
@@ -28,11 +28,11 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.85.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.25"
mavenBom "io.netty:netty-bom:4.1.86.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.27"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.49.v20220914"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.50.v20221201"
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"
@@ -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.68') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.70') {
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.68') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.70') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.21.Final') {
dependencySet(group: 'io.undertow', version: '2.2.22.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -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.23.0"
dependency "org.assertj:assertj-core:3.24.1"
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.8.1') {
dependencySet(group: 'org.mockito', version: '4.9.0') { // spring-beans tests fail with 4.10+
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.66.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.69.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.66.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.67.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.4"
toolVersion = "10.6.0"
configDirectory.set(rootProject.file("src/checkstyle"))
}
-1
View File
@@ -5,7 +5,6 @@ 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
-6
View File
@@ -5,15 +5,9 @@ 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
View File
@@ -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 java11 java17
for jdk in java8 java17
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+2 -38
View File
@@ -125,14 +125,6 @@ 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
@@ -237,34 +229,6 @@ 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
@@ -282,7 +246,7 @@ jobs:
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 15
TEST_TOOLCHAIN: 17
<<: *build-project-task-params
on_failure:
do:
@@ -471,7 +435,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk11-build", "jdk17-build"]
jobs: ["build", "jdk17-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+1 -1
View File
@@ -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=JDK11,JDK15 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.24-SNAPSHOT
version=5.3.25
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+4 -15
View File
@@ -81,6 +81,10 @@ 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"])
}
}
}
}
@@ -130,21 +134,6 @@ 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
@@ -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://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
* <a href="https://github.com/ExpediaGroup/bull">BULL - Bean Utils Light Library</a>,
* or similar third-party frameworks for more comprehensive bean utilities.
*
* @author Rod Johnson
@@ -16,6 +16,7 @@
package org.springframework.beans;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
@@ -51,6 +52,10 @@ public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
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);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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,6 +77,21 @@ 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();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -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.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.DefaultParameterNameDiscoverer;
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 LocalVariableTableParameterNameDiscoverer());
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
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 LocalVariableTableParameterNameDiscoverer());
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
nonqualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
assertThat(nonqualifiedParam.getParameterName()).isEqualTo("tpb");
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -502,7 +502,7 @@ class CustomEditorTests {
CharBean cb = new CharBean();
BeanWrapper bw = new BeanWrapperImpl(cb);
bw.setPropertyValue("myChar", new Character('c'));
bw.setPropertyValue("myChar", '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(Character.valueOf('c'));
assertThat(cb.getMyCharacter()).isEqualTo('c');
bw.setPropertyValue("myCharacter", "c");
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
assertThat(cb.getMyCharacter()).isEqualTo('c');
bw.setPropertyValue("myCharacter", "\u0041");
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('A'));
assertThat(cb.getMyCharacter()).isEqualTo('A');
bw.setPropertyValue("myCharacter", " ");
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf(' '));
assertThat(cb.getMyCharacter()).isEqualTo(' ');
bw.setPropertyValue("myCharacter", "");
assertThat(cb.getMyCharacter()).isNull();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -20,7 +20,6 @@ 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;
@@ -32,6 +31,7 @@ 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,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Sam Brannen
*/
class JmxUtilsTests {
@@ -131,10 +132,11 @@ class JmxUtilsTests {
MBeanServer server = null;
try {
server = JmxUtils.locateMBeanServer();
assertThat(server).isNotNull();
}
finally {
if (server != null) {
MBeanServerFactory.releaseMBeanServer(server);
MBeanTestUtils.releaseMBeanServer(server);
}
}
}
@@ -21,21 +21,25 @@ 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
*/
public class JndiLocatorDelegateTests {
@DisabledForJreRange(
min = JAVA_16,
disabledReason = "Cannot use reflection to set private static field in javax.naming.spi.NamingManager")
class JndiLocatorDelegateTests {
@Test
public void isDefaultJndiEnvironmentAvailableFalse() throws Exception {
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-2019 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.
@@ -18,37 +18,59 @@ 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 {
/**
* Resets MBeanServerFactory and ManagementFactory to a known consistent state.
* <p>This involves releasing all currently registered MBeanServers and resetting
* the platformMBeanServer to null.
* 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}.
*/
public static synchronized void resetMBeanServers() throws Exception {
for (MBeanServer server : MBeanServerFactory.findMBeanServer(null)) {
try {
MBeanServerFactory.releaseMBeanServer(server);
}
catch (IllegalArgumentException ex) {
if (!ex.getMessage().contains("not in list")) {
throw ex;
}
}
releaseMBeanServer(server);
}
Field field = ManagementFactory.class.getDeclaredField("platformMBeanServer");
field.setAccessible(true);
field.set(null, null);
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;
}
}
}
static boolean isCurrentJreWithinRange(JRE min, JRE max) {
return EnumSet.range(min, max).contains(JRE.currentVersion());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -18,9 +18,9 @@ package org.springframework.core;
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
* 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.
* 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).
*
* <p>If a Kotlin reflection implementation is present,
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -31,11 +31,13 @@ import org.springframework.lang.Nullable;
* {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities
* for introspecting parameter names.
*
* Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
* <p>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 {
@@ -47,12 +47,18 @@ 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,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -26,10 +26,15 @@ 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 {
@@ -86,17 +86,6 @@ 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}.
@@ -166,8 +166,8 @@ public abstract class RepeatableContainers {
private static Object computeRepeatedAnnotationsMethod(Class<? extends Annotation> annotationType) {
AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType);
if (methods.hasOnlyValueAttribute()) {
Method method = methods.get(0);
Method method = methods.get(MergedAnnotation.VALUE);
if (method != null) {
Class<?> returnType = method.getReturnType();
if (returnType.isArray()) {
Class<?> componentType = returnType.getComponentType();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -0,0 +1,34 @@
/*
* 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-2019 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.
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class LocalVariableTableParameterNameDiscovererTests {
private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
private final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
@Test
@@ -162,23 +162,23 @@ class LocalVariableTableParameterNameDiscovererTests {
Constructor<?> ctor = clazz.getDeclaredConstructor(Object.class);
String[] names = discoverer.getParameterNames(ctor);
assertThat(names.length).isEqualTo(1);
assertThat(names).hasSize(1);
assertThat(names[0]).isEqualTo("key");
ctor = clazz.getDeclaredConstructor(Object.class, Object.class);
names = discoverer.getParameterNames(ctor);
assertThat(names.length).isEqualTo(2);
assertThat(names).hasSize(2);
assertThat(names[0]).isEqualTo("key");
assertThat(names[1]).isEqualTo("value");
Method m = clazz.getMethod("generifiedStaticMethod", Object.class);
names = discoverer.getParameterNames(m);
assertThat(names.length).isEqualTo(1);
assertThat(names).hasSize(1);
assertThat(names[0]).isEqualTo("param");
m = clazz.getMethod("generifiedMethod", Object.class, long.class, Object.class, Object.class);
names = discoverer.getParameterNames(m);
assertThat(names.length).isEqualTo(4);
assertThat(names).hasSize(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.length).isEqualTo(3);
assertThat(names).hasSize(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.length).isEqualTo(3);
assertThat(names).hasSize(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.length).isEqualTo(0);
assertThat(names).isEmpty();
}
@Disabled("Ignored because Ubuntu packages OpenJDK with debug symbols enabled. See SPR-8078.")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -43,11 +43,10 @@ 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();
}
@@ -20,6 +20,7 @@ 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;
@@ -77,6 +78,7 @@ import static org.springframework.core.annotation.AnnotationUtilsTests.asArray;
* @see AnnotationUtilsTests
* @see MultipleComposedAnnotationsOnSingleAnnotatedElementTests
* @see ComposedRepeatableAnnotationsTests
* @see NestedRepeatableAnnotationsTests
*/
class AnnotatedElementUtilsTests {
@@ -908,6 +910,31 @@ 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");
}
// -------------------------------------------------------------------------
@@ -1557,4 +1584,24 @@ 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 {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -58,24 +58,6 @@ 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);
@@ -20,7 +20,9 @@ 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;
@@ -31,193 +33,175 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* Tests for {@link RepeatableContainers}.
*
* @author Phillip Webb
* @author Sam Brannen
*/
class RepeatableContainersTests {
@Test
void standardRepeatablesWhenNonRepeatableReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.standardRepeatables(), WithNonRepeatable.class,
NonRepeatable.class);
assertThat(values).isNull();
@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 standardRepeatablesWhenSingleReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.standardRepeatables(),
WithSingleStandardRepeatable.class, StandardRepeatable.class);
assertThat(values).isNull();
}
@Nested
class ExplicitRepeatableContainerTests {
@Test
void standardRepeatablesWhenContainerReturnsRepeats() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.standardRepeatables(), WithStandardRepeatables.class,
StandardContainer.class);
assertThat(values).containsExactly("a", "b");
}
@Test
void ofExplicitWhenNonRepeatableReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
NonRepeatableTestCase.class, NonRepeatable.class);
assertThat(values).isNull();
}
@Test
void standardRepeatablesWhenContainerButNotRepeatableReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.standardRepeatables(), WithExplicitRepeatables.class,
ExplicitContainer.class);
assertThat(values).isNull();
}
@Test
void ofExplicitWhenStandardRepeatableContainerReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.of(ExplicitRepeatable.class, ExplicitContainer.class),
StandardRepeatablesTestCase.class, StandardContainer.class);
assertThat(values).isNull();
}
@Test
void ofExplicitWhenNonRepeatableReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.of(ExplicitRepeatable.class,
ExplicitContainer.class),
WithNonRepeatable.class, NonRepeatable.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 ofExplicitWhenStandardRepeatableContainerReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.of(ExplicitRepeatable.class,
ExplicitContainer.class),
WithStandardRepeatables.class, StandardContainer.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 ofExplicitWhenContainerReturnsRepeats() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.of(ExplicitRepeatable.class,
ExplicitContainer.class),
WithExplicitRepeatables.class, ExplicitContainer.class);
assertThat(values).containsExactly("a", "b");
}
@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 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 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 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 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 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 ofExplicitWhenAnnotationIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> RepeatableContainers.of(null, null))
.withMessage("Repeatable must not be null");
}
@Test
void ofExplicitWhenAnnotationIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() ->
RepeatableContainers.of(null, null))
.withMessage("Repeatable must not be null");
}
@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 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,
WithStandardRepeatables.class, StandardContainer.class)).containsExactly(
"a", "b");
assertThat(findRepeatedAnnotationValues(repeatableContainers,
WithExplicitRepeatables.class, ExplicitContainer.class)).containsExactly(
"a", "b");
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");
}
@Test
void noneAlwaysReturnsNull() {
Object[] values = findRepeatedAnnotationValues(
RepeatableContainers.none(), WithStandardRepeatables.class,
StandardContainer.class);
Object[] values = findRepeatedAnnotationValues(RepeatableContainers.none(), StandardRepeatablesTestCase.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.hashCode()).isEqualTo(c2.hashCode());
RepeatableContainers c4 = RepeatableContainers.standardRepeatables().and(ExplicitContainer.class, ExplicitRepeatable.class);
assertThat(c1).hasSameHashCodeAs(c2);
assertThat(c1).isEqualTo(c1).isEqualTo(c2);
assertThat(c1).isNotEqualTo(c3).isNotEqualTo(c4);
}
private Object[] findRepeatedAnnotationValues(RepeatableContainers containers,
private static 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 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);
private static Object[] extractValues(Annotation[] annotations) {
if (annotations == null) {
return null;
}
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 {
@@ -225,7 +209,8 @@ class RepeatableContainersTests {
}
@Retention(RetentionPolicy.RUNTIME)
@interface ExplicitRepeatable {
@Repeatable(StandardContainer.class)
@interface StandardRepeatable {
String value() default "";
}
@@ -236,6 +221,12 @@ class RepeatableContainersTests {
ExplicitRepeatable[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@interface ExplicitRepeatable {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@interface InvalidNoValue {
}
@@ -253,20 +244,40 @@ class RepeatableContainersTests {
}
@NonRepeatable("a")
static class WithNonRepeatable {
static class NonRepeatableTestCase {
}
@StandardRepeatable("a")
static class WithSingleStandardRepeatable {
static class SingleStandardRepeatableTestCase {
}
@StandardRepeatable("a")
@StandardRepeatable("b")
static class WithStandardRepeatables {
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 {
}
@ExplicitContainer({ @ExplicitRepeatable("a"), @ExplicitRepeatable("b") })
static class WithExplicitRepeatables {
static class ExplicitRepeatablesTestCase {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -251,32 +251,28 @@ class ObjectUtilsTests {
@Deprecated
void hashCodeWithDouble() {
double dbl = 9830.43;
int expected = (new Double(dbl)).hashCode();
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(expected);
assertThat(ObjectUtils.hashCode(dbl)).isEqualTo(Double.hashCode(dbl));
}
@Test
@Deprecated
void hashCodeWithFloat() {
float flt = 34.8f;
int expected = (Float.valueOf(flt)).hashCode();
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(expected);
assertThat(ObjectUtils.hashCode(flt)).isEqualTo(Float.hashCode(flt));
}
@Test
@Deprecated
void hashCodeWithLong() {
long lng = 883L;
int expected = (Long.valueOf(lng)).hashCode();
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(expected);
assertThat(ObjectUtils.hashCode(lng)).isEqualTo(Long.hashCode(lng));
}
@Test
void identityToString() {
Object obj = new Object();
String expected = obj.getClass().getName() + "@" + ObjectUtils.getIdentityHexString(obj);
String actual = ObjectUtils.identityToString(obj);
assertThat(actual).isEqualTo(expected);
assertThat(ObjectUtils.identityToString(obj)).isEqualTo(expected);
}
@Test
@@ -732,7 +728,7 @@ class ObjectUtilsTests {
@Test
void nullSafeToStringWithObjectArray() {
Object[] array = {"Han", Long.valueOf(43)};
Object[] array = {"Han", 43};
assertThat(ObjectUtils.nullSafeToString(array)).isEqualTo("{Han, 43}");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -33,8 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link StreamUtils}.
@@ -57,53 +55,47 @@ class StreamUtilsTests {
@Test
void copyToByteArray() throws Exception {
InputStream inputStream = spy(new ByteArrayInputStream(bytes));
InputStream inputStream = new ByteArrayInputStream(bytes);
byte[] actual = StreamUtils.copyToByteArray(inputStream);
assertThat(actual).isEqualTo(bytes);
verify(inputStream, never()).close();
}
@Test
void copyToString() throws Exception {
Charset charset = Charset.defaultCharset();
InputStream inputStream = spy(new ByteArrayInputStream(string.getBytes(charset)));
InputStream inputStream = new ByteArrayInputStream(string.getBytes(charset));
String actual = StreamUtils.copyToString(inputStream, charset);
assertThat(actual).isEqualTo(string);
verify(inputStream, never()).close();
}
@Test
void copyBytes() throws Exception {
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.copy(bytes, out);
assertThat(out.toByteArray()).isEqualTo(bytes);
verify(out, never()).close();
}
@Test
void copyString() throws Exception {
Charset charset = Charset.defaultCharset();
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.copy(string, charset, out);
assertThat(out.toByteArray()).isEqualTo(string.getBytes(charset));
verify(out, never()).close();
}
@Test
void copyStream() throws Exception {
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.copy(new ByteArrayInputStream(bytes), out);
assertThat(out.toByteArray()).isEqualTo(bytes);
verify(out, never()).close();
}
@Test
void copyRange() throws Exception {
ByteArrayOutputStream out = spy(new ByteArrayOutputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.copyRange(new ByteArrayInputStream(bytes), out, 0, 100);
byte[] range = Arrays.copyOfRange(bytes, 0, 101);
assertThat(out.toByteArray()).isEqualTo(range);
verify(out, never()).close();
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import org.springframework.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor;
@@ -46,10 +47,15 @@ import org.springframework.util.Assert;
* Represents the invocation of a constructor. Either a constructor on a regular type or
* construction of an array. When an array is constructed, an initializer can be specified.
*
* <p>Examples:<br>
* new String('hello world')<br>
* new int[]{1,2,3,4}<br>
* new int[3] new int[3]{1,2,3}
* <h4>Examples</h4>
* <ul>
* <li><code>new example.Foo()</code></li>
* <li><code>new String('hello world')</code></li>
* <li><code>new int[] {1,2,3,4}</code></li>
* <li><code>new String[] {'abc','xyz'}</code></li>
* <li><code>new int[5]</code></li>
* <li><code>new int[3][4]</code></li>
* </ul>
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -68,7 +74,7 @@ public class ConstructorReference extends SpelNodeImpl {
private final boolean isArrayConstructor;
@Nullable
private SpelNodeImpl[] dimensions;
private final SpelNodeImpl[] dimensions;
// TODO is this caching safe - passing the expression around will mean this executor is also being passed around
/** The cached executor that may be reused on subsequent evaluations. */
@@ -83,6 +89,7 @@ public class ConstructorReference extends SpelNodeImpl {
public ConstructorReference(int startPos, int endPos, SpelNodeImpl... arguments) {
super(startPos, endPos, arguments);
this.isArrayConstructor = false;
this.dimensions = null;
}
/**
@@ -214,16 +221,33 @@ public class ConstructorReference extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder("new ");
int index = 0;
sb.append(getChild(index++).toStringAST());
sb.append('(');
for (int i = index; i < getChildCount(); i++) {
if (i > index) {
sb.append(',');
sb.append(getChild(0).toStringAST()); // constructor or array type
// Arrays
if (this.isArrayConstructor) {
if (hasInitializer()) {
// new int[] {1, 2, 3, 4, 5}, etc.
InlineList initializer = (InlineList) getChild(1);
sb.append("[] ").append(initializer.toStringAST());
}
else {
// new int[3], new java.lang.String[3][4], etc.
for (SpelNodeImpl dimension : this.dimensions) {
sb.append('[').append(dimension.toStringAST()).append(']');
}
}
sb.append(getChild(i).toStringAST());
}
sb.append(')');
// Constructors
else {
// new String('hello'), new org.example.Person('Jane', 32), etc.
StringJoiner sj = new StringJoiner(",", "(", ")");
int count = getChildCount();
for (int i = 1; i < count; i++) {
sj.add(getChild(i).toStringAST());
}
sb.append(sj.toString());
}
return sb.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -26,6 +26,7 @@ import org.springframework.util.StringUtils;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class StringLiteral extends Literal {
@@ -36,9 +37,19 @@ public class StringLiteral extends Literal {
public StringLiteral(String payload, int startPos, int endPos, String value) {
super(payload, startPos, endPos);
// The original enclosing quote character for the string literal: ' or ".
char quoteCharacter = value.charAt(0);
// Remove enclosing quotes
String valueWithinQuotes = value.substring(1, value.length() - 1);
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "''", "'");
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "\"\"", "\"");
// Replace escaped internal quote characters
if (quoteCharacter == '\'') {
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "''", "'");
}
else {
valueWithinQuotes = StringUtils.replace(valueWithinQuotes, "\"\"", "\"");
}
this.value = new TypedValue(valueWithinQuotes);
this.exitTypeDescriptor = "Ljava/lang/String";
@@ -52,7 +63,9 @@ public class StringLiteral extends Literal {
@Override
public String toString() {
return "'" + getLiteralValue().getValue() + "'";
String ast = String.valueOf(getLiteralValue().getValue());
ast = StringUtils.replace(ast, "'", "''");
return "'" + ast + "'";
}
@Override
@@ -364,6 +364,53 @@ class EvaluationTests extends AbstractExpressionTests {
}
@Nested
class StringLiterals {
@Test
void insideSingleQuotes() {
evaluate("'hello'", "hello", String.class);
evaluate("'hello world'", "hello world", String.class);
}
@Test
void insideDoubleQuotes() {
evaluate("\"hello\"", "hello", String.class);
evaluate("\"hello world\"", "hello world", String.class);
}
@Test
void singleQuotesInsideSingleQuotes() {
evaluate("'Tony''s Pizza'", "Tony's Pizza", String.class);
evaluate("'big ''''pizza'''' parlor'", "big ''pizza'' parlor", String.class);
}
@Test
void doubleQuotesInsideDoubleQuotes() {
evaluate("\"big \"\"pizza\"\" parlor\"", "big \"pizza\" parlor", String.class);
evaluate("\"big \"\"\"\"pizza\"\"\"\" parlor\"", "big \"\"pizza\"\" parlor", String.class);
}
@Test
void singleQuotesInsideDoubleQuotes() {
evaluate("\"Tony's Pizza\"", "Tony's Pizza", String.class);
evaluate("\"big ''pizza'' parlor\"", "big ''pizza'' parlor", String.class);
}
@Test
void doubleQuotesInsideSingleQuotes() {
evaluate("'big \"pizza\" parlor'", "big \"pizza\" parlor", String.class);
evaluate("'two double \"\" quotes'", "two double \"\" quotes", String.class);
}
@Test
void inCompoundExpressions() {
evaluate("'123''4' == '123''4'", true, Boolean.class);
evaluate("\"123\"\"4\" == \"123\"\"4\"", true, Boolean.class);
}
}
@Nested
class RelationalOperatorTests {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -29,52 +29,70 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Andy Clement
*/
public class LiteralExpressionTests {
class LiteralExpressionTests {
private final LiteralExpression lEx = new LiteralExpression("somevalue");
@Test
public void testGetValue() throws Exception {
LiteralExpression lEx = new LiteralExpression("somevalue");
assertThat(lEx.getValue()).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
void getValue() throws Exception {
assertThat(lEx.getValue()).isEqualTo("somevalue");
assertThat(lEx.getValue(String.class)).isEqualTo("somevalue");
assertThat(lEx.getValue(new Rooty())).isEqualTo("somevalue");
assertThat(lEx.getValue(new Rooty(), String.class)).isEqualTo("somevalue");
}
@Test
void getValueWithSuppliedEvaluationContext() throws Exception {
EvaluationContext ctx = new StandardEvaluationContext();
assertThat(lEx.getValue(ctx)).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(new Rooty())).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(new Rooty(), String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, new Rooty())).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, new Rooty(),String.class)).isInstanceOf(String.class).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx)).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, String.class)).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, new Rooty())).isEqualTo("somevalue");
assertThat(lEx.getValue(ctx, new Rooty(), String.class)).isEqualTo("somevalue");
}
@Test
void getExpressionString() {
assertThat(lEx.getExpressionString()).isEqualTo("somevalue");
}
@Test
void isWritable() throws Exception {
assertThat(lEx.isWritable(new StandardEvaluationContext())).isFalse();
assertThat(lEx.isWritable(new Rooty())).isFalse();
assertThat(lEx.isWritable(new StandardEvaluationContext(), new Rooty())).isFalse();
}
static class Rooty {}
@Test
public void testSetValue() {
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
new LiteralExpression("somevalue").setValue(new StandardEvaluationContext(), "flibble"))
void setValue() {
assertThatExceptionOfType(EvaluationException.class)
.isThrownBy(() -> lEx.setValue(new StandardEvaluationContext(), "flibble"))
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
new LiteralExpression("somevalue").setValue(new Rooty(), "flibble"))
assertThatExceptionOfType(EvaluationException.class)
.isThrownBy(() -> lEx.setValue(new Rooty(), "flibble"))
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
new LiteralExpression("somevalue").setValue(new StandardEvaluationContext(), new Rooty(), "flibble"))
assertThatExceptionOfType(EvaluationException.class)
.isThrownBy(() -> lEx.setValue(new StandardEvaluationContext(), new Rooty(), "flibble"))
.satisfies(ex -> assertThat(ex.getExpressionString()).isEqualTo("somevalue"));
}
@Test
public void testGetValueType() throws Exception {
LiteralExpression lEx = new LiteralExpression("somevalue");
void getValueType() throws Exception {
assertThat(lEx.getValueType()).isEqualTo(String.class);
assertThat(lEx.getValueType(new StandardEvaluationContext())).isEqualTo(String.class);
assertThat(lEx.getValueType(new Rooty())).isEqualTo(String.class);
assertThat(lEx.getValueType(new StandardEvaluationContext(), new Rooty())).isEqualTo(String.class);
}
@Test
void getValueTypeDescriptor() throws Exception {
assertThat(lEx.getValueTypeDescriptor().getType()).isEqualTo(String.class);
assertThat(lEx.getValueTypeDescriptor(new StandardEvaluationContext()).getType()).isEqualTo(String.class);
assertThat(lEx.getValueTypeDescriptor(new Rooty()).getType()).isEqualTo(String.class);
assertThat(lEx.getValueTypeDescriptor(new StandardEvaluationContext(), new Rooty()).getType()).isEqualTo(String.class);
}
static class Rooty {}
}
@@ -16,6 +16,8 @@
package org.springframework.expression.spel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.expression.spel.standard.SpelExpression;
@@ -37,413 +39,517 @@ class ParsingTests {
private final SpelExpressionParser parser = new SpelExpressionParser();
// literals
@Test
void literalBoolean01() {
parseCheck("false");
@Nested
class Miscellaneous {
@Test
void literalNull() {
parseCheck("null");
}
@Test
void literalDate01() {
parseCheck("date('1974/08/24')");
}
@Test
void literalDate02() {
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
}
@Test
void mixedOperators() {
parseCheck("true and 5>3", "(true and (5 > 3))");
}
@Test
void assignmentToVariables() {
parseCheck("#var1='value1'");
}
@Test
void collectionProcessorsCountStringArray() {
parseCheck("new String[] {'abc','def','xyz'}.count()");
}
@Test
void collectionProcessorsCountIntArray() {
parseCheck("new int[] {1,2,3}.count()");
}
@Test
void collectionProcessorsMax() {
parseCheck("new int[] {1,2,3}.max()");
}
@Test
void collectionProcessorsMin() {
parseCheck("new int[] {1,2,3}.min()");
}
@Test
void collectionProcessorsAverage() {
parseCheck("new int[] {1,2,3}.average()");
}
@Test
void collectionProcessorsSort() {
parseCheck("new int[] {3,2,1}.sort()");
}
@Test
void collectionProcessorsNonNull() {
parseCheck("{'a','b',null,'d',null}.nonNull()");
}
@Test
void collectionProcessorsDistinct() {
parseCheck("{'a','b','a','d','e'}.distinct()");
}
@Disabled("Unsupported syntax/feature")
@Test
void lambdaMax() {
parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))",
"(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
}
@Disabled("Unsupported syntax/feature")
@Test
void lambdaFactorial() {
parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
"(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
}
@Disabled("Unsupported syntax/feature")
@Test
void projection() {
parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
}
@Disabled("Unsupported syntax/feature")
@Test
void selection() {
parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
"{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
}
@Disabled("Unsupported syntax/feature")
@Test
void selectionFirst() {
parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
"{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
}
@Disabled("Unsupported syntax/feature")
@Test
void selectionLast() {
parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
"{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
}
}
@Test
void literalLong01() {
parseCheck("37L", "37");
@Nested
class LiteralBooleans {
@Test
void literalBooleanFalse() {
parseCheck("false");
}
@Test
void literalBooleanTrue() {
parseCheck("true");
}
@Test
void literalBooleanNotTrue() {
parseCheck("!true");
parseCheck("not true", "!true");
}
}
@Test
void literalBoolean02() {
parseCheck("true");
@Nested
class LiteralNumbers {
@Test
void literalLong() {
parseCheck("37L", "37");
}
@Test
void literalIntegers() {
parseCheck("1");
parseCheck("1415");
}
@Test
void literalReal() {
parseCheck("6.0221415E+23", "6.0221415E23");
}
@Test
void literalHex() {
parseCheck("0x7FFFFFFF", "2147483647");
}
}
@Test
void literalBoolean03() {
parseCheck("!true");
@Nested
class LiteralStrings {
@Test
void insideSingleQuotes() {
parseCheck("'hello'");
parseCheck("'hello world'");
}
@Test
void insideDoubleQuotes() {
parseCheck("\"hello\"", "'hello'");
parseCheck("\"hello world\"", "'hello world'");
}
@Test
void singleQuotesInsideSingleQuotes() {
parseCheck("'Tony''s Pizza'");
parseCheck("'big ''''pizza'''' parlor'");
}
@Test
void doubleQuotesInsideDoubleQuotes() {
parseCheck("\"big \"\"pizza\"\" parlor\"", "'big \"pizza\" parlor'");
parseCheck("\"big \"\"\"\"pizza\"\"\"\" parlor\"", "'big \"\"pizza\"\" parlor'");
}
@Test
void singleQuotesInsideDoubleQuotes() {
parseCheck("\"Tony's Pizza\"", "'Tony''s Pizza'");
parseCheck("\"big ''pizza'' parlor\"", "'big ''''pizza'''' parlor'");
}
@Test
void doubleQuotesInsideSingleQuotes() {
parseCheck("'big \"pizza\" parlor'");
parseCheck("'two double \"\" quotes'");
}
@Test
void inCompoundExpressions() {
parseCheck("'123''4' == '123''4'", "('123''4' == '123''4')");
parseCheck("('123''4'=='123''4')", "('123''4' == '123''4')");
parseCheck("\"123\"\"4\" == \"123\"\"4\"", "('123\"4' == '123\"4')");
}
}
@Test
void literalInteger01() {
parseCheck("1");
@Nested
class BooleanOperators {
@Test
void booleanOperatorsOr01() {
parseCheck("false or false", "(false or false)");
}
@Test
void booleanOperatorsOr02() {
parseCheck("false or true", "(false or true)");
}
@Test
void booleanOperatorsOr03() {
parseCheck("true or false", "(true or false)");
}
@Test
void booleanOperatorsOr04() {
parseCheck("true or false", "(true or false)");
}
@Test
void booleanOperatorsMix() {
parseCheck("false or true and false", "(false or (true and false))");
}
}
@Test
void literalInteger02() {
parseCheck("1415");
@Nested
class RelationalOperators {
@Test
void relOperatorsGT() {
parseCheck("3>6", "(3 > 6)");
}
@Test
void relOperatorsLT() {
parseCheck("3<6", "(3 < 6)");
}
@Test
void relOperatorsLE() {
parseCheck("3<=6", "(3 <= 6)");
}
@Test
void relOperatorsGE01() {
parseCheck("3>=6", "(3 >= 6)");
}
@Test
void relOperatorsGE02() {
parseCheck("3>=3", "(3 >= 3)");
}
@Disabled("Unsupported syntax/feature")
@Test
void relOperatorsIn() {
parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
}
@Test
void relOperatorsBetweenNumbers() {
parseCheck("1 between {1, 5}", "(1 between {1,5})");
}
@Test
void relOperatorsBetweenStrings() {
parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
}
@Test
void relOperatorsInstanceOfInt() {
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
}
@Test
void relOperatorsInstanceOfList() {
parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
}
@Test
void relOperatorsMatches() {
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
}
}
@Test
void literalString01() {
parseCheck("'hello'");
@Nested
class MathematicalOperators {
@Test
void mathOperatorsAddIntegers() {
parseCheck("2+4", "(2 + 4)");
}
@Test
void mathOperatorsAddStrings() {
parseCheck("'a'+'b'", "('a' + 'b')");
}
@Test
void mathOperatorsAddMultipleStrings() {
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
}
@Test
void mathOperatorsSubtract() {
parseCheck("5-4", "(5 - 4)");
}
@Test
void mathOperatorsMultiply() {
parseCheck("7*4", "(7 * 4)");
}
@Test
void mathOperatorsDivide() {
parseCheck("8/4", "(8 / 4)");
}
@Test
void mathOperatorModulus() {
parseCheck("7 % 4", "(7 % 4)");
}
}
@Test
void literalString02() {
parseCheck("'joe bloggs'");
@Nested
class References {
@Test
void references() {
parseCheck("@foo");
parseCheck("@'foo.bar'");
parseCheck("@\"foo.bar.goo\"" , "@'foo.bar.goo'");
parseCheck("@$$foo");
}
}
@Test
void literalString03() {
parseCheck("'Tony''s Pizza'", "'Tony's Pizza'");
@Nested
class Properties {
@Test
void propertiesSingle() {
parseCheck("name");
}
@Test
void propertiesDouble() {
parseCheck("placeofbirth.CitY");
}
@Test
void propertiesMultiple() {
parseCheck("a.b.c.d.e");
}
}
@Test
void literalReal01() {
parseCheck("6.0221415E+23", "6.0221415E23");
@Nested
class InlineCollections {
@Test
void inlineListOfIntegers() {
parseCheck("{1,2,3,4}");
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
}
@Test
void inlineListOfStrings() {
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
parseCheck("{\"abc\", 'xyz'}", "{'abc','xyz'}");
}
@Test
void inlineMapStringToObject() {
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
}
@Test
void inlineMapIntegerToString() {
parseCheck("{1:'January',2:'February',3:'March'}");
}
}
@Test
void literalHex01() {
parseCheck("0x7FFFFFFF", "2147483647");
@Nested
class MethodsConstructorsAndArrays {
@Test
void methods() {
parseCheck("echo()");
parseCheck("echo(12)");
parseCheck("echo(name)");
parseCheck("echo('Jane')");
parseCheck("echo('Jane',32)");
parseCheck("echo('Jane', 32)", "echo('Jane',32)");
parseCheck("age.doubleItAndAdd(12)");
}
@Test
void constructorWithNoArguments() {
parseCheck("new Foo()");
parseCheck("new example.Foo()");
}
@Test
void constructorWithOneArgument() {
parseCheck("new String('hello')");
parseCheck("new String( 'hello' )", "new String('hello')");
parseCheck("new String(\"hello\" )", "new String('hello')");
}
@Test
void constructorWithMultipleArguments() {
parseCheck("new example.Person('Jane',32,true)");
parseCheck("new example.Person('Jane', 32, true)", "new example.Person('Jane',32,true)");
parseCheck("new example.Person('Jane', 2 * 16, true)", "new example.Person('Jane',(2 * 16),true)");
}
@Test
void arrayConstructionWithOneDimensionalReferenceType() {
parseCheck("new String[3]");
}
@Test
void arrayConstructionWithOneDimensionalFullyQualifiedReferenceType() {
parseCheck("new java.lang.String[3]");
}
@Test
void arrayConstructionWithOneDimensionalPrimitiveType() {
parseCheck("new int[3]");
}
@Test
void arrayConstructionWithMultiDimensionalReferenceType() {
parseCheck("new Float[3][4]");
}
@Test
void arrayConstructionWithMultiDimensionalPrimitiveType() {
parseCheck("new int[3][4]");
}
@Test
void arrayConstructionWithOneDimensionalReferenceTypeWithInitializer() {
parseCheck("new String[] {'abc','xyz'}");
parseCheck("new String[] {'abc', 'xyz'}", "new String[] {'abc','xyz'}");
}
@Test
void arrayConstructionWithOneDimensionalPrimitiveTypeWithInitializer() {
parseCheck("new int[] {1,2,3,4,5}");
parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
}
}
@Test
void literalDate01() {
parseCheck("date('1974/08/24')");
@Nested
class VariablesAndFunctions {
@Test
void variables() {
parseCheck("#foo");
}
@Test
void functions() {
parseCheck("#fn(1,2,3)");
parseCheck("#fn('hello')");
}
}
@Test
void literalDate02() {
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
@Nested
class ElvisAndTernaryOperators {
@Test
void elvis() {
parseCheck("3?:1", "(3 ?: 1)");
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
}
@Test
void ternary() {
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
}
}
@Test
void literalNull01() {
parseCheck("null");
@Nested
class TypeReferences {
@Test
void typeReferences01() {
parseCheck("T(java.lang.String)");
}
@Test
void typeReferences02() {
parseCheck("T(String)");
}
}
// boolean operators
@Test
void booleanOperatorsOr01() {
parseCheck("false or false", "(false or false)");
}
@Test
void booleanOperatorsOr02() {
parseCheck("false or true", "(false or true)");
}
@Test
void booleanOperatorsOr03() {
parseCheck("true or false", "(true or false)");
}
@Test
void booleanOperatorsOr04() {
parseCheck("true or false", "(true or false)");
}
@Test
void booleanOperatorsMix01() {
parseCheck("false or true and false", "(false or (true and false))");
}
// relational operators
@Test
void relOperatorsGT01() {
parseCheck("3>6", "(3 > 6)");
}
@Test
void relOperatorsLT01() {
parseCheck("3<6", "(3 < 6)");
}
@Test
void relOperatorsLE01() {
parseCheck("3<=6", "(3 <= 6)");
}
@Test
void relOperatorsGE01() {
parseCheck("3>=6", "(3 >= 6)");
}
@Test
void relOperatorsGE02() {
parseCheck("3>=3", "(3 >= 3)");
}
@Test
void elvis() {
parseCheck("3?:1", "(3 ?: 1)");
parseCheck("(2*3)?:1*10", "((2 * 3) ?: (1 * 10))");
parseCheck("((2*3)?:1)*10", "(((2 * 3) ?: 1) * 10)");
}
// void relOperatorsIn01() {
// parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
// }
//
// void relOperatorsBetween01() {
// parseCheck("1 between {1, 5}", "(1 between {1,5})");
// }
// void relOperatorsBetween02() {
// parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
// }// true
@Test
void relOperatorsIs01() {
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
}// false
// void relOperatorsIs02() {
// parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
// }// true
@Test
void relOperatorsMatches01() {
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
}// false
@Test
void relOperatorsMatches02() {
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
}// true
// mathematical operators
@Test
void mathOperatorsAdd01() {
parseCheck("2+4", "(2 + 4)");
}
@Test
void mathOperatorsAdd02() {
parseCheck("'a'+'b'", "('a' + 'b')");
}
@Test
void mathOperatorsAdd03() {
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
}
@Test
void mathOperatorsSubtract01() {
parseCheck("5-4", "(5 - 4)");
}
@Test
void mathOperatorsMultiply01() {
parseCheck("7*4", "(7 * 4)");
}
@Test
void mathOperatorsDivide01() {
parseCheck("8/4", "(8 / 4)");
}
@Test
void mathOperatorModulus01() {
parseCheck("7 % 4", "(7 % 4)");
}
// mixed operators
@Test
void mixedOperators01() {
parseCheck("true and 5>3", "(true and (5 > 3))");
}
// collection processors
// void collectionProcessorsCount01() {
// parseCheck("new String[] {'abc','def','xyz'}.count()");
// }
// void collectionProcessorsCount02() {
// parseCheck("new int[] {1,2,3}.count()");
// }
//
// void collectionProcessorsMax01() {
// parseCheck("new int[] {1,2,3}.max()");
// }
//
// void collectionProcessorsMin01() {
// parseCheck("new int[] {1,2,3}.min()");
// }
//
// void collectionProcessorsAverage01() {
// parseCheck("new int[] {1,2,3}.average()");
// }
//
// void collectionProcessorsSort01() {
// parseCheck("new int[] {3,2,1}.sort()");
// }
//
// void collectionProcessorsNonNull01() {
// parseCheck("{'a','b',null,'d',null}.nonNull()");
// }
//
// void collectionProcessorsDistinct01() {
// parseCheck("{'a','b','a','d','e'}.distinct()");
// }
// references
@Test
void references01() {
parseCheck("@foo");
parseCheck("@'foo.bar'");
parseCheck("@\"foo.bar.goo\"" , "@'foo.bar.goo'");
}
@Test
void references03() {
parseCheck("@$$foo");
}
// properties
@Test
void properties01() {
parseCheck("name");
}
@Test
void properties02() {
parseCheck("placeofbirth.CitY");
}
@Test
void properties03() {
parseCheck("a.b.c.d.e");
}
// inline list creation
@Test
void inlineListCreation01() {
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
}
@Test
void inlineListCreation02() {
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
}
// inline map creation
@Test
void inlineMapCreation01() {
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
}
@Test
void inlineMapCreation02() {
parseCheck("{1:'January',2:'February',3:'March'}");
}
// methods
@Test
void methods01() {
parseCheck("echo(12)");
}
@Test
void methods02() {
parseCheck("echo(name)");
}
@Test
void methods03() {
parseCheck("age.doubleItAndAdd(12)");
}
// constructors
@Test
void constructors01() {
parseCheck("new String('hello')");
}
// void constructors02() {
// parseCheck("new String[3]");
// }
// array construction
// void arrayConstruction01() {
// parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
// }
//
// void arrayConstruction02() {
// parseCheck("new String[] {'abc','xyz'}", "new String[] {'abc','xyz'}");
// }
// variables and functions
@Test
void variables01() {
parseCheck("#foo");
}
@Test
void functions01() {
parseCheck("#fn(1,2,3)");
}
@Test
void functions02() {
parseCheck("#fn('hello')");
}
// projections and selections
// void projections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
// }
// void selections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
// }
// void selectionsFirst01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
// }
// void selectionsLast01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
// }
// assignment
@Test
void assignmentToVariables01() {
parseCheck("#var1='value1'");
}
// ternary operator
@Test
void ternaryOperator01() {
parseCheck("1>2?3:4", "((1 > 2) ? 3 : 4)");
parseCheck("(a ? 1 : 0) * 10", "((a ? 1 : 0) * 10)");
parseCheck("(a?1:0)*10", "((a ? 1 : 0) * 10)");
parseCheck("(4 % 2 == 0 ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
parseCheck("((4 % 2 == 0) ? 1 : 0) * 10", "((((4 % 2) == 0) ? 1 : 0) * 10)");
}
@Test
void ternaryOperator02() {
parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
"(({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd')");
}
//
// void lambdaMax() {
// parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
// }
//
// void lambdaFactorial() {
// parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
// "(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
// } // 120
// Type references
@Test
void typeReferences01() {
parseCheck("T(java.lang.String)");
}
@Test
void typeReferences02() {
parseCheck("T(String)");
}
@Test
void inlineList1() {
parseCheck("{1,2,3,4}");
}
/**
* Parse the supplied expression and then create a string representation of the resultant AST, it should be the same
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -209,7 +209,7 @@ class SpelReproTests extends AbstractExpressionTests {
checkTemplateParsingError("abc${ } }", "No expression defined within delimiter '${}' at character 3");
checkTemplateParsingError("abc$[ } ]", DOLLARSQUARE_TEMPLATE_PARSER_CONTEXT, "Found closing '}' at position 6 without an opening '{'");
checkTemplateParsing("abc ${\"def''g}hi\"} jkl", "abc def'g}hi jkl");
checkTemplateParsing("abc ${\"def''g}hi\"} jkl", "abc def''g}hi jkl");
checkTemplateParsing("abc ${'def''g}hi'} jkl", "abc def'g}hi jkl");
checkTemplateParsing("}", "}");
checkTemplateParsing("${'hello'} world", "hello world");
@@ -0,0 +1,34 @@
/*
* 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.expression.spel;
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-expression module.
*
* <p><strong>This suite is only intended to be used manually within an IDE.</strong>
*
* @author Sam Brannen
*/
@Suite
@SelectPackages("org.springframework.expression.spel")
@IncludeClassNamePatterns(".*Tests?$")
class SpringExpressionTestSuite {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -30,15 +30,15 @@ import org.springframework.lang.Nullable;
/**
* Object to represent an SQL BLOB/CLOB value parameter. BLOBs can either be an
* InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream
* InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream,
* or String. Each CLOB/BLOB value will be stored together with its length.
* The type is based on which constructor is used. Objects of this class are
* immutable except for the LobCreator reference. Use them and discard them.
* The type is based on which constructor is used. Instances of this class are
* stateful and immutable: use them and discard them.
*
* <p>This class holds a reference to a LocCreator that must be closed after the
* update has completed. This is done via a call to the closeLobCreator method.
* All handling of the LobCreator is done by the framework classes that use it -
* no need to set or close the LobCreator for end users of this class.
* <p>This class holds a reference to a {@link LobCreator} that must be closed after
* the update has completed. This is done via a call to the {@link #cleanup()} method.
* All handling of the {@code LobCreator} is done by the framework classes that use it -
* no need to set or close the {@code LobCreator} for end users of this class.
*
* <p>A usage example:
*
@@ -72,8 +72,7 @@ public class SqlLobValue implements DisposableSqlTypeValue {
private final int length;
/**
* This contains a reference to the LobCreator - so we can close it
* once the update is done.
* Reference to the LobCreator - so we can close it once the update is done.
*/
private final LobCreator lobCreator;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -69,10 +69,10 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
if (ex instanceof SQLTransientConnectionException) {
return new TransientDataAccessResourceException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLTransactionRollbackException) {
if (ex instanceof SQLTransactionRollbackException) {
return new ConcurrencyFailureException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLTimeoutException) {
if (ex instanceof SQLTimeoutException) {
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
}
}
@@ -80,19 +80,19 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
if (ex instanceof SQLNonTransientConnectionException) {
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLDataException) {
if (ex instanceof SQLDataException) {
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLIntegrityConstraintViolationException) {
if (ex instanceof SQLIntegrityConstraintViolationException) {
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLInvalidAuthorizationSpecException) {
if (ex instanceof SQLInvalidAuthorizationSpecException) {
return new PermissionDeniedDataAccessException(buildMessage(task, sql, ex), ex);
}
else if (ex instanceof SQLSyntaxErrorException) {
if (ex instanceof SQLSyntaxErrorException) {
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
}
else if (ex instanceof SQLFeatureNotSupportedException) {
if (ex instanceof SQLFeatureNotSupportedException) {
return new InvalidDataAccessApiUsageException(buildMessage(task, sql, ex), ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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,6 +16,7 @@
package org.springframework.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import org.junit.jupiter.api.Test;
@@ -37,8 +38,8 @@ public class SQLExceptionCustomTranslatorTests {
private static SQLErrorCodes ERROR_CODES = new SQLErrorCodes();
static {
ERROR_CODES.setBadSqlGrammarCodes(new String[] { "1" });
ERROR_CODES.setDataAccessResourceFailureCodes(new String[] { "2" });
ERROR_CODES.setBadSqlGrammarCodes("1");
ERROR_CODES.setDataAccessResourceFailureCodes("2");
ERROR_CODES.setCustomSqlExceptionTranslatorClass(CustomSqlExceptionTranslator.class);
}
@@ -47,7 +48,7 @@ public class SQLExceptionCustomTranslatorTests {
@Test
public void badSqlGrammarException() {
SQLException badSqlGrammarExceptionEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 1);
SQLException badSqlGrammarExceptionEx = new SQLDataException("", "", 1);
DataAccessException dae = sext.translate("task", "SQL", badSqlGrammarExceptionEx);
assertThat(dae.getCause()).isEqualTo(badSqlGrammarExceptionEx);
assertThat(dae).isInstanceOf(BadSqlGrammarException.class);
@@ -55,7 +56,7 @@ public class SQLExceptionCustomTranslatorTests {
@Test
public void dataAccessResourceException() {
SQLException dataAccessResourceEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 2);
SQLException dataAccessResourceEx = new SQLDataException("", "", 2);
DataAccessException dae = sext.translate("task", "SQL", dataAccessResourceEx);
assertThat(dae.getCause()).isEqualTo(dataAccessResourceEx);
assertThat(dae).isInstanceOf(TransientDataAccessResourceException.class);
@@ -1,78 +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.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientConnectionException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransactionRollbackException;
import java.sql.SQLTransientConnectionException;
/**
* Class to generate {@link SQLException} subclasses for testing purposes.
*
* @author Thomas Risberg
*/
public class SQLExceptionSubclassFactory {
public static SQLException newSQLDataException(String reason, String SQLState, int vendorCode) {
return new SQLDataException(reason, SQLState, vendorCode);
}
public static SQLException newSQLFeatureNotSupportedException(String reason, String SQLState, int vendorCode) {
return new SQLFeatureNotSupportedException(reason, SQLState, vendorCode);
}
public static SQLException newSQLIntegrityConstraintViolationException(String reason, String SQLState, int vendorCode) {
return new SQLIntegrityConstraintViolationException(reason, SQLState, vendorCode);
}
public static SQLException newSQLInvalidAuthorizationSpecException(String reason, String SQLState, int vendorCode) {
return new SQLInvalidAuthorizationSpecException(reason, SQLState, vendorCode);
}
public static SQLException newSQLNonTransientConnectionException(String reason, String SQLState, int vendorCode) {
return new SQLNonTransientConnectionException(reason, SQLState, vendorCode);
}
public static SQLException newSQLSyntaxErrorException(String reason, String SQLState, int vendorCode) {
return new SQLSyntaxErrorException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTransactionRollbackException(String reason, String SQLState, int vendorCode) {
return new SQLTransactionRollbackException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTransientConnectionException(String reason, String SQLState, int vendorCode) {
return new SQLTransientConnectionException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTimeoutException(String reason, String SQLState, int vendorCode) {
return new SQLTimeoutException(reason, SQLState, vendorCode);
}
public static SQLException newSQLRecoverableException(String reason, String SQLState, int vendorCode) {
return new SQLRecoverableException(reason, SQLState, vendorCode);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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,11 +16,22 @@
package org.springframework.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientConnectionException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransactionRollbackException;
import java.sql.SQLTransientConnectionException;
import org.junit.jupiter.api.Test;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -34,78 +45,41 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Thomas Risberg
* @author Juergen Hoeller
*/
public class SQLExceptionSubclassTranslatorTests {
private static SQLErrorCodes ERROR_CODES = new SQLErrorCodes();
@Test
public void exceptionClassTranslation() {
doTest(new SQLDataException("", "", 0), DataIntegrityViolationException.class);
doTest(new SQLFeatureNotSupportedException("", "", 0), InvalidDataAccessApiUsageException.class);
doTest(new SQLIntegrityConstraintViolationException("", "", 0), DataIntegrityViolationException.class);
doTest(new SQLInvalidAuthorizationSpecException("", "", 0), PermissionDeniedDataAccessException.class);
doTest(new SQLNonTransientConnectionException("", "", 0), DataAccessResourceFailureException.class);
doTest(new SQLRecoverableException("", "", 0), RecoverableDataAccessException.class);
doTest(new SQLSyntaxErrorException("", "", 0), BadSqlGrammarException.class);
doTest(new SQLTimeoutException("", "", 0), QueryTimeoutException.class);
doTest(new SQLTransactionRollbackException("", "", 0), ConcurrencyFailureException.class);
doTest(new SQLTransientConnectionException("", "", 0), TransientDataAccessResourceException.class);
}
static {
ERROR_CODES.setBadSqlGrammarCodes("1");
@Test
public void fallbackStateTranslation() {
// Test fallback. We assume that no database will ever return this error code,
// but 07xxx will be bad grammar picked up by the fallback SQLState translator
doTest(new SQLException("", "07xxx", 666666666), BadSqlGrammarException.class);
// and 08xxx will be data resource failure (non-transient) picked up by the fallback SQLState translator
doTest(new SQLException("", "08xxx", 666666666), DataAccessResourceFailureException.class);
}
@Test
public void errorCodeTranslation() {
SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES);
private void doTest(SQLException ex, Class<?> dataAccessExceptionType) {
SQLExceptionTranslator translator = new SQLExceptionSubclassTranslator();
DataAccessException dax = translator.translate("task", "SQL", ex);
SQLException dataIntegrityViolationEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 0);
DataIntegrityViolationException divex = (DataIntegrityViolationException) sext.translate("task", "SQL", dataIntegrityViolationEx);
assertThat(divex.getCause()).isEqualTo(dataIntegrityViolationEx);
SQLException featureNotSupEx = SQLExceptionSubclassFactory.newSQLFeatureNotSupportedException("", "", 0);
InvalidDataAccessApiUsageException idaex = (InvalidDataAccessApiUsageException) sext.translate("task", "SQL", featureNotSupEx);
assertThat(idaex.getCause()).isEqualTo(featureNotSupEx);
SQLException dataIntegrityViolationEx2 = SQLExceptionSubclassFactory.newSQLIntegrityConstraintViolationException("", "", 0);
DataIntegrityViolationException divex2 = (DataIntegrityViolationException) sext.translate("task", "SQL", dataIntegrityViolationEx2);
assertThat(divex2.getCause()).isEqualTo(dataIntegrityViolationEx2);
SQLException permissionDeniedEx = SQLExceptionSubclassFactory.newSQLInvalidAuthorizationSpecException("", "", 0);
PermissionDeniedDataAccessException pdaex = (PermissionDeniedDataAccessException) sext.translate("task", "SQL", permissionDeniedEx);
assertThat(pdaex.getCause()).isEqualTo(permissionDeniedEx);
SQLException dataAccessResourceEx = SQLExceptionSubclassFactory.newSQLNonTransientConnectionException("", "", 0);
DataAccessResourceFailureException darex = (DataAccessResourceFailureException) sext.translate("task", "SQL", dataAccessResourceEx);
assertThat(darex.getCause()).isEqualTo(dataAccessResourceEx);
SQLException badSqlEx2 = SQLExceptionSubclassFactory.newSQLSyntaxErrorException("", "", 0);
BadSqlGrammarException bsgex2 = (BadSqlGrammarException) sext.translate("task", "SQL2", badSqlEx2);
assertThat(bsgex2.getSql()).isEqualTo("SQL2");
assertThat((Object) bsgex2.getSQLException()).isEqualTo(badSqlEx2);
SQLException tranRollbackEx = SQLExceptionSubclassFactory.newSQLTransactionRollbackException("", "", 0);
ConcurrencyFailureException cfex = (ConcurrencyFailureException) sext.translate("task", "SQL", tranRollbackEx);
assertThat(cfex.getCause()).isEqualTo(tranRollbackEx);
SQLException transientConnEx = SQLExceptionSubclassFactory.newSQLTransientConnectionException("", "", 0);
TransientDataAccessResourceException tdarex = (TransientDataAccessResourceException) sext.translate("task", "SQL", transientConnEx);
assertThat(tdarex.getCause()).isEqualTo(transientConnEx);
SQLException transientConnEx2 = SQLExceptionSubclassFactory.newSQLTimeoutException("", "", 0);
QueryTimeoutException tdarex2 = (QueryTimeoutException) sext.translate("task", "SQL", transientConnEx2);
assertThat(tdarex2.getCause()).isEqualTo(transientConnEx2);
SQLException recoverableEx = SQLExceptionSubclassFactory.newSQLRecoverableException("", "", 0);
RecoverableDataAccessException rdaex2 = (RecoverableDataAccessException) sext.translate("task", "SQL", recoverableEx);
assertThat(rdaex2.getCause()).isEqualTo(recoverableEx);
// Test classic error code translation. We should move there next if the exception we pass in is not one
// of the new subclasses.
SQLException sexEct = new SQLException("", "", 1);
BadSqlGrammarException bsgEct = (BadSqlGrammarException) sext.translate("task", "SQL-ECT", sexEct);
assertThat(bsgEct.getSql()).isEqualTo("SQL-ECT");
assertThat((Object) bsgEct.getSQLException()).isEqualTo(sexEct);
// Test fallback. We assume that no database will ever return this error code,
// but 07xxx will be bad grammar picked up by the fallback SQLState translator
SQLException sexFbt = new SQLException("", "07xxx", 666666666);
BadSqlGrammarException bsgFbt = (BadSqlGrammarException) sext.translate("task", "SQL-FBT", sexFbt);
assertThat(bsgFbt.getSql()).isEqualTo("SQL-FBT");
assertThat((Object) bsgFbt.getSQLException()).isEqualTo(sexFbt);
// and 08xxx will be data resource failure (non-transient) picked up by the fallback SQLState translator
SQLException sexFbt2 = new SQLException("", "08xxx", 666666666);
DataAccessResourceFailureException darfFbt = (DataAccessResourceFailureException) sext.translate("task", "SQL-FBT2", sexFbt2);
assertThat(darfFbt.getCause()).isEqualTo(sexFbt2);
assertThat(dax).as("Specific translation must not result in null").isNotNull();
assertThat(dax).as("Wrong DataAccessException type returned").isExactlyInstanceOf(dataAccessExceptionType);
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved").isSameAs(ex);
}
}
@@ -1,77 +0,0 @@
/*
* 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.
* 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.jdbc.support;
import java.sql.SQLException;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.BadSqlGrammarException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @since 13-Jan-03
*/
public class SQLStateExceptionTranslatorTests {
private static final String sql = "SELECT FOO FROM BAR";
private final SQLStateSQLExceptionTranslator trans = new SQLStateSQLExceptionTranslator();
// ALSO CHECK CHAIN of SQLExceptions!?
// also allow chain of translators? default if can't do specific?
@Test
public void badSqlGrammar() {
SQLException sex = new SQLException("Message", "42001", 1);
try {
throw this.trans.translate("task", sql, sex);
}
catch (BadSqlGrammarException ex) {
// OK
assertThat(sql.equals(ex.getSql())).as("SQL is correct").isTrue();
assertThat(sex.equals(ex.getSQLException())).as("Exception matches").isTrue();
}
}
@Test
public void invalidSqlStateCode() {
SQLException sex = new SQLException("Message", "NO SUCH CODE", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
}
/**
* PostgreSQL can return null.
* SAP DB can apparently return empty SQL code.
* Bug 729170
*/
@Test
public void malformedSqlStateCodes() {
SQLException sex = new SQLException("Message", null, 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
sex = new SQLException("Message", "", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
// One char's not allowed
sex = new SQLException("Message", "I", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -26,6 +26,7 @@ import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -37,58 +38,73 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/
public class SQLStateSQLExceptionTranslatorTests {
private static final String REASON = "The game is afoot!";
private static final String TASK = "Counting sheep... yawn.";
private static final String SQL = "select count(0) from t_sheep where over_fence = ... yawn... 1";
@Test
public void testTranslateNullException() {
public void translateNullException() {
assertThatIllegalArgumentException().isThrownBy(() ->
new SQLStateSQLExceptionTranslator().translate("", "", null));
}
@Test
public void testTranslateBadSqlGrammar() {
public void translateBadSqlGrammar() {
doTest("07", BadSqlGrammarException.class);
}
@Test
public void testTranslateDataIntegrityViolation() {
public void translateDataIntegrityViolation() {
doTest("23", DataIntegrityViolationException.class);
}
@Test
public void testTranslateDataAccessResourceFailure() {
public void translateDataAccessResourceFailure() {
doTest("53", DataAccessResourceFailureException.class);
}
@Test
public void testTranslateTransientDataAccessResourceFailure() {
public void translateTransientDataAccessResourceFailure() {
doTest("S1", TransientDataAccessResourceException.class);
}
@Test
public void testTranslateConcurrencyFailure() {
public void translateConcurrencyFailure() {
doTest("40", ConcurrencyFailureException.class);
}
@Test
public void testTranslateUncategorized() {
assertThat(new SQLStateSQLExceptionTranslator().translate("", "", new SQLException(REASON, "00000000"))).isNull();
public void translateUncategorized() {
doTest("00000000", null);
}
@Test
public void invalidSqlStateCode() {
doTest("NO SUCH CODE", null);
}
/**
* PostgreSQL can return null.
* SAP DB can apparently return empty SQL code.
* Bug 729170
*/
@Test
public void malformedSqlStateCodes() {
doTest(null, null);
doTest("", null);
doTest("I", null);
}
private void doTest(String sqlState, Class<?> dataAccessExceptionType) {
SQLException ex = new SQLException(REASON, sqlState);
private void doTest(@Nullable String sqlState, @Nullable Class<?> dataAccessExceptionType) {
SQLExceptionTranslator translator = new SQLStateSQLExceptionTranslator();
DataAccessException dax = translator.translate(TASK, SQL, ex);
assertThat(dax).as("Specific translation must not result in a null DataAccessException being returned.").isNotNull();
assertThat(dax.getClass()).as("Wrong DataAccessException type returned as the result of the translation").isEqualTo(dataAccessExceptionType);
assertThat(dax.getCause()).as("The original SQLException must be preserved in the translated DataAccessException").isNotNull();
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved in the translated DataAccessException").isSameAs(ex);
SQLException ex = new SQLException("reason", sqlState);
DataAccessException dax = translator.translate("task", "SQL", ex);
if (dataAccessExceptionType == null) {
assertThat(dax).as("Expected translation to null").isNull();
return;
}
assertThat(dax).as("Specific translation must not result in null").isNotNull();
assertThat(dax).as("Wrong DataAccessException type returned").isExactlyInstanceOf(dataAccessExceptionType);
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved").isSameAs(ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -193,7 +193,13 @@ public abstract class JmsAccessor implements InitializingBean {
* @see javax.jms.ConnectionFactory#createConnection()
*/
protected Connection createConnection() throws JMSException {
return obtainConnectionFactory().createConnection();
ConnectionFactory cf = obtainConnectionFactory();
Connection con = cf.createConnection();
if (con == null) {
throw new javax.jms.IllegalStateException(
"ConnectionFactory returned null from createConnection(): " + cf);
}
return con;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -26,7 +26,7 @@ import java.util.Locale;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.messaging.Message;
@@ -81,7 +81,7 @@ public class PayloadMethodArgumentResolverTests {
this.paramAnnotatedRequired = new SynthesizingMethodParameter(payloadMethod, 2);
this.paramWithSpelExpression = new SynthesizingMethodParameter(payloadMethod, 3);
this.paramValidated = new SynthesizingMethodParameter(payloadMethod, 4);
this.paramValidated.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
this.paramValidated.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
this.paramValidatedNotAnnotated = new SynthesizingMethodParameter(payloadMethod, 5);
this.paramNotAnnotated = new SynthesizingMethodParameter(payloadMethod, 6);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
@@ -131,7 +131,7 @@ public class ResolvableMethod {
private static final SpringObjenesis objenesis = new SpringObjenesis();
private static final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private static final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
// Matches ValueConstants.DEFAULT_NONE (spring-web and spring-messaging)
private static final String DEFAULT_VALUE_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -221,7 +221,7 @@ public abstract class ConnectionFactoryUtils {
return new QueryTimeoutException(buildMessage(task, sql, ex), ex);
}
}
if (ex instanceof R2dbcNonTransientException) {
else if (ex instanceof R2dbcNonTransientException) {
if (ex instanceof R2dbcNonTransientResourceException) {
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -17,19 +17,15 @@
package org.springframework.test.web.servlet.samples.spr;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
@@ -37,26 +33,23 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
*
* @author Wesley Hall
*/
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration
public class MockMvcBuilderMethodChainTests {
@Autowired
private WebApplicationContext wac;
@SpringJUnitWebConfig
class MockMvcBuilderMethodChainTests {
@Test
public void chainMultiple() {
MockMvcBuilders
void chainMultiple(WebApplicationContext wac) {
assertThatNoException().isThrownBy(() ->
MockMvcBuilders
.webAppContextSetup(wac)
.addFilter(new CharacterEncodingFilter() )
.defaultRequest(get("/").contextPath("/mywebapp"))
.build();
.build()
);
}
@Configuration
@EnableWebMvc
static class WebConfig implements WebMvcConfigurer {
static class WebConfig {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -20,6 +20,9 @@ package org.springframework.dao;
* Exception thrown on failure to acquire a lock during an update,
* for example during a "select for update" statement.
*
* <p>Consider handling the general {@link PessimisticLockingFailureException}
* instead, semantically including a wider range of locking-related failures.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -20,6 +20,9 @@ package org.springframework.dao;
* Exception thrown on failure to complete a transaction in serialized mode
* due to update conflicts.
*
* <p>Consider handling the general {@link PessimisticLockingFailureException}
* instead, semantically including a wider range of locking-related failures.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -19,17 +19,15 @@ package org.springframework.dao;
import org.springframework.lang.Nullable;
/**
* Exception thrown on concurrency failure.
* Exception thrown on various data access concurrency failures.
*
* <p>This exception should be subclassed to indicate the type of failure:
* optimistic locking, failure to acquire lock, etc.
* <p>This exception provides subclasses for specific types of failure,
* in particular optimistic locking versus pessimistic locking.
*
* @author Thomas Risberg
* @since 1.1
* @see OptimisticLockingFailureException
* @see PessimisticLockingFailureException
* @see CannotAcquireLockException
* @see DeadlockLoserDataAccessException
*/
@SuppressWarnings("serial")
public class ConcurrencyFailureException extends TransientDataAccessException {
@@ -19,8 +19,13 @@ package org.springframework.dao;
/**
* Exception thrown when an attempt to insert or update data
* results in violation of an integrity constraint. Note that this
* is not purely a relational concept; unique primary keys are
* required by most database types.
* is not purely a relational concept; integrity constraints such
* as unique primary keys are required by most database types.
*
* <p>Serves as a superclass for more specific exceptions, e.g.
* {@link DuplicateKeyException}. However, it is generally
* recommended to handle {@code DataIntegrityViolationException}
* itself instead of relying on specific exception subclasses.
*
* @author Rod Johnson
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -20,6 +20,9 @@ package org.springframework.dao;
* Generic exception thrown when the current process was
* a deadlock loser, and its transaction rolled back.
*
* <p>Consider handling the general {@link PessimisticLockingFailureException}
* instead, semantically including a wider range of locking-related failures.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -22,6 +22,9 @@ package org.springframework.dao;
* Note that this is not necessarily a purely relational concept;
* unique primary keys are required by most database types.
*
* <p>Consider handling the general {@link DataIntegrityViolationException}
* instead, semantically including a wider range of constraint violations.
*
* @author Thomas Risberg
*/
@SuppressWarnings("serial")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -21,13 +21,13 @@ package org.springframework.dao;
* Thrown by Spring's SQLException translation mechanism
* if a corresponding database error is encountered.
*
* <p>Serves as superclass for more specific exceptions, like
* CannotAcquireLockException and DeadlockLoserDataAccessException.
* <p>Serves as a superclass for more specific exceptions, e.g.
* {@link CannotAcquireLockException}. However, it is generally
* recommended to handle {@code PessimisticLockingFailureException}
* itself instead of relying on specific exception subclasses.
*
* @author Thomas Risberg
* @since 1.2
* @see CannotAcquireLockException
* @see DeadlockLoserDataAccessException
* @see OptimisticLockingFailureException
*/
@SuppressWarnings("serial")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -233,9 +233,9 @@ public final class ContentDisposition {
result = 31 * result + ObjectUtils.nullSafeHashCode(this.filename);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.charset);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.size);
result = 31 * result + (this.creationDate != null ? this.creationDate.hashCode() : 0);
result = 31 * result + (this.modificationDate != null ? this.modificationDate.hashCode() : 0);
result = 31 * result + (this.readDate != null ? this.readDate.hashCode() : 0);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.creationDate);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.modificationDate);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.readDate);
return result;
}
@@ -360,7 +360,7 @@ public final class ContentDisposition {
if (idx1 != -1 && idx2 != -1) {
charset = Charset.forName(value.substring(0, idx1).trim());
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset),
"Charset should be UTF-8 or ISO-8859-1");
"Charset must be UTF-8 or ISO-8859-1");
filename = decodeFilename(value.substring(idx2 + 1), charset);
}
else {
@@ -532,10 +532,11 @@ public final class ContentDisposition {
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
*/
private static String encodeFilename(String input, Charset charset) {
Assert.notNull(input, "'input' is required");
Assert.notNull(charset, "'charset' is required");
Assert.notNull(input, "'input' must not be null");
Assert.notNull(charset, "'charset' must not be null");
Assert.isTrue(!StandardCharsets.US_ASCII.equals(charset), "ASCII does not require encoding");
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset), "Only UTF-8 and ISO-8859-1 are supported");
byte[] source = input.getBytes(charset);
int len = source.length;
StringBuilder sb = new StringBuilder(len << 1);
@@ -984,7 +984,8 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
/**
* Return the {@linkplain MediaType media type} of the body, as specified
* by the {@code Content-Type} header.
* <p>Returns {@code null} when the content-type is unknown.
* <p>Returns {@code null} when the {@code Content-Type} header is not set.
* @throws InvalidMediaTypeException if the media type value cannot be parsed
*/
@Nullable
public MediaType getContentType() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -170,7 +170,6 @@ class JettyHeadersAdapter implements MultiValueMap<String, String> {
public Iterator<Entry<String, List<String>>> iterator() {
return new EntryIterator();
}
@Override
public int size() {
return headers.size();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -92,11 +92,11 @@ public interface ClientCodecConfigurer extends CodecConfigurer {
/**
* Configure the {@code Decoder} to use for Server-Sent Events.
* <p>By default if this is not set, and Jackson is available, the
* {@link #jackson2JsonDecoder} override is used instead. Use this property
* if you want to further customize the SSE decoder.
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
* applied to the given decoder.
* <p>By default if this is not set, and Jackson is available,
* the {@link #jackson2JsonDecoder} override is used instead.
* Use this method to customize the SSE decoder.
* <p>Note that {@link #maxInMemorySize(int)}, if configured,
* will be applied to the given decoder.
* @param decoder the decoder to use
*/
void serverSentEventDecoder(Decoder<?> decoder);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -78,7 +78,7 @@ public interface ServerCodecConfigurer extends CodecConfigurer {
/**
* {@link CodecConfigurer.DefaultCodecs} extension with extra client-side options.
* {@link CodecConfigurer.DefaultCodecs} extension with extra server-side options.
*/
interface ServerDefaultCodecs extends DefaultCodecs {
@@ -101,9 +101,9 @@ public interface ServerCodecConfigurer extends CodecConfigurer {
/**
* Configure the {@code Encoder} to use for Server-Sent Events.
* <p>By default if this is not set, and Jackson is available, the
* {@link #jackson2JsonEncoder} override is used instead. Use this method
* to customize the SSE encoder.
* <p>By default if this is not set, and Jackson is available,
* the {@link #jackson2JsonEncoder} override is used instead.
* Use this method to customize the SSE encoder.
*/
void serverSentEventEncoder(Encoder<?> encoder);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -170,7 +170,6 @@ class JettyHeadersAdapter implements MultiValueMap<String, String> {
public Iterator<Entry<String, List<String>>> iterator() {
return new EntryIterator();
}
@Override
public int size() {
return headers.size();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -65,12 +65,12 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context)
throws IOException, URISyntaxException {
// TODO: need to compile against Jetty 10 to use HttpFields (class->interface)
if (jetty10Present) {
// No HttpFields optimization on Jetty 10 due to binary incompatibility
return super.createRequest(request, context);
}
Assert.notNull(getServletPath(), "Servlet path is not initialized");
Assert.state(getServletPath() != null, "Servlet path is not initialized");
return new JettyServerHttpRequest(
request, context, getServletPath(), getDataBufferFactory(), getBufferSize());
}
@@ -79,15 +79,14 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
protected ServletServerHttpResponse createResponse(HttpServletResponse response,
AsyncContext context, ServletServerHttpRequest request) throws IOException {
// TODO: need to compile against Jetty 10 to use HttpFields (class->interface)
if (jetty10Present) {
// No HttpFields optimization on Jetty 10 due to binary incompatibility
return new BaseJettyServerHttpResponse(
response, context, getDataBufferFactory(), getBufferSize(), request);
}
else {
return new JettyServerHttpResponse(
response, context, getDataBufferFactory(), getBufferSize(), request);
}
return new JettyServerHttpResponse(
response, context, getDataBufferFactory(), getBufferSize(), request);
}
@@ -120,8 +119,6 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
"] to org.eclipse.jetty.server.Request");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -170,7 +170,7 @@ public class ServletHttpHandlerAdapter implements Servlet {
AsyncListener requestListener;
String logPrefix;
try {
httpRequest = createRequest(((HttpServletRequest) request), asyncContext);
httpRequest = createRequest((HttpServletRequest) request, asyncContext);
requestListener = httpRequest.getAsyncListener();
logPrefix = httpRequest.getLogPrefix();
}
@@ -183,8 +183,10 @@ public class ServletHttpHandlerAdapter implements Servlet {
return;
}
ServerHttpResponse httpResponse = createResponse(((HttpServletResponse) response), asyncContext, httpRequest);
AsyncListener responseListener = ((ServletServerHttpResponse) httpResponse).getAsyncListener();
ServletServerHttpResponse wrappedResponse =
createResponse((HttpServletResponse) response, asyncContext, httpRequest);
ServerHttpResponse httpResponse = wrappedResponse;
AsyncListener responseListener = wrappedResponse.getAsyncListener();
if (httpRequest.getMethod() == HttpMethod.HEAD) {
httpResponse = new HttpHeadResponseDecorator(httpResponse);
}
@@ -201,7 +203,7 @@ public class ServletHttpHandlerAdapter implements Servlet {
protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context)
throws IOException, URISyntaxException {
Assert.notNull(this.servletPath, "Servlet path is not initialized");
Assert.state(this.servletPath != null, "Servlet path is not initialized");
return new ServletServerHttpRequest(
request, context, this.servletPath, getDataBufferFactory(), getBufferSize());
}
@@ -264,9 +266,7 @@ public class ServletHttpHandlerAdapter implements Servlet {
private final String logPrefix;
public HttpHandlerAsyncListener(
AsyncListener requestAsyncListener, AsyncListener responseAsyncListener,
public HttpHandlerAsyncListener(AsyncListener requestAsyncListener, AsyncListener responseAsyncListener,
Runnable handlerDisposeTask, AtomicBoolean completionFlag, String logPrefix) {
this.requestAsyncListener = requestAsyncListener;
@@ -276,7 +276,6 @@ public class ServletHttpHandlerAdapter implements Servlet {
this.logPrefix = logPrefix;
}
@Override
public void onTimeout(AsyncEvent event) {
// Should never happen since we call asyncContext.setTimeout(-1)
@@ -362,9 +361,7 @@ public class ServletHttpHandlerAdapter implements Servlet {
@Nullable
private volatile Subscription subscription;
public HandlerResultSubscriber(
AsyncContext asyncContext, AtomicBoolean completionFlag, String logPrefix) {
public HandlerResultSubscriber(AsyncContext asyncContext, AtomicBoolean completionFlag, String logPrefix) {
this.asyncContext = asyncContext;
this.completionFlag = completionFlag;
this.logPrefix = logPrefix;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -58,7 +58,6 @@ import org.springframework.util.ReflectionUtils;
*/
public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
public TomcatHttpHandlerAdapter(HttpHandler httpHandler) {
super(httpHandler);
}
@@ -68,7 +67,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext asyncContext)
throws IOException, URISyntaxException {
Assert.notNull(getServletPath(), "Servlet path is not initialized");
Assert.state(getServletPath() != null, "Servlet path is not initialized");
return new TomcatServerHttpRequest(
request, asyncContext, getServletPath(), getDataBufferFactory(), getBufferSize());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.

Some files were not shown because too many files have changed in this diff Show More