Add XML namespace for WebSocket config
This commit adds an XML namespace equivalent of @EnableWebSocket and @EnableWebSocketMessageBroker. Those are <websocket:handlers> and <websocket:message-broker> respectively. Examples can be found in the test suite. This commit also alters the way MessageHandler's subscribe to their respective MessageChannel's of interest. Rather than performing the subscriptions in configuration code, the message channels are now passed into MessageHandler's so they can subscribe themselves on startup. Issue: SPR-11063
This commit is contained in:
committed by
Rossen Stoyanchev
parent
8f1fefc159
commit
10f5d96a78
@@ -24,10 +24,12 @@ import java.util.List;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class StubMessageChannel implements MessageChannel {
|
||||
public class StubMessageChannel implements SubscribableChannel {
|
||||
|
||||
private final List<Message<byte[]>> messages = new ArrayList<>();
|
||||
|
||||
private final List<MessageHandler> handlers = new ArrayList<>();
|
||||
|
||||
|
||||
public List<Message<byte[]>> getMessages() {
|
||||
return this.messages;
|
||||
@@ -47,4 +49,15 @@ public class StubMessageChannel implements MessageChannel {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean subscribe(MessageHandler handler) {
|
||||
this.handlers.add(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unsubscribe(MessageHandler handler) {
|
||||
this.handlers.remove(handler);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -319,6 +318,7 @@ public class MessageBrokerConfigurationTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel brokerChannel() {
|
||||
return new TestChannel();
|
||||
}
|
||||
@@ -334,7 +334,7 @@ public class MessageBrokerConfigurationTests {
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.enableStompBrokerRelay("/topic", "/queue").setAutoStartup(false);
|
||||
registry.enableStompBrokerRelay("/topic", "/queue").setAutoStartup(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.mockito.Mockito;
|
||||
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.Header;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
@@ -53,9 +54,9 @@ public class SimpAnnotationMethodMessageHandlerTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MessageChannel channel = Mockito.mock(MessageChannel.class);
|
||||
SubscribableChannel channel = Mockito.mock(SubscribableChannel.class);
|
||||
SimpMessageSendingOperations brokerTemplate = new SimpMessagingTemplate(channel);
|
||||
this.messageHandler = new TestSimpAnnotationMethodMessageHandler(brokerTemplate, channel);
|
||||
this.messageHandler = new TestSimpAnnotationMethodMessageHandler(brokerTemplate, channel, channel);
|
||||
this.messageHandler.setApplicationContext(new StaticApplicationContext());
|
||||
this.messageHandler.afterPropertiesSet();
|
||||
|
||||
@@ -145,9 +146,9 @@ public class SimpAnnotationMethodMessageHandlerTests {
|
||||
private static class TestSimpAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {
|
||||
|
||||
public TestSimpAnnotationMethodMessageHandler(SimpMessageSendingOperations brokerTemplate,
|
||||
MessageChannel clientOutboundChannel) {
|
||||
SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
|
||||
|
||||
super(brokerTemplate, clientOutboundChannel);
|
||||
super(clientInboundChannel, clientOutboundChannel, brokerTemplate);
|
||||
}
|
||||
|
||||
public void registerHandler(Object handler) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
@@ -43,7 +44,13 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
private SimpleBrokerMessageHandler messageHandler;
|
||||
|
||||
@Mock
|
||||
private MessageChannel clientChannel;
|
||||
private SubscribableChannel clientInboundChannel;
|
||||
|
||||
@Mock
|
||||
private MessageChannel clientOutboundChannel;
|
||||
|
||||
@Mock
|
||||
private SubscribableChannel brokerChannel;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<Message<?>> messageCaptor;
|
||||
@@ -52,7 +59,8 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.messageHandler = new SimpleBrokerMessageHandler(this.clientChannel, Collections.<String>emptyList());
|
||||
this.messageHandler = new SimpleBrokerMessageHandler(this.clientInboundChannel,
|
||||
this.clientOutboundChannel, this.brokerChannel, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +80,7 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
this.messageHandler.handleMessage(createMessage("/foo", "message1"));
|
||||
this.messageHandler.handleMessage(createMessage("/bar", "message2"));
|
||||
|
||||
verify(this.clientChannel, times(6)).send(this.messageCaptor.capture());
|
||||
verify(this.clientOutboundChannel, times(6)).send(this.messageCaptor.capture());
|
||||
assertCapturedMessage("sess1", "sub1", "/foo");
|
||||
assertCapturedMessage("sess1", "sub2", "/foo");
|
||||
assertCapturedMessage("sess2", "sub1", "/foo");
|
||||
@@ -105,7 +113,7 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
this.messageHandler.handleMessage(createMessage("/foo", "message1"));
|
||||
this.messageHandler.handleMessage(createMessage("/bar", "message2"));
|
||||
|
||||
verify(this.clientChannel, times(3)).send(this.messageCaptor.capture());
|
||||
verify(this.clientOutboundChannel, times(3)).send(this.messageCaptor.capture());
|
||||
assertCapturedMessage(sess2, "sub1", "/foo");
|
||||
assertCapturedMessage(sess2, "sub2", "/foo");
|
||||
assertCapturedMessage(sess2, "sub3", "/bar");
|
||||
@@ -121,7 +129,7 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
Message<String> connectMessage = createConnectMessage(sess1);
|
||||
this.messageHandler.handleMessage(connectMessage);
|
||||
|
||||
verify(this.clientChannel, times(1)).send(this.messageCaptor.capture());
|
||||
verify(this.clientOutboundChannel, times(1)).send(this.messageCaptor.capture());
|
||||
Message<?> connectAckMessage = this.messageCaptor.getValue();
|
||||
|
||||
SimpMessageHeaderAccessor connectAckHeaders = SimpMessageHeaderAccessor.wrap(connectAckMessage);
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
|
||||
package org.springframework.messaging.simp.handler;
|
||||
|
||||
import org.apache.activemq.transport.stomp.Stomp;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.core.MessageSendingOperations;
|
||||
import org.springframework.messaging.StubMessageChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.messaging.simp.TestPrincipal;
|
||||
import org.springframework.messaging.simp.stomp.StompCommand;
|
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UserDestinationMessageHandler}.
|
||||
@@ -39,54 +40,60 @@ public class UserDestinationMessageHandlerTests {
|
||||
|
||||
private UserDestinationMessageHandler messageHandler;
|
||||
|
||||
private MessageSendingOperations<String> messagingTemplate;
|
||||
|
||||
@Mock
|
||||
private SubscribableChannel brokerChannel;
|
||||
|
||||
private UserSessionRegistry registry;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.messagingTemplate = Mockito.mock(MessageSendingOperations.class);
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.registry = new DefaultUserSessionRegistry();
|
||||
DefaultUserDestinationResolver resolver = new DefaultUserDestinationResolver(this.registry);
|
||||
this.messageHandler = new UserDestinationMessageHandler(this.messagingTemplate, resolver);
|
||||
this.messageHandler = new UserDestinationMessageHandler(new StubMessageChannel(),
|
||||
new StubMessageChannel(), this.brokerChannel, resolver);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handleSubscribe() {
|
||||
this.registry.registerSessionId("joe", "123");
|
||||
when(this.brokerChannel.send(Mockito.any(Message.class))).thenReturn(true);
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.SUBSCRIBE, "joe", "/user/queue/foo"));
|
||||
|
||||
ArgumentCaptor<String> captor1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<Message> captor2 = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.messagingTemplate).send(captor1.capture(), captor2.capture());
|
||||
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.brokerChannel).send(captor.capture());
|
||||
|
||||
assertEquals("/queue/foo-user123", captor1.getValue());
|
||||
assertEquals("/queue/foo-user123",
|
||||
captor.getValue().getHeaders().get(SimpMessageHeaderAccessor.DESTINATION_HEADER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUnsubscribe() {
|
||||
this.registry.registerSessionId("joe", "123");
|
||||
when(this.brokerChannel.send(Mockito.any(Message.class))).thenReturn(true);
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.UNSUBSCRIBE, "joe", "/user/queue/foo"));
|
||||
|
||||
ArgumentCaptor<String> captor1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<Message> captor2 = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.messagingTemplate).send(captor1.capture(), captor2.capture());
|
||||
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.brokerChannel).send(captor.capture());
|
||||
|
||||
assertEquals("/queue/foo-user123", captor1.getValue());
|
||||
assertEquals("/queue/foo-user123",
|
||||
captor.getValue().getHeaders().get(SimpMessageHeaderAccessor.DESTINATION_HEADER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMessage() {
|
||||
this.registry.registerSessionId("joe", "123");
|
||||
when(this.brokerChannel.send(Mockito.any(Message.class))).thenReturn(true);
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.MESSAGE, "joe", "/user/joe/queue/foo"));
|
||||
|
||||
ArgumentCaptor<String> captor1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<Message> captor2 = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.messagingTemplate).send(captor1.capture(), captor2.capture());
|
||||
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
|
||||
Mockito.verify(this.brokerChannel).send(captor.capture());
|
||||
|
||||
assertEquals("/queue/foo-user123", captor1.getValue());
|
||||
assertEquals("/queue/foo-user123",
|
||||
captor.getValue().getHeaders().get(SimpMessageHeaderAccessor.DESTINATION_HEADER));
|
||||
}
|
||||
|
||||
|
||||
@@ -95,23 +102,23 @@ public class UserDestinationMessageHandlerTests {
|
||||
|
||||
// no destination
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.MESSAGE, "joe", null));
|
||||
Mockito.verifyZeroInteractions(this.messagingTemplate);
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// not a user destination
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.MESSAGE, "joe", "/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.messagingTemplate);
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// subscribe + no user
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.SUBSCRIBE, null, "/user/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.messagingTemplate);
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// subscribe + not a user destination
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.SUBSCRIBE, "joe", "/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.messagingTemplate);
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// no match on message type
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.CONNECT, "joe", "user/joe/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.messagingTemplate);
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,10 +32,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.*;
|
||||
import org.springframework.messaging.simp.BrokerAvailabilityEvent;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
@@ -92,7 +89,8 @@ public class StompBrokerRelayMessageHandlerIntegrationTests {
|
||||
}
|
||||
|
||||
private void createAndStartRelay() throws InterruptedException {
|
||||
this.relay = new StompBrokerRelayMessageHandler(this.responseChannel, Arrays.asList("/queue/", "/topic/"));
|
||||
this.relay = new StompBrokerRelayMessageHandler(new StubMessageChannel(),
|
||||
this.responseChannel, new StubMessageChannel(), Arrays.asList("/queue/", "/topic/"));
|
||||
this.relay.setRelayPort(this.port);
|
||||
this.relay.setApplicationEventPublisher(this.eventPublisher);
|
||||
this.relay.setSystemHeartbeatReceiveInterval(0);
|
||||
|
||||
@@ -53,7 +53,8 @@ public class StompBrokerRelayMessageHandlerTests {
|
||||
|
||||
this.tcpClient = new StubTcpOperations();
|
||||
|
||||
this.brokerRelay = new StompBrokerRelayMessageHandler(new StubMessageChannel(), Arrays.asList("/topic"));
|
||||
this.brokerRelay = new StompBrokerRelayMessageHandler(new StubMessageChannel(),
|
||||
new StubMessageChannel(), new StubMessageChannel(), Arrays.asList("/topic"));
|
||||
this.brokerRelay.setTcpClient(tcpClient);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user