Polishing
This commit is contained in:
parent
b35103cc5c
commit
b1d6ae77e1
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
@ -22,10 +22,11 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A {@link HandlerMethodArgumentResolver} for {@link Message} parameters. Validates
|
||||
* that the generic type of the payload matches with the message value.
|
||||
* A {@link HandlerMethodArgumentResolver} for {@link Message} parameters.
|
||||
* Validates that the generic type of the payload matches with the message value.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Stephane Nicoll
|
||||
|
@ -33,7 +34,6 @@ import org.springframework.messaging.handler.invocation.HandlerMethodArgumentRes
|
|||
*/
|
||||
public class MessageMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return Message.class.isAssignableFrom(parameter.getParameterType());
|
||||
|
@ -41,22 +41,19 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
|
|||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
|
||||
if (!paramType.isAssignableFrom(message.getClass())) {
|
||||
throw new MethodArgumentTypeMismatchException(message, parameter,
|
||||
"The actual message type [" + message.getClass().getName() + "] " +
|
||||
"does not match the expected type [" + paramType.getName() + "]");
|
||||
"The actual message type [" + ClassUtils.getQualifiedName(message.getClass()) + "] " +
|
||||
"does not match the expected type [" + ClassUtils.getQualifiedName(paramType) + "]");
|
||||
}
|
||||
|
||||
Class<?> expectedPayloadType = getPayloadType(parameter);
|
||||
Object payload = message.getPayload();
|
||||
|
||||
if (expectedPayloadType != null && !expectedPayloadType.isInstance(payload)) {
|
||||
if (payload != null && expectedPayloadType != null && !expectedPayloadType.isInstance(payload)) {
|
||||
throw new MethodArgumentTypeMismatchException(message, parameter,
|
||||
"The expected Message<?> payload type [" + expectedPayloadType.getName() +
|
||||
"] does not match the actual payload type [" + payload.getClass().getName() + "]");
|
||||
"The expected Message<?> payload type [" + ClassUtils.getQualifiedName(expectedPayloadType) +
|
||||
"] does not match the actual payload type [" + ClassUtils.getQualifiedName(payload.getClass()) + "]");
|
||||
}
|
||||
|
||||
return message;
|
||||
|
|
|
@ -40,16 +40,16 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link HandlerMethodReturnValueHandler} for sending to destinations specified in a
|
||||
* {@link SendTo} or {@link SendToUser} method-level annotations.
|
||||
*
|
||||
* <p>The value returned from the method is converted, and turned to a {@link Message} and
|
||||
* sent through the provided {@link MessageChannel}. The
|
||||
* message is then enriched with the sessionId of the input message as well as the
|
||||
* destination from the annotation(s). If multiple destinations are specified, a copy of
|
||||
* the message is sent to each destination.
|
||||
* sent through the provided {@link MessageChannel}. The message is then enriched with the
|
||||
* session id of the input message as well as the destination from the annotation(s).
|
||||
* If multiple destinations are specified, a copy of the message is sent to each destination.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
|
@ -116,7 +116,6 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
|||
/**
|
||||
* Configure a {@link MessageHeaderInitializer} to apply to the headers of all
|
||||
* messages sent to the client outbound channel.
|
||||
*
|
||||
* <p>By default this property is not set.
|
||||
*/
|
||||
public void setHeaderInitializer(MessageHeaderInitializer headerInitializer) {
|
||||
|
@ -133,8 +132,8 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
|||
|
||||
@Override
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
if ((returnType.getMethodAnnotation(SendTo.class) != null) ||
|
||||
(returnType.getMethodAnnotation(SendToUser.class) != null)) {
|
||||
if (returnType.getMethodAnnotation(SendTo.class) != null ||
|
||||
returnType.getMethodAnnotation(SendToUser.class) != null) {
|
||||
return true;
|
||||
}
|
||||
return (!this.annotationRequired);
|
||||
|
@ -164,10 +163,12 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
|||
for (String destination : destinations) {
|
||||
destination = this.placeholderHelper.replacePlaceholders(destination, varResolver);
|
||||
if (broadcast) {
|
||||
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(null, returnType));
|
||||
this.messagingTemplate.convertAndSendToUser(
|
||||
user, destination, returnValue, createHeaders(null, returnType));
|
||||
}
|
||||
else {
|
||||
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId, returnType));
|
||||
this.messagingTemplate.convertAndSendToUser(
|
||||
user, destination, returnValue, createHeaders(sessionId, returnType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +207,9 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
|||
}
|
||||
String name = DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER;
|
||||
String destination = (String) message.getHeaders().get(name);
|
||||
Assert.hasText(destination, "No lookup destination header in " + message);
|
||||
if (!StringUtils.hasText(destination)) {
|
||||
throw new IllegalStateException("No lookup destination header in " + message);
|
||||
}
|
||||
|
||||
return (destination.startsWith("/") ?
|
||||
new String[] {defaultPrefix + destination} : new String[] {defaultPrefix + "/" + destination});
|
||||
|
@ -231,11 +234,11 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
|||
return "SendToMethodReturnValueHandler [annotationRequired=" + annotationRequired + "]";
|
||||
}
|
||||
|
||||
|
||||
private static class DestinationVariablePlaceholderResolver implements PlaceholderResolver {
|
||||
|
||||
private final Map<String, String> vars;
|
||||
|
||||
|
||||
public DestinationVariablePlaceholderResolver(Map<String, String> vars) {
|
||||
this.vars = vars;
|
||||
}
|
||||
|
|
|
@ -104,14 +104,16 @@ public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturn
|
|||
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);
|
||||
String subscriptionId = SimpMessageHeaderAccessor.getSubscriptionId(headers);
|
||||
|
||||
Assert.state(subscriptionId != null,
|
||||
"No subscriptionId in message=" + message + ", method=" + returnType.getMethod());
|
||||
if (subscriptionId == null) {
|
||||
throw new IllegalStateException(
|
||||
"No subscriptionId in " + message + " returned by: " + returnType.getMethod());
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reply to @SubscribeMapping: " + returnValue);
|
||||
}
|
||||
|
||||
this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId, subscriptionId, returnType));
|
||||
this.messagingTemplate.convertAndSend(
|
||||
destination, returnValue, createHeaders(sessionId, subscriptionId, returnType));
|
||||
}
|
||||
|
||||
private MessageHeaders createHeaders(String sessionId, String subscriptionId, MethodParameter returnType) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
@ -145,6 +145,7 @@ public class MessageMethodArgumentResolverTests {
|
|||
assertSame(message, this.resolver.resolveArgument(parameter, message));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleMessage(
|
||||
Message<?> wildcardPayload,
|
||||
|
|
|
@ -83,13 +83,12 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedConfigClasses() {
|
||||
return new Class<?>[] { TestMessageBrokerConfiguration.class, TestMessageBrokerConfigurer.class };
|
||||
return new Class<?>[] {TestMessageBrokerConfiguration.class, TestMessageBrokerConfigurer.class};
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void sendMessageToController() throws Exception {
|
||||
|
||||
TextMessage message = create(StompCommand.SEND).headers("destination:/app/simple").build();
|
||||
WebSocketSession session = doHandshake(new TestClientWebSocketHandler(0, message), "/ws").get();
|
||||
|
||||
|
@ -104,10 +103,8 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Test
|
||||
public void sendMessageToControllerAndReceiveReplyViaTopic() throws Exception {
|
||||
|
||||
TextMessage message1 = create(StompCommand.SUBSCRIBE)
|
||||
.headers("id:subs1", "destination:/topic/increment").build();
|
||||
|
||||
TextMessage message2 = create(StompCommand.SEND)
|
||||
.headers("destination:/app/increment").body("5").build();
|
||||
|
||||
|
@ -126,7 +123,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Test
|
||||
public void sendMessageToBrokerAndReceiveReplyViaTopic() throws Exception {
|
||||
|
||||
TextMessage m1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", "destination:/topic/foo").build();
|
||||
TextMessage m2 = create(StompCommand.SEND).headers("destination:/topic/foo").body("5").build();
|
||||
|
||||
|
@ -148,7 +144,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Test
|
||||
public void sendSubscribeToControllerAndReceiveReply() throws Exception {
|
||||
|
||||
String destHeader = "destination:/app/number";
|
||||
TextMessage message = create(StompCommand.SUBSCRIBE).headers("id:subs1", destHeader).build();
|
||||
|
||||
|
@ -168,7 +163,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Test
|
||||
public void handleExceptionAndSendToUser() throws Exception {
|
||||
|
||||
String destHeader = "destination:/user/queue/error";
|
||||
TextMessage m1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", destHeader).build();
|
||||
TextMessage m2 = create(StompCommand.SEND).headers("destination:/app/exception").build();
|
||||
|
@ -178,7 +172,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
try {
|
||||
assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS));
|
||||
|
||||
String payload = clientHandler.actual.get(0).getPayload();
|
||||
assertTrue(payload.startsWith("MESSAGE\n"));
|
||||
assertTrue(payload.contains("destination:/user/queue/error\n"));
|
||||
|
@ -191,10 +184,8 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
@Test
|
||||
public void webSocketScope() throws Exception {
|
||||
|
||||
TextMessage message1 = create(StompCommand.SUBSCRIBE)
|
||||
.headers("id:subs1", "destination:/topic/scopedBeanValue").build();
|
||||
|
||||
TextMessage message2 = create(StompCommand.SEND)
|
||||
.headers("destination:/app/scopedBeanValue").build();
|
||||
|
||||
|
@ -203,7 +194,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
try {
|
||||
assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS));
|
||||
|
||||
String payload = clientHandler.actual.get(0).getPayload();
|
||||
assertTrue(payload.startsWith("MESSAGE\n"));
|
||||
assertTrue(payload.contains("destination:/topic/scopedBeanValue\n"));
|
||||
|
@ -221,6 +211,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
private @interface IntegrationTestController {
|
||||
}
|
||||
|
||||
|
||||
@IntegrationTestController
|
||||
static class SimpleController {
|
||||
|
||||
|
@ -243,6 +234,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@IntegrationTestController
|
||||
static class IncrementController {
|
||||
|
||||
|
@ -257,6 +249,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@IntegrationTestController
|
||||
static class ScopedBeanController {
|
||||
|
||||
|
@ -279,6 +272,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
String getValue();
|
||||
}
|
||||
|
||||
|
||||
static class ScopedBeanImpl implements ScopedBean {
|
||||
|
||||
private final String value;
|
||||
|
@ -304,7 +298,6 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
|
||||
private final CountDownLatch latch;
|
||||
|
||||
|
||||
public TestClientWebSocketHandler(int expectedNumberOfMessages, TextMessage... messagesToSend) {
|
||||
this.messagesToSend = messagesToSend;
|
||||
this.expected = expectedNumberOfMessages;
|
||||
|
@ -325,6 +318,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackageClasses=StompWebSocketIntegrationTests.class,
|
||||
|
@ -333,7 +327,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
static class TestMessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
private HandshakeHandler handshakeHandler; // can't rely on classpath for server detection
|
||||
private HandshakeHandler handshakeHandler; // can't rely on classpath for server detection
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
|
@ -353,19 +347,20 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class TestMessageBrokerConfiguration extends DelegatingWebSocketMessageBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientInboundChannel() {
|
||||
return new ExecutorSubscribableChannel(); // synchronous
|
||||
return new ExecutorSubscribableChannel(); // synchronous
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientOutboundChannel() {
|
||||
return new ExecutorSubscribableChannel(); // synchronous
|
||||
return new ExecutorSubscribableChannel(); // synchronous
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue