View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.socket.config;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.List;
22  
23  import org.hamcrest.Matchers;
24  import org.junit.Before;
25  import org.junit.Test;
26  
27  import org.springframework.beans.DirectFieldAccessor;
28  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
29  import org.springframework.beans.factory.config.CustomScopeConfigurer;
30  import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
31  import org.springframework.core.MethodParameter;
32  import org.springframework.core.io.ClassPathResource;
33  import org.springframework.messaging.Message;
34  import org.springframework.messaging.MessageHandler;
35  import org.springframework.messaging.converter.ByteArrayMessageConverter;
36  import org.springframework.messaging.converter.CompositeMessageConverter;
37  import org.springframework.messaging.converter.ContentTypeResolver;
38  import org.springframework.messaging.converter.DefaultContentTypeResolver;
39  import org.springframework.messaging.converter.MappingJackson2MessageConverter;
40  import org.springframework.messaging.converter.MessageConverter;
41  import org.springframework.messaging.converter.StringMessageConverter;
42  import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
43  import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
44  import org.springframework.messaging.simp.SimpMessagingTemplate;
45  import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
46  import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler;
47  import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler;
48  import org.springframework.messaging.simp.user.DefaultUserDestinationResolver;
49  import org.springframework.messaging.simp.user.UserDestinationMessageHandler;
50  import org.springframework.messaging.simp.user.UserDestinationResolver;
51  import org.springframework.messaging.simp.user.UserSessionRegistry;
52  import org.springframework.messaging.support.AbstractSubscribableChannel;
53  import org.springframework.messaging.support.ChannelInterceptor;
54  import org.springframework.messaging.support.ImmutableMessageChannelInterceptor;
55  import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
56  import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
57  import org.springframework.util.MimeTypeUtils;
58  import org.springframework.web.HttpRequestHandler;
59  import org.springframework.web.context.support.GenericWebApplicationContext;
60  import org.springframework.web.servlet.HandlerMapping;
61  import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
62  import org.springframework.web.socket.WebSocketHandler;
63  import org.springframework.web.socket.WebSocketSession;
64  import org.springframework.web.socket.handler.TestWebSocketSession;
65  import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
66  import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
67  import org.springframework.web.socket.messaging.StompSubProtocolHandler;
68  import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
69  import org.springframework.web.socket.server.HandshakeHandler;
70  import org.springframework.web.socket.server.HandshakeInterceptor;
71  import org.springframework.web.socket.server.support.OriginHandshakeInterceptor;
72  import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
73  import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
74  import org.springframework.web.socket.sockjs.transport.TransportType;
75  import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
76  import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
77  
78  import static org.hamcrest.Matchers.*;
79  import static org.junit.Assert.*;
80  
81  /**
82   * Test fixture for MessageBrokerBeanDefinitionParser.
83   * See test configuration files websocket-config-broker-*.xml.
84   *
85   * @author Brian Clozel
86   * @author Artem Bilan
87   * @author Rossen Stoyanchev
88   */
89  public class MessageBrokerBeanDefinitionParserTests {
90  
91  	private GenericWebApplicationContext appContext;
92  
93  
94  	@Before
95  	public void setup() {
96  		this.appContext = new GenericWebApplicationContext();
97  	}
98  
99  
100 	@Test
101 	public void simpleBroker() throws Exception {
102 		loadBeanDefinitions("websocket-config-broker-simple.xml");
103 
104 		HandlerMapping hm = this.appContext.getBean(HandlerMapping.class);
105 		assertThat(hm, Matchers.instanceOf(SimpleUrlHandlerMapping.class));
106 		SimpleUrlHandlerMapping suhm = (SimpleUrlHandlerMapping) hm;
107 		assertThat(suhm.getUrlMap().keySet(), Matchers.hasSize(4));
108 		assertThat(suhm.getUrlMap().values(), Matchers.hasSize(4));
109 
110 		HttpRequestHandler httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/foo");
111 		assertNotNull(httpRequestHandler);
112 		assertThat(httpRequestHandler, Matchers.instanceOf(WebSocketHttpRequestHandler.class));
113 
114 		WebSocketHttpRequestHandler wsHttpRequestHandler = (WebSocketHttpRequestHandler) httpRequestHandler;
115 		HandshakeHandler handshakeHandler = wsHttpRequestHandler.getHandshakeHandler();
116 		assertNotNull(handshakeHandler);
117 		assertTrue(handshakeHandler instanceof TestHandshakeHandler);
118 		List<HandshakeInterceptor> interceptors = wsHttpRequestHandler.getHandshakeInterceptors();
119 		assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class),
120 				instanceOf(BarTestInterceptor.class), instanceOf(OriginHandshakeInterceptor.class)));
121 
122 		WebSocketSession session = new TestWebSocketSession("id");
123 		wsHttpRequestHandler.getWebSocketHandler().afterConnectionEstablished(session);
124 		assertEquals(true, session.getAttributes().get("decorated"));
125 
126 		WebSocketHandler wsHandler = unwrapWebSocketHandler(wsHttpRequestHandler.getWebSocketHandler());
127 		assertNotNull(wsHandler);
128 		assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
129 
130 		SubProtocolWebSocketHandler subProtocolWsHandler = (SubProtocolWebSocketHandler) wsHandler;
131 		assertEquals(Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp"), subProtocolWsHandler.getSubProtocols());
132 		assertEquals(25 * 1000, subProtocolWsHandler.getSendTimeLimit());
133 		assertEquals(1024 * 1024, subProtocolWsHandler.getSendBufferSizeLimit());
134 
135 		StompSubProtocolHandler stompHandler = (StompSubProtocolHandler) subProtocolWsHandler.getProtocolHandlerMap().get("v12.stomp");
136 		assertNotNull(stompHandler);
137 		assertEquals(128 * 1024, stompHandler.getMessageSizeLimit());
138 
139 		assertNotNull(new DirectFieldAccessor(stompHandler).getPropertyValue("eventPublisher"));
140 
141 		httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/test/**");
142 		assertNotNull(httpRequestHandler);
143 		assertThat(httpRequestHandler, Matchers.instanceOf(SockJsHttpRequestHandler.class));
144 
145 		SockJsHttpRequestHandler sockJsHttpRequestHandler = (SockJsHttpRequestHandler) httpRequestHandler;
146 		wsHandler = unwrapWebSocketHandler(sockJsHttpRequestHandler.getWebSocketHandler());
147 		assertNotNull(wsHandler);
148 		assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
149 		assertNotNull(sockJsHttpRequestHandler.getSockJsService());
150 		assertThat(sockJsHttpRequestHandler.getSockJsService(), Matchers.instanceOf(DefaultSockJsService.class));
151 
152 		DefaultSockJsService defaultSockJsService = (DefaultSockJsService) sockJsHttpRequestHandler.getSockJsService();
153 		WebSocketTransportHandler wsTransportHandler = (WebSocketTransportHandler) defaultSockJsService
154 				.getTransportHandlers().get(TransportType.WEBSOCKET);
155 		assertNotNull(wsTransportHandler.getHandshakeHandler());
156 		assertThat(wsTransportHandler.getHandshakeHandler(), Matchers.instanceOf(TestHandshakeHandler.class));
157 		assertFalse(defaultSockJsService.shouldSuppressCors());
158 
159 		ThreadPoolTaskScheduler scheduler = (ThreadPoolTaskScheduler) defaultSockJsService.getTaskScheduler();
160 		assertEquals(Runtime.getRuntime().availableProcessors(), scheduler.getScheduledThreadPoolExecutor().getCorePoolSize());
161 		assertTrue(scheduler.getScheduledThreadPoolExecutor().getRemoveOnCancelPolicy());
162 
163 		interceptors = defaultSockJsService.getHandshakeInterceptors();
164 		assertThat(interceptors, contains(instanceOf(FooTestInterceptor.class),
165 				instanceOf(BarTestInterceptor.class), instanceOf(OriginHandshakeInterceptor.class)));
166 		assertEquals(Arrays.asList("http://mydomain3.com", "http://mydomain4.com"), defaultSockJsService.getAllowedOrigins());
167 
168 		UserSessionRegistry userSessionRegistry = this.appContext.getBean(UserSessionRegistry.class);
169 		assertNotNull(userSessionRegistry);
170 
171 		UserDestinationResolver userDestResolver = this.appContext.getBean(UserDestinationResolver.class);
172 		assertNotNull(userDestResolver);
173 		assertThat(userDestResolver, Matchers.instanceOf(DefaultUserDestinationResolver.class));
174 		DefaultUserDestinationResolver defaultUserDestResolver = (DefaultUserDestinationResolver) userDestResolver;
175 		assertEquals("/personal/", defaultUserDestResolver.getDestinationPrefix());
176 		assertSame(stompHandler.getUserSessionRegistry(), defaultUserDestResolver.getUserSessionRegistry());
177 
178 		UserDestinationMessageHandler userDestHandler = this.appContext.getBean(UserDestinationMessageHandler.class);
179 		assertNotNull(userDestHandler);
180 
181 		SimpleBrokerMessageHandler brokerMessageHandler = this.appContext.getBean(SimpleBrokerMessageHandler.class);
182 		assertNotNull(brokerMessageHandler);
183 		assertEquals(Arrays.asList("/topic", "/queue"),
184 				new ArrayList<String>(brokerMessageHandler.getDestinationPrefixes()));
185 
186 		List<Class<? extends MessageHandler>> subscriberTypes =
187 				Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
188 						UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);
189 		testChannel("clientInboundChannel", subscriberTypes, 2);
190 		testExecutor("clientInboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
191 
192 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
193 		testChannel("clientOutboundChannel", subscriberTypes, 1);
194 		testExecutor("clientOutboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
195 
196 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SimpleBrokerMessageHandler.class, UserDestinationMessageHandler.class);
197 		testChannel("brokerChannel", subscriberTypes, 1);
198 		try {
199 			this.appContext.getBean("brokerChannelExecutor", ThreadPoolTaskExecutor.class);
200 			fail("expected exception");
201 		}
202 		catch (NoSuchBeanDefinitionException ex) {
203 			// expected
204 		}
205 
206 		assertNotNull(this.appContext.getBean("webSocketScopeConfigurer", CustomScopeConfigurer.class));
207 
208 		DirectFieldAccessor subscriptionRegistryAccessor = new DirectFieldAccessor(brokerMessageHandler.getSubscriptionRegistry());
209 		String pathSeparator = (String)new DirectFieldAccessor(subscriptionRegistryAccessor.getPropertyValue("pathMatcher")).getPropertyValue("pathSeparator");
210 		assertEquals(".", pathSeparator);
211 	}
212 
213 	@Test
214 	public void stompBrokerRelay() {
215 		loadBeanDefinitions("websocket-config-broker-relay.xml");
216 
217 		HandlerMapping hm = this.appContext.getBean(HandlerMapping.class);
218 		assertNotNull(hm);
219 		assertThat(hm, Matchers.instanceOf(SimpleUrlHandlerMapping.class));
220 
221 		SimpleUrlHandlerMapping suhm = (SimpleUrlHandlerMapping) hm;
222 		assertThat(suhm.getUrlMap().keySet(), Matchers.hasSize(1));
223 		assertThat(suhm.getUrlMap().values(), Matchers.hasSize(1));
224 		assertEquals(2, suhm.getOrder());
225 
226 		HttpRequestHandler httpRequestHandler = (HttpRequestHandler) suhm.getUrlMap().get("/foo/**");
227 		assertNotNull(httpRequestHandler);
228 		assertThat(httpRequestHandler, Matchers.instanceOf(SockJsHttpRequestHandler.class));
229 		SockJsHttpRequestHandler sockJsHttpRequestHandler = (SockJsHttpRequestHandler) httpRequestHandler;
230 		WebSocketHandler wsHandler = unwrapWebSocketHandler(sockJsHttpRequestHandler.getWebSocketHandler());
231 		assertNotNull(wsHandler);
232 		assertThat(wsHandler, Matchers.instanceOf(SubProtocolWebSocketHandler.class));
233 		assertNotNull(sockJsHttpRequestHandler.getSockJsService());
234 
235 		UserDestinationResolver userDestResolver = this.appContext.getBean(UserDestinationResolver.class);
236 		assertNotNull(userDestResolver);
237 		assertThat(userDestResolver, Matchers.instanceOf(DefaultUserDestinationResolver.class));
238 		DefaultUserDestinationResolver defaultUserDestResolver = (DefaultUserDestinationResolver) userDestResolver;
239 		assertEquals("/user/", defaultUserDestResolver.getDestinationPrefix());
240 
241 		StompBrokerRelayMessageHandler messageBroker = this.appContext.getBean(StompBrokerRelayMessageHandler.class);
242 		assertNotNull(messageBroker);
243 		assertEquals("clientlogin", messageBroker.getClientLogin());
244 		assertEquals("clientpass", messageBroker.getClientPasscode());
245 		assertEquals("syslogin", messageBroker.getSystemLogin());
246 		assertEquals("syspass", messageBroker.getSystemPasscode());
247 		assertEquals("relayhost", messageBroker.getRelayHost());
248 		assertEquals(1234, messageBroker.getRelayPort());
249 		assertEquals("spring.io", messageBroker.getVirtualHost());
250 		assertEquals(5000, messageBroker.getSystemHeartbeatReceiveInterval());
251 		assertEquals(5000, messageBroker.getSystemHeartbeatSendInterval());
252 		assertThat(messageBroker.getDestinationPrefixes(), Matchers.containsInAnyOrder("/topic","/queue"));
253 
254 		List<Class<? extends MessageHandler>> subscriberTypes =
255 				Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
256 						UserDestinationMessageHandler.class, StompBrokerRelayMessageHandler.class);
257 		testChannel("clientInboundChannel", subscriberTypes, 2);
258 		testExecutor("clientInboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
259 
260 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
261 		testChannel("clientOutboundChannel", subscriberTypes, 1);
262 		testExecutor("clientOutboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
263 
264 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(
265 				StompBrokerRelayMessageHandler.class, UserDestinationMessageHandler.class);
266 		testChannel("brokerChannel", subscriberTypes, 1);
267 		try {
268 			this.appContext.getBean("brokerChannelExecutor", ThreadPoolTaskExecutor.class);
269 			fail("expected exception");
270 		}
271 		catch (NoSuchBeanDefinitionException ex) {
272 			// expected
273 		}
274 
275 		String name = "webSocketMessageBrokerStats";
276 		WebSocketMessageBrokerStats stats = this.appContext.getBean(name, WebSocketMessageBrokerStats.class);
277 		String actual = stats.toString();
278 		String expected = "WebSocketSession\\[0 current WS\\(0\\)-HttpStream\\(0\\)-HttpPoll\\(0\\), " +
279 				"0 total, 0 closed abnormally \\(0 connect failure, 0 send limit, 0 transport error\\)\\], " +
280 				"stompSubProtocol\\[processed CONNECT\\(0\\)-CONNECTED\\(0\\)-DISCONNECT\\(0\\)\\], " +
281 				"stompBrokerRelay\\[0 sessions, relayhost:1234 \\(not available\\), processed CONNECT\\(0\\)-CONNECTED\\(0\\)-DISCONNECT\\(0\\)\\], " +
282 				"inboundChannel\\[pool size = \\d, active threads = \\d, queued tasks = \\d, completed tasks = \\d\\], " +
283 				"outboundChannelpool size = \\d, active threads = \\d, queued tasks = \\d, completed tasks = \\d\\], " +
284 				"sockJsScheduler\\[pool size = \\d, active threads = \\d, queued tasks = \\d, completed tasks = \\d\\]";
285 
286 		assertTrue("\nExpected: " + expected.replace("\\", "") + "\n  Actual: " + actual, actual.matches(expected));
287 	}
288 
289 	@Test
290 	public void annotationMethodMessageHandler() {
291 		loadBeanDefinitions("websocket-config-broker-simple.xml");
292 
293 		SimpAnnotationMethodMessageHandler annotationMethodMessageHandler =
294 				this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);
295 
296 		assertNotNull(annotationMethodMessageHandler);
297 		MessageConverter messageConverter = annotationMethodMessageHandler.getMessageConverter();
298 		assertNotNull(messageConverter);
299 		assertTrue(messageConverter instanceof CompositeMessageConverter);
300 
301 		CompositeMessageConverter compositeMessageConverter = this.appContext.getBean(CompositeMessageConverter.class);
302 		assertNotNull(compositeMessageConverter);
303 
304 		SimpMessagingTemplate simpMessagingTemplate = this.appContext.getBean(SimpMessagingTemplate.class);
305 		assertNotNull(simpMessagingTemplate);
306 		assertEquals("/personal/", simpMessagingTemplate.getUserDestinationPrefix());
307 
308 		List<MessageConverter> converters = compositeMessageConverter.getConverters();
309 		assertThat(converters.size(), Matchers.is(3));
310 		assertThat(converters.get(0), Matchers.instanceOf(StringMessageConverter.class));
311 		assertThat(converters.get(1), Matchers.instanceOf(ByteArrayMessageConverter.class));
312 		assertThat(converters.get(2), Matchers.instanceOf(MappingJackson2MessageConverter.class));
313 
314 		ContentTypeResolver resolver = ((MappingJackson2MessageConverter) converters.get(2)).getContentTypeResolver();
315 		assertEquals(MimeTypeUtils.APPLICATION_JSON, ((DefaultContentTypeResolver) resolver).getDefaultMimeType());
316 
317 		DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(annotationMethodMessageHandler);
318 		String pathSeparator = (String)new DirectFieldAccessor(handlerAccessor.getPropertyValue("pathMatcher")).getPropertyValue("pathSeparator");
319 		assertEquals(".", pathSeparator);
320 	}
321 
322 	@Test
323 	public void customChannels() {
324 		loadBeanDefinitions("websocket-config-broker-customchannels.xml");
325 
326 		List<Class<? extends MessageHandler>> subscriberTypes =
327 				Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
328 						UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);
329 
330 		testChannel("clientInboundChannel", subscriberTypes, 3);
331 		testExecutor("clientInboundChannel", 100, 200, 600);
332 
333 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SubProtocolWebSocketHandler.class);
334 
335 		testChannel("clientOutboundChannel", subscriberTypes, 3);
336 		testExecutor("clientOutboundChannel", 101, 201, 601);
337 
338 		subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SimpleBrokerMessageHandler.class,
339 				UserDestinationMessageHandler.class);
340 
341 		testChannel("brokerChannel", subscriberTypes, 1);
342 		testExecutor("brokerChannel", 102, 202, 602);
343 	}
344 
345 	// SPR-11623
346 
347 	@Test
348 	public void customChannelsWithDefaultExecutor() {
349 		loadBeanDefinitions("websocket-config-broker-customchannels-default-executor.xml");
350 
351 		testExecutor("clientInboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
352 		testExecutor("clientOutboundChannel", Runtime.getRuntime().availableProcessors() * 2, Integer.MAX_VALUE, 60);
353 		assertFalse(this.appContext.containsBean("brokerChannelExecutor"));
354 	}
355 
356 	@Test
357 	public void customArgumentAndReturnValueTypes() {
358 		loadBeanDefinitions("websocket-config-broker-custom-argument-and-return-value-types.xml");
359 
360 		SimpAnnotationMethodMessageHandler handler = this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);
361 
362 		List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
363 		assertEquals(2, customResolvers.size());
364 		assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));
365 		assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(1)));
366 
367 		List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
368 		assertEquals(2, customHandlers.size());
369 		assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
370 		assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(1)));
371 	}
372 
373 	@Test
374 	public void messageConverters() {
375 		loadBeanDefinitions("websocket-config-broker-converters.xml");
376 
377 		CompositeMessageConverter compositeConverter = this.appContext.getBean(CompositeMessageConverter.class);
378 		assertNotNull(compositeConverter);
379 
380 		assertEquals(4, compositeConverter.getConverters().size());
381 		assertEquals(StringMessageConverter.class, compositeConverter.getConverters().iterator().next().getClass());
382 	}
383 
384 	@Test
385 	public void messageConvertersDefaultsOff() {
386 		loadBeanDefinitions("websocket-config-broker-converters-defaults-off.xml");
387 
388 		CompositeMessageConverter compositeConverter = this.appContext.getBean(CompositeMessageConverter.class);
389 		assertNotNull(compositeConverter);
390 
391 		assertEquals(1, compositeConverter.getConverters().size());
392 		assertEquals(StringMessageConverter.class, compositeConverter.getConverters().iterator().next().getClass());
393 	}
394 
395 
396 	private void testChannel(String channelName, List<Class<? extends  MessageHandler>> subscriberTypes,
397 			int interceptorCount) {
398 
399 		AbstractSubscribableChannel channel = this.appContext.getBean(channelName, AbstractSubscribableChannel.class);
400 
401 		for (Class<? extends  MessageHandler> subscriberType : subscriberTypes) {
402 			MessageHandler subscriber = this.appContext.getBean(subscriberType);
403 			assertNotNull("No subsription for " + subscriberType, subscriber);
404 			assertTrue(channel.hasSubscription(subscriber));
405 		}
406 
407 		List<ChannelInterceptor> interceptors = channel.getInterceptors();
408 		assertEquals(interceptorCount, interceptors.size());
409 		assertEquals(ImmutableMessageChannelInterceptor.class, interceptors.get(interceptors.size()-1).getClass());
410 	}
411 
412 	private void testExecutor(String channelName, int corePoolSize, int maxPoolSize, int keepAliveSeconds) {
413 
414 		ThreadPoolTaskExecutor taskExecutor =
415 				this.appContext.getBean(channelName + "Executor", ThreadPoolTaskExecutor.class);
416 
417 		assertEquals(corePoolSize, taskExecutor.getCorePoolSize());
418 		assertEquals(maxPoolSize, taskExecutor.getMaxPoolSize());
419 		assertEquals(keepAliveSeconds, taskExecutor.getKeepAliveSeconds());
420 	}
421 
422 	private void loadBeanDefinitions(String fileName) {
423 		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.appContext);
424 		ClassPathResource resource = new ClassPathResource(fileName, MessageBrokerBeanDefinitionParserTests.class);
425 		reader.loadBeanDefinitions(resource);
426 		this.appContext.refresh();
427 	}
428 
429 	private WebSocketHandler unwrapWebSocketHandler(WebSocketHandler handler) {
430 		return (handler instanceof WebSocketHandlerDecorator) ?
431 				((WebSocketHandlerDecorator) handler).getLastHandler() : handler;
432 	}
433 
434 }
435 
436 class CustomArgumentResolver implements HandlerMethodArgumentResolver {
437 
438 	@Override
439 	public boolean supportsParameter(MethodParameter parameter) {
440 		return false;
441 	}
442 
443 	@Override
444 	public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
445 		return null;
446 	}
447 }
448 
449 class CustomReturnValueHandler implements HandlerMethodReturnValueHandler {
450 
451 	@Override
452 	public boolean supportsReturnType(MethodParameter returnType) {
453 		return false;
454 	}
455 
456 	@Override
457 	public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message) throws Exception {
458 
459 	}
460 }
461 
462 class TestWebSocketHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {
463 
464 	@Override
465 	public WebSocketHandler decorate(WebSocketHandler handler) {
466 		return new WebSocketHandlerDecorator(handler) {
467 			@Override
468 			public void afterConnectionEstablished(WebSocketSession session) throws Exception {
469 				session.getAttributes().put("decorated", true);
470 				super.afterConnectionEstablished(session);
471 			}
472 		};
473 	}
474 }