/* * Copyright 2002-2018 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.simp.config; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.Ordered; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.converter.ByteArrayMessageConverter; import org.springframework.messaging.converter.CompositeMessageConverter; import org.springframework.messaging.converter.ContentTypeResolver; import org.springframework.messaging.converter.DefaultContentTypeResolver; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.StringMessageConverter; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageType; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.annotation.SubscribeMapping; import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler; import org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry; import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler; import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.simp.user.DefaultUserDestinationResolver; import org.springframework.messaging.simp.user.MultiServerUserRegistry; import org.springframework.messaging.simp.user.SimpSubscription; import org.springframework.messaging.simp.user.SimpSubscriptionMatcher; import org.springframework.messaging.simp.user.SimpUser; import org.springframework.messaging.simp.user.SimpUserRegistry; import org.springframework.messaging.simp.user.UserDestinationMessageHandler; import org.springframework.messaging.simp.user.UserRegistryMessageHandler; import org.springframework.messaging.support.AbstractSubscribableChannel; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.ExecutorSubscribableChannel; import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Controller; import org.springframework.util.AntPathMatcher; import org.springframework.util.MimeTypeUtils; import org.springframework.util.PathMatcher; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean; import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** * Test fixture for {@link AbstractMessageBrokerConfiguration}. * * @author Rossen Stoyanchev * @author Brian Clozel * @author Sebastien Deleuze */ public class MessageBrokerConfigurationTests { @Test public void clientInboundChannel() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class); Set<MessageHandler> handlers = channel.getSubscribers(); assertEquals(3, handlers.size()); assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class))); assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class))); assertTrue(handlers.contains(context.getBean(SimpleBrokerMessageHandler.class))); } @Test public void clientInboundChannelWithBrokerRelay() { ApplicationContext context = loadConfig(BrokerRelayConfig.class); TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class); Set<MessageHandler> handlers = channel.getSubscribers(); assertEquals(3, handlers.size()); assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class))); assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class))); assertTrue(handlers.contains(context.getBean(StompBrokerRelayMessageHandler.class))); } @Test public void clientInboundChannelCustomized() { ApplicationContext context = loadConfig(CustomConfig.class); AbstractSubscribableChannel channel = context.getBean( "clientInboundChannel", AbstractSubscribableChannel.class); assertEquals(3, channel.getInterceptors().size()); CustomThreadPoolTaskExecutor taskExecutor = context.getBean( "clientInboundChannelExecutor", CustomThreadPoolTaskExecutor.class); assertEquals(11, taskExecutor.getCorePoolSize()); assertEquals(12, taskExecutor.getMaxPoolSize()); assertEquals(13, taskExecutor.getKeepAliveSeconds()); } @Test public void clientOutboundChannelUsedByAnnotatedMethod() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); TestChannel channel = context.getBean("clientOutboundChannel", TestChannel.class); SimpAnnotationMethodMessageHandler messageHandler = context.getBean(SimpAnnotationMethodMessageHandler.class); StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE); headers.setSessionId("sess1"); headers.setSessionAttributes(new ConcurrentHashMap<>()); headers.setSubscriptionId("subs1"); headers.setDestination("/foo"); Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build(); messageHandler.handleMessage(message); message = channel.messages.get(0); headers = StompHeaderAccessor.wrap(message); assertEquals(SimpMessageType.MESSAGE, headers.getMessageType()); assertEquals("/foo", headers.getDestination()); assertEquals("bar", new String((byte[]) message.getPayload())); } @Test public void clientOutboundChannelUsedBySimpleBroker() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); TestChannel outboundChannel = context.getBean("clientOutboundChannel", TestChannel.class); SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class); StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE); headers.setSessionId("sess1"); headers.setSubscriptionId("subs1"); headers.setDestination("/foo"); Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders()); // subscribe broker.handleMessage(createConnectMessage("sess1", new long[] {0,0})); broker.handleMessage(message); headers = StompHeaderAccessor.create(StompCommand.SEND); headers.setSessionId("sess1"); headers.setDestination("/foo"); message = MessageBuilder.createMessage("bar".getBytes(), headers.getMessageHeaders()); // message broker.handleMessage(message); message = outboundChannel.messages.get(1); headers = StompHeaderAccessor.wrap(message); assertEquals(SimpMessageType.MESSAGE, headers.getMessageType()); assertEquals("/foo", headers.getDestination()); assertEquals("bar", new String((byte[]) message.getPayload())); } @Test public void clientOutboundChannelCustomized() { ApplicationContext context = loadConfig(CustomConfig.class); AbstractSubscribableChannel channel = context.getBean( "clientOutboundChannel", AbstractSubscribableChannel.class); assertEquals(4, channel.getInterceptors().size()); ThreadPoolTaskExecutor taskExecutor = context.getBean( "clientOutboundChannelExecutor", ThreadPoolTaskExecutor.class); assertEquals(21, taskExecutor.getCorePoolSize()); assertEquals(22, taskExecutor.getMaxPoolSize()); assertEquals(23, taskExecutor.getKeepAliveSeconds()); SimpleBrokerMessageHandler broker = context.getBean("simpleBrokerMessageHandler", SimpleBrokerMessageHandler.class); assertTrue(broker.isPreservePublishOrder()); } @Test public void brokerChannel() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); TestChannel channel = context.getBean("brokerChannel", TestChannel.class); Set<MessageHandler> handlers = channel.getSubscribers(); assertEquals(2, handlers.size()); assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class))); assertTrue(handlers.contains(context.getBean(SimpleBrokerMessageHandler.class))); assertNull(channel.getExecutor()); } @Test public void brokerChannelWithBrokerRelay() { ApplicationContext context = loadConfig(BrokerRelayConfig.class); TestChannel channel = context.getBean("brokerChannel", TestChannel.class); Set<MessageHandler> handlers = channel.getSubscribers(); assertEquals(2, handlers.size()); assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class))); assertTrue(handlers.contains(context.getBean(StompBrokerRelayMessageHandler.class))); } @Test public void brokerChannelUsedByAnnotatedMethod() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); TestChannel channel = context.getBean("brokerChannel", TestChannel.class); SimpAnnotationMethodMessageHandler messageHandler = context.getBean(SimpAnnotationMethodMessageHandler.class); StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND); headers.setSessionId("sess1"); headers.setSessionAttributes(new ConcurrentHashMap<>()); headers.setDestination("/foo"); Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders()); messageHandler.handleMessage(message); message = channel.messages.get(0); headers = StompHeaderAccessor.wrap(message); assertEquals(SimpMessageType.MESSAGE, headers.getMessageType()); assertEquals("/bar", headers.getDestination()); assertEquals("bar", new String((byte[]) message.getPayload())); } @Test public void brokerChannelCustomized() { ApplicationContext context = loadConfig(CustomConfig.class); AbstractSubscribableChannel channel = context.getBean( "brokerChannel", AbstractSubscribableChannel.class); assertEquals(4, channel.getInterceptors().size()); ThreadPoolTaskExecutor taskExecutor = context.getBean( "brokerChannelExecutor", ThreadPoolTaskExecutor.class); assertEquals(31, taskExecutor.getCorePoolSize()); assertEquals(32, taskExecutor.getMaxPoolSize()); assertEquals(33, taskExecutor.getKeepAliveSeconds()); } @Test public void configureMessageConvertersDefault() { AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig(); CompositeMessageConverter compositeConverter = config.brokerMessageConverter(); List<MessageConverter> converters = compositeConverter.getConverters(); assertThat(converters.size(), Matchers.is(3)); assertThat(converters.get(0), Matchers.instanceOf(StringMessageConverter.class)); assertThat(converters.get(1), Matchers.instanceOf(ByteArrayMessageConverter.class)); assertThat(converters.get(2), Matchers.instanceOf(MappingJackson2MessageConverter.class)); ContentTypeResolver resolver = ((MappingJackson2MessageConverter) converters.get(2)).getContentTypeResolver(); assertEquals(MimeTypeUtils.APPLICATION_JSON, ((DefaultContentTypeResolver) resolver).getDefaultMimeType()); } @Test public void threadPoolSizeDefault() { ApplicationContext context = loadConfig(DefaultConfig.class); String name = "clientInboundChannelExecutor"; ThreadPoolTaskExecutor executor = context.getBean(name, ThreadPoolTaskExecutor.class); assertEquals(Runtime.getRuntime().availableProcessors() * 2, executor.getCorePoolSize()); // No way to verify queue capacity name = "clientOutboundChannelExecutor"; executor = context.getBean(name, ThreadPoolTaskExecutor.class); assertEquals(Runtime.getRuntime().availableProcessors() * 2, executor.getCorePoolSize()); name = "brokerChannelExecutor"; executor = context.getBean(name, ThreadPoolTaskExecutor.class); assertEquals(0, executor.getCorePoolSize()); assertEquals(1, executor.getMaxPoolSize()); } @Test public void configureMessageConvertersCustom() { final MessageConverter testConverter = mock(MessageConverter.class); AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig() { @Override protected boolean configureMessageConverters(List<MessageConverter> messageConverters) { messageConverters.add(testConverter); return false; } }; CompositeMessageConverter compositeConverter = config.brokerMessageConverter(); assertThat(compositeConverter.getConverters().size(), Matchers.is(1)); Iterator<MessageConverter> iterator = compositeConverter.getConverters().iterator(); assertThat(iterator.next(), Matchers.is(testConverter)); } @Test public void configureMessageConvertersCustomAndDefault() { final MessageConverter testConverter = mock(MessageConverter.class); AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig() { @Override protected boolean configureMessageConverters(List<MessageConverter> messageConverters) { messageConverters.add(testConverter); return true; } }; CompositeMessageConverter compositeConverter = config.brokerMessageConverter(); assertThat(compositeConverter.getConverters().size(), Matchers.is(4)); Iterator<MessageConverter> iterator = compositeConverter.getConverters().iterator(); assertThat(iterator.next(), Matchers.is(testConverter)); assertThat(iterator.next(), Matchers.instanceOf(StringMessageConverter.class)); assertThat(iterator.next(), Matchers.instanceOf(ByteArrayMessageConverter.class)); assertThat(iterator.next(), Matchers.instanceOf(MappingJackson2MessageConverter.class)); } @Test public void customArgumentAndReturnValueTypes() { ApplicationContext context = loadConfig(CustomConfig.class); SimpAnnotationMethodMessageHandler handler = context.getBean(SimpAnnotationMethodMessageHandler.class); List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers(); assertEquals(1, customResolvers.size()); assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0))); List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers(); assertEquals(1, customHandlers.size()); assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0))); } @Test public void simpValidatorDefault() { AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig() {}; config.setApplicationContext(new StaticApplicationContext()); assertThat(config.simpValidator(), Matchers.notNullValue()); assertThat(config.simpValidator(), Matchers.instanceOf(OptionalValidatorFactoryBean.class)); } @Test public void simpValidatorCustom() { final Validator validator = mock(Validator.class); AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig() { @Override public Validator getValidator() { return validator; } }; assertSame(validator, config.simpValidator()); } @Test public void simpValidatorMvc() { StaticApplicationContext appCxt = new StaticApplicationContext(); appCxt.registerSingleton("mvcValidator", TestValidator.class); AbstractMessageBrokerConfiguration config = new BaseTestMessageBrokerConfig() {}; config.setApplicationContext(appCxt); assertThat(config.simpValidator(), Matchers.notNullValue()); assertThat(config.simpValidator(), Matchers.instanceOf(TestValidator.class)); } @Test public void simpValidatorInjected() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); SimpAnnotationMethodMessageHandler messageHandler = context.getBean(SimpAnnotationMethodMessageHandler.class); assertThat(messageHandler.getValidator(), Matchers.notNullValue(Validator.class)); } @Test public void customPathMatcher() { ApplicationContext context = loadConfig(CustomConfig.class); SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class); DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry(); assertEquals("a.a", registry.getPathMatcher().combine("a", "a")); PathMatcher pathMatcher = context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher(); assertEquals("a.a", pathMatcher.combine("a", "a")); DefaultUserDestinationResolver resolver = context.getBean(DefaultUserDestinationResolver.class); assertNotNull(resolver); assertEquals(false, resolver.isRemoveLeadingSlash()); } @Test public void customCacheLimit() { ApplicationContext context = loadConfig(CustomConfig.class); SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class); DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry(); assertEquals(8192, registry.getCacheLimit()); } @Test public void customUserRegistryOrder() { ApplicationContext context = loadConfig(CustomConfig.class); SimpUserRegistry registry = context.getBean(SimpUserRegistry.class); assertTrue(registry instanceof TestUserRegistry); assertEquals(99, ((TestUserRegistry) registry).getOrder()); } @Test public void userBroadcasts() { ApplicationContext context = loadConfig(BrokerRelayConfig.class); SimpUserRegistry userRegistry = context.getBean(SimpUserRegistry.class); assertEquals(MultiServerUserRegistry.class, userRegistry.getClass()); UserDestinationMessageHandler handler1 = context.getBean(UserDestinationMessageHandler.class); assertEquals("/topic/unresolved-user-destination", handler1.getBroadcastDestination()); UserRegistryMessageHandler handler2 = context.getBean(UserRegistryMessageHandler.class); assertEquals("/topic/simp-user-registry", handler2.getBroadcastDestination()); StompBrokerRelayMessageHandler relay = context.getBean(StompBrokerRelayMessageHandler.class); assertNotNull(relay.getSystemSubscriptions()); assertEquals(2, relay.getSystemSubscriptions().size()); assertSame(handler1, relay.getSystemSubscriptions().get("/topic/unresolved-user-destination")); assertSame(handler2, relay.getSystemSubscriptions().get("/topic/simp-user-registry")); } @Test public void userBroadcastsDisabledWithSimpleBroker() { ApplicationContext context = loadConfig(SimpleBrokerConfig.class); SimpUserRegistry registry = context.getBean(SimpUserRegistry.class); assertNotNull(registry); assertNotEquals(MultiServerUserRegistry.class, registry.getClass()); UserDestinationMessageHandler handler = context.getBean(UserDestinationMessageHandler.class); assertNull(handler.getBroadcastDestination()); Object nullBean = context.getBean("userRegistryMessageHandler"); assertTrue(nullBean.equals(null)); } @Test // SPR-16275 public void dotSeparatorWithBrokerSlashConvention() { ApplicationContext context = loadConfig(DotSeparatorWithSlashBrokerConventionConfig.class); testDotSeparator(context, true); } @Test // SPR-16275 public void dotSeparatorWithBrokerDotConvention() { ApplicationContext context = loadConfig(DotSeparatorWithDotBrokerConventionConfig.class); testDotSeparator(context, false); } private void testDotSeparator(ApplicationContext context, boolean expectLeadingSlash) { MessageChannel inChannel = context.getBean("clientInboundChannel", MessageChannel.class); TestChannel outChannel = context.getBean("clientOutboundChannel", TestChannel.class); MessageChannel brokerChannel = context.getBean("brokerChannel", MessageChannel.class); inChannel.send(createConnectMessage("sess1", new long[] {0,0})); // 1. Subscribe to user destination StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE); headers.setSessionId("sess1"); headers.setSubscriptionId("subs1"); headers.setDestination("/user/queue.q1"); Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders()); inChannel.send(message); // 2. Send message to user via inboundChannel headers = StompHeaderAccessor.create(StompCommand.SEND); headers.setSessionId("sess1"); headers.setDestination("/user/sess1/queue.q1"); message = MessageBuilder.createMessage("123".getBytes(), headers.getMessageHeaders()); inChannel.send(message); assertEquals(2, outChannel.messages.size()); Message<?> outputMessage = outChannel.messages.remove(1); headers = StompHeaderAccessor.wrap(outputMessage); assertEquals(SimpMessageType.MESSAGE, headers.getMessageType()); assertEquals(expectLeadingSlash ? "/queue.q1-usersess1" : "queue.q1-usersess1", headers.getDestination()); assertEquals("123", new String((byte[]) outputMessage.getPayload())); outChannel.messages.clear(); // 3. Send message via broker channel SimpMessagingTemplate template = new SimpMessagingTemplate(brokerChannel); SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); accessor.setSessionId("sess1"); template.convertAndSendToUser("sess1", "queue.q1", "456".getBytes(), accessor.getMessageHeaders()); assertEquals(1, outChannel.messages.size()); outputMessage = outChannel.messages.remove(0); headers = StompHeaderAccessor.wrap(outputMessage); assertEquals(SimpMessageType.MESSAGE, headers.getMessageType()); assertEquals(expectLeadingSlash ? "/queue.q1-usersess1" : "queue.q1-usersess1", headers.getDestination()); assertEquals("456", new String((byte[]) outputMessage.getPayload())); } private AnnotationConfigApplicationContext loadConfig(Class<?> configClass) { return new AnnotationConfigApplicationContext(configClass); } private Message<String> createConnectMessage(String sessionId, long[] heartbeat) { SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT); accessor.setSessionId(sessionId); accessor.setHeader(SimpMessageHeaderAccessor.HEART_BEAT_HEADER, heartbeat); return MessageBuilder.createMessage("", accessor.getMessageHeaders()); } @SuppressWarnings("unused") @Controller static class TestController { @SubscribeMapping("/foo") public String handleSubscribe() { return "bar"; } @MessageMapping("/foo") @SendTo("/bar") public String handleMessage() { return "bar"; } } static class BaseTestMessageBrokerConfig extends AbstractMessageBrokerConfiguration { @Override protected SimpUserRegistry createLocalUserRegistry(@Nullable Integer order) { TestUserRegistry registry = new TestUserRegistry(); if (order != null) { registry.setOrder(order); } return registry; } } @SuppressWarnings("unused") @Configuration static class SimpleBrokerConfig extends BaseTestMessageBrokerConfig { @Bean public TestController subscriptionController() { return new TestController(); } @Override @Bean public AbstractSubscribableChannel clientInboundChannel() { return new TestChannel(); } @Override @Bean public AbstractSubscribableChannel clientOutboundChannel() { return new TestChannel(); } @Override @Bean public AbstractSubscribableChannel brokerChannel() { return new TestChannel(); } } @Configuration static class BrokerRelayConfig extends SimpleBrokerConfig { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableStompBrokerRelay("/topic", "/queue").setAutoStartup(true) .setUserDestinationBroadcast("/topic/unresolved-user-destination") .setUserRegistryBroadcast("/topic/simp-user-registry"); } } @Configuration static class DefaultConfig extends BaseTestMessageBrokerConfig { } @Configuration static class CustomConfig extends BaseTestMessageBrokerConfig { private ChannelInterceptor interceptor = new ChannelInterceptor() {}; @Override protected void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(this.interceptor); registration.taskExecutor(new CustomThreadPoolTaskExecutor()) .corePoolSize(11).maxPoolSize(12).keepAliveSeconds(13).queueCapacity(14); } @Override protected void configureClientOutboundChannel(ChannelRegistration registration) { registration.interceptors(this.interceptor, this.interceptor); registration.taskExecutor().corePoolSize(21).maxPoolSize(22).keepAliveSeconds(23).queueCapacity(24); } @Override protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(mock(HandlerMethodArgumentResolver.class)); } @Override protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { returnValueHandlers.add(mock(HandlerMethodReturnValueHandler.class)); } @Override protected void configureMessageBroker(MessageBrokerRegistry registry) { registry.configureBrokerChannel().interceptors(this.interceptor, this.interceptor, this.interceptor); registry.configureBrokerChannel().taskExecutor() .corePoolSize(31).maxPoolSize(32).keepAliveSeconds(33).queueCapacity(34); registry.setPathMatcher(new AntPathMatcher(".")).enableSimpleBroker("/topic", "/queue"); registry.setCacheLimit(8192); registry.setPreservePublishOrder(true); registry.setUserRegistryOrder(99); } } @Configuration static abstract class BaseDotSeparatorConfig extends BaseTestMessageBrokerConfig { @Override protected void configureMessageBroker(MessageBrokerRegistry registry) { registry.setPathMatcher(new AntPathMatcher(".")); } @Override @Bean public AbstractSubscribableChannel clientInboundChannel() { // synchronous return new ExecutorSubscribableChannel(null); } @Override @Bean public AbstractSubscribableChannel clientOutboundChannel() { return new TestChannel(); } @Override @Bean public AbstractSubscribableChannel brokerChannel() { // synchronous return new ExecutorSubscribableChannel(null); } } @Configuration static class DotSeparatorWithSlashBrokerConventionConfig extends BaseDotSeparatorConfig { // RabbitMQ-style broker convention for STOMP destinations @Override protected void configureMessageBroker(MessageBrokerRegistry registry) { super.configureMessageBroker(registry); registry.enableSimpleBroker("/topic", "/queue"); } } @Configuration static class DotSeparatorWithDotBrokerConventionConfig extends BaseDotSeparatorConfig { // Artemis-style broker convention for STOMP destinations @Override protected void configureMessageBroker(MessageBrokerRegistry registry) { super.configureMessageBroker(registry); registry.enableSimpleBroker("topic.", "queue."); } } private static class TestChannel extends ExecutorSubscribableChannel { private final List<Message<?>> messages = new ArrayList<>(); @Override public boolean sendInternal(Message<?> message, long timeout) { this.messages.add(message); return true; } } private static class TestUserRegistry implements SimpUserRegistry, Ordered { private Integer order; public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public SimpUser getUser(String userName) { return null; } @Override public Set<SimpUser> getUsers() { return null; } @Override public int getUserCount() { return 0; } @Override public Set<SimpSubscription> findSubscriptions(SimpSubscriptionMatcher matcher) { return null; } } private static class TestValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return false; } @Override public void validate(@Nullable Object target, Errors errors) { } } @SuppressWarnings("serial") private static class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { } }