Merge branch '5.1.x'
This commit is contained in:
commit
a5cb8799fa
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -571,8 +571,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
bean.destroy();
|
bean.destroy();
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isWarnEnabled()) {
|
||||||
logger.info("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
|
logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,10 +261,10 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.info(msg, ex);
|
logger.warn(msg, ex);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info(msg + ": " + ex);
|
logger.warn(msg + ": " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,14 +343,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||||
String msg = "Destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
String msg = "Destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||||
this.beanName + "' threw an exception";
|
this.beanName + "' threw an exception";
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.info(msg, ex.getTargetException());
|
logger.warn(msg, ex.getTargetException());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info(msg + ": " + ex.getTargetException());
|
logger.warn(msg + ": " + ex.getTargetException());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
logger.info("Failed to invoke destroy method '" + this.destroyMethodName +
|
logger.warn("Failed to invoke destroy method '" + this.destroyMethodName +
|
||||||
"' on bean with name '" + this.beanName + "'", ex);
|
"' on bean with name '" + this.beanName + "'", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
package org.springframework.validation.beanvalidation2;
|
package org.springframework.validation.beanvalidation2;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Inherited;
|
import java.lang.annotation.Inherited;
|
||||||
import java.lang.annotation.Repeatable;
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -50,13 +52,11 @@ import org.springframework.beans.BeanWrapper;
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
import org.springframework.context.support.StaticMessageSource;
|
import org.springframework.context.support.StaticMessageSource;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.SerializationTestUtils;
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +88,7 @@ public class SpringValidatorAdapterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
public void testNoStringArgumentValue() {
|
public void testNoStringArgumentValue() throws Exception {
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
testBean.setPassword("pass");
|
testBean.setPassword("pass");
|
||||||
testBean.setConfirmPassword("pass");
|
testBean.setConfirmPassword("pass");
|
||||||
|
@ -103,10 +103,11 @@ public class SpringValidatorAdapterTests {
|
||||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128");
|
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128");
|
||||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||||
|
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() {
|
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() throws Exception {
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
testBean.setPassword("password");
|
testBean.setPassword("password");
|
||||||
testBean.setConfirmPassword("PASSWORD");
|
testBean.setConfirmPassword("PASSWORD");
|
||||||
|
@ -121,6 +122,7 @@ public class SpringValidatorAdapterTests {
|
||||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)");
|
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)");
|
||||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||||
|
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
|
@ -323,8 +325,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Constraint(validatedBy = {SameValidator.class})
|
@Constraint(validatedBy = {SameValidator.class})
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Repeatable(SameGroup.class)
|
@Repeatable(SameGroup.class)
|
||||||
@interface Same {
|
@interface Same {
|
||||||
|
|
||||||
|
@ -338,8 +340,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
String comparingField();
|
String comparingField();
|
||||||
|
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@interface List {
|
@interface List {
|
||||||
Same[] value();
|
Same[] value();
|
||||||
|
@ -349,8 +351,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Inherited
|
@Inherited
|
||||||
@Retention(RUNTIME)
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@interface SameGroup {
|
@interface SameGroup {
|
||||||
|
|
||||||
Same[] value();
|
Same[] value();
|
||||||
|
@ -486,7 +488,7 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
|
|
||||||
@Constraint(validatedBy = AnythingValidator.class)
|
@Constraint(validatedBy = AnythingValidator.class)
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface AnythingValid {
|
public @interface AnythingValid {
|
||||||
|
|
||||||
String message() default "{AnythingValid.message}";
|
String message() default "{AnythingValid.message}";
|
||||||
|
@ -507,23 +509,22 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||||
List<Field> fieldsErros = new ArrayList<>();
|
List<Field> fieldsErrors = new ArrayList<>();
|
||||||
Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
|
Arrays.asList(value.getClass().getDeclaredFields()).forEach(field -> {
|
||||||
f.setAccessible(true);
|
field.setAccessible(true);
|
||||||
try {
|
try {
|
||||||
if (!f.getName().equals(ID) && f.get(value) == null) {
|
if (!field.getName().equals(ID) && field.get(value) == null) {
|
||||||
fieldsErros.add(f);
|
fieldsErrors.add(field);
|
||||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
|
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
|
||||||
.addPropertyNode(f.getName())
|
.addPropertyNode(field.getName())
|
||||||
.addConstraintViolation();
|
.addConstraintViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException ex) {
|
catch (IllegalAccessException ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return fieldsErros.isEmpty();
|
return fieldsErrors.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,27 +165,15 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||||
String nestedField = bindingResult.getNestedPath() + field;
|
String nestedField = bindingResult.getNestedPath() + field;
|
||||||
if (nestedField.isEmpty()) {
|
if (nestedField.isEmpty()) {
|
||||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
|
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
|
||||||
ObjectError error = new ObjectError(
|
ObjectError error = new ViolationObjectError(
|
||||||
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()) {
|
errors.getObjectName(), errorCodes, errorArgs, violation, this);
|
||||||
@Override
|
|
||||||
public boolean shouldRenderDefaultMessage() {
|
|
||||||
return requiresMessageFormat(violation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
error.wrap(violation);
|
|
||||||
bindingResult.addError(error);
|
bindingResult.addError(error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
|
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
|
||||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
|
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
|
||||||
FieldError error = new FieldError(errors.getObjectName(), nestedField,
|
FieldError error = new ViolationFieldError(errors.getObjectName(), nestedField,
|
||||||
rejectedValue, false, errorCodes, errorArgs, violation.getMessage()) {
|
rejectedValue, errorCodes, errorArgs, violation, this);
|
||||||
@Override
|
|
||||||
public boolean shouldRenderDefaultMessage() {
|
|
||||||
return requiresMessageFormat(violation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
error.wrap(violation);
|
|
||||||
bindingResult.addError(error);
|
bindingResult.addError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,29 +295,6 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||||
return new DefaultMessageSourceResolvable(codes, field);
|
return new DefaultMessageSourceResolvable(codes, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate whether this violation's interpolated message has remaining
|
|
||||||
* placeholders and therefore requires {@link java.text.MessageFormat}
|
|
||||||
* to be applied to it. Called for a Bean Validation defined message
|
|
||||||
* (coming out {@code ValidationMessages.properties}) when rendered
|
|
||||||
* as the default message in Spring's MessageSource.
|
|
||||||
* <p>The default implementation considers a Spring-style "{0}" placeholder
|
|
||||||
* for the field name as an indication for {@link java.text.MessageFormat}.
|
|
||||||
* Any other placeholder or escape syntax occurrences are typically a
|
|
||||||
* mismatch, coming out of regex pattern values or the like. Note that
|
|
||||||
* standard Bean Validation does not support "{0}" style placeholders at all;
|
|
||||||
* this is a feature typically used in Spring MessageSource resource bundles.
|
|
||||||
* @param violation the Bean Validation constraint violation, including
|
|
||||||
* BV-defined interpolation of named attribute references in its message
|
|
||||||
* @return {@code true} if {@code java.text.MessageFormat} is to be applied,
|
|
||||||
* or {@code false} if the violation's message should be used as-is
|
|
||||||
* @since 5.1.8
|
|
||||||
* @see #getArgumentsForConstraint
|
|
||||||
*/
|
|
||||||
protected boolean requiresMessageFormat(ConstraintViolation<?> violation) {
|
|
||||||
return violation.getMessage().contains("{0}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the rejected value behind the given constraint violation,
|
* Extract the rejected value behind the given constraint violation,
|
||||||
* for exposure through the Spring errors representation.
|
* for exposure through the Spring errors representation.
|
||||||
|
@ -354,6 +319,33 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||||
return invalidValue;
|
return invalidValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate whether this violation's interpolated message has remaining
|
||||||
|
* placeholders and therefore requires {@link java.text.MessageFormat}
|
||||||
|
* to be applied to it. Called for a Bean Validation defined message
|
||||||
|
* (coming out {@code ValidationMessages.properties}) when rendered
|
||||||
|
* as the default message in Spring's MessageSource.
|
||||||
|
* <p>The default implementation considers a Spring-style "{0}" placeholder
|
||||||
|
* for the field name as an indication for {@link java.text.MessageFormat}.
|
||||||
|
* Any other placeholder or escape syntax occurrences are typically a
|
||||||
|
* mismatch, coming out of regex pattern values or the like. Note that
|
||||||
|
* standard Bean Validation does not support "{0}" style placeholders at all;
|
||||||
|
* this is a feature typically used in Spring MessageSource resource bundles.
|
||||||
|
* @param violation the Bean Validation constraint violation, including
|
||||||
|
* BV-defined interpolation of named attribute references in its message
|
||||||
|
* @return {@code true} if {@code java.text.MessageFormat} is to be applied,
|
||||||
|
* or {@code false} if the violation's message should be used as-is
|
||||||
|
* @since 5.1.8
|
||||||
|
* @see #getArgumentsForConstraint
|
||||||
|
*/
|
||||||
|
protected boolean requiresMessageFormat(ConstraintViolation<?> violation) {
|
||||||
|
return containsSpringStylePlaceholder(violation.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsSpringStylePlaceholder(@Nullable String message) {
|
||||||
|
return (message != null && message.contains("{0}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Implementation of JSR-303 Validator interface
|
// Implementation of JSR-303 Validator interface
|
||||||
|
@ -436,6 +428,71 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||||
public String getDefaultMessage() {
|
public String getDefaultMessage() {
|
||||||
return this.resolvableString;
|
return this.resolvableString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.resolvableString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass of {@code ObjectError} with Spring-style default message rendering.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ViolationObjectError extends ObjectError implements Serializable {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private transient SpringValidatorAdapter adapter;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private transient ConstraintViolation<?> violation;
|
||||||
|
|
||||||
|
public ViolationObjectError(String objectName, String[] codes, Object[] arguments,
|
||||||
|
ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||||
|
|
||||||
|
super(objectName, codes, arguments, violation.getMessage());
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.violation = violation;
|
||||||
|
wrap(violation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRenderDefaultMessage() {
|
||||||
|
return (this.adapter != null && this.violation != null ?
|
||||||
|
this.adapter.requiresMessageFormat(this.violation) :
|
||||||
|
containsSpringStylePlaceholder(getDefaultMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass of {@code FieldError} with Spring-style default message rendering.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ViolationFieldError extends FieldError implements Serializable {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private transient SpringValidatorAdapter adapter;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private transient ConstraintViolation<?> violation;
|
||||||
|
|
||||||
|
public ViolationFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||||
|
Object[] arguments, ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||||
|
|
||||||
|
super(objectName, field, rejectedValue, false, codes, arguments, violation.getMessage());
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.violation = violation;
|
||||||
|
wrap(violation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRenderDefaultMessage() {
|
||||||
|
return (this.adapter != null && this.violation != null ?
|
||||||
|
this.adapter.requiresMessageFormat(this.violation) :
|
||||||
|
containsSpringStylePlaceholder(getDefaultMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
package org.springframework.validation.beanvalidation;
|
package org.springframework.validation.beanvalidation;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Inherited;
|
import java.lang.annotation.Inherited;
|
||||||
import java.lang.annotation.Repeatable;
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -48,12 +50,10 @@ import org.springframework.beans.BeanWrapper;
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
import org.springframework.context.support.StaticMessageSource;
|
import org.springframework.context.support.StaticMessageSource;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.SerializationTestUtils;
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +85,7 @@ public class SpringValidatorAdapterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
public void testNoStringArgumentValue() {
|
public void testNoStringArgumentValue() throws Exception {
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
testBean.setPassword("pass");
|
testBean.setPassword("pass");
|
||||||
testBean.setConfirmPassword("pass");
|
testBean.setConfirmPassword("pass");
|
||||||
|
@ -100,10 +100,11 @@ public class SpringValidatorAdapterTests {
|
||||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128");
|
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128");
|
||||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||||
|
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() {
|
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() throws Exception {
|
||||||
TestBean testBean = new TestBean();
|
TestBean testBean = new TestBean();
|
||||||
testBean.setPassword("password");
|
testBean.setPassword("password");
|
||||||
testBean.setConfirmPassword("PASSWORD");
|
testBean.setConfirmPassword("PASSWORD");
|
||||||
|
@ -118,6 +119,7 @@ public class SpringValidatorAdapterTests {
|
||||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)");
|
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)");
|
||||||
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
assertThat(error.contains(ConstraintViolation.class)).isTrue();
|
||||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password");
|
||||||
|
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-13406
|
@Test // SPR-13406
|
||||||
|
@ -278,8 +280,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Constraint(validatedBy = {SameValidator.class})
|
@Constraint(validatedBy = {SameValidator.class})
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Repeatable(SameGroup.class)
|
@Repeatable(SameGroup.class)
|
||||||
@interface Same {
|
@interface Same {
|
||||||
|
|
||||||
|
@ -293,8 +295,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
String comparingField();
|
String comparingField();
|
||||||
|
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@interface List {
|
@interface List {
|
||||||
Same[] value();
|
Same[] value();
|
||||||
|
@ -304,8 +306,8 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Inherited
|
@Inherited
|
||||||
@Retention(RUNTIME)
|
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
|
||||||
@Target({TYPE, ANNOTATION_TYPE})
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@interface SameGroup {
|
@interface SameGroup {
|
||||||
|
|
||||||
Same[] value();
|
Same[] value();
|
||||||
|
@ -441,7 +443,7 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
|
|
||||||
@Constraint(validatedBy = AnythingValidator.class)
|
@Constraint(validatedBy = AnythingValidator.class)
|
||||||
@Retention(RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface AnythingValid {
|
public @interface AnythingValid {
|
||||||
|
|
||||||
String message() default "{AnythingValid.message}";
|
String message() default "{AnythingValid.message}";
|
||||||
|
@ -462,14 +464,14 @@ public class SpringValidatorAdapterTests {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||||
List<Field> fieldsErros = new ArrayList<>();
|
List<Field> fieldsErrors = new ArrayList<>();
|
||||||
Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
|
Arrays.asList(value.getClass().getDeclaredFields()).forEach(field -> {
|
||||||
f.setAccessible(true);
|
field.setAccessible(true);
|
||||||
try {
|
try {
|
||||||
if (!f.getName().equals(ID) && f.get(value) == null) {
|
if (!field.getName().equals(ID) && field.get(value) == null) {
|
||||||
fieldsErros.add(f);
|
fieldsErrors.add(field);
|
||||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
|
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
|
||||||
.addPropertyNode(f.getName())
|
.addPropertyNode(field.getName())
|
||||||
.addConstraintViolation();
|
.addConstraintViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +479,7 @@ public class SpringValidatorAdapterTests {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return fieldsErros.isEmpty();
|
return fieldsErrors.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||||
Property property = new Property(type, method, null);
|
Property property = new Property(type, method, null);
|
||||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||||
|
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||||
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
|
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
|
||||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||||
return true;
|
return true;
|
||||||
|
@ -180,6 +181,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||||
Property property = new Property(type, method, null);
|
Property property = new Property(type, method, null);
|
||||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||||
|
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||||
invoker = new InvokerPair(method, typeDescriptor);
|
invoker = new InvokerPair(method, typeDescriptor);
|
||||||
this.lastReadInvokerPair = invoker;
|
this.lastReadInvokerPair = invoker;
|
||||||
this.readerCache.put(cacheKey, invoker);
|
this.readerCache.put(cacheKey, invoker);
|
||||||
|
@ -239,6 +241,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
// Treat it like a property
|
// Treat it like a property
|
||||||
Property property = new Property(type, null, method);
|
Property property = new Property(type, null, method);
|
||||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||||
|
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||||
this.writerCache.put(cacheKey, method);
|
this.writerCache.put(cacheKey, method);
|
||||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||||
return true;
|
return true;
|
||||||
|
@ -287,6 +290,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
method = findSetterForProperty(name, type, target);
|
method = findSetterForProperty(name, type, target);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
|
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||||
cachedMember = method;
|
cachedMember = method;
|
||||||
this.writerCache.put(cacheKey, cachedMember);
|
this.writerCache.put(cacheKey, cachedMember);
|
||||||
}
|
}
|
||||||
|
@ -414,13 +418,24 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
method.getParameterCount() == numberOfParams &&
|
method.getParameterCount() == numberOfParams &&
|
||||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
|
(!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
|
||||||
(requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
|
(requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
|
||||||
return ClassUtils.getInterfaceMethodIfPossible(method);
|
return method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return class methods ordered with non-bridge methods appearing higher.
|
||||||
|
*/
|
||||||
|
private Method[] getSortedMethods(Class<?> clazz) {
|
||||||
|
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
|
||||||
|
Method[] methods = key.getMethods();
|
||||||
|
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
|
||||||
|
return methods;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the given {@code Method} is a candidate for property access
|
* Determine whether the given {@code Method} is a candidate for property access
|
||||||
* on an instance of the given target class.
|
* on an instance of the given target class.
|
||||||
|
@ -434,17 +449,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return class methods ordered with non-bridge methods appearing higher.
|
|
||||||
*/
|
|
||||||
private Method[] getSortedMethods(Class<?> clazz) {
|
|
||||||
return this.sortedMethodsCache.computeIfAbsent(clazz, key -> {
|
|
||||||
Method[] methods = key.getMethods();
|
|
||||||
Arrays.sort(methods, (o1, o2) -> (o1.isBridge() == o2.isBridge() ? 0 : (o1.isBridge() ? 1 : -1)));
|
|
||||||
return methods;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the method suffixes for a given property name. The default implementation
|
* Return the method suffixes for a given property name. The default implementation
|
||||||
* uses JavaBean conventions with additional support for properties of the form 'xY'
|
* uses JavaBean conventions with additional support for properties of the form 'xY'
|
||||||
|
@ -536,7 +540,9 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
method = findGetterForProperty(name, clazz, target);
|
method = findGetterForProperty(name, clazz, target);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
invocationTarget = new InvokerPair(method, new TypeDescriptor(new MethodParameter(method, -1)));
|
TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(method, -1));
|
||||||
|
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||||
|
invocationTarget = new InvokerPair(method, typeDescriptor);
|
||||||
ReflectionUtils.makeAccessible(method);
|
ReflectionUtils.makeAccessible(method);
|
||||||
this.readerCache.put(cacheKey, invocationTarget);
|
this.readerCache.put(cacheKey, invocationTarget);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||||
import org.springframework.expression.spel.testresources.le.div.mod.reserved.Reserver;
|
import org.springframework.expression.spel.testresources.le.div.mod.reserved.Reserver;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -1177,9 +1178,13 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
public void SPR9994_bridgeMethods() throws Exception {
|
public void SPR9994_bridgeMethods() throws Exception {
|
||||||
ReflectivePropertyAccessor accessor = new ReflectivePropertyAccessor();
|
ReflectivePropertyAccessor accessor = new ReflectivePropertyAccessor();
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
Object target = new GenericImplementation();
|
GenericImplementation target = new GenericImplementation();
|
||||||
|
accessor.write(context, target, "property", "1");
|
||||||
|
assertThat(target.value).isEqualTo(1);
|
||||||
TypedValue value = accessor.read(context, target, "property");
|
TypedValue value = accessor.read(context, target, "property");
|
||||||
|
assertThat(value.getValue()).isEqualTo(1);
|
||||||
assertThat(value.getTypeDescriptor().getType()).isEqualTo(Integer.class);
|
assertThat(value.getTypeDescriptor().getType()).isEqualTo(Integer.class);
|
||||||
|
assertThat(value.getTypeDescriptor().getAnnotations()).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1188,6 +1193,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
Object target = new OnlyBridgeMethod();
|
Object target = new OnlyBridgeMethod();
|
||||||
TypedValue value = accessor.read(context, target, "property");
|
TypedValue value = accessor.read(context, target, "property");
|
||||||
|
assertThat(value.getValue()).isNull();
|
||||||
assertThat(value.getTypeDescriptor().getType()).isEqualTo(Integer.class);
|
assertThat(value.getTypeDescriptor().getType()).isEqualTo(Integer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1196,7 +1202,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||||
Class<?> valueType = parser.parseExpression("simpleProperty").getValueType(evaluationContext);
|
Class<?> valueType = parser.parseExpression("simpleProperty").getValueType(evaluationContext);
|
||||||
assertThat(valueType).isNotNull();
|
assertThat(valueType).isEqualTo(Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1204,7 +1210,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||||
Object value = parser.parseExpression("simpleProperty").getValue(evaluationContext);
|
Object value = parser.parseExpression("simpleProperty").getValue(evaluationContext);
|
||||||
assertThat(value).isNotNull();
|
assertThat(value).isInstanceOf(Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1212,7 +1218,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||||
Class<?> valueType = parser.parseExpression("primitiveProperty").getValueType(evaluationContext);
|
Class<?> valueType = parser.parseExpression("primitiveProperty").getValueType(evaluationContext);
|
||||||
assertThat(valueType).isNotNull();
|
assertThat(valueType).isEqualTo(Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1220,7 +1226,7 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new BooleanHolder());
|
||||||
Object value = parser.parseExpression("primitiveProperty").getValue(evaluationContext);
|
Object value = parser.parseExpression("primitiveProperty").getValue(evaluationContext);
|
||||||
assertThat(value).isNotNull();
|
assertThat(value).isInstanceOf(Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2172,15 +2178,25 @@ public class SpelReproTests extends AbstractExpressionTests {
|
||||||
|
|
||||||
private interface GenericInterface<T extends Number> {
|
private interface GenericInterface<T extends Number> {
|
||||||
|
|
||||||
|
void setProperty(T value);
|
||||||
|
|
||||||
T getProperty();
|
T getProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class GenericImplementation implements GenericInterface<Integer> {
|
private static class GenericImplementation implements GenericInterface<Integer> {
|
||||||
|
|
||||||
|
int value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public void setProperty(Integer value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
public Integer getProperty() {
|
public Integer getProperty() {
|
||||||
return null;
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -26,7 +26,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.concurrent.ListenableFuture;
|
import org.springframework.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,9 +138,10 @@ public class HandlerMethodReturnValueHandlerComposite implements AsyncHandlerMet
|
||||||
@Nullable
|
@Nullable
|
||||||
public ListenableFuture<?> toListenableFuture(Object returnValue, MethodParameter returnType) {
|
public ListenableFuture<?> toListenableFuture(Object returnValue, MethodParameter returnType) {
|
||||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
|
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
|
||||||
Assert.state(handler instanceof AsyncHandlerMethodReturnValueHandler,
|
if (handler instanceof AsyncHandlerMethodReturnValueHandler) {
|
||||||
"AsyncHandlerMethodReturnValueHandler required");
|
return ((AsyncHandlerMethodReturnValueHandler) handler).toListenableFuture(returnValue, returnType);
|
||||||
return ((AsyncHandlerMethodReturnValueHandler) handler).toListenableFuture(returnValue, returnType);
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import reactor.core.publisher.Mono;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ReactiveAdapter;
|
import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.concurrent.ListenableFuture;
|
import org.springframework.util.concurrent.ListenableFuture;
|
||||||
import org.springframework.util.concurrent.MonoToListenableFutureAdapter;
|
import org.springframework.util.concurrent.MonoToListenableFutureAdapter;
|
||||||
|
|
||||||
|
@ -60,8 +59,10 @@ public class ReactiveReturnValueHandler extends AbstractAsyncReturnValueHandler
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<?> toListenableFuture(Object returnValue, MethodParameter returnType) {
|
public ListenableFuture<?> toListenableFuture(Object returnValue, MethodParameter returnType) {
|
||||||
ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnType.getParameterType(), returnValue);
|
ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnType.getParameterType(), returnValue);
|
||||||
Assert.state(adapter != null, () -> "No ReactiveAdapter found for " + returnType.getParameterType());
|
if (adapter != null) {
|
||||||
return new MonoToListenableFutureAdapter<>(Mono.from(adapter.toPublisher(returnValue)));
|
return new MonoToListenableFutureAdapter<>(Mono.from(adapter.toPublisher(returnValue)));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ import org.springframework.messaging.support.MessageHeaderInitializer;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.util.StringValueResolver;
|
import org.springframework.util.StringValueResolver;
|
||||||
|
@ -93,6 +94,10 @@ import org.springframework.validation.Validator;
|
||||||
public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHandler<SimpMessageMappingInfo>
|
public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHandler<SimpMessageMappingInfo>
|
||||||
implements EmbeddedValueResolverAware, SmartLifecycle {
|
implements EmbeddedValueResolverAware, SmartLifecycle {
|
||||||
|
|
||||||
|
private static final boolean reactorPresent = ClassUtils.isPresent(
|
||||||
|
"reactor.core.publisher.Flux", SimpAnnotationMethodMessageHandler.class.getClassLoader());
|
||||||
|
|
||||||
|
|
||||||
private final SubscribableChannel clientInboundChannel;
|
private final SubscribableChannel clientInboundChannel;
|
||||||
|
|
||||||
private final SimpMessageSendingOperations clientMessagingTemplate;
|
private final SimpMessageSendingOperations clientMessagingTemplate;
|
||||||
|
@ -328,7 +333,9 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
|
||||||
|
|
||||||
handlers.add(new ListenableFutureReturnValueHandler());
|
handlers.add(new ListenableFutureReturnValueHandler());
|
||||||
handlers.add(new CompletableFutureReturnValueHandler());
|
handlers.add(new CompletableFutureReturnValueHandler());
|
||||||
handlers.add(new ReactiveReturnValueHandler());
|
if (reactorPresent) {
|
||||||
|
handlers.add(new ReactiveReturnValueHandler());
|
||||||
|
}
|
||||||
|
|
||||||
// Annotation-based return value types
|
// Annotation-based return value types
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,9 @@
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck" />
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck" />
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck" />
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.MutableExceptionCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.MutableExceptionCheck">
|
||||||
|
<property name="format" value="^.*Exception$" />
|
||||||
|
</module>
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.InnerTypeLastCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.InnerTypeLastCheck" />
|
||||||
<module name="com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck" />
|
<module name="com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue