From 30bbf91dbada76e156a70c4928e5eef81812646a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 4 May 2019 17:35:34 +0200 Subject: [PATCH] Rename PayloadArgumentResolver to PayloadMethodArgumentResolver Closes gh-22888 --- .../DefaultMessageHandlerMethodFactory.java | 4 +- .../support/PayloadArgumentResolver.java | 149 +------------ .../PayloadMethodArgumentResolver.java | 211 ++++++++++++++++++ .../SimpAnnotationMethodMessageHandler.java | 8 +- ...> PayloadMethodArgumentResolverTests.java} | 22 +- 5 files changed, 233 insertions(+), 161 deletions(-) create mode 100644 spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java rename spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/{PayloadArgumentResolverTests.java => PayloadMethodArgumentResolverTests.java} (91%) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java index 9d85abb18c3..c3feebc1a09 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java @@ -100,7 +100,7 @@ public class DefaultMessageHandlerMethodFactory /** * Set the Validator instance used for validating {@code @Payload} arguments. * @see org.springframework.validation.annotation.Validated - * @see org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver + * @see PayloadMethodArgumentResolver */ public void setValidator(Validator validator) { this.validator = validator; @@ -173,7 +173,7 @@ public class DefaultMessageHandlerMethodFactory } Assert.notNull(this.messageConverter, "MessageConverter not configured"); - resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator)); + resolvers.add(new PayloadMethodArgumentResolver(this.messageConverter, this.validator)); return resolvers; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java index 3a57517f229..e2829b10bc1 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,50 +16,24 @@ package org.springframework.messaging.handler.annotation.support; -import java.lang.annotation.Annotation; - -import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; -import org.springframework.messaging.Message; -import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.converter.MessageConverter; -import org.springframework.messaging.converter.SmartMessageConverter; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; -import org.springframework.validation.BeanPropertyBindingResult; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.validation.SmartValidator; import org.springframework.validation.Validator; -import org.springframework.validation.annotation.Validated; /** * A resolver to extract and convert the payload of a message using a * {@link MessageConverter}. It also validates the payload using a * {@link Validator} if the argument is annotated with a Validation annotation. * - *

This {@link HandlerMethodArgumentResolver} should be ordered last as it - * supports all types and does not require the {@link Payload} annotation. - * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @author Brian Clozel * @author Stephane Nicoll * @since 4.0 + * @deprecated as of 5.2, in favor of {@link PayloadMethodArgumentResolver} */ -public class PayloadArgumentResolver implements HandlerMethodArgumentResolver { - - private final MessageConverter converter; - - @Nullable - private final Validator validator; - - private final boolean useDefaultResolution; - +@Deprecated +public class PayloadArgumentResolver extends PayloadMethodArgumentResolver { /** * Create a new {@code PayloadArgumentResolver} with the given @@ -93,119 +67,8 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver { public PayloadArgumentResolver(MessageConverter messageConverter, @Nullable Validator validator, boolean useDefaultResolution) { - Assert.notNull(messageConverter, "MessageConverter must not be null"); - this.converter = messageConverter; - this.validator = validator; - this.useDefaultResolution = useDefaultResolution; - } - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return (parameter.hasParameterAnnotation(Payload.class) || this.useDefaultResolution); - } - - @Override - @Nullable - public Object resolveArgument(MethodParameter parameter, Message message) throws Exception { - Payload ann = parameter.getParameterAnnotation(Payload.class); - if (ann != null && StringUtils.hasText(ann.expression())) { - throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver"); - } - - Object payload = message.getPayload(); - if (isEmptyPayload(payload)) { - if (ann == null || ann.required()) { - String paramName = getParameterName(parameter); - BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName); - bindingResult.addError(new ObjectError(paramName, "Payload value must not be empty")); - throw new MethodArgumentNotValidException(message, parameter, bindingResult); - } - else { - return null; - } - } - - Class targetClass = parameter.getParameterType(); - Class payloadClass = payload.getClass(); - if (ClassUtils.isAssignable(targetClass, payloadClass)) { - validate(message, parameter, payload); - return payload; - } - else { - if (this.converter instanceof SmartMessageConverter) { - SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter; - payload = smartConverter.fromMessage(message, targetClass, parameter); - } - else { - payload = this.converter.fromMessage(message, targetClass); - } - if (payload == null) { - throw new MessageConversionException(message, "Cannot convert from [" + - payloadClass.getName() + "] to [" + targetClass.getName() + "] for " + message); - } - validate(message, parameter, payload); - return payload; - } - } - - private String getParameterName(MethodParameter param) { - String paramName = param.getParameterName(); - return (paramName != null ? paramName : "Arg " + param.getParameterIndex()); - } - - /** - * Specify if the given {@code payload} is empty. - * @param payload the payload to check (can be {@code null}) - */ - protected boolean isEmptyPayload(@Nullable Object payload) { - if (payload == null) { - return true; - } - else if (payload instanceof byte[]) { - return ((byte[]) payload).length == 0; - } - else if (payload instanceof String) { - return !StringUtils.hasText((String) payload); - } - else { - return false; - } - } - - /** - * Validate the payload if applicable. - *

The default implementation checks for {@code @javax.validation.Valid}, - * Spring's {@link org.springframework.validation.annotation.Validated}, - * and custom annotations whose name starts with "Valid". - * @param message the currently processed message - * @param parameter the method parameter - * @param target the target payload object - * @throws MethodArgumentNotValidException in case of binding errors - */ - protected void validate(Message message, MethodParameter parameter, Object target) { - if (this.validator == null) { - return; - } - for (Annotation ann : parameter.getParameterAnnotations()) { - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { - Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); - Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); - BeanPropertyBindingResult bindingResult = - new BeanPropertyBindingResult(target, getParameterName(parameter)); - if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator) { - ((SmartValidator) this.validator).validate(target, bindingResult, validationHints); - } - else { - this.validator.validate(target, bindingResult); - } - if (bindingResult.hasErrors()) { - throw new MethodArgumentNotValidException(message, parameter, bindingResult); - } - break; - } - } + super(messageConverter, validator, useDefaultResolution); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java new file mode 100644 index 00000000000..f3bc844db98 --- /dev/null +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java @@ -0,0 +1,211 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.messaging.handler.annotation.support; + +import java.lang.annotation.Annotation; + +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.Nullable; +import org.springframework.messaging.Message; +import org.springframework.messaging.converter.MessageConversionException; +import org.springframework.messaging.converter.MessageConverter; +import org.springframework.messaging.converter.SmartMessageConverter; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.validation.SmartValidator; +import org.springframework.validation.Validator; +import org.springframework.validation.annotation.Validated; + +/** + * A resolver to extract and convert the payload of a message using a + * {@link MessageConverter}. It also validates the payload using a + * {@link Validator} if the argument is annotated with a Validation annotation. + * + *

This {@link HandlerMethodArgumentResolver} should be ordered last as it + * supports all types and does not require the {@link Payload} annotation. + * + * @author Rossen Stoyanchev + * @author Juergen Hoeller + * @author Brian Clozel + * @author Stephane Nicoll + * @since 5.2 + */ +public class PayloadMethodArgumentResolver implements HandlerMethodArgumentResolver { + + private final MessageConverter converter; + + @Nullable + private final Validator validator; + + private final boolean useDefaultResolution; + + + /** + * Create a new {@code PayloadArgumentResolver} with the given + * {@link MessageConverter}. + * @param messageConverter the MessageConverter to use (required) + */ + public PayloadMethodArgumentResolver(MessageConverter messageConverter) { + this(messageConverter, null); + } + + /** + * Create a new {@code PayloadArgumentResolver} with the given + * {@link MessageConverter} and {@link Validator}. + * @param messageConverter the MessageConverter to use (required) + * @param validator the Validator to use (optional) + */ + public PayloadMethodArgumentResolver(MessageConverter messageConverter, @Nullable Validator validator) { + this(messageConverter, validator, true); + } + + /** + * Create a new {@code PayloadArgumentResolver} with the given + * {@link MessageConverter} and {@link Validator}. + * @param messageConverter the MessageConverter to use (required) + * @param validator the Validator to use (optional) + * @param useDefaultResolution if "true" (the default) this resolver supports + * all parameters; if "false" then only arguments with the {@code @Payload} + * annotation are supported. + */ + public PayloadMethodArgumentResolver(MessageConverter messageConverter, @Nullable Validator validator, + boolean useDefaultResolution) { + + Assert.notNull(messageConverter, "MessageConverter must not be null"); + this.converter = messageConverter; + this.validator = validator; + this.useDefaultResolution = useDefaultResolution; + } + + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return (parameter.hasParameterAnnotation(Payload.class) || this.useDefaultResolution); + } + + @Override + @Nullable + public Object resolveArgument(MethodParameter parameter, Message message) throws Exception { + Payload ann = parameter.getParameterAnnotation(Payload.class); + if (ann != null && StringUtils.hasText(ann.expression())) { + throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver"); + } + + Object payload = message.getPayload(); + if (isEmptyPayload(payload)) { + if (ann == null || ann.required()) { + String paramName = getParameterName(parameter); + BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName); + bindingResult.addError(new ObjectError(paramName, "Payload value must not be empty")); + throw new MethodArgumentNotValidException(message, parameter, bindingResult); + } + else { + return null; + } + } + + Class targetClass = parameter.getParameterType(); + Class payloadClass = payload.getClass(); + if (ClassUtils.isAssignable(targetClass, payloadClass)) { + validate(message, parameter, payload); + return payload; + } + else { + if (this.converter instanceof SmartMessageConverter) { + SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter; + payload = smartConverter.fromMessage(message, targetClass, parameter); + } + else { + payload = this.converter.fromMessage(message, targetClass); + } + if (payload == null) { + throw new MessageConversionException(message, "Cannot convert from [" + + payloadClass.getName() + "] to [" + targetClass.getName() + "] for " + message); + } + validate(message, parameter, payload); + return payload; + } + } + + private String getParameterName(MethodParameter param) { + String paramName = param.getParameterName(); + return (paramName != null ? paramName : "Arg " + param.getParameterIndex()); + } + + /** + * Specify if the given {@code payload} is empty. + * @param payload the payload to check (can be {@code null}) + */ + protected boolean isEmptyPayload(@Nullable Object payload) { + if (payload == null) { + return true; + } + else if (payload instanceof byte[]) { + return ((byte[]) payload).length == 0; + } + else if (payload instanceof String) { + return !StringUtils.hasText((String) payload); + } + else { + return false; + } + } + + /** + * Validate the payload if applicable. + *

The default implementation checks for {@code @javax.validation.Valid}, + * Spring's {@link Validated}, + * and custom annotations whose name starts with "Valid". + * @param message the currently processed message + * @param parameter the method parameter + * @param target the target payload object + * @throws MethodArgumentNotValidException in case of binding errors + */ + protected void validate(Message message, MethodParameter parameter, Object target) { + if (this.validator == null) { + return; + } + for (Annotation ann : parameter.getParameterAnnotations()) { + Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); + if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { + Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); + Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); + BeanPropertyBindingResult bindingResult = + new BeanPropertyBindingResult(target, getParameterName(parameter)); + if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator) { + ((SmartValidator) this.validator).validate(target, bindingResult, validationHints); + } + else { + this.validator.validate(target, bindingResult); + } + if (bindingResult.hasErrors()) { + throw new MethodArgumentNotValidException(message, parameter, bindingResult); + } + break; + } + } + } + +} diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index c7a1c9c6c32..2b349c555f9 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -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"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ import org.springframework.messaging.handler.annotation.support.DestinationVaria import org.springframework.messaging.handler.annotation.support.HeaderMethodArgumentResolver; import org.springframework.messaging.handler.annotation.support.HeadersMethodArgumentResolver; import org.springframework.messaging.handler.annotation.support.MessageMethodArgumentResolver; -import org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver; +import org.springframework.messaging.handler.annotation.support.PayloadMethodArgumentResolver; import org.springframework.messaging.handler.invocation.AbstractExceptionHandlerMethodResolver; import org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler; import org.springframework.messaging.handler.invocation.CompletableFutureReturnValueHandler; @@ -238,7 +238,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan /** * Set the Validator instance used for validating {@code @Payload} arguments. * @see org.springframework.validation.annotation.Validated - * @see PayloadArgumentResolver + * @see PayloadMethodArgumentResolver */ public void setValidator(@Nullable Validator validator) { this.validator = validator; @@ -315,7 +315,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan resolvers.add(new MessageMethodArgumentResolver(this.messageConverter)); resolvers.addAll(getCustomArgumentResolvers()); - resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator)); + resolvers.add(new PayloadMethodArgumentResolver(this.messageConverter, this.validator)); return resolvers; } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolverTests.java similarity index 91% rename from spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java rename to spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolverTests.java index 518e021811b..c3ffcb257bd 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 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. @@ -43,15 +43,15 @@ import org.springframework.validation.annotation.Validated; import static org.junit.Assert.*; /** - * Test fixture for {@link PayloadArgumentResolver}. + * Test fixture for {@link PayloadMethodArgumentResolver}. * * @author Rossen Stoyanchev * @author Brian Clozel * @author Stephane Nicoll */ -public class PayloadArgumentResolverTests { +public class PayloadMethodArgumentResolverTests { - private PayloadArgumentResolver resolver; + private PayloadMethodArgumentResolver resolver; private MethodParameter paramAnnotated; @@ -74,10 +74,9 @@ public class PayloadArgumentResolverTests { @Before public void setup() throws Exception { + this.resolver = new PayloadMethodArgumentResolver(new StringMessageConverter(), testValidator()); - this.resolver = new PayloadArgumentResolver(new StringMessageConverter(), testValidator()); - - Method payloadMethod = PayloadArgumentResolverTests.class.getDeclaredMethod( + Method payloadMethod = PayloadMethodArgumentResolverTests.class.getDeclaredMethod( "handleMessage", String.class, String.class, Locale.class, String.class, String.class, String.class, String.class); @@ -92,12 +91,11 @@ public class PayloadArgumentResolverTests { } @Test - public void supportsParameter() throws Exception { - + public void supportsParameter() { assertTrue(this.resolver.supportsParameter(this.paramAnnotated)); assertTrue(this.resolver.supportsParameter(this.paramNotAnnotated)); - PayloadArgumentResolver strictResolver = new PayloadArgumentResolver( + PayloadMethodArgumentResolver strictResolver = new PayloadMethodArgumentResolver( new StringMessageConverter(), testValidator(), false); assertTrue(strictResolver.supportsParameter(this.paramAnnotated)); @@ -116,7 +114,7 @@ public class PayloadArgumentResolverTests { public void resolveRequiredEmpty() throws Exception { Message message = MessageBuilder.withPayload("").build(); - thrown.expect(MethodArgumentNotValidException.class); // Required but empty + thrown.expect(MethodArgumentNotValidException.class); // required but empty this.resolver.resolveArgument(paramAnnotated, message); } @@ -124,7 +122,7 @@ public class PayloadArgumentResolverTests { public void resolveRequiredEmptyNonAnnotatedParameter() throws Exception { Message message = MessageBuilder.withPayload("").build(); - thrown.expect(MethodArgumentNotValidException.class); // Required but empty + thrown.expect(MethodArgumentNotValidException.class); // required but empty this.resolver.resolveArgument(this.paramNotAnnotated, message); }