Support mutable headers in MessagingTemplate

AbstractMessageConverter and messaging template implementations now
detect and use mutable headers if passed in.

The SimpMessagingTemplate is optimized to supporting using a single
MessageHeaders instance while preparing a message.

This commit also updates code using the SimpMessagingTemplate to take
advantage of its new capabilities.

Issue: SPR-11468
This commit is contained in:
Rossen Stoyanchev
2014-04-12 00:19:14 -04:00
parent ae942ffdb8
commit fda9c633c4
17 changed files with 636 additions and 215 deletions

View File

@@ -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,6 +26,8 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -34,7 +36,8 @@ import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* Test fixture for {@link org.springframework.messaging.converter.AbstractMessageConverter}.
* Unit tests for
* {@link org.springframework.messaging.converter.AbstractMessageConverter}.
*
* @author Rossen Stoyanchev
*/
@@ -109,15 +112,32 @@ public class MessageConverterTests {
}
@Test
public void toMessageHeadersCopied() {
public void toMessageWithHeaders() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("foo", "bar");
MessageHeaders headers = new MessageHeaders(map);
Message<?> message = this.converter.toMessage("ABC", headers);
assertEquals("bar", message.getHeaders().get("foo"));
assertNotNull(message.getHeaders().getId());
assertNotNull(message.getHeaders().getTimestamp());
assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
assertEquals("bar", message.getHeaders().get("foo"));
}
@Test
public void toMessageWithMutableMessageHeaders() {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
accessor.setHeader("foo", "bar");
accessor.setNativeHeader("fooNative", "barNative");
accessor.setLeaveMutable(true);
MessageHeaders headers = accessor.getMessageHeaders();
Message<?> message = this.converter.toMessage("ABC", headers);
assertSame(headers, message.getHeaders());
assertNull(message.getHeaders().getId());
assertNull(message.getHeaders().getTimestamp());
assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
@Test

View File

@@ -0,0 +1,74 @@
/*
* 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.converter;
import org.junit.Before;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageHeaderAccessor;
import java.util.Collections;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
* Unit tests for
* {@link org.springframework.messaging.converter.SimpleMessageConverter}.
*
* @author Rossen Stoyanchev
*/
public class SimpleMessageConverterTests {
private SimpleMessageConverter converter;
@Before
public void setup() {
this.converter = new SimpleMessageConverter();
}
@Test
public void toMessageWithNullPayload() {
assertNull(this.converter.toMessage(null, null));
}
@Test
public void toMessageWithPayloadAndHeaders() {
MessageHeaders headers = new MessageHeaders(Collections.<String, Object>singletonMap("foo", "bar"));
Message<?> message = this.converter.toMessage("payload", headers);
assertEquals("payload", message.getPayload());
assertEquals("bar", message.getHeaders().get("foo"));
}
@Test
public void toMessageWithPayloadAndMutableHeaders() {
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setHeader("foo", "bar");
accessor.setLeaveMutable(true);
MessageHeaders headers = accessor.getMessageHeaders();
Message<?> message = this.converter.toMessage("payload", headers);
assertEquals("payload", message.getPayload());
assertSame(headers, message.getHeaders());
assertEquals("bar", message.getHeaders().get("foo"));
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.messaging.core;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -27,10 +28,13 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.StubMessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import static org.junit.Assert.*;
@@ -44,12 +48,17 @@ public class GenericMessagingTemplateTests {
private GenericMessagingTemplate template;
private StubMessageChannel messageChannel;
private ThreadPoolTaskExecutor executor;
@Before
public void setup() {
this.messageChannel = new StubMessageChannel();
this.template = new GenericMessagingTemplate();
this.template.setDefaultDestination(this.messageChannel);
this.template.setDestinationResolver(new TestDestinationResolver());
this.executor = new ThreadPoolTaskExecutor();
this.executor.afterPropertiesSet();
}
@@ -114,4 +123,26 @@ public class GenericMessagingTemplateTests {
}
}
@Test
public void convertAndSendWithSimpMessageHeaders() {
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setHeader("key", "value");
accessor.setLeaveMutable(true);
MessageHeaders headers = accessor.getMessageHeaders();
this.template.convertAndSend("channel", "data", headers);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> message = messages.get(0);
assertSame(headers, message.getHeaders());
assertFalse(accessor.isMutable());
}
private class TestDestinationResolver implements DestinationResolver<MessageChannel> {
@Override
public MessageChannel resolveDestination(String name) throws DestinationResolutionException {
return messageChannel;
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.messaging.core;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -27,6 +28,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.*;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import static org.junit.Assert.*;
@@ -122,6 +125,22 @@ public class MessageSendingTemplateTests {
assertEquals("payload", this.template.message.getPayload());
}
@Test
public void convertAndSendPayloadAndMutableHeadersToDestination() {
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setHeader("foo", "bar");
accessor.setLeaveMutable(true);
MessageHeaders messageHeaders = accessor.getMessageHeaders();
this.template.setMessageConverter(new StringMessageConverter());
this.template.convertAndSend("somewhere", "payload", messageHeaders);
MessageHeaders actual = this.template.message.getHeaders();
assertSame(messageHeaders, actual);
assertEquals(new MimeType("text", "plain", Charset.forName("UTF-8")), actual.get(MessageHeaders.CONTENT_TYPE));
assertEquals("bar", actual.get("foo"));
}
@Test
public void convertAndSendPayloadWithPostProcessor() {
this.template.setDefaultDestination("home");

View File

@@ -16,17 +16,27 @@
package org.springframework.messaging.simp;
import org.apache.activemq.transport.stomp.Stomp;
import org.junit.Before;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.StubMessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
import org.springframework.util.LinkedMultiValueMap;
import java.util.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link org.springframework.messaging.simp.SimpMessagingTemplate}.
@@ -43,7 +53,7 @@ public class SimpMessagingTemplateTests {
@Before
public void setup() {
this.messageChannel = new StubMessageChannel();
this.messagingTemplate = new SimpMessagingTemplate(messageChannel);
this.messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
}
@@ -55,10 +65,12 @@ public class SimpMessagingTemplateTests {
assertEquals(1, messages.size());
Message<byte[]> message = messages.get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.class);
assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
assertEquals("/user/joe/queue/foo", headers.getDestination());
assertNotNull(headerAccessor);
assertEquals(SimpMessageType.MESSAGE, headerAccessor.getMessageType());
assertEquals("/user/joe/queue/foo", headerAccessor.getDestination());
}
@Test
@@ -68,9 +80,11 @@ public class SimpMessagingTemplateTests {
assertEquals(1, messages.size());
Message<byte[]> message = messages.get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals("/user/http:%2F%2Fjoe.openid.example.org%2F/queue/foo", headers.getDestination());
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(messages.get(0), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertEquals("/user/http:%2F%2Fjoe.openid.example.org%2F/queue/foo", headerAccessor.getDestination());
}
@Test
@@ -79,11 +93,13 @@ public class SimpMessagingTemplateTests {
this.messagingTemplate.convertAndSend("/foo", "data", headers);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> message = messages.get(0);
SimpMessageHeaderAccessor resultHeaders = SimpMessageHeaderAccessor.wrap(message);
assertNull(resultHeaders.toMap().get("key"));
assertEquals(Arrays.asList("value"), resultHeaders.getNativeHeader("key"));
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(messages.get(0), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertNull(headerAccessor.toMap().get("key"));
assertEquals(Arrays.asList("value"), headerAccessor.getNativeHeader("key"));
}
@Test
@@ -94,11 +110,78 @@ public class SimpMessagingTemplateTests {
this.messagingTemplate.convertAndSend("/foo", "data", headers);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> message = messages.get(0);
SimpMessageHeaderAccessor resultHeaders = SimpMessageHeaderAccessor.wrap(message);
assertEquals("value", resultHeaders.toMap().get("key"));
assertNull(resultHeaders.getNativeHeader("key"));
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(messages.get(0), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertEquals("value", headerAccessor.toMap().get("key"));
assertNull(headerAccessor.getNativeHeader("key"));
}
}
@Test
public void convertAndSendWithMutableSimpMessageHeaders() {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
accessor.setHeader("key", "value");
accessor.setNativeHeader("fooNative", "barNative");
accessor.setLeaveMutable(true);
MessageHeaders headers = accessor.getMessageHeaders();
this.messagingTemplate.convertAndSend("/foo", "data", headers);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> message = messages.get(0);
assertSame(headers, message.getHeaders());
assertFalse(accessor.isMutable());
}
@Test
public void processHeadersToSend() {
Map<String, Object> map = this.messagingTemplate.processHeadersToSend(null);
assertNotNull(map);
assertTrue("Actual: " + map.getClass().toString(), MessageHeaders.class.isAssignableFrom(map.getClass()));
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor((MessageHeaders) map, SimpMessageHeaderAccessor.class);
assertTrue(headerAccessor.isMutable());
assertEquals(SimpMessageType.MESSAGE, headerAccessor.getMessageType());
}
@Test
public void doSendWithMutableHeaders() {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
accessor.setHeader("key", "value");
accessor.setNativeHeader("fooNative", "barNative");
accessor.setLeaveMutable(true);
MessageHeaders headers = accessor.getMessageHeaders();
Message<?> message = MessageBuilder.createMessage("payload", headers);
this.messagingTemplate.doSend("/topic/foo", message);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> sentMessage = messages.get(0);
assertSame(message, sentMessage);
assertFalse(accessor.isMutable());
}
@Test
public void doSendWithStompHeaders() {
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
accessor.setDestination("/user/queue/foo");
Message<?> message = MessageBuilder.createMessage(new byte[0], accessor.getMessageHeaders());
this.messagingTemplate.doSend("/queue/foo-user123", message);
List<Message<byte[]>> messages = this.messageChannel.getMessages();
Message<byte[]> sentMessage = messages.get(0);
MessageHeaderAccessor sentAccessor = MessageHeaderAccessor.getAccessor(sentMessage, MessageHeaderAccessor.class);
assertEquals(StompHeaderAccessor.class, sentAccessor.getClass());
assertEquals("/queue/foo-user123", ((StompHeaderAccessor) sentAccessor).getDestination());
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.messaging.simp.annotation.support;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.security.Principal;
import javax.security.auth.Subject;
@@ -27,22 +28,29 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.user.DestinationUserNameProvider;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.MimeType;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
/**
@@ -52,7 +60,9 @@ import static org.mockito.Mockito.*;
*/
public class SendToMethodReturnValueHandlerTests {
private static final String payloadContent = "payload";
public static final MimeType MIME_TYPE = new MimeType("text", "plain", Charset.forName("UTF-8"));
private static final String PAYLOAD = "payload";
private SendToMethodReturnValueHandler handler;
@@ -63,8 +73,6 @@ public class SendToMethodReturnValueHandlerTests {
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Mock private MessageConverter messageConverter;
private MethodParameter noAnnotationsReturnType;
private MethodParameter sendToReturnType;
private MethodParameter sendToDefaultDestReturnType;
@@ -78,11 +86,8 @@ public class SendToMethodReturnValueHandlerTests {
MockitoAnnotations.initMocks(this);
Message message = MessageBuilder.withPayload(payloadContent).build();
when(this.messageConverter.toMessage(payloadContent, null)).thenReturn(message);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setMessageConverter(this.messageConverter);
messagingTemplate.setMessageConverter(new StringMessageConverter());
this.handler = new SendToMethodReturnValueHandler(messagingTemplate, true);
this.handlerAnnotationNotRequired = new SendToMethodReturnValueHandler(messagingTemplate, false);
@@ -118,15 +123,16 @@ public class SendToMethodReturnValueHandlerTests {
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app", "/dest", null);
this.handler.handleReturnValue(payloadContent, this.noAnnotationsReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.noAnnotationsReturnType, inputMessage);
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals("sess1", headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/topic/dest", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
}
@Test
@@ -136,21 +142,23 @@ public class SendToMethodReturnValueHandlerTests {
String sessionId = "sess1";
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, null);
this.handler.handleReturnValue(payloadContent, this.sendToReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.sendToReturnType, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/dest1", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
message = this.messageCaptor.getAllValues().get(1);
headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/dest2", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
}
@Test
@@ -160,15 +168,38 @@ public class SendToMethodReturnValueHandlerTests {
String sessionId = "sess1";
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/app", "/dest", null);
this.handler.handleReturnValue(payloadContent, this.sendToDefaultDestReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.sendToDefaultDestReturnType, inputMessage);
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/topic/dest", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
}
@Test
public void testHeadersToSend() throws Exception {
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app", "/dest", null);
SimpMessageSendingOperations messagingTemplate = Mockito.mock(SimpMessageSendingOperations.class);
SendToMethodReturnValueHandler handler = new SendToMethodReturnValueHandler(messagingTemplate, false);
handler.handleReturnValue(PAYLOAD, this.noAnnotationsReturnType, inputMessage);
ArgumentCaptor<MessageHeaders> captor = ArgumentCaptor.forClass(MessageHeaders.class);
verify(messagingTemplate).convertAndSend(eq("/topic/dest"), eq(PAYLOAD), captor.capture());
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(captor.getValue(), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertTrue(headerAccessor.isMutable());
assertEquals("sess1", headerAccessor.getSessionId());
assertNull("Subscription id should not be copied", headerAccessor.getSubscriptionId());
}
@Test
@@ -179,21 +210,23 @@ public class SendToMethodReturnValueHandlerTests {
String sessionId = "sess1";
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, user);
this.handler.handleReturnValue(payloadContent, this.sendToUserReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.sendToUserReturnType, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals(MIME_TYPE, headers.getContentType());
assertEquals("/user/" + user.getName() + "/dest1", headers.getDestination());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
message = this.messageCaptor.getAllValues().get(1);
headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/dest2", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
}
@Test
@@ -204,7 +237,7 @@ public class SendToMethodReturnValueHandlerTests {
String sessionId = "sess1";
TestUser user = new UniqueUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, user);
this.handler.handleReturnValue(payloadContent, this.sendToUserReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.sendToUserReturnType, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
@@ -223,31 +256,56 @@ public class SendToMethodReturnValueHandlerTests {
String sessionId = "sess1";
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/app", "/dest", user);
this.handler.handleReturnValue(payloadContent, this.sendToUserDefaultDestReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.sendToUserDefaultDestReturnType, inputMessage);
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/queue/dest", headers.getDestination());
assertEquals(MIME_TYPE, headers.getContentType());
assertNull("Subscription id should not be copied", headers.getSubscriptionId());
}
@Test
public void testHeadersToSendToUser() throws Exception {
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app", "/dest", user);
SimpMessageSendingOperations messagingTemplate = Mockito.mock(SimpMessageSendingOperations.class);
SendToMethodReturnValueHandler handler = new SendToMethodReturnValueHandler(messagingTemplate, false);
handler.handleReturnValue(PAYLOAD, this.sendToUserDefaultDestReturnType, inputMessage);
ArgumentCaptor<MessageHeaders> captor = ArgumentCaptor.forClass(MessageHeaders.class);
verify(messagingTemplate).convertAndSendToUser(eq("joe"), eq("/queue/dest"), eq(PAYLOAD), captor.capture());
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(captor.getValue(), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertTrue(headerAccessor.isMutable());
assertEquals("sess1", headerAccessor.getSessionId());
assertNull("Subscription id should not be copied", headerAccessor.getSubscriptionId());
}
private Message<?> createInputMessage(String sessId, String subsId, String destinationPrefix,
String destination, Principal principal) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
headers.setSessionId(sessId);
headers.setSubscriptionId(subsId);
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create();
headerAccessor.setSessionId(sessId);
headerAccessor.setSubscriptionId(subsId);
if (destination != null && destinationPrefix != null) {
headers.setDestination(destinationPrefix + destination);
headers.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, destination);
headerAccessor.setDestination(destinationPrefix + destination);
headerAccessor.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, destination);
}
if (principal != null) {
headers.setUser(principal);
headerAccessor.setUser(principal);
}
return MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toMap()).build();
return MessageBuilder.createMessage(new byte[0], headerAccessor.getMessageHeaders());
}
private static class TestUser implements Principal {
@@ -270,27 +328,27 @@ public class SendToMethodReturnValueHandlerTests {
}
public String handleNoAnnotations() {
return payloadContent;
return PAYLOAD;
}
@SendTo
public String handleAndSendToDefaultDestination() {
return payloadContent;
return PAYLOAD;
}
@SendTo({"/dest1", "/dest2"})
public String handleAndSendTo() {
return payloadContent;
return PAYLOAD;
}
@SendToUser
public String handleAndSendToUserDefaultDestination() {
return payloadContent;
return PAYLOAD;
}
@SendToUser({"/dest1", "/dest2"})
public String handleAndSendToUser() {
return payloadContent;
return PAYLOAD;
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.messaging.simp.annotation.support;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.security.Principal;
import org.junit.Before;
@@ -24,17 +25,22 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.MimeType;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
@@ -47,7 +53,9 @@ import static org.mockito.Mockito.*;
*/
public class SubscriptionMethodReturnValueHandlerTests {
private static final String payloadContent = "payload";
public static final MimeType MIME_TYPE = new MimeType("text", "plain", Charset.forName("UTF-8"));
private static final String PAYLOAD = "payload";
private SubscriptionMethodReturnValueHandler handler;
@@ -56,8 +64,6 @@ public class SubscriptionMethodReturnValueHandlerTests {
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Mock private MessageConverter messageConverter;
private MethodParameter subscribeEventReturnType;
private MethodParameter subscribeEventSendToReturnType;
@@ -71,11 +77,8 @@ public class SubscriptionMethodReturnValueHandlerTests {
MockitoAnnotations.initMocks(this);
Message message = MessageBuilder.withPayload(payloadContent).build();
when(this.messageConverter.toMessage(payloadContent, null)).thenReturn(message);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setMessageConverter(this.messageConverter);
messagingTemplate.setMessageConverter(new StringMessageConverter());
this.handler = new SubscriptionMethodReturnValueHandler(messagingTemplate);
@@ -98,7 +101,7 @@ public class SubscriptionMethodReturnValueHandlerTests {
}
@Test
public void subscribeEventMethod() throws Exception {
public void testMessageSentToChannel() throws Exception {
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
@@ -107,17 +110,46 @@ public class SubscriptionMethodReturnValueHandlerTests {
String destination = "/dest";
Message<?> inputMessage = createInputMessage(sessionId, subscriptionId, destination, null);
this.handler.handleReturnValue(payloadContent, this.subscribeEventReturnType, inputMessage);
this.handler.handleReturnValue(PAYLOAD, this.subscribeEventReturnType, inputMessage);
verify(this.messageChannel).send(this.messageCaptor.capture());
assertNotNull(this.messageCaptor.getValue());
Message<?> message = this.messageCaptor.getValue();
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message);
assertEquals("sessionId should always be copied", sessionId, headers.getSessionId());
assertEquals(subscriptionId, headers.getSubscriptionId());
assertEquals(destination, headers.getDestination());
assertNull("SimpMessageHeaderAccessor should have disabled id", headerAccessor.getId());
assertNull("SimpMessageHeaderAccessor should have disabled timestamp", headerAccessor.getTimestamp());
assertEquals(sessionId, headerAccessor.getSessionId());
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
assertEquals(destination, headerAccessor.getDestination());
assertEquals(MIME_TYPE, headerAccessor.getContentType());
}
@SuppressWarnings("unchecked")
@Test
public void testHeadersPassedToMessagingTemplate() throws Exception {
String sessionId = "sess1";
String subscriptionId = "subs1";
String destination = "/dest";
Message<?> inputMessage = createInputMessage(sessionId, subscriptionId, destination, null);
MessageSendingOperations messagingTemplate = Mockito.mock(MessageSendingOperations.class);
SubscriptionMethodReturnValueHandler handler = new SubscriptionMethodReturnValueHandler(messagingTemplate);
handler.handleReturnValue(PAYLOAD, this.subscribeEventReturnType, inputMessage);
ArgumentCaptor<MessageHeaders> captor = ArgumentCaptor.forClass(MessageHeaders.class);
verify(messagingTemplate).convertAndSend(eq("/dest"), eq(PAYLOAD), captor.capture());
SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(captor.getValue(), SimpMessageHeaderAccessor.class);
assertNotNull(headerAccessor);
assertTrue(headerAccessor.isMutable());
assertEquals(sessionId, headerAccessor.getSessionId());
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
}
@@ -131,19 +163,22 @@ public class SubscriptionMethodReturnValueHandlerTests {
}
@SuppressWarnings("unused")
@SubscribeMapping("/data") // not needed for the tests but here for completeness
private String getData() {
return payloadContent;
return PAYLOAD;
}
@SuppressWarnings("unused")
@SubscribeMapping("/data") // not needed for the tests but here for completeness
@SendTo("/sendToDest")
private String getDataAndSendTo() {
return payloadContent;
return PAYLOAD;
}
@SuppressWarnings("unused")
@MessageMapping("/handle") // not needed for the tests but here for completeness
public String handle() {
return payloadContent;
return PAYLOAD;
}
}