diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java
index 8e039045ea..0c040f503e 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java
@@ -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.
+ * 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.
*
{@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.
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java
new file mode 100644
index 0000000000..06d0f838a7
--- /dev/null
+++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java
@@ -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();
+ }
+
+}
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 ebba7db716..ba1fa9c8eb 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-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 @@
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.
*
* 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 {
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 {
}
}
+ 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;
+ }
+ }
+ }
+
}
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 1a3ed620a6..40fdb884a2 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-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 @@
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;
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
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
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
resolvers.add(new MessageMethodArgumentResolver());
resolvers.addAll(getCustomArgumentResolvers());
- resolvers.add(new PayloadArgumentResolver(this.messageConverter));
+ resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));
return resolvers;
}
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
index f76b0dd3c3..aa94adec83 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
@@ -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;
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;
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;
* 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 {
private MessageBrokerRegistry brokerRegistry;
+ private ApplicationContext applicationContext;
+
/**
* Protected constructor.
@@ -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 {
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:
+ *
+ * - delegating to getValidator() first
+ * - if none returned, getting an existing instance with its well-known name "mvcValidator", created by an MVC configuration
+ * - if none returned, checking the classpath for the presence of a JSR-303 implementation before creating a
+ * {@code LocalValidatorFactoryBean}
+ * - returning a no-op Validator instance
+ *
+ */
+ 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 {
};
+ private static final Validator noopValidator = new Validator() {
+ @Override
+ public boolean supports(Class> clazz) {
+ return false;
+ }
+ @Override
+ public void validate(Object target, Errors errors) {
+ }
+ };
+
+
}
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/PayloadArgumentResolverTests.java
index f318437da0..acf88bcf8c 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/PayloadArgumentResolverTests.java
@@ -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;
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.*;
* Test fixture for {@link PayloadArgumentResolver}.
*
* @author Rossen Stoyanchev
+ * @author Brian Clozel
*/
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 {
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) {
}
}
diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java
index 043b3d0b85..4fe4d88428 100644
--- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java
+++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java
@@ -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;
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;
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 {
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 {
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 {
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 {
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");
+ }
+ }
+ }
+
}
diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
index 3495c28ac5..7888cc43f1 100644
--- a/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
+++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
@@ -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;
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;
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.*;
* Test fixture for {@link AbstractMessageBrokerConfiguration}.
*
* @author Rossen Stoyanchev
+ * @author Brian Clozel
*/
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 {
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 {
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 {
}
}
+ @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 {
diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc
index 0f32d285c4..e2ba6a10f5 100644
--- a/src/asciidoc/index.adoc
+++ b/src/asciidoc/index.adoc
@@ -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:
* `@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.