Compare commits

..
1 Commits
Author SHA1 Message Date
Spring Builds 707a24c48b Release v5.3.18 2022-03-31 08:48:28 +00:00
62 changed files with 453 additions and 975 deletions
+12 -12
View File
@@ -29,10 +29,10 @@ configure(allprojects) { project ->
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.6"
mavenBom "io.netty:netty-bom:4.1.75.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.18"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.2"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.46.v20220331"
mavenBom "io.projectreactor:reactor-bom:2020.0.17"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR12"
mavenBom "io.rsocket:rsocket-bom:1.1.1"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.45.v20220203"
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"
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
dependency "com.h2database:h2:2.1.210"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
dependency "com.github.librepdf:openpdf:1.3.27"
dependency "com.github.librepdf:openpdf:1.3.26"
dependency "com.rometools:rome:1.18.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.4"
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.62') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.60') {
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.62') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.60') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.17.Final') {
dependencySet(group: 'io.undertow', version: '2.2.16.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -150,7 +150,7 @@ configure(allprojects) { project ->
}
}
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.11"
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.10"
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3'
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.12.1"
dependency("net.sourceforge.htmlunit:htmlunit:2.60.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.59.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.60.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.59.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.1"
toolVersion = "9.3"
configDirectory.set(rootProject.file("src/checkstyle"))
}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220404
FROM ubuntu:focal-20220302
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1 -1
View File
@@ -12,7 +12,7 @@ case "$1" in
echo "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.2%2B8/OpenJDK17U-jdk_x64_linux_hotspot_17.0.2_8.tar.gz"
;;
java18)
echo "https://github.com/adoptium/temurin18-binaries/releases/download/jdk-18%2B36/OpenJDK18U-jdk_x64_linux_hotspot_18_36.tar.gz"
echo "https://github.com/adoptium/temurin18-binaries/releases/download/jdk18-2022-02-12-08-06-beta/OpenJDK18-jdk_x64_linux_hotspot_2022-02-12-08-06.tar.gz"
;;
*)
echo $"Unknown java version"
-5
View File
@@ -41,11 +41,6 @@ anchors:
GITHUB_TOKEN: ((github-ci-release-token))
resource_types:
- name: registry-image
type: registry-image
source:
repository: concourse/registry-image-resource
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.19
version=5.3.18
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
@@ -134,7 +134,7 @@ public abstract class AopProxyUtils {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
else if (Proxy.isProxyClass(targetClass) || isLambda(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
@@ -245,4 +245,18 @@ public abstract class AopProxyUtils {
return arguments;
}
/**
* Determine if the supplied {@link Class} is a JVM-generated implementation
* class for a lambda expression or method reference.
* <p>This method makes a best-effort attempt at determining this, based on
* checks that work on modern, main stream JVMs.
* @param clazz the class to check
* @return {@code true} if the class is a lambda implementation class
* @since 5.3.16
*/
static boolean isLambda(Class<?> clazz) {
return (clazz.isSynthetic() && (clazz.getSuperclass() == Object.class) &&
(clazz.getInterfaces().length > 0) && clazz.getName().contains("$$Lambda"));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -375,22 +375,6 @@ class CglibAopProxy implements AopProxy, Serializable {
return false;
}
/**
* Invoke the given method with a CGLIB MethodProxy if possible, falling back
* to a plain reflection invocation in case of a fast-class generation failure.
*/
@Nullable
private static Object invokeMethod(@Nullable Object target, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
try {
return methodProxy.invoke(target, args);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
}
/**
* Process a return value. Wraps a return of {@code this} if necessary to be the
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
@@ -441,7 +425,7 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object retVal = invokeMethod(this.target, method, args, methodProxy);
Object retVal = methodProxy.invoke(this.target, args);
return processReturnType(proxy, this.target, method, retVal);
}
}
@@ -466,7 +450,7 @@ class CglibAopProxy implements AopProxy, Serializable {
Object oldProxy = null;
try {
oldProxy = AopContext.setCurrentProxy(proxy);
Object retVal = invokeMethod(this.target, method, args, methodProxy);
Object retVal = methodProxy.invoke(this.target, args);
return processReturnType(proxy, this.target, method, retVal);
}
finally {
@@ -494,7 +478,7 @@ class CglibAopProxy implements AopProxy, Serializable {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object target = this.targetSource.getTarget();
try {
Object retVal = invokeMethod(target, method, args, methodProxy);
Object retVal = methodProxy.invoke(target, args);
return processReturnType(proxy, target, method, retVal);
}
finally {
@@ -524,7 +508,7 @@ class CglibAopProxy implements AopProxy, Serializable {
Object target = this.targetSource.getTarget();
try {
oldProxy = AopContext.setCurrentProxy(proxy);
Object retVal = invokeMethod(target, method, args, methodProxy);
Object retVal = methodProxy.invoke(target, args);
return processReturnType(proxy, target, method, retVal);
}
finally {
@@ -701,7 +685,13 @@ class CglibAopProxy implements AopProxy, Serializable {
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = invokeMethod(target, method, argsToUse, methodProxy);
try {
retVal = methodProxy.invoke(target, argsToUse);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
}
else {
// We need to create a method invocation...
@@ -21,7 +21,6 @@ import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
import org.springframework.core.NativeDetector;
import org.springframework.util.ClassUtils;
/**
* Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy
@@ -61,7 +60,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || AopProxyUtils.isLambda(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,6 @@ import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostP
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@@ -86,7 +85,6 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Rod Johnson
* @author Rob Harrop
* @author Sam Brannen
* @since 13.10.2003
* @see #setInterceptorNames
* @see #getAdvicesAndAdvisorsForBean
@@ -444,8 +442,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
@@ -19,6 +19,7 @@ package org.springframework.aop.framework;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
@@ -133,4 +134,61 @@ public class AopProxyUtilsTests {
AopProxyUtils.proxiedUserInterfaces(proxy));
}
@Test
void isLambda() {
assertIsLambda(AopProxyUtilsTests.staticLambdaExpression);
assertIsLambda(AopProxyUtilsTests::staticStringFactory);
assertIsLambda(this.instanceLambdaExpression);
assertIsLambda(this::instanceStringFactory);
}
@Test
void isNotLambda() {
assertIsNotLambda(new EnigmaSupplier());
assertIsNotLambda(new Supplier<String>() {
@Override
public String get() {
return "anonymous inner class";
}
});
assertIsNotLambda(new Fake$$LambdaSupplier());
}
private static void assertIsLambda(Supplier<String> supplier) {
assertThat(AopProxyUtils.isLambda(supplier.getClass())).isTrue();
}
private static void assertIsNotLambda(Supplier<String> supplier) {
assertThat(AopProxyUtils.isLambda(supplier.getClass())).isFalse();
}
private static final Supplier<String> staticLambdaExpression = () -> "static lambda expression";
private final Supplier<String> instanceLambdaExpression = () -> "instance lambda expressions";
private static String staticStringFactory() {
return "static string factory";
}
private String instanceStringFactory() {
return "instance string factory";
}
private static class EnigmaSupplier implements Supplier<String> {
@Override
public String get() {
return "enigma";
}
}
private static class Fake$$LambdaSupplier implements Supplier<String> {
@Override
public String get() {
return "fake lambda";
}
}
}
@@ -287,15 +287,13 @@ public final class CachedIntrospectionResults {
// This call is slow so we do it once.
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class == beanClass && !("name".equals(pd.getName()) ||
(pd.getName().endsWith("Name") && String.class == pd.getPropertyType()))) {
if (Class.class == beanClass && (!"name".equals(pd.getName()) && !pd.getName().endsWith("Name"))) {
// Only allow all name variants of Class properties
continue;
}
if (pd.getWriteMethod() == null && pd.getPropertyType() != null &&
(ClassLoader.class.isAssignableFrom(pd.getPropertyType()) ||
ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those
if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType())
|| ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those
continue;
}
if (logger.isTraceEnabled()) {
@@ -344,10 +342,9 @@ public final class CachedIntrospectionResults {
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
// against a declared read method, so we prefer read method descriptors here.
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
if (pd.getWriteMethod() == null && pd.getPropertyType() != null &&
(ClassLoader.class.isAssignableFrom(pd.getPropertyType()) ||
ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those
if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType())
|| ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those
continue;
}
this.propertyDescriptors.put(pd.getName(), pd);
@@ -367,7 +364,7 @@ public final class CachedIntrospectionResults {
for (Method method : beanClass.getMethods()) {
if (!this.propertyDescriptors.containsKey(method.getName()) &&
!readMethodNames.contains(method.getName()) && isPlainAccessor(method)) {
!readMethodNames.contains((method.getName())) && isPlainAccessor(method)) {
this.propertyDescriptors.put(method.getName(),
new GenericTypeAwarePropertyDescriptor(beanClass, method.getName(), method, null, null));
readMethodNames.add(method.getName());
@@ -376,11 +373,8 @@ public final class CachedIntrospectionResults {
}
private boolean isPlainAccessor(Method method) {
if (Modifier.isStatic(method.getModifiers()) ||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
method.getParameterCount() > 0 || method.getReturnType() == void.class ||
ClassLoader.class.isAssignableFrom(method.getReturnType()) ||
ProtectionDomain.class.isAssignableFrom(method.getReturnType())) {
if (method.getParameterCount() > 0 || method.getReturnType() == void.class ||
method.getDeclaringClass() == Object.class || Modifier.isStatic(method.getModifiers())) {
return false;
}
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,9 +23,8 @@ import org.springframework.lang.Nullable;
/**
* Common interface for classes that can access named properties
* (such as bean properties of an object or fields in an object).
*
* <p>Serves as base interface for {@link BeanWrapper}.
* (such as bean properties of an object or fields in an object)
* Serves as base interface for {@link BeanWrapper}.
*
* @author Juergen Hoeller
* @since 1.1
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,6 @@ import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.io.DefaultResourceLoader;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -107,7 +105,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
.satisfies(ex -> assertThat(ex.getPossibleMatches()).containsExactly("age"));
}
@Test // Can't be shared; there is no such thing as a read-only field
@Test // Can't be shared; there is no such thing as a read-only field
void setReadOnlyMapProperty() {
TypedReadOnlyMap map = new TypedReadOnlyMap(Collections.singletonMap("key", new TestBean()));
TypedReadOnlyMapClient target = new TypedReadOnlyMapClient();
@@ -159,34 +157,12 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
BeanWrapper accessor = createAccessor(target);
accessor.setPropertyValue("name", "a");
accessor.setPropertyValue("spouse.name", "b");
assertThat(target.getName()).isEqualTo("a");
assertThat(target.getSpouse().getName()).isEqualTo("b");
assertThat(accessor.getPropertyValue("name")).isEqualTo("a");
assertThat(accessor.getPropertyValue("spouse.name")).isEqualTo("b");
assertThat(accessor.getPropertyDescriptor("name").getPropertyType()).isEqualTo(String.class);
assertThat(accessor.getPropertyDescriptor("spouse.name").getPropertyType()).isEqualTo(String.class);
assertThat(accessor.isReadableProperty("class.package")).isFalse();
assertThat(accessor.isReadableProperty("class.module")).isFalse();
assertThat(accessor.isReadableProperty("class.classLoader")).isFalse();
assertThat(accessor.isReadableProperty("class.name")).isTrue();
assertThat(accessor.isReadableProperty("class.simpleName")).isTrue();
assertThat(accessor.getPropertyValue("class.name")).isEqualTo(TestBean.class.getName());
assertThat(accessor.getPropertyValue("class.simpleName")).isEqualTo(TestBean.class.getSimpleName());
assertThat(accessor.getPropertyDescriptor("class.name").getPropertyType()).isEqualTo(String.class);
assertThat(accessor.getPropertyDescriptor("class.simpleName").getPropertyType()).isEqualTo(String.class);
accessor = createAccessor(new DefaultResourceLoader());
assertThat(accessor.isReadableProperty("class.package")).isFalse();
assertThat(accessor.isReadableProperty("class.module")).isFalse();
assertThat(accessor.isReadableProperty("class.classLoader")).isFalse();
assertThat(accessor.isReadableProperty("classLoader")).isTrue();
assertThat(accessor.isWritableProperty("classLoader")).isTrue();
OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader());
accessor.setPropertyValue("classLoader", ocl);
assertThat(accessor.getPropertyValue("classLoader")).isSameAs(ocl);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@ import java.util.Set;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import javax.validation.Valid;
@@ -44,7 +43,6 @@ import org.hibernate.validator.HibernateValidatorFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.convert.support.DefaultConversionService;
@@ -54,18 +52,18 @@ import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Juergen Hoeller
*/
class ValidatorFactoryTests {
@SuppressWarnings("resource")
public class ValidatorFactoryTests {
@Test
void simpleValidation() {
@SuppressWarnings("resource")
@SuppressWarnings("cast")
public void testSimpleValidation() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -80,15 +78,15 @@ class ValidatorFactoryTests {
Validator nativeValidator = validator.unwrap(Validator.class);
assertThat(nativeValidator.getClass().getName().startsWith("org.hibernate")).isTrue();
assertThat(validator.unwrap(ValidatorFactory.class)).isInstanceOf(HibernateValidatorFactory.class);
assertThat(validator.unwrap(HibernateValidatorFactory.class)).isInstanceOf(HibernateValidatorFactory.class);
assertThat(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue();
assertThat(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue();
validator.destroy();
}
@Test
void simpleValidationWithCustomProvider() {
@SuppressWarnings("resource")
@SuppressWarnings("cast")
public void testSimpleValidationWithCustomProvider() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
validator.afterPropertiesSet();
@@ -104,15 +102,14 @@ class ValidatorFactoryTests {
Validator nativeValidator = validator.unwrap(Validator.class);
assertThat(nativeValidator.getClass().getName().startsWith("org.hibernate")).isTrue();
assertThat(validator.unwrap(ValidatorFactory.class)).isInstanceOf(HibernateValidatorFactory.class);
assertThat(validator.unwrap(HibernateValidatorFactory.class)).isInstanceOf(HibernateValidatorFactory.class);
assertThat(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue();
assertThat(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue();
validator.destroy();
}
@Test
void simpleValidationWithClassLevel() {
@SuppressWarnings("resource")
public void testSimpleValidationWithClassLevel() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -125,13 +122,10 @@ class ValidatorFactoryTests {
ConstraintViolation<?> cv = iterator.next();
assertThat(cv.getPropertyPath().toString()).isEqualTo("");
assertThat(cv.getConstraintDescriptor().getAnnotation() instanceof NameAddressValid).isTrue();
validator.destroy();
}
@Test
void springValidationFieldType() {
@SuppressWarnings("resource")
public void testSpringValidationFieldType() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -141,16 +135,11 @@ class ValidatorFactoryTests {
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
assertThat(errors.getErrorCount()).isEqualTo(1);
assertThat(errors.getFieldError("address").getRejectedValue())
.as("Field/Value type mismatch")
.isInstanceOf(ValidAddress.class);
validator.destroy();
assertThat(errors.getFieldError("address").getRejectedValue()).isInstanceOf(ValidAddress.class);
}
@Test
void springValidation() {
@SuppressWarnings("resource")
public void testSpringValidation() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -175,13 +164,10 @@ class ValidatorFactoryTests {
assertThat(errorCodes.contains("NotNull.street")).isTrue();
assertThat(errorCodes.contains("NotNull.java.lang.String")).isTrue();
assertThat(errorCodes.contains("NotNull")).isTrue();
validator.destroy();
}
@Test
void springValidationWithClassLevel() {
@SuppressWarnings("resource")
public void testSpringValidationWithClassLevel() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -196,12 +182,10 @@ class ValidatorFactoryTests {
assertThat(errorCodes.size()).isEqualTo(2);
assertThat(errorCodes.contains("NameAddressValid.person")).isTrue();
assertThat(errorCodes.contains("NameAddressValid")).isTrue();
validator.destroy();
}
@Test
void springValidationWithAutowiredValidator() {
public void testSpringValidationWithAutowiredValidator() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(
LocalValidatorFactoryBean.class);
LocalValidatorFactoryBean validator = ctx.getBean(LocalValidatorFactoryBean.class);
@@ -218,14 +202,11 @@ class ValidatorFactoryTests {
assertThat(errorCodes.size()).isEqualTo(2);
assertThat(errorCodes.contains("NameAddressValid.person")).isTrue();
assertThat(errorCodes.contains("NameAddressValid")).isTrue();
validator.destroy();
ctx.close();
}
@Test
void springValidationWithErrorInListElement() {
@SuppressWarnings("resource")
public void testSpringValidationWithErrorInListElement() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -240,13 +221,10 @@ class ValidatorFactoryTests {
assertThat(fieldError.getField()).isEqualTo("address.street");
fieldError = result.getFieldError("addressList[0].street");
assertThat(fieldError.getField()).isEqualTo("addressList[0].street");
validator.destroy();
}
@Test
void springValidationWithErrorInSetElement() {
@SuppressWarnings("resource")
public void testSpringValidationWithErrorInSetElement() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -261,13 +239,10 @@ class ValidatorFactoryTests {
assertThat(fieldError.getField()).isEqualTo("address.street");
fieldError = result.getFieldError("addressSet[].street");
assertThat(fieldError.getField()).isEqualTo("addressSet[].street");
validator.destroy();
}
@Test
void innerBeanValidation() {
@SuppressWarnings("resource")
public void testInnerBeanValidation() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -276,13 +251,10 @@ class ValidatorFactoryTests {
validator.validate(mainBean, errors);
Object rejected = errors.getFieldValue("inner.value");
assertThat(rejected).isNull();
validator.destroy();
}
@Test
void validationWithOptionalField() {
@SuppressWarnings("resource")
public void testValidationWithOptionalField() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -291,13 +263,10 @@ class ValidatorFactoryTests {
validator.validate(mainBean, errors);
Object rejected = errors.getFieldValue("inner.value");
assertThat(rejected).isNull();
validator.destroy();
}
@Test
void listValidation() {
@SuppressWarnings("resource")
public void testListValidation() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
@@ -313,34 +282,6 @@ class ValidatorFactoryTests {
assertThat(fieldError).isNotNull();
assertThat(fieldError.getRejectedValue()).isEqualTo("X");
assertThat(errors.getFieldValue("list[1]")).isEqualTo("X");
validator.destroy();
}
@Test
void withConstraintValidatorFactory() {
ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory());
@SuppressWarnings("resource")
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setConstraintValidatorFactory(cvf);
validator.afterPropertiesSet();
assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf);
validator.destroy();
}
@Test
void withCustomInitializer() {
ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory());
@SuppressWarnings("resource")
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setConfigurationInitializer(configuration -> configuration.constraintValidatorFactory(cvf));
validator.afterPropertiesSet();
assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf);
validator.destroy();
}
@@ -439,8 +380,8 @@ class ValidatorFactoryTests {
}
boolean valid = (value.name == null || !value.address.street.contains(value.name));
if (!valid && "Phil".equals(value.name)) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
context.getDefaultConstraintMessageTemplate()).addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation();
}
return valid;
}
@@ -476,7 +417,6 @@ class ValidatorFactoryTests {
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@@ -485,8 +425,8 @@ class ValidatorFactoryTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = InnerValidator.class)
public @interface InnerValid {
@Constraint(validatedBy=InnerValidator.class)
public static @interface InnerValid {
String message() default "NOT VALID";
@@ -506,8 +446,7 @@ class ValidatorFactoryTests {
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
if (bean.getValue() == null) {
context.buildConstraintViolationWithTemplate("NULL")
.addPropertyNode("value").addConstraintViolation();
context.buildConstraintViolationWithTemplate("NULL").addPropertyNode("value").addConstraintViolation();
return false;
}
return true;
@@ -555,8 +494,7 @@ class ValidatorFactoryTests {
boolean valid = true;
for (int i = 0; i < list.size(); i++) {
if ("X".equals(list.get(i))) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addBeanNode().inIterable().atIndex(i).addConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation();
valid = false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,20 +51,18 @@ import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
/**
* Binder that allows for setting property values on a target object, including
* support for validation and binding result analysis.
*
* <p>The binding process can be customized by specifying allowed field patterns,
* Binder that allows for setting property values onto a target object,
* including support for validation and binding result analysis.
* The binding process can be customized through specifying allowed fields,
* required fields, custom editors, etc.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
* <p>Note that there are potential security implications in failing to set an array
* of allowed fields. In the case of HTTP form POST data for example, malicious clients
* can attempt to subvert an application by supplying values for fields or properties
* that do not exist on the form. In some cases this could lead to illegal data being
* set on command objects <i>or their nested objects</i>. For this reason, it is
* <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b>
* on the DataBinder.
*
* <p>The binding results can be examined via the {@link BindingResult} interface,
* extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
@@ -98,7 +96,6 @@ import org.springframework.util.StringUtils;
* @author Rob Harrop
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @author Sam Brannen
* @see #setAllowedFields
* @see #setRequiredFields
* @see #registerCustomEditor
@@ -421,21 +418,15 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Register field patterns that should be allowed for binding.
* <p>Default is all fields.
* <p>Restrict this for example to avoid unwanted modifications by malicious
* Register fields that should be allowed for binding. Default is all fields.
* Restrict this for example to avoid unwanted modifications by malicious
* users when binding HTTP request parameters.
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality.
* <p>The default implementation of this method stores allowed field patterns
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
* form. Subclasses which override this method must therefore take this into
* account.
* <p>More sophisticated matching can be implemented by overriding the
* {@link #isAllowed} method.
* <p>Alternatively, specify a list of <i>disallowed</i> field patterns.
* @param allowedFields array of allowed field patterns
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
* arbitrary number of pattern parts), as well as direct equality. More
* sophisticated matching can be implemented by overriding the
* {@code isAllowed} method.
* <p>Alternatively, specify a list of <i>disallowed</i> fields.
* @param allowedFields array of field names
* @see #setDisallowedFields
* @see #isAllowed(String)
*/
@@ -444,9 +435,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Return the field patterns that should be allowed for binding.
* @return array of allowed field patterns
* @see #setAllowedFields(String...)
* Return the fields that should be allowed for binding.
* @return array of field names
*/
@Nullable
public String[] getAllowedFields() {
@@ -454,44 +444,25 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Register field patterns that should <i>not</i> be allowed for binding.
* <p>Default is none.
* <p>Mark fields as disallowed, for example to avoid unwanted
* Register fields that should <i>not</i> be allowed for binding. Default
* is none. Mark fields as disallowed for example to avoid unwanted
* modifications by malicious users when binding HTTP request parameters.
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality.
* <p>The default implementation of this method stores disallowed field patterns
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
* form. As of Spring Framework 5.2.21, the default implementation also transforms
* disallowed field patterns to {@linkplain String#toLowerCase() lowercase} to
* support case-insensitive pattern matching in {@link #isAllowed}. Subclasses
* which override this method must therefore take both of these transformations
* into account.
* <p>More sophisticated matching can be implemented by overriding the
* {@link #isAllowed} method.
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
* @param disallowedFields array of disallowed field patterns
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
* arbitrary number of pattern parts), as well as direct equality.
* More sophisticated matching can be implemented by overriding the
* {@code isAllowed} method.
* <p>Alternatively, specify a list of <i>allowed</i> fields.
* @param disallowedFields array of field names
* @see #setAllowedFields
* @see #isAllowed(String)
*/
public void setDisallowedFields(@Nullable String... disallowedFields) {
if (disallowedFields == null) {
this.disallowedFields = null;
}
else {
String[] fieldPatterns = new String[disallowedFields.length];
for (int i = 0; i < fieldPatterns.length; i++) {
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]).toLowerCase();
}
this.disallowedFields = fieldPatterns;
}
this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields);
}
/**
* Return the field patterns that should <i>not</i> be allowed for binding.
* @return array of disallowed field patterns
* @see #setDisallowedFields(String...)
* Return the fields that should <i>not</i> be allowed for binding.
* @return array of field names
*/
@Nullable
public String[] getDisallowedFields() {
@@ -803,20 +774,16 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
/**
* Determine if the given field is allowed for binding.
* <p>Invoked for each passed-in property value.
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality, in the configured lists of allowed field patterns
* and disallowed field patterns.
* <p>Matching against allowed field patterns is case-sensitive; whereas,
* matching against disallowed field patterns is case-insensitive.
* <p>A field matching a disallowed pattern will not be accepted even if it
* also happens to match a pattern in the allowed list.
* <p>Can be overridden in subclasses, but care must be taken to honor the
* aforementioned contract.
* Return if the given field is allowed for binding.
* Invoked for each passed-in property value.
* <p>The default implementation checks for "xxx*", "*xxx", "*xxx*" and "xxx*yyy"
* matches (with an arbitrary number of pattern parts), as well as direct equality,
* in the specified lists of allowed fields and disallowed fields. A field matching
* a disallowed pattern will not be accepted even if it also happens to match a
* pattern in the allowed list.
* <p>Can be overridden in subclasses.
* @param field the field to check
* @return {@code true} if the field is allowed
* @return if the field is allowed
* @see #setAllowedFields
* @see #setDisallowedFields
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
@@ -825,7 +792,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
String[] allowed = getAllowedFields();
String[] disallowed = getDisallowedFields();
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase())));
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field)));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.function.Consumer;
import javax.validation.Configuration;
import javax.validation.ConstraintValidatorFactory;
@@ -114,9 +113,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
private final Map<String, String> validationPropertyMap = new HashMap<>();
@Nullable
private Consumer<Configuration<?>> configurationInitializer;
@Nullable
private ApplicationContext applicationContext;
@@ -238,18 +234,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
return this.validationPropertyMap;
}
/**
* Specify a callback for customizing the Bean Validation {@code Configuration} instance,
* as an alternative to overriding the {@link #postProcessConfiguration(Configuration)}
* method in custom {@code LocalValidatorFactoryBean} subclasses.
* <p>This enables convenient customizations for application purposes. Infrastructure
* extensions may keep overriding the {@link #postProcessConfiguration} template method.
* @since 5.3.19
*/
public void setConfigurationInitializer(Consumer<Configuration<?>> configurationInitializer) {
this.configurationInitializer = configurationInitializer;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
@@ -328,9 +312,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
this.validationPropertyMap.forEach(configuration::addProperty);
// Allow for custom post-processing before we actually build the ValidatorFactory.
if (this.configurationInitializer != null) {
this.configurationInitializer.accept(configuration);
}
postProcessConfiguration(configuration);
try {
@@ -21,8 +21,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@@ -33,17 +31,11 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionInterceptor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.aspectj.annotation.AspectMetadata;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.beans.PropertyValue;
@@ -60,7 +52,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.NestedRuntimeException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@@ -313,26 +304,10 @@ public class AspectJAutoProxyCreatorTests {
@ValueSource(classes = {ProxyTargetClassFalseConfig.class, ProxyTargetClassTrueConfig.class})
void lambdaIsAlwaysProxiedWithJdkProxy(Class<?> configClass) {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
@SuppressWarnings("unchecked")
Supplier<String> supplier = context.getBean(Supplier.class);
Supplier<?> supplier = context.getBean(Supplier.class);
assertThat(AopUtils.isAopProxy(supplier)).as("AOP proxy").isTrue();
assertThat(AopUtils.isJdkDynamicProxy(supplier)).as("JDK Dynamic proxy").isTrue();
assertThat(supplier.getClass().getInterfaces())
.containsExactlyInAnyOrder(Supplier.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
assertThat(supplier.get()).isEqualTo("advised: lambda");
}
}
@ParameterizedTest(name = "[{index}] {0}")
@ValueSource(classes = {MixinProxyTargetClassFalseConfig.class, MixinProxyTargetClassTrueConfig.class})
void lambdaIsAlwaysProxiedWithJdkProxyWithIntroductions(Class<?> configClass) {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
MessageGenerator messageGenerator = context.getBean(MessageGenerator.class);
assertThat(AopUtils.isAopProxy(messageGenerator)).as("AOP proxy").isTrue();
assertThat(AopUtils.isJdkDynamicProxy(messageGenerator)).as("JDK Dynamic proxy").isTrue();
assertThat(messageGenerator.getClass().getInterfaces())
.containsExactlyInAnyOrder(MessageGenerator.class, Mixin.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
assertThat(messageGenerator.generateMessage()).isEqualTo("mixin: lambda");
assertThat(supplier.get()).asString().isEqualTo("advised: lambda");
}
}
@@ -641,79 +616,3 @@ class ProxyTargetClassFalseConfig extends AbstractProxyTargetClassConfig {
@EnableAspectJAutoProxy(proxyTargetClass = true)
class ProxyTargetClassTrueConfig extends AbstractProxyTargetClassConfig {
}
@FunctionalInterface
interface MessageGenerator {
String generateMessage();
}
interface Mixin {
}
class MixinIntroductionInterceptor implements IntroductionInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return "mixin: " + invocation.proceed();
}
@Override
public boolean implementsInterface(Class<?> intf) {
return Mixin.class.isAssignableFrom(intf);
}
}
@SuppressWarnings("serial")
class MixinAdvisor extends AbstractPointcutAdvisor implements IntroductionAdvisor {
@Override
public org.springframework.aop.Pointcut getPointcut() {
return org.springframework.aop.Pointcut.TRUE;
}
@Override
public Advice getAdvice() {
return new MixinIntroductionInterceptor();
}
@Override
public Class<?>[] getInterfaces() {
return new Class[] { Mixin.class };
}
@Override
public ClassFilter getClassFilter() {
return MessageGenerator.class::isAssignableFrom;
}
@Override
public void validateInterfaces() {
/* no-op */
}
}
abstract class AbstractMixinConfig {
@Bean
MessageGenerator messageGenerator() {
return () -> "lambda";
}
@Bean
MixinAdvisor mixinAdvisor() {
return new MixinAdvisor();
}
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
class MixinProxyTargetClassFalseConfig extends AbstractMixinConfig {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
class MixinProxyTargetClassTrueConfig extends AbstractMixinConfig {
}
@@ -64,26 +64,23 @@ import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.lang.Nullable;
import org.springframework.tests.sample.beans.BeanWithObjectProperty;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.entry;
/**
* Unit tests for {@link DataBinder}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Kazuki Shimizu
* @author Sam Brannen
*/
class DataBinderTests {
@Test
void bindingNoErrors() throws BindException {
void testBindingNoErrors() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
assertThat(binder.isIgnoreUnknownFields()).isTrue();
@@ -113,11 +110,12 @@ class DataBinderTests {
assertThat(ex).isEqualTo(binder.getBindingResult());
other.reject("xxx");
assertThat(other).isNotEqualTo(binder.getBindingResult());
boolean condition = !other.equals(binder.getBindingResult());
assertThat(condition).isTrue();
}
@Test
void bindingWithDefaultConversionNoErrors() throws BindException {
void testBindingWithDefaultConversionNoErrors() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
assertThat(binder.isIgnoreUnknownFields()).isTrue();
@@ -133,7 +131,7 @@ class DataBinderTests {
}
@Test
void nestedBindingWithDefaultConversionNoErrors() throws BindException {
void testNestedBindingWithDefaultConversionNoErrors() throws BindException {
TestBean rod = new TestBean(new TestBean());
DataBinder binder = new DataBinder(rod, "person");
assertThat(binder.isIgnoreUnknownFields()).isTrue();
@@ -149,7 +147,7 @@ class DataBinderTests {
}
@Test
void bindingNoErrorsNotIgnoreUnknown() {
void testBindingNoErrorsNotIgnoreUnknown() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
binder.setIgnoreUnknownFields(false);
@@ -162,7 +160,7 @@ class DataBinderTests {
}
@Test
void bindingNoErrorsWithInvalidField() {
void testBindingNoErrorsWithInvalidField() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -173,7 +171,7 @@ class DataBinderTests {
}
@Test
void bindingNoErrorsWithIgnoreInvalid() throws BindException {
void testBindingNoErrorsWithIgnoreInvalid() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
binder.setIgnoreInvalidFields(true);
@@ -182,14 +180,10 @@ class DataBinderTests {
pvs.add("spouse.age", 32);
binder.bind(pvs);
binder.close();
assertThat(rod.getName()).isEqualTo("Rod");
assertThat(rod.getSpouse()).isNull();
}
@Test
void bindingWithErrors() {
void testBindingWithErrors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -251,7 +245,7 @@ class DataBinderTests {
}
@Test
void bindingWithSystemFieldError() {
void testBindingWithSystemFieldError() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -263,7 +257,7 @@ class DataBinderTests {
}
@Test
void bindingWithErrorsAndCustomEditors() {
void testBindingWithErrorsAndCustomEditors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
binder.registerCustomEditor(String.class, "touchy", new PropertyEditorSupport() {
@@ -331,7 +325,7 @@ class DataBinderTests {
}
@Test
void bindingWithCustomEditorOnObjectField() {
void testBindingWithCustomEditorOnObjectField() {
BeanWithObjectProperty tb = new BeanWithObjectProperty();
DataBinder binder = new DataBinder(tb);
binder.registerCustomEditor(Integer.class, "object", new CustomNumberEditor(Integer.class, true));
@@ -342,7 +336,7 @@ class DataBinderTests {
}
@Test
void bindingWithFormatter() {
void testBindingWithFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -374,7 +368,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithFormatter() {
void testBindingErrorWithFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -397,7 +391,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithParseExceptionFromFormatter() {
void testBindingErrorWithParseExceptionFromFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -425,7 +419,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithRuntimeExceptionFromFormatter() {
void testBindingErrorWithRuntimeExceptionFromFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -453,7 +447,7 @@ class DataBinderTests {
}
@Test
void bindingWithFormatterAgainstList() {
void testBindingWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -475,7 +469,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithFormatterAgainstList() {
void testBindingErrorWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -498,7 +492,7 @@ class DataBinderTests {
}
@Test
void bindingWithFormatterAgainstFields() {
void testBindingWithFormatterAgainstFields() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
@@ -531,7 +525,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithFormatterAgainstFields() {
void testBindingErrorWithFormatterAgainstFields() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.initDirectFieldAccess();
@@ -555,7 +549,7 @@ class DataBinderTests {
}
@Test
void bindingWithCustomFormatter() {
void testBindingWithCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.addCustomFormatter(new NumberStyleFormatter(), Float.class);
@@ -584,7 +578,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithCustomFormatter() {
void testBindingErrorWithCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.addCustomFormatter(new NumberStyleFormatter());
@@ -605,7 +599,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithParseExceptionFromCustomFormatter() {
void testBindingErrorWithParseExceptionFromCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@@ -630,7 +624,7 @@ class DataBinderTests {
}
@Test
void bindingErrorWithRuntimeExceptionFromCustomFormatter() {
void testBindingErrorWithRuntimeExceptionFromCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@@ -655,7 +649,7 @@ class DataBinderTests {
}
@Test
void conversionWithInappropriateStringEditor() {
void testConversionWithInappropriateStringEditor() {
DataBinder dataBinder = new DataBinder(null);
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
dataBinder.setConversionService(conversionService);
@@ -668,7 +662,7 @@ class DataBinderTests {
}
@Test
void bindingWithAllowedFields() throws BindException {
void testBindingWithAllowedFields() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
binder.setAllowedFields("name", "myparam");
@@ -678,32 +672,30 @@ class DataBinderTests {
binder.bind(pvs);
binder.close();
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
assertThat(rod.getAge()).as("did not change age").isZero();
assertThat(rod.getName().equals("Rod")).as("changed name correctly").isTrue();
assertThat(rod.getAge() == 0).as("did not change age").isTrue();
}
@Test
void bindingWithDisallowedFields() throws BindException {
void testBindingWithDisallowedFields() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
binder.setDisallowedFields(" ", "\t", "favouriteColour", null, "age");
binder.setDisallowedFields("age");
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "Rod");
pvs.add("age", "32x");
pvs.add("favouriteColour", "BLUE");
binder.bind(pvs);
binder.close();
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
assertThat(rod.getAge()).as("did not change age").isZero();
assertThat(rod.getFavouriteColour()).as("did not change favourite colour").isNull();
assertThat(binder.getBindingResult().getSuppressedFields()).containsExactlyInAnyOrder("age", "favouriteColour");
assertThat(rod.getName().equals("Rod")).as("changed name correctly").isTrue();
assertThat(rod.getAge() == 0).as("did not change age").isTrue();
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertThat(disallowedFields.length).isEqualTo(1);
assertThat(disallowedFields[0]).isEqualTo("age");
}
@Test
void bindingWithAllowedAndDisallowedFields() throws BindException {
void testBindingWithAllowedAndDisallowedFields() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
binder.setAllowedFields("name", "myparam");
@@ -714,32 +706,34 @@ class DataBinderTests {
binder.bind(pvs);
binder.close();
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
assertThat(rod.getAge()).as("did not change age").isZero();
assertThat(binder.getBindingResult().getSuppressedFields()).containsExactly("age");
assertThat(rod.getName().equals("Rod")).as("changed name correctly").isTrue();
assertThat(rod.getAge() == 0).as("did not change age").isTrue();
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertThat(disallowedFields).hasSize(1);
assertThat(disallowedFields[0]).isEqualTo("age");
}
@Test
void bindingWithOverlappingAllowedAndDisallowedFields() throws BindException {
void testBindingWithOverlappingAllowedAndDisallowedFields() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
binder.setAllowedFields("name", "age");
binder.setDisallowedFields("AGE");
binder.setDisallowedFields("age");
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "Rod");
pvs.add("age", "32x");
binder.bind(pvs);
binder.close();
assertThat(rod.getName()).as("changed name correctly").isEqualTo("Rod");
assertThat(rod.getAge()).as("did not change age").isZero();
assertThat(binder.getBindingResult().getSuppressedFields()).containsExactly("age");
assertThat(rod.getName().equals("Rod")).as("changed name correctly").isTrue();
assertThat(rod.getAge() == 0).as("did not change age").isTrue();
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertThat(disallowedFields).hasSize(1);
assertThat(disallowedFields[0]).isEqualTo("age");
}
@Test
void bindingWithAllowedFieldsUsingAsterisks() throws BindException {
void testBindingWithAllowedFieldsUsingAsterisks() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
binder.setAllowedFields("nam*", "*ouchy");
@@ -766,11 +760,11 @@ class DataBinderTests {
}
@Test
void bindingWithAllowedAndDisallowedMapFields() throws BindException {
void testBindingWithAllowedAndDisallowedMapFields() throws BindException {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
binder.setAllowedFields("someMap[key1]", "someMap[key2]");
binder.setDisallowedFields("someMap['KEY3']", "SomeMap[key4]");
binder.setDisallowedFields("someMap['key3']", "someMap[key4]");
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("someMap[key1]", "value1");
@@ -780,18 +774,21 @@ class DataBinderTests {
binder.bind(pvs);
binder.close();
@SuppressWarnings("unchecked")
Map<String, String> someMap = (Map<String, String>) rod.getSomeMap();
assertThat(someMap).containsOnly(entry("key1", "value1"), entry("key2", "value2"));
assertThat(binder.getBindingResult().getSuppressedFields()).containsExactly("someMap[key3]", "someMap[key4]");
assertThat(rod.getSomeMap().get("key1")).isEqualTo("value1");
assertThat(rod.getSomeMap().get("key2")).isEqualTo("value2");
assertThat(rod.getSomeMap().get("key3")).isNull();
assertThat(rod.getSomeMap().get("key4")).isNull();
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertThat(disallowedFields).hasSize(2);
assertThat(ObjectUtils.containsElement(disallowedFields, "someMap[key3]")).isTrue();
assertThat(ObjectUtils.containsElement(disallowedFields, "someMap[key4]")).isTrue();
}
/**
* Tests for required field, both null, non-existing and empty strings.
*/
@Test
void bindingWithRequiredFields() {
void testBindingWithRequiredFields() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@@ -822,7 +819,7 @@ class DataBinderTests {
}
@Test
void bindingWithRequiredMapFields() {
void testBindingWithRequiredMapFields() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@@ -842,7 +839,7 @@ class DataBinderTests {
}
@Test
void bindingWithNestedObjectCreation() {
void testBindingWithNestedObjectCreation() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "person");
@@ -863,7 +860,7 @@ class DataBinderTests {
}
@Test
void customEditorWithOldValueAccess() {
void testCustomEditorWithOldValueAccess() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@@ -888,7 +885,7 @@ class DataBinderTests {
}
@Test
void customEditorForSingleProperty() {
void testCustomEditorForSingleProperty() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
DataBinder binder = new DataBinder(tb, "tb");
@@ -928,7 +925,7 @@ class DataBinderTests {
}
@Test
void customEditorForPrimitiveProperty() {
void testCustomEditorForPrimitiveProperty() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@@ -952,7 +949,7 @@ class DataBinderTests {
}
@Test
void customEditorForAllStringProperties() {
void testCustomEditorForAllStringProperties() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@@ -984,7 +981,7 @@ class DataBinderTests {
}
@Test
void customFormatterForSingleProperty() {
void testCustomFormatterForSingleProperty() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
DataBinder binder = new DataBinder(tb, "tb");
@@ -1024,7 +1021,7 @@ class DataBinderTests {
}
@Test
void customFormatterForPrimitiveProperty() {
void testCustomFormatterForPrimitiveProperty() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@@ -1048,7 +1045,7 @@ class DataBinderTests {
}
@Test
void customFormatterForAllStringProperties() {
void testCustomFormatterForAllStringProperties() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@@ -1080,7 +1077,7 @@ class DataBinderTests {
}
@Test
void javaBeanPropertyConventions() {
void testJavaBeanPropertyConventions() {
Book book = new Book();
DataBinder binder = new DataBinder(book);
@@ -1104,7 +1101,7 @@ class DataBinderTests {
}
@Test
void optionalProperty() {
void testOptionalProperty() {
OptionalHolder bean = new OptionalHolder();
DataBinder binder = new DataBinder(bean);
binder.setConversionService(new DefaultConversionService());
@@ -1125,7 +1122,7 @@ class DataBinderTests {
}
@Test
void validatorNoErrors() throws Exception {
void testValidatorNoErrors() throws Exception {
TestBean tb = new TestBean();
tb.setAge(33);
tb.setName("Rod");
@@ -1178,13 +1175,15 @@ class DataBinderTests {
assertThat(errors.getNestedPath()).isEqualTo("spouse.");
assertThat(errors.getErrorCount()).isEqualTo(1);
assertThat(errors.hasGlobalErrors()).isFalse();
boolean condition1 = !errors.hasGlobalErrors();
assertThat(condition1).isTrue();
assertThat(errors.getFieldErrorCount("age")).isEqualTo(1);
assertThat(errors.hasFieldErrors("name")).isFalse();
boolean condition = !errors.hasFieldErrors("name");
assertThat(condition).isTrue();
}
@Test
void validatorWithErrors() {
void testValidatorWithErrors() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@@ -1253,7 +1252,7 @@ class DataBinderTests {
}
@Test
void validatorWithErrorsAndCodesPrefix() {
void testValidatorWithErrorsAndCodesPrefix() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@@ -1325,7 +1324,7 @@ class DataBinderTests {
}
@Test
void validatorWithNestedObjectNull() {
void testValidatorWithNestedObjectNull() {
TestBean tb = new TestBean();
Errors errors = new BeanPropertyBindingResult(tb, "tb");
Validator testValidator = new TestBeanValidator();
@@ -1344,7 +1343,7 @@ class DataBinderTests {
}
@Test
void nestedValidatorWithoutNestedPath() {
void testNestedValidatorWithoutNestedPath() {
TestBean tb = new TestBean();
tb.setName("XXX");
Errors errors = new BeanPropertyBindingResult(tb, "tb");
@@ -1358,8 +1357,7 @@ class DataBinderTests {
}
@Test
@SuppressWarnings("unchecked")
void bindingStringArrayToIntegerSet() {
void testBindingStringArrayToIntegerSet() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(Set.class, new CustomCollectionEditor(TreeSet.class) {
@@ -1373,8 +1371,12 @@ class DataBinderTests {
binder.bind(pvs);
assertThat(binder.getBindingResult().getFieldValue("set")).isEqualTo(tb.getSet());
assertThat(tb.getSet()).isInstanceOf(TreeSet.class);
assertThat((Set<Integer>) tb.getSet()).containsExactly(10, 20, 30);
boolean condition = tb.getSet() instanceof TreeSet;
assertThat(condition).isTrue();
assertThat(tb.getSet().size()).isEqualTo(3);
assertThat(tb.getSet().contains(10)).isTrue();
assertThat(tb.getSet().contains(20)).isTrue();
assertThat(tb.getSet().contains(30)).isTrue();
pvs = new MutablePropertyValues();
pvs.add("set", null);
@@ -1384,7 +1386,7 @@ class DataBinderTests {
}
@Test
void bindingNullToEmptyCollection() {
void testBindingNullToEmptyCollection() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(Set.class, new CustomCollectionEditor(TreeSet.class, true));
@@ -1392,12 +1394,13 @@ class DataBinderTests {
pvs.add("set", null);
binder.bind(pvs);
assertThat(tb.getSet()).isInstanceOf(TreeSet.class);
assertThat(tb.getSet()).isEmpty();
boolean condition = tb.getSet() instanceof TreeSet;
assertThat(condition).isTrue();
assertThat(tb.getSet().isEmpty()).isTrue();
}
@Test
void bindingToIndexedField() {
void testBindingToIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(String.class, "array.name", new PropertyEditorSupport() {
@@ -1436,7 +1439,7 @@ class DataBinderTests {
}
@Test
void bindingToNestedIndexedField() {
void testBindingToNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
tb.getArray()[1].setNestedIndexedBean(new IndexedTestBean());
@@ -1467,7 +1470,7 @@ class DataBinderTests {
}
@Test
void editorForNestedIndexedField() {
void testEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
tb.getArray()[1].setNestedIndexedBean(new IndexedTestBean());
@@ -1493,7 +1496,7 @@ class DataBinderTests {
}
@Test
void specificEditorForNestedIndexedField() {
void testSpecificEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
tb.getArray()[1].setNestedIndexedBean(new IndexedTestBean());
@@ -1519,7 +1522,7 @@ class DataBinderTests {
}
@Test
void innerSpecificEditorForNestedIndexedField() {
void testInnerSpecificEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
tb.getArray()[1].setNestedIndexedBean(new IndexedTestBean());
@@ -1545,7 +1548,7 @@ class DataBinderTests {
}
@Test
void directBindingToIndexedField() {
void testDirectBindingToIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(TestBean.class, "array", new PropertyEditorSupport() {
@@ -1598,7 +1601,7 @@ class DataBinderTests {
}
@Test
void directBindingToEmptyIndexedFieldWithRegisteredSpecificEditor() {
void testDirectBindingToEmptyIndexedFieldWithRegisteredSpecificEditor() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(TestBean.class, "map[key0]", new PropertyEditorSupport() {
@@ -1629,7 +1632,7 @@ class DataBinderTests {
}
@Test
void directBindingToEmptyIndexedFieldWithRegisteredGenericEditor() {
void testDirectBindingToEmptyIndexedFieldWithRegisteredGenericEditor() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(TestBean.class, "map", new PropertyEditorSupport() {
@@ -1660,7 +1663,7 @@ class DataBinderTests {
}
@Test
void customEditorWithSubclass() {
void testCustomEditorWithSubclass() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(TestBean.class, new PropertyEditorSupport() {
@@ -1694,7 +1697,7 @@ class DataBinderTests {
}
@Test
void bindToStringArrayWithArrayEditor() {
void testBindToStringArrayWithArrayEditor() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(String[].class, "stringArray", new PropertyEditorSupport() {
@@ -1706,12 +1709,15 @@ class DataBinderTests {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("stringArray", "a1-b2");
binder.bind(pvs);
assertThat(binder.getBindingResult().hasErrors()).isFalse();
assertThat(tb.getStringArray()).containsExactly("a1", "b2");
boolean condition = !binder.getBindingResult().hasErrors();
assertThat(condition).isTrue();
assertThat(tb.getStringArray().length).isEqualTo(2);
assertThat(tb.getStringArray()[0]).isEqualTo("a1");
assertThat(tb.getStringArray()[1]).isEqualTo("b2");
}
@Test
void bindToStringArrayWithComponentEditor() {
void testBindToStringArrayWithComponentEditor() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(String.class, "stringArray", new PropertyEditorSupport() {
@@ -1723,14 +1729,15 @@ class DataBinderTests {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("stringArray", new String[] {"a1", "b2"});
binder.bind(pvs);
assertThat(binder.getBindingResult().hasErrors()).isFalse();
boolean condition = !binder.getBindingResult().hasErrors();
assertThat(condition).isTrue();
assertThat(tb.getStringArray().length).isEqualTo(2);
assertThat(tb.getStringArray()[0]).isEqualTo("Xa1");
assertThat(tb.getStringArray()[1]).isEqualTo("Xb2");
}
@Test
void bindingErrors() {
void testBindingErrors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -1757,7 +1764,7 @@ class DataBinderTests {
}
@Test
void addAllErrors() {
void testAddAllErrors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -1777,7 +1784,7 @@ class DataBinderTests {
@Test
@SuppressWarnings("unchecked")
void bindingWithResortedList() {
void testBindingWithResortedList() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -1795,7 +1802,7 @@ class DataBinderTests {
}
@Test
void rejectWithoutDefaultMessage() {
void testRejectWithoutDefaultMessage() {
TestBean tb = new TestBean();
tb.setName("myName");
tb.setAge(99);
@@ -1813,7 +1820,7 @@ class DataBinderTests {
}
@Test
void bindExceptionSerializable() throws Exception {
void testBindExceptionSerializable() throws Exception {
SerializablePerson tb = new SerializablePerson();
tb.setName("myName");
tb.setAge(99);
@@ -1842,27 +1849,27 @@ class DataBinderTests {
}
@Test
void trackDisallowedFields() {
void testTrackDisallowedFields() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.setAllowedFields("name", "age");
String name = "Rob Harrop";
int age = 42;
String beanName = "foobar";
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("name", name);
mpvs.add("age", age);
mpvs.add("beanName", "foobar");
mpvs.add("beanName", beanName);
binder.bind(mpvs);
assertThat(testBean.getName()).isEqualTo(name);
assertThat(testBean.getAge()).isEqualTo(age);
assertThat(binder.getBindingResult().getSuppressedFields()).containsExactly("beanName");
String[] disallowedFields = binder.getBindingResult().getSuppressedFields();
assertThat(disallowedFields).hasSize(1);
assertThat(disallowedFields[0]).isEqualTo("beanName");
}
@Test
void autoGrowWithinDefaultLimit() {
void testAutoGrowWithinDefaultLimit() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -1874,7 +1881,7 @@ class DataBinderTests {
}
@Test
void autoGrowBeyondDefaultLimit() {
void testAutoGrowBeyondDefaultLimit() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -1887,7 +1894,7 @@ class DataBinderTests {
}
@Test
void autoGrowWithinCustomLimit() {
void testAutoGrowWithinCustomLimit() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.setAutoGrowCollectionLimit(10);
@@ -1900,7 +1907,7 @@ class DataBinderTests {
}
@Test
void autoGrowBeyondCustomLimit() {
void testAutoGrowBeyondCustomLimit() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.setAutoGrowCollectionLimit(10);
@@ -1914,7 +1921,7 @@ class DataBinderTests {
}
@Test
void nestedGrowingList() {
void testNestedGrowingList() {
Form form = new Form();
DataBinder binder = new DataBinder(form, "form");
MutablePropertyValues mpv = new MutablePropertyValues();
@@ -1930,7 +1937,7 @@ class DataBinderTests {
}
@Test
void fieldErrorAccessVariations() {
void testFieldErrorAccessVariations() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
assertThat(binder.getBindingResult().getGlobalError()).isNull();
@@ -1951,7 +1958,7 @@ class DataBinderTests {
}
@Test // SPR-14888
void setAutoGrowCollectionLimit() {
void testSetAutoGrowCollectionLimit() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
binder.setAutoGrowCollectionLimit(257);
@@ -1965,7 +1972,7 @@ class DataBinderTests {
}
@Test // SPR-14888
void setAutoGrowCollectionLimitAfterInitialization() {
void testSetAutoGrowCollectionLimitAfterInitialization() {
DataBinder binder = new DataBinder(new BeanWithIntegerList());
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
assertThatIllegalStateException().isThrownBy(() ->
@@ -1974,7 +1981,7 @@ class DataBinderTests {
}
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() {
void testSetCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
DefaultMessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();
@@ -1991,7 +1998,7 @@ class DataBinderTests {
}
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() {
void testSetCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
DefaultMessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();
@@ -2006,7 +2013,7 @@ class DataBinderTests {
}
@Test // SPR-15009
void setCustomMessageCodesResolverAfterInitializeBindingResult() {
void testSetCustomMessageCodesResolverAfterInitializeBindingResult() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.initBeanPropertyAccess();
@@ -2021,7 +2028,7 @@ class DataBinderTests {
}
@Test // SPR-15009
void setMessageCodesResolverIsNullAfterInitializeBindingResult() {
void testSetMessageCodesResolverIsNullAfterInitializeBindingResult() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.initBeanPropertyAccess();
@@ -2035,7 +2042,8 @@ class DataBinderTests {
}
@Test // SPR-15009
void callSetMessageCodesResolverTwice() {
void testCallSetMessageCodesResolverTwice() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
binder.setMessageCodesResolver(new DefaultMessageCodesResolver());
@@ -31,7 +31,6 @@ import java.util.Set;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import javax.validation.Valid;
@@ -44,7 +43,6 @@ import org.hibernate.validator.HibernateValidatorFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.convert.support.DefaultConversionService;
@@ -315,32 +313,6 @@ class ValidatorFactoryTests {
validator.destroy();
}
@Test
void withConstraintValidatorFactory() {
ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory());
@SuppressWarnings("resource")
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setConstraintValidatorFactory(cvf);
validator.afterPropertiesSet();
assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf);
validator.destroy();
}
@Test
void withCustomInitializer() {
ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory());
@SuppressWarnings("resource")
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setConfigurationInitializer(configuration -> configuration.constraintValidatorFactory(cvf));
validator.afterPropertiesSet();
assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf);
validator.destroy();
}
@NameAddressValid
public static class ValidPerson {
@@ -437,8 +409,8 @@ class ValidatorFactoryTests {
}
boolean valid = (value.name == null || !value.address.street.contains(value.name));
if (!valid && "Phil".equals(value.name)) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
context.getDefaultConstraintMessageTemplate()).addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation();
}
return valid;
}
@@ -474,7 +446,6 @@ class ValidatorFactoryTests {
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@@ -483,7 +454,7 @@ class ValidatorFactoryTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = InnerValidator.class)
@Constraint(validatedBy=InnerValidator.class)
public @interface InnerValid {
String message() default "NOT VALID";
@@ -504,8 +475,7 @@ class ValidatorFactoryTests {
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
if (bean.getValue() == null) {
context.buildConstraintViolationWithTemplate("NULL")
.addPropertyNode("value").addConstraintViolation();
context.buildConstraintViolationWithTemplate("NULL").addPropertyNode("value").addConstraintViolation();
return false;
}
return true;
@@ -553,8 +523,7 @@ class ValidatorFactoryTests {
boolean valid = true;
for (int i = 0; i < list.size(); i++) {
if ("X".equals(list.get(i))) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addBeanNode().inIterable().atIndex(i).addConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation();
valid = false;
}
}
@@ -5,19 +5,19 @@
<bean id="publisher" class="org.springframework.jmx.export.NotificationPublisherTests$MyNotificationPublisher"/>
<bean id="publisherMBean" class="org.springframework.jmx.export.NotificationPublisherTests$MyNotificationPublisherMBean"/>
<bean id="publisherMBean" class="org.springframework.jmx.export.NotificationPublisherTests$MyNotificationPublisherMBean"/>
<bean id="publisherStandardMBean" class="org.springframework.jmx.export.NotificationPublisherTests$MyNotificationPublisherStandardMBean"/>
<bean id="publisherStandardMBean" class="org.springframework.jmx.export.NotificationPublisherTests$MyNotificationPublisherStandardMBean"/>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="spring:type=Publisher" value-ref="publisher"/>
<entry key="spring:type=PublisherMBean" value-ref="publisherMBean"/>
<entry key="spring:type=PublisherStandardMBean" value-ref="publisherStandardMBean"/>
<entry key="spring:type=PublisherMBean" value-ref="publisherMBean"/>
<entry key="spring:type=PublisherStandardMBean" value-ref="publisherStandardMBean"/>
</map>
</property>
<property name="server" ref="server"/>
<property name="server" ref="server"/>
</bean>
</beans>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ import org.springframework.util.StringUtils;
*/
public final class Property {
private static final Map<Property, Annotation[]> annotationCache = new ConcurrentReferenceHashMap<>();
private static Map<Property, Annotation[]> annotationCache = new ConcurrentReferenceHashMap<>();
private final Class<?> objectType;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1061,7 +1061,7 @@ public abstract class DataBufferUtils {
@Override
public Context currentContext() {
return Context.of(this.sink.contextView());
return this.sink.currentContext();
}
}
@@ -1158,7 +1158,7 @@ public abstract class DataBufferUtils {
@Override
public Context currentContext() {
return Context.of(this.sink.contextView());
return this.sink.currentContext();
}
}
@@ -842,20 +842,6 @@ public abstract class ClassUtils {
return (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()));
}
/**
* Determine if the supplied {@link Class} is a JVM-generated implementation
* class for a lambda expression or method reference.
* <p>This method makes a best-effort attempt at determining this, based on
* checks that work on modern, mainstream JVMs.
* @param clazz the class to check
* @return {@code true} if the class is a lambda implementation class
* @since 5.3.19
*/
public static boolean isLambdaClass(Class<?> clazz) {
return (clazz.isSynthetic() && (clazz.getSuperclass() == Object.class) &&
(clazz.getInterfaces().length > 0) && clazz.getName().contains("$$Lambda"));
}
/**
* Check whether the given object is a CGLIB proxy.
* @param object the object to check
@@ -65,17 +65,6 @@ public abstract class MimeTypeUtils {
*/
public static final String ALL_VALUE = "*/*";
/**
* Public constant mime type for {@code application/graphql+json}.
* @see <a href="https://github.com/graphql/graphql-over-http">GraphQL over HTTP spec</a>
* */
public static final MimeType APPLICATION_GRAPHQL;
/**
* A String equivalent of {@link MimeTypeUtils#APPLICATION_GRAPHQL}.
*/
public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql+json";
/**
* Public constant mime type for {@code application/json}.
* */
@@ -176,7 +165,6 @@ public abstract class MimeTypeUtils {
static {
// Not using "parseMimeType" to avoid static init cost
ALL = new MimeType("*", "*");
APPLICATION_GRAPHQL = new MimeType("application", "graphql+json");
APPLICATION_JSON = new MimeType("application", "json");
APPLICATION_OCTET_STREAM = new MimeType("application", "octet-stream");
APPLICATION_XML = new MimeType("application", "xml");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
@@ -409,29 +408,6 @@ class ClassUtilsTests {
assertThat(ClassUtils.isPrimitiveOrWrapper(type)).isTrue();
}
@Test
void isLambda() {
assertIsLambda(ClassUtilsTests.staticLambdaExpression);
assertIsLambda(ClassUtilsTests::staticStringFactory);
assertIsLambda(this.instanceLambdaExpression);
assertIsLambda(this::instanceStringFactory);
}
@Test
void isNotLambda() {
assertIsNotLambda(new EnigmaSupplier());
assertIsNotLambda(new Supplier<String>() {
@Override
public String get() {
return "anonymous inner class";
}
});
assertIsNotLambda(new Fake$$LambdaSupplier());
}
@Nested
class GetStaticMethodTests {
@@ -524,38 +500,4 @@ class ClassUtilsTests {
}
}
private static void assertIsLambda(Supplier<String> supplier) {
assertThat(ClassUtils.isLambdaClass(supplier.getClass())).isTrue();
}
private static void assertIsNotLambda(Supplier<String> supplier) {
assertThat(ClassUtils.isLambdaClass(supplier.getClass())).isFalse();
}
private static final Supplier<String> staticLambdaExpression = () -> "static lambda expression";
private final Supplier<String> instanceLambdaExpression = () -> "instance lambda expressions";
private static String staticStringFactory() {
return "static string factory";
}
private String instanceStringFactory() {
return "instance string factory";
}
private static class EnigmaSupplier implements Supplier<String> {
@Override
public String get() {
return "enigma";
}
}
private static class Fake$$LambdaSupplier implements Supplier<String> {
@Override
public String get() {
return "fake lambda";
}
}
}
@@ -288,8 +288,8 @@ public class ConstructorReference extends SpelNodeImpl {
else {
// There is an initializer
if (this.dimensions == null || this.dimensions.length > 1) {
// There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}})
// - this is not currently supported
// There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}}) - this
// is not currently supported
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,18 +98,9 @@ public class DataClassRowMapper<T> extends BeanPropertyRowMapper<T> {
if (this.constructorParameterNames != null && this.constructorParameterTypes != null) {
args = new Object[this.constructorParameterNames.length];
for (int i = 0; i < args.length; i++) {
String name = this.constructorParameterNames[i];
int index;
try {
// Try direct name match first
index = rs.findColumn(lowerCaseName(name));
}
catch (SQLException ex) {
// Try underscored name match instead
index = rs.findColumn(underscoreName(name));
}
String name = underscoreName(this.constructorParameterNames[i]);
TypeDescriptor td = this.constructorParameterTypes[i];
Object value = getColumnValue(rs, index, td.getType());
Object value = getColumnValue(rs, rs.findColumn(name), td.getType());
args[i] = tc.convertIfNecessary(value, td.getType(), td);
}
}
@@ -20,7 +20,6 @@ import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.sql.Timestamp;
@@ -64,7 +63,7 @@ public abstract class AbstractRowMapperTests {
protected void verifyPerson(ConcretePerson person) {
assertThat(person.getName()).isEqualTo("Bubba");
assertThat(person.getAge()).isEqualTo(22L);
assertThat(person.getBirthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.getBirth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.getBalance()).isEqualTo(new BigDecimal("1234.56"));
verifyPersonViaBeanWrapper(person);
}
@@ -95,14 +94,7 @@ public abstract class AbstractRowMapperTests {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(person);
assertThat(bw.getPropertyValue("name")).isEqualTo("Bubba");
assertThat(bw.getPropertyValue("age")).isEqualTo(22L);
Date birthDate;
if (bw.isReadableProperty("birth_date")) {
birthDate = (Date) bw.getPropertyValue("birth_date");
}
else {
birthDate = (Date) bw.getPropertyValue("birthDate");
}
assertThat(birthDate).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat((Date) bw.getPropertyValue("birth_date")).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(bw.getPropertyValue("balance")).isEqualTo(new BigDecimal("1234.56"));
}
@@ -115,7 +107,7 @@ public abstract class AbstractRowMapperTests {
}
protected enum MockType {ONE, TWO, THREE, FOUR}
protected enum MockType {ONE, TWO, THREE}
protected static class Mock {
@@ -160,19 +152,13 @@ public abstract class AbstractRowMapperTests {
given(resultSetMetaData.getColumnLabel(1)).willReturn(
type == MockType.THREE ? "Last Name" : "name");
given(resultSetMetaData.getColumnLabel(2)).willReturn("age");
given(resultSetMetaData.getColumnLabel(3)).willReturn(type == MockType.FOUR ? "birthdate" :"birth_date");
given(resultSetMetaData.getColumnLabel(3)).willReturn("birth_date");
given(resultSetMetaData.getColumnLabel(4)).willReturn("balance");
given(resultSetMetaData.getColumnLabel(5)).willReturn("e_mail");
given(resultSet.findColumn("name")).willReturn(1);
given(resultSet.findColumn("age")).willReturn(2);
if (type == MockType.FOUR) {
given(resultSet.findColumn("birthdate")).willReturn(3);
}
else {
given(resultSet.findColumn("birthdate")).willThrow(new SQLException());
given(resultSet.findColumn("birth_date")).willReturn(3);
}
given(resultSet.findColumn("birth_date")).willReturn(3);
given(resultSet.findColumn("balance")).willReturn(4);
given(resultSet.findColumn("e_mail")).willReturn(5);
@@ -140,17 +140,6 @@ class BeanPropertyRowMapperTests extends AbstractRowMapperTests {
mock.verifyClosed();
}
@Test
void queryWithDirectNameMatchOnBirthDate() throws Exception {
Mock mock = new Mock(MockType.FOUR);
List<ConcretePerson> result = mock.getJdbcTemplate().query(
"select name, age, birthdate, balance from people",
new BeanPropertyRowMapper<>(ConcretePerson.class));
assertThat(result).hasSize(1);
verifyPerson(result.get(0));
mock.verifyClosed();
}
@Test
void queryWithUnderscoreInColumnNameAndPersonWithMultipleAdjacentUppercaseLettersInPropertyName() throws Exception {
Mock mock = new Mock();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests {
ConstructorPersonWithGenerics person = result.get(0);
assertThat(person.name()).isEqualTo("Bubba");
assertThat(person.age()).isEqualTo(22L);
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.balance()).isEqualTo(Collections.singletonList(new BigDecimal("1234.56")));
mock.verifyClosed();
@@ -65,15 +65,15 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests {
@Test
public void testStaticQueryWithDataClassAndSetters() throws Exception {
Mock mock = new Mock(MockType.FOUR);
Mock mock = new Mock();
List<ConstructorPersonWithSetters> result = mock.getJdbcTemplate().query(
"select name, age, birthdate, balance from people",
"select name, age, birth_date, balance from people",
new DataClassRowMapper<>(ConstructorPersonWithSetters.class));
assertThat(result.size()).isEqualTo(1);
ConstructorPersonWithSetters person = result.get(0);
assertThat(person.name()).isEqualTo("BUBBA");
assertThat(person.age()).isEqualTo(22L);
assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L));
assertThat(person.balance()).isEqualTo(new BigDecimal("1234.56"));
mock.verifyClosed();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ public abstract class AbstractPerson {
private long age;
private Date birthDate;
private Date birth_date;
public String getName() {
@@ -46,12 +46,12 @@ public abstract class AbstractPerson {
this.age = age;
}
public Date getBirthDate() {
return birthDate;
public Date getBirth_date() {
return birth_date;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ public class ConstructorPersonWithGenerics {
private final long age;
private final Date birthDate;
private final Date birth_date;
private final List<BigDecimal> balance;
@@ -37,7 +37,7 @@ public class ConstructorPersonWithGenerics {
public ConstructorPersonWithGenerics(String name, long age, Date birth_date, List<BigDecimal> balance) {
this.name = name;
this.age = age;
this.birthDate = birth_date;
this.birth_date = birth_date;
this.balance = balance;
}
@@ -50,8 +50,8 @@ public class ConstructorPersonWithGenerics {
return this.age;
}
public Date birthDate() {
return this.birthDate;
public Date birth_date() {
return this.birth_date;
}
public List<BigDecimal> balance() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,15 +28,15 @@ public class ConstructorPersonWithSetters {
private long age;
private Date birthDate;
private Date birth_date;
private BigDecimal balance;
public ConstructorPersonWithSetters(String name, long age, Date birthDate, BigDecimal balance) {
public ConstructorPersonWithSetters(String name, long age, Date birth_date, BigDecimal balance) {
this.name = name.toUpperCase();
this.age = age;
this.birthDate = birthDate;
this.birth_date = birth_date;
this.balance = balance;
}
@@ -49,8 +49,8 @@ public class ConstructorPersonWithSetters {
this.age = age;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}
public void setBalance(BigDecimal balance) {
@@ -65,8 +65,8 @@ public class ConstructorPersonWithSetters {
return this.age;
}
public Date birthDate() {
return this.birthDate;
public Date birth_date() {
return this.birth_date;
}
public BigDecimal balance() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,8 +60,8 @@ public class SpacePerson {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
public void setBalance(BigDecimal balanace) {
this.balance = balanace;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -420,21 +420,21 @@ public class HandlerMethod {
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Class<?> returnValueType;
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValueType = (returnValue != null ? returnValue.getClass() : null);
this.returnValue = returnValue;
}
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
super(original);
this.returnValueType = original.returnValueType;
this.returnValue = original.returnValue;
}
@Override
public Class<?> getParameterType() {
return (this.returnValueType != null ? this.returnValueType : super.getParameterType());
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
}
@Override
@@ -95,17 +95,6 @@ public class MediaType extends MimeType implements Serializable {
*/
public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
/**
* Public constant media type for {@code application/graphql+json}.
* @see <a href="https://github.com/graphql/graphql-over-http">GraphQL over HTTP spec</a>
*/
public static final MediaType APPLICATION_GRAPHQL;
/**
* A String equivalent of {@link MediaType#APPLICATION_GRAPHQL}.
*/
public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql+json";
/**
* Public constant media type for {@code application/json}.
*/
@@ -407,7 +396,6 @@ public class MediaType extends MimeType implements Serializable {
APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
APPLICATION_CBOR = new MediaType("application", "cbor");
APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");
APPLICATION_JSON = new MediaType("application", "json");
APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
APPLICATION_NDJSON = new MediaType("application", "x-ndjson");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -105,7 +105,7 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
@Override
public Context currentContext() {
return Context.of(this.sink.contextView());
return this.sink.currentContext();
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -116,7 +116,7 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
@Override
public Context currentContext() {
return Context.of(this.sink.contextView());
return this.sink.currentContext();
}
@Override
@@ -197,7 +197,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
@Override
public InetSocketAddress getLocalAddress() {
return new InetSocketAddress(this.servletRequest.getLocalAddr(), this.servletRequest.getLocalPort());
return new InetSocketAddress(this.servletRequest.getLocalName(), this.servletRequest.getLocalPort());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,6 @@ import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Operators;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.log.LogDelegateFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -58,8 +56,6 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
*/
protected static Log rsReadLogger = LogDelegateFactory.getHiddenLog(AbstractListenerReadPublisher.class);
final static DataBuffer EMPTY_BUFFER = DefaultDataBufferFactory.sharedInstance.allocateBuffer(0);
private final AtomicReference<State> state = new AtomicReference<>(State.UNSUBSCRIBED);
@@ -184,7 +180,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
/**
* Read and publish data one at a time until there is no more data, no more
* demand, or perhaps we completed meanwhile.
* demand, or perhaps we completed in the mean time.
* @return {@code true} if there is more demand; {@code false} if there is
* no more demand or we have completed.
*/
@@ -192,12 +188,7 @@ public abstract class AbstractListenerReadPublisher<T> implements Publisher<T> {
long r;
while ((r = this.demand) > 0 && (this.state.get() != State.COMPLETED)) {
T data = read();
if (data == EMPTY_BUFFER) {
if (rsReadLogger.isTraceEnabled()) {
rsReadLogger.trace(getLogPrefix() + "0 bytes read, trying again");
}
}
else if (data != null) {
if (data != null) {
if (r != Long.MAX_VALUE) {
DEMAND_FIELD_UPDATER.addAndGet(this, -1L);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -198,7 +198,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
@Nullable
protected SslInfo initSslInfo() {
X509Certificate[] certificates = getX509Certificates();
return (certificates != null ? new DefaultSslInfo(getSslSessionId(), certificates) : null);
return certificates != null ? new DefaultSslInfo(getSslSessionId(), certificates) : null;
}
@Nullable
@@ -208,7 +208,8 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
@Nullable
private X509Certificate[] getX509Certificates() {
return (X509Certificate[]) this.request.getAttribute("javax.servlet.request.X509Certificate");
String name = "javax.servlet.request.X509Certificate";
return (X509Certificate[]) this.request.getAttribute(name);
}
@Override
@@ -235,10 +236,10 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
/**
* Read from the request body InputStream and return a DataBuffer.
* Invoked only when {@link ServletInputStream#isReady()} returns "true".
* @return a DataBuffer with data read, or
* {@link AbstractListenerReadPublisher#EMPTY_BUFFER} if 0 bytes were read,
* or {@link #EOF_BUFFER} if the input stream returned -1.
* @return a DataBuffer with data read, or {@link #EOF_BUFFER} if the input
* stream returned -1, or null if 0 bytes were read.
*/
@Nullable
DataBuffer readFromInputStream() throws IOException {
int read = this.request.getInputStream().read(this.buffer);
logBytesRead(read);
@@ -253,7 +254,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
return EOF_BUFFER;
}
return AbstractListenerReadPublisher.EMPTY_BUFFER;
return null;
}
protected final void logBytesRead(int read) {
@@ -153,7 +153,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
return EOF_BUFFER;
}
else {
return AbstractListenerReadPublisher.EMPTY_BUFFER;
return null;
}
}
finally {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,15 +33,6 @@ import org.springframework.web.util.WebUtils;
* Special {@link org.springframework.validation.DataBinder} to perform data binding
* from servlet request parameters to JavaBeans, including support for multipart files.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* <p>See the DataBinder/WebDataBinder superclasses for customization options,
* which include specifying allowed/required fields, and registering custom
* property editors.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,15 +34,6 @@ import org.springframework.web.multipart.MultipartFile;
* the Servlet API; serves as base class for more specific DataBinder variants,
* such as {@link org.springframework.web.bind.ServletRequestDataBinder}.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* <p>Includes support for field markers which address a common problem with
* HTML checkboxes and select options: detecting that a field was part of
* the form, but did not generate a request parameter because it was empty.
@@ -100,7 +100,6 @@ import java.lang.annotation.Target;
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @see ControllerAdvice
* @see org.springframework.web.context.request.WebRequest
*/
@Target(ElementType.METHOD)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,24 +23,15 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that identifies methods that initialize the
* Annotation that identifies methods which initialize the
* {@link org.springframework.web.bind.WebDataBinder} which
* will be used for populating command and form object arguments
* of annotated handler methods.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* <p>{@code @InitBinder} methods support all arguments that
* {@link RequestMapping @RequestMapping} methods support, except for command/form
* objects and corresponding validation result objects. {@code @InitBinder} methods
* must not have a return value; they are usually declared as {@code void}.
* <p>Such init-binder methods support all arguments that {@link RequestMapping}
* supports, except for command/form objects and corresponding validation result
* objects. Init-binder methods must not have a return value; they are usually
* declared as {@code void}.
*
* <p>Typical arguments are {@link org.springframework.web.bind.WebDataBinder}
* in combination with {@link org.springframework.web.context.request.WebRequest}
@@ -48,7 +39,6 @@ import java.lang.annotation.Target;
*
* @author Juergen Hoeller
* @since 2.5
* @see ControllerAdvice
* @see org.springframework.web.bind.WebDataBinder
* @see org.springframework.web.context.request.WebRequest
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,27 +31,18 @@ import org.springframework.ui.Model;
* for controller classes with {@link RequestMapping @RequestMapping}
* methods.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
* <p>Can be used to expose command objects to a web view, using
* specific attribute names, through annotating corresponding
* parameters of an {@link RequestMapping @RequestMapping} method.
*
* <p>{@code @ModelAttribute} can be used to expose command objects to a web view,
* using specific attribute names, by annotating corresponding parameters of an
* {@link RequestMapping @RequestMapping} method.
*
* <p>{@code @ModelAttribute} can also be used to expose reference data to a web
* view by annotating accessor methods in a controller class with
* <p>Can also be used to expose reference data to a web view
* through annotating accessor methods in a controller class with
* {@link RequestMapping @RequestMapping} methods. Such accessor
* methods are allowed to have any arguments that
* {@link RequestMapping @RequestMapping} methods support, returning
* the model attribute value to expose.
*
* <p>Note however that reference data and all other model content are
* <p>Note however that reference data and all other model content is
* not available to web views when request processing results in an
* {@code Exception} since the exception could be raised at any time
* making the content of the model unreliable. For this reason
@@ -61,7 +52,6 @@ import org.springframework.ui.Model;
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 2.5
* @see ControllerAdvice
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@@ -87,7 +77,7 @@ public @interface ModelAttribute {
String name() default "";
/**
* Allows data binding to be disabled directly on an {@code @ModelAttribute}
* Allows declaring data binding disabled directly on an {@code @ModelAttribute}
* method parameter or on the attribute returned from an {@code @ModelAttribute}
* method, both of which would prevent data binding for that attribute.
* <p>By default this is set to {@code true} in which case data binding applies.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,15 +36,6 @@ import org.springframework.web.server.ServerWebExchange;
* Specialized {@link org.springframework.validation.DataBinder} to perform data
* binding from URL query parameters or form data in the request data to Java objects.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 5.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,15 +35,6 @@ import org.springframework.web.multipart.support.StandardServletPartUtils;
* Special {@link org.springframework.validation.DataBinder} to perform data binding
* from web request parameters to JavaBeans, including support for multipart files.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* <p>See the DataBinder/WebDataBinder superclasses for customization options,
* which include specifying allowed/required fields, and registering custom
* property editors.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -582,21 +582,21 @@ public class HandlerMethod {
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Class<?> returnValueType;
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValueType = (returnValue != null ? returnValue.getClass() : null);
this.returnValue = returnValue;
}
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
super(original);
this.returnValueType = original.returnValueType;
this.returnValue = original.returnValue;
}
@Override
public Class<?> getParameterType() {
return (this.returnValueType != null ? this.returnValueType : super.getParameterType());
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
}
@Override
@@ -103,7 +103,7 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
);
}
@Test // SPR-15866
@Test // SPR-15866
public void canEncodeWithCustomMimeType() {
MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(new ObjectMapper(), textJavascript);
@@ -231,8 +231,9 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
);
}
@Test // gh-28045
@Test // gh-28045
public void jacksonValueUnwrappedBeforeObjectMapperSelection() {
JacksonViewBean bean = new JacksonViewBean();
bean.setWithView1("with");
bean.setWithView2("with");
@@ -247,15 +248,13 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests<Jackson2JsonE
ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true);
this.encoder.registerObjectMappersForType(JacksonViewBean.class, map -> map.put(halMediaType, mapper));
String ls = System.lineSeparator(); // output below is different between Unix and Windows
testEncode(Mono.just(jacksonValue), type, halMediaType, Collections.emptyMap(), step -> step
.consumeNextWith(expectString("{" + ls + " \"withView1\" : \"with\"" + ls + "}")
.andThen(DataBufferUtils::release))
.consumeNextWith(expectString("{\n \"withView1\" : \"with\"\n}").andThen(DataBufferUtils::release))
.verifyComplete()
);
}
@Test // gh-22771
@Test // gh-22771
public void encodeWithFlushAfterWriteOff() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -116,7 +116,7 @@ public class InitBinderDataBinderFactoryTests {
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, "foo");
assertThat(dataBinder.getDisallowedFields()).isNotNull();
assertThat(dataBinder.getDisallowedFields()[0]).isEqualToIgnoringCase("requestParam-22");
assertThat(dataBinder.getDisallowedFields()[0]).isEqualTo("requestParam-22");
}
private WebDataBinderFactory createFactory(String methodName, Class<?>... parameterTypes)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -269,7 +269,7 @@ public class ModelAttributeMethodProcessorTests {
assertThat(this.container.getModel().get("testBean")).isSameAs(testBean);
}
@Test // gh-25182
@Test // gh-25182
public void resolveConstructorListArgumentFromCommaSeparatedRequestParameter() throws Exception {
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addParameter("listOfStrings", "1,2");
@@ -279,6 +279,7 @@ public class ModelAttributeMethodProcessorTests {
given(factory.createBinder(any(), any(), eq("testBeanWithConstructorArgs")))
.willAnswer(invocation -> {
WebRequestDataBinder binder = new WebRequestDataBinder(invocation.getArgument(1));
// Add conversion service which will convert "1,2" to a list
binder.setConversionService(new DefaultFormattingConversionService());
return binder;
@@ -308,6 +309,7 @@ public class ModelAttributeMethodProcessorTests {
private boolean validateInvoked;
public StubRequestDataBinder(Object target, String objectName) {
super(target, objectName);
}
@@ -343,7 +345,7 @@ public class ModelAttributeMethodProcessorTests {
}
@SessionAttributes(types = TestBean.class)
@SessionAttributes(types=TestBean.class)
private static class ModelAttributeHandler {
@SuppressWarnings("unused")
@@ -358,7 +360,6 @@ public class ModelAttributeMethodProcessorTests {
}
}
static class TestBeanWithConstructorArgs {
final List<String> listOfStrings;
@@ -366,15 +367,15 @@ public class ModelAttributeMethodProcessorTests {
public TestBeanWithConstructorArgs(List<String> listOfStrings) {
this.listOfStrings = listOfStrings;
}
}
@ModelAttribute("modelAttrName")
@SuppressWarnings("unused")
@ModelAttribute("modelAttrName") @SuppressWarnings("unused")
private String annotatedReturnValue() {
return null;
}
@SuppressWarnings("unused")
private TestBean notAnnotatedReturnValue() {
return null;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -121,7 +121,7 @@ public class InitBinderBindingContextTests {
WebDataBinder dataBinder = context.createDataBinder(exchange, null, "foo");
assertThat(dataBinder.getDisallowedFields()).isNotNull();
assertThat(dataBinder.getDisallowedFields()[0]).isEqualToIgnoringCase("requestParam-22");
assertThat(dataBinder.getDisallowedFields()[0]).isEqualTo("requestParam-22");
}
@@ -25,6 +25,7 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.junit.jupiter.api.Disabled;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@@ -62,6 +63,7 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.Undert
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
@Disabled
class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTests {
private WebClient webClient;
@@ -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.
@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.jupiter.api.Disabled
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan
@@ -39,6 +40,7 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.HttpSe
import reactor.core.publisher.Flux
import java.time.Duration
@Disabled
class CoroutinesIntegrationTests : AbstractRequestMappingIntegrationTests() {
override fun initApplicationContext(): ApplicationContext {
@@ -242,12 +242,12 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
}
if (mediaTypesToUse.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,15 +29,6 @@ import org.springframework.web.servlet.HandlerMapping;
* Subclass of {@link ServletRequestDataBinder} that adds URI template variables
* to the values used for data binding.
*
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
* parts of the object graph that are not meant to be accessed or modified by
* external clients. Therefore the design and use of data binding should be considered
* carefully with regard to security. For more details, please refer to the dedicated
* sections on data binding for
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
* in the reference manual.
*
* @author Rossen Stoyanchev
* @since 3.1
* @see ServletRequestDataBinder
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2236,7 +2236,6 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertThat(response.getContentAsString()).isEqualTo("foo-body");
}
@Controller
static class ControllerWithEmptyValueMapping {
@@ -3574,6 +3573,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertThat(headers.getContentType()).as("Invalid Content-Type").isEqualTo(new MediaType("text", "html"));
multiValueMap(headers, writer);
}
}
@Controller
@@ -1,95 +0,0 @@
In the context of web applications, _data binding_ involves the binding of HTTP request
parameters (that is, form data or query parameters) to properties in a model object and
its nested objects.
Only `public` properties following the
https://www.oracle.com/java/technologies/javase/javabeans-spec.html[JavaBeans naming conventions]
are exposed for data binding — for example, `public String getFirstName()` and
`public void setFirstName(String)` methods for a `firstName` property.
TIP: The model object, and its nested object graph, is also sometimes referred to as a
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
By default, Spring permits binding to all public properties in the model object graph.
This means you need to carefully consider what public properties the model has, since a
client could target any public property path, even some that are not expected to be
targeted for a given use case.
For example, given an HTTP form data endpoint, a malicious client could supply values for
properties that exist in the model object graph but are not part of the HTML form
presented in the browser. This could lead to data being set on the model object and any
of its nested objects, that is not expected to be updated.
The recommended approach is to use a _dedicated model object_ that exposes only
properties that are relevant for the form submission. For example, on a form for changing
a user's email address, the model object should declare a minimum set of properties such
as in the following `ChangeEmailForm`.
[source,java,indent=0,subs="verbatim,quotes"]
----
public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}
----
If you cannot or do not want to use a _dedicated model object_ for each data
binding use case, you **must** limit the properties that are allowed for data binding.
Ideally, you can achieve this by registering _allowed field patterns_ via the
`setAllowedFields()` method on `WebDataBinder`.
For example, to register allowed field patterns in your application, you can implement an
`@InitBinder` method in a `@Controller` or `@ControllerAdvice` component as shown below:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class ChangeEmailController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}
// @RequestMapping methods, etc.
}
----
In addition to registering allowed patterns, it is also possible to register _disallowed
field patterns_ via the `setDisallowedFields()` method in `DataBinder` and its subclasses.
Please note, however, that an "allow list" is safer than a "deny list". Consequently,
`setAllowedFields()` should be favored over `setDisallowedFields()`.
Note that matching against allowed field patterns is case-sensitive; whereas, matching
against disallowed field patterns is case-insensitive. In addition, a field matching a
disallowed pattern will not be accepted even if it also happens to match a pattern in the
allowed list.
[WARNING]
====
It is extremely important to properly configure allowed and disallowed field patterns
when exposing your domain model directly for data binding purposes. Otherwise, it is a
big security risk.
Furthermore, it is strongly recommended that you do **not** use types from your domain
model such as JPA or Hibernate entities as the model object in data binding scenarios.
====
-5
View File
@@ -3319,11 +3319,6 @@ controller-specific `Formatter` instances, as the following example shows:
----
<1> Adding a custom formatter (a `DateFormatter`, in this case).
[[webflux-ann-initbinder-model-design]]
==== Model Design
[.small]#<<web.adoc#mvc-ann-initbinder-model-design, Web MVC>>#
include::web-data-binding-model-design.adoc[]
[[webflux-ann-controller-exceptions]]
-7
View File
@@ -3751,13 +3751,6 @@ controller-specific `Formatter` implementations, as the following example shows:
----
<1> Defining an `@InitBinder` method on a custom formatter.
[[mvc-ann-initbinder-model-design]]
==== Model Design
[.small]#<<web-reactive.adoc#webflux-ann-initbinder-model-design, WebFlux>>#
include::web-data-binding-model-design.adoc[]
[[mvc-ann-exceptionhandler]]
=== Exceptions
[.small]#<<web-reactive.adoc#webflux-ann-controller-exceptions, WebFlux>>#