Browse Source

Support Validation in @MessageMapping annotated methods

Payload parameters in @MessageMapping annotated
methods can now also be validated when annotated
with a Validation annotation (@Valid, @Validated...).

A default Validator is registered by the MessageBroker
Configurer, but it is possible to provide a list of custom
validators as well.

Issue: SPR-11185
pull/432/head
Brian Clozel 11 years ago
parent
commit
2c8f670d5f
  1. 5
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java
  2. 56
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java
  3. 48
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java
  4. 31
      spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java
  5. 82
      spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
  6. 48
      spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java
  7. 47
      spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java
  8. 43
      spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
  9. 4
      src/asciidoc/index.adoc

5
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java

@ -41,7 +41,10 @@ import org.springframework.messaging.Message; @@ -41,7 +41,10 @@ import org.springframework.messaging.Message;
* a message and optionally convert it using a
* {@link org.springframework.messaging.converter.MessageConverter}.
* The presence of the annotation is not required since it is assumed by default
* for method arguments that are not annotated.</li>
* for method arguments that are not annotated. Payload method arguments annotated
* with Validation annotations (like
* {@link org.springframework.validation.annotation.Validated}) will be subject to
* JSR-303 validation.</li>
* <li>{@link Header}-annotated method arguments to extract a specific
* header value along with type conversion with a
* {@link org.springframework.core.convert.converter.Converter} if necessary.</li>

56
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
/*
* Copyright 2002-2014 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
*
* http://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 org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
/**
* Exception to be thrown when validation on an method parameter annotated with {@code @Valid} fails.
* @author Brian Clozel
* @since 4.0.1
*/
@SuppressWarnings("serial")
public class MethodArgumentNotValidException extends MessagingException {
private final MethodParameter parameter;
private final BindingResult bindingResult;
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter, BindingResult bindingResult) {
super(message);
this.parameter = parameter;
this.bindingResult = bindingResult;
}
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder("Validation failed for parameter at index ")
.append(this.parameter.getParameterIndex()).append(" in method: ")
.append(this.parameter.getMethod().toGenericString())
.append(", with ").append(this.bindingResult.getErrorCount()).append(" error(s): ");
for (ObjectError error : this.bindingResult.getAllErrors()) {
sb.append("[").append(error).append("] ");
}
return sb.toString();
}
}

48
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,32 +17,45 @@ @@ -17,32 +17,45 @@
package org.springframework.messaging.handler.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.converter.MessageConverter;
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.SmartValidator;
import org.springframework.validation.Validator;
import java.lang.annotation.Annotation;
/**
* A resolver to extract and convert the payload of a message using a
* {@link MessageConverter}.
* {@link MessageConverter}. It also validates the payload using a
* {@link Validator} if the argument is annotated with a Validation annotation.
*
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it supports all
* types and does not require the {@link Payload} annotation.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 4.0
*/
public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
private final MessageConverter converter;
private final Validator validator;
public PayloadArgumentResolver(MessageConverter messageConverter) {
public PayloadArgumentResolver(MessageConverter messageConverter, Validator validator) {
Assert.notNull(messageConverter, "converter must not be null");
Assert.notNull(validator, "validator must not be null");
this.converter = messageConverter;
this.validator = validator;
}
@Override
@ -72,7 +85,10 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver { @@ -72,7 +85,10 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver.");
}
return this.converter.fromMessage(message, targetClass);
Object target = this.converter.fromMessage(message, targetClass);
validate(message, parameter, target);
return target;
}
protected boolean isEmptyPayload(Message<?> message) {
@ -88,4 +104,28 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver { @@ -88,4 +104,28 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
}
protected void validate(Message<?> message, MethodParameter parameter, Object target) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation annot : annotations) {
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, parameter.getParameterName());
Object hints = AnnotationUtils.getValue(annot);
Object[] validationHints = hints instanceof Object[] ? (Object[]) hints : new Object[] {hints};
if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator) {
((SmartValidator) this.validator).validate(target, bindingResult, validationHints);
}
else if (this.validator != null) {
this.validator.validate(target, bindingResult);
}
if (bindingResult.hasErrors()) {
throw new MethodArgumentNotValidException(message, parameter, bindingResult);
}
break;
}
}
}
}

31
spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,7 @@ @@ -17,13 +17,7 @@
package org.springframework.messaging.simp.annotation.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
@ -60,6 +54,7 @@ import org.springframework.util.AntPathMatcher; @@ -60,6 +54,7 @@ import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.PathMatcher;
import org.springframework.validation.Validator;
/**
* A handler for messages delegating to
@ -87,6 +82,8 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -87,6 +82,8 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
private PathMatcher pathMatcher = new AntPathMatcher();
private Validator validator;
private final Object lifecycleMonitor = new Object();
private volatile boolean running = false;
@ -171,6 +168,22 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -171,6 +168,22 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
return this.pathMatcher;
}
/**
* The configured Validator instance
*/
public Validator getValidator() {
return validator;
}
/**
* Set the Validator instance used for validating @Payload arguments
* @see org.springframework.validation.annotation.Validated
* @see PayloadArgumentResolver
*/
public void setValidator(Validator validator) {
this.validator = validator;
}
@Override
public boolean isAutoStartup() {
return true;
@ -230,7 +243,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @@ -230,7 +243,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
resolvers.add(new MessageMethodArgumentResolver());
resolvers.addAll(getCustomArgumentResolvers());
resolvers.add(new PayloadArgumentResolver(this.messageConverter));
resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));
return resolvers;
}

82
spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,11 @@ package org.springframework.messaging.simp.config; @@ -19,6 +19,11 @@ package org.springframework.messaging.simp.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
@ -41,6 +46,8 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel; @@ -41,6 +46,8 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* Provides essential configuration for handling messages with simple messaging
@ -62,13 +69,15 @@ import org.springframework.util.MimeTypeUtils; @@ -62,13 +69,15 @@ import org.springframework.util.MimeTypeUtils;
* to and from the client inbound/outbound channels (e.g. STOMP over WebSocket).
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 4.0
*/
public abstract class AbstractMessageBrokerConfiguration {
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
private static final boolean jackson2Present= ClassUtils.isPresent(
"com.fasterxml.jackson.databind.ObjectMapper", AbstractMessageBrokerConfiguration.class.getClassLoader());
private static final String MVC_VALIDATOR_NAME = "mvcValidator";
private ChannelRegistration clientInboundChannelRegistration;
@ -76,6 +85,8 @@ public abstract class AbstractMessageBrokerConfiguration { @@ -76,6 +85,8 @@ public abstract class AbstractMessageBrokerConfiguration {
private MessageBrokerRegistry brokerRegistry;
private ApplicationContext applicationContext;
/**
* Protected constructor.
@ -204,6 +215,7 @@ public abstract class AbstractMessageBrokerConfiguration { @@ -204,6 +215,7 @@ public abstract class AbstractMessageBrokerConfiguration {
handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
handler.setMessageConverter(brokerMessageConverter());
handler.setValidator(simpValidator());
return handler;
}
@ -268,6 +280,61 @@ public abstract class AbstractMessageBrokerConfiguration { @@ -268,6 +280,61 @@ public abstract class AbstractMessageBrokerConfiguration {
return new DefaultUserSessionRegistry();
}
/**
* Override this method to provide a custom {@link Validator}.
* @since 4.0.1
*/
public Validator getValidator() {
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* Return a {@link org.springframework.validation.Validator}s instance for validating
* {@code @Payload} method arguments.
* In order, this method tries to get a Validator instance:
* <ul>
* <li>delegating to getValidator() first</li>
* <li>if none returned, getting an existing instance with its well-known name "mvcValidator", created by an MVC configuration</li>
* <li>if none returned, checking the classpath for the presence of a JSR-303 implementation before creating a
* {@code LocalValidatorFactoryBean}</li>
* <li>returning a no-op Validator instance</li>
* </ul>
*/
protected Validator simpValidator() {
Validator validator = getValidator();
if (validator == null) {
if(this.applicationContext.containsBean(MVC_VALIDATOR_NAME)) {
validator = this.applicationContext.getBean(MVC_VALIDATOR_NAME, Validator.class);
}
else if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
Class<?> clazz;
try {
String className = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean";
clazz = ClassUtils.forName(className, AbstractMessageBrokerConfiguration.class.getClassLoader());
}
catch (ClassNotFoundException e) {
throw new BeanInitializationException("Could not find default validator", e);
}
catch (LinkageError e) {
throw new BeanInitializationException("Could not find default validator", e);
}
validator = (Validator) BeanUtils.instantiate(clazz);
}
else {
validator = noopValidator;
}
}
return validator;
}
private static final AbstractBrokerMessageHandler noopBroker = new AbstractBrokerMessageHandler(null) {
@ -285,4 +352,15 @@ public abstract class AbstractMessageBrokerConfiguration { @@ -285,4 +352,15 @@ public abstract class AbstractMessageBrokerConfiguration {
};
private static final Validator noopValidator = new Validator() {
@Override
public boolean supports(Class<?> clazz) {
return false;
}
@Override
public void validate(Object target, Errors errors) {
}
};
}

48
spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,12 +20,16 @@ import java.lang.reflect.Method; @@ -20,12 +20,16 @@ import java.lang.reflect.Method;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import static org.junit.Assert.*;
@ -33,6 +37,7 @@ import static org.junit.Assert.*; @@ -33,6 +37,7 @@ import static org.junit.Assert.*;
* Test fixture for {@link PayloadArgumentResolver}.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
public class PayloadArgumentResolverTests {
@ -41,20 +46,22 @@ public class PayloadArgumentResolverTests { @@ -41,20 +46,22 @@ public class PayloadArgumentResolverTests {
private MethodParameter param;
private MethodParameter paramNotRequired;
private MethodParameter paramWithSpelExpression;
private MethodParameter paramValidated;
@Before
public void setup() throws Exception {
MessageConverter messageConverter = new StringMessageConverter();
this.resolver = new PayloadArgumentResolver(messageConverter );
this.resolver = new PayloadArgumentResolver(new StringMessageConverter(), testValidator());
Method method = PayloadArgumentResolverTests.class.getDeclaredMethod("handleMessage",
String.class, String.class, String.class);
String.class, String.class, String.class, String.class);
this.param = new MethodParameter(method , 0);
this.paramNotRequired = new MethodParameter(method , 1);
this.paramWithSpelExpression = new MethodParameter(method , 2);
this.paramValidated = new MethodParameter(method , 3);
this.paramValidated.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
}
@ -82,12 +89,41 @@ public class PayloadArgumentResolverTests { @@ -82,12 +89,41 @@ public class PayloadArgumentResolverTests {
this.resolver.resolveArgument(this.paramWithSpelExpression, message);
}
@Test
public void resolveValidation() throws Exception {
Message<?> message = MessageBuilder.withPayload("ABC".getBytes()).build();
this.resolver.resolveArgument(this.paramValidated, message);
}
@Test(expected=MethodArgumentNotValidException.class)
public void resolveFailValidation() throws Exception {
Message<?> message = MessageBuilder.withPayload("".getBytes()).build();
this.resolver.resolveArgument(this.paramValidated, message);
}
private Validator testValidator() {
return new Validator() {
@Override
public boolean supports(Class<?> clazz) {
return String.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
String value = (String) target;
if (StringUtils.isEmpty(value.toString())) {
errors.reject("empty value");
}
}
};
}
@SuppressWarnings("unused")
private void handleMessage(
@Payload String param,
@Payload(required=false) String paramNotRequired,
@Payload("foo.bar") String paramWithSpelExpression) {
@Payload("foo.bar") String paramWithSpelExpression,
@Validated @Payload String validParam) {
}
}

47
spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,10 +26,8 @@ import org.springframework.context.support.StaticApplicationContext; @@ -26,10 +26,8 @@ import org.springframework.context.support.StaticApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.*;
import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessageType;
@ -37,6 +35,10 @@ import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -37,6 +35,10 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import static org.junit.Assert.*;
@ -58,6 +60,7 @@ public class SimpAnnotationMethodMessageHandlerTests { @@ -58,6 +60,7 @@ public class SimpAnnotationMethodMessageHandlerTests {
SimpMessageSendingOperations brokerTemplate = new SimpMessagingTemplate(channel);
this.messageHandler = new TestSimpAnnotationMethodMessageHandler(brokerTemplate, channel, channel);
this.messageHandler.setApplicationContext(new StaticApplicationContext());
this.messageHandler.setValidator(new StringNotEmptyValidator());
this.messageHandler.afterPropertiesSet();
testController = new TestController();
@ -142,6 +145,15 @@ public class SimpAnnotationMethodMessageHandlerTests { @@ -142,6 +145,15 @@ public class SimpAnnotationMethodMessageHandlerTests {
assertEquals(12L, this.testController.arguments.get("id"));
}
@Test
public void validationError() {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
headers.setDestination("/pre/validation/payload");
Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
this.messageHandler.handleMessage(message);
assertEquals("handleValidationException", this.testController.method);
}
private static class TestSimpAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {
@ -210,6 +222,17 @@ public class SimpAnnotationMethodMessageHandlerTests { @@ -210,6 +222,17 @@ public class SimpAnnotationMethodMessageHandlerTests {
this.method = "simpleBinding";
this.arguments.put("id", id);
}
@MessageMapping("/validation/payload")
public void payloadValidation(@Validated @Payload String payload) {
this.method = "payloadValidation";
this.arguments.put("message", payload);
}
@MessageExceptionHandler(MethodArgumentNotValidException.class)
public void handleValidationException() {
this.method = "handleValidationException";
}
}
@Controller
@ -222,4 +245,18 @@ public class SimpAnnotationMethodMessageHandlerTests { @@ -222,4 +245,18 @@ public class SimpAnnotationMethodMessageHandlerTests {
public void handle2() { }
}
private static class StringNotEmptyValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return String.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
String value = (String) target;
if (StringUtils.isEmpty(value.toString())) {
errors.reject("empty value");
}
}
}
}

43
spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ package org.springframework.messaging.simp.config; @@ -19,6 +19,7 @@ package org.springframework.messaging.simp.config;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
@ -48,6 +49,8 @@ import org.springframework.messaging.support.MessageBuilder; @@ -48,6 +49,8 @@ import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import static org.junit.Assert.*;
@ -55,6 +58,7 @@ import static org.junit.Assert.*; @@ -55,6 +58,7 @@ import static org.junit.Assert.*;
* Test fixture for {@link AbstractMessageBrokerConfiguration}.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
public class MessageBrokerConfigurationTests {
@ -64,6 +68,7 @@ public class MessageBrokerConfigurationTests { @@ -64,6 +68,7 @@ public class MessageBrokerConfigurationTests {
private AnnotationConfigApplicationContext cxtCustomizedChannelConfig;
private AnnotationConfigApplicationContext cxtCustomizedValidator;
@Before
public void setupOnce() {
@ -79,6 +84,10 @@ public class MessageBrokerConfigurationTests { @@ -79,6 +84,10 @@ public class MessageBrokerConfigurationTests {
this.cxtCustomizedChannelConfig = new AnnotationConfigApplicationContext();
this.cxtCustomizedChannelConfig.register(CustomizedChannelConfig.class);
this.cxtCustomizedChannelConfig.refresh();
this.cxtCustomizedValidator = new AnnotationConfigApplicationContext();
this.cxtCustomizedValidator.register(ValidationConfig.class);
this.cxtCustomizedValidator.refresh();
}
@ -271,6 +280,20 @@ public class MessageBrokerConfigurationTests { @@ -271,6 +280,20 @@ public class MessageBrokerConfigurationTests {
assertEquals(MimeTypeUtils.APPLICATION_JSON, resolver.getDefaultMimeType());
}
@Test
public void defaultValidator() {
SimpAnnotationMethodMessageHandler messageHandler =
this.cxtSimpleBroker.getBean(SimpAnnotationMethodMessageHandler.class);
assertThat(messageHandler.getValidator(),Matchers.notNullValue(Validator.class));
}
@Test
public void customValidator() {
SimpAnnotationMethodMessageHandler messageHandler =
this.cxtCustomizedValidator.getBean(SimpAnnotationMethodMessageHandler.class);
assertThat(messageHandler.getValidator(),Matchers.notNullValue(Validator.class));
assertThat(messageHandler.getValidator(),Matchers.instanceOf(Validator.class));
}
@Controller
static class TestController {
@ -363,6 +386,24 @@ public class MessageBrokerConfigurationTests { @@ -363,6 +386,24 @@ public class MessageBrokerConfigurationTests {
}
}
@Configuration
static class ValidationConfig extends TestMessageBrokerConfiguration {
@Override
public Validator getValidator() {
return new TestValidator();
}
}
private static class TestValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return false;
}
@Override
public void validate(Object target, Errors errors) {}
}
private static class TestChannel extends ExecutorSubscribableChannel {

4
src/asciidoc/index.adoc

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
= Spring Framework Reference Documentation
Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch
Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel
:javadoc-baseurl: http://docs.spring.io/spring/docs/current/javadoc-api
@ -37402,6 +37402,8 @@ The following method arguments are supported for `@MessageMapping` methods: @@ -37402,6 +37402,8 @@ The following method arguments are supported for `@MessageMapping` methods:
* `@Payload`-annotated argument for access to the payload of a message, converted with
a `org.springframework.messaging.converter.MessageConverter`.
The presence of the annotation is not required since it is assumed by default.
Payload method arguments annotated with Validation annotations (like `@Validated`) will
be subject to JSR-303 validation.
* `@Header`-annotated arguments for access to a specific header value along with
type conversion using an `org.springframework.core.convert.converter.Converter`
if necessary.

Loading…
Cancel
Save