Skip to content

Commit f0dda0e

Browse files
committed
Add WebSocket integration tests w/ Java configuration
Issue: SPR-10835
1 parent 744e1ed commit f0dda0e

20 files changed

+682
-291
lines changed

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ project("spring-messaging") {
334334
testCompile("org.eclipse.jetty:jetty-webapp:9.0.5.v20130815") {
335335
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
336336
}
337-
testCompile("org.eclipse.jetty.websocket:websocket-server:9.0.5.v20130815")
337+
optional("org.eclipse.jetty.websocket:websocket-server:9.0.5.v20130815")
338+
optional("org.eclipse.jetty.websocket:websocket-client:9.0.5.v20130815")
338339
testCompile("javax.servlet:javax.servlet-api:3.0.1")
339340
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
340341
testCompile("log4j:log4j:1.2.17")
@@ -527,6 +528,8 @@ project("spring-websocket") {
527528
optional("org.eclipse.jetty.websocket:websocket-client:9.0.5.v20130815")
528529
optional("com.fasterxml.jackson.core:jackson-databind:2.2.0")
529530
optional("org.codehaus.jackson:jackson-mapper-asl:1.9.12")
531+
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
532+
testCompile("log4j:log4j:1.2.17")
530533
}
531534

532535
repositories {

spring-messaging/src/main/java/org/springframework/messaging/handler/websocket/SubProtocolWebSocketHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public SubProtocolWebSocketHandler(MessageChannel outputChannel) {
7272
this.outputChannel = outputChannel;
7373
}
7474

75+
7576
/**
7677
* Configure one or more handlers to use depending on the sub-protocol requested by
7778
* the client in the WebSocket handshake request.
@@ -130,6 +131,12 @@ public SubProtocolHandler getDefaultProtocolHandler() {
130131
return this.defaultProtocolHandler;
131132
}
132133

134+
/**
135+
* Return all supported protocols.
136+
*/
137+
public Set<String> getSupportedProtocols() {
138+
return this.protocolHandlers.keySet();
139+
}
133140

134141
@Override
135142
public void afterConnectionEstablished(WebSocketSession session) throws Exception {

spring-messaging/src/main/java/org/springframework/messaging/simp/config/StompEndpointRegistration.java

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import java.util.ArrayList;
2020
import java.util.Collection;
2121
import java.util.List;
22+
import java.util.Set;
2223

2324
import org.springframework.messaging.handler.websocket.SubProtocolWebSocketHandler;
2425
import org.springframework.scheduling.TaskScheduler;
2526
import org.springframework.util.LinkedMultiValueMap;
2627
import org.springframework.util.MultiValueMap;
28+
import org.springframework.util.ObjectUtils;
2729
import org.springframework.web.HttpRequestHandler;
2830
import org.springframework.web.socket.server.DefaultHandshakeHandler;
2931
import org.springframework.web.socket.server.HandshakeHandler;
@@ -46,53 +48,43 @@ public class StompEndpointRegistration {
4648

4749
private final SubProtocolWebSocketHandler wsHandler;
4850

51+
private HandshakeHandler handshakeHandler;
52+
4953
private StompSockJsServiceRegistration sockJsServiceRegistration;
5054

51-
private TaskScheduler defaultTaskScheduler;
55+
private final TaskScheduler defaultSockJsTaskScheduler;
56+
5257

58+
public StompEndpointRegistration(Collection<String> paths, SubProtocolWebSocketHandler webSocketHandler,
59+
TaskScheduler defaultSockJsTaskScheduler) {
5360

54-
public StompEndpointRegistration(Collection<String> paths, SubProtocolWebSocketHandler webSocketHandler) {
5561
this.paths = new ArrayList<String>(paths);
5662
this.wsHandler = webSocketHandler;
63+
this.defaultSockJsTaskScheduler = defaultSockJsTaskScheduler;
5764
}
5865

5966

60-
protected List<String> getPaths() {
61-
return this.paths;
62-
}
63-
64-
protected SubProtocolWebSocketHandler getSubProtocolWebSocketHandler() {
65-
return this.wsHandler;
66-
}
67-
68-
protected StompSockJsServiceRegistration getSockJsServiceRegistration() {
69-
return this.sockJsServiceRegistration;
67+
public StompEndpointRegistration setHandshakeHandler(HandshakeHandler handshakeHandler) {
68+
this.handshakeHandler = handshakeHandler;
69+
return this;
7070
}
7171

7272
public SockJsServiceRegistration withSockJS() {
73-
this.sockJsServiceRegistration = new StompSockJsServiceRegistration(this.defaultTaskScheduler);
73+
this.sockJsServiceRegistration = new StompSockJsServiceRegistration(this.defaultSockJsTaskScheduler);
7474
return this.sockJsServiceRegistration;
7575
}
7676

77-
protected void setDefaultTaskScheduler(TaskScheduler defaultTaskScheduler) {
78-
this.defaultTaskScheduler = defaultTaskScheduler;
79-
}
80-
81-
protected TaskScheduler getDefaultTaskScheduler() {
82-
return this.defaultTaskScheduler;
83-
}
84-
8577
protected MultiValueMap<HttpRequestHandler, String> getMappings() {
8678
MultiValueMap<HttpRequestHandler, String> mappings = new LinkedMultiValueMap<HttpRequestHandler, String>();
87-
if (getSockJsServiceRegistration() == null) {
88-
HandshakeHandler handshakeHandler = createHandshakeHandler();
89-
for (String path : getPaths()) {
79+
if (this.sockJsServiceRegistration == null) {
80+
HandshakeHandler handshakeHandler = getOrCreateHandshakeHandler();
81+
for (String path : this.paths) {
9082
WebSocketHttpRequestHandler handler = new WebSocketHttpRequestHandler(this.wsHandler, handshakeHandler);
9183
mappings.add(handler, path);
9284
}
9385
}
9486
else {
95-
SockJsService sockJsService = getSockJsServiceRegistration().getSockJsService();
87+
SockJsService sockJsService = this.sockJsServiceRegistration.getSockJsService();
9688
for (String path : this.paths) {
9789
SockJsHttpRequestHandler httpHandler = new SockJsHttpRequestHandler(sockJsService, this.wsHandler);
9890
mappings.add(httpHandler, path.endsWith("/") ? path + "**" : path + "/**");
@@ -101,8 +93,20 @@ protected MultiValueMap<HttpRequestHandler, String> getMappings() {
10193
return mappings;
10294
}
10395

104-
protected DefaultHandshakeHandler createHandshakeHandler() {
105-
return new DefaultHandshakeHandler();
96+
private HandshakeHandler getOrCreateHandshakeHandler() {
97+
98+
HandshakeHandler handler = (this.handshakeHandler != null)
99+
? this.handshakeHandler : new DefaultHandshakeHandler();
100+
101+
if (handler instanceof DefaultHandshakeHandler) {
102+
DefaultHandshakeHandler defaultHandshakeHandler = (DefaultHandshakeHandler) handler;
103+
if (ObjectUtils.isEmpty(defaultHandshakeHandler.getSupportedProtocols())) {
104+
Set<String> protocols = this.wsHandler.getSupportedProtocols();
105+
defaultHandshakeHandler.setSupportedProtocols(protocols.toArray(new String[protocols.size()]));
106+
}
107+
}
108+
109+
return handler;
106110
}
107111

108112

@@ -114,7 +118,7 @@ public StompSockJsServiceRegistration(TaskScheduler defaultTaskScheduler) {
114118
}
115119

116120
protected SockJsService getSockJsService() {
117-
return super.getSockJsService(getPaths().toArray(new String[getPaths().size()]));
121+
return super.getSockJsService(paths.toArray(new String[paths.size()]));
118122
}
119123
}
120124

spring-messaging/src/main/java/org/springframework/messaging/simp/config/StompEndpointRegistry.java

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,37 +51,30 @@ public class StompEndpointRegistry {
5151

5252
private int order = 1;
5353

54-
private TaskScheduler defaultTaskScheduler;
54+
private final TaskScheduler defaultSockJsTaskScheduler;
5555

5656

5757
public StompEndpointRegistry(SubProtocolWebSocketHandler webSocketHandler,
58-
MutableUserQueueSuffixResolver userQueueSuffixResolver) {
58+
MutableUserQueueSuffixResolver userQueueSuffixResolver, TaskScheduler defaultSockJsTaskScheduler) {
5959

6060
Assert.notNull(webSocketHandler);
6161
Assert.notNull(userQueueSuffixResolver);
6262

6363
this.wsHandler = webSocketHandler;
6464
this.stompHandler = new StompProtocolHandler();
6565
this.stompHandler.setUserQueueSuffixResolver(userQueueSuffixResolver);
66+
this.defaultSockJsTaskScheduler = defaultSockJsTaskScheduler;
6667
}
6768

6869

6970
public StompEndpointRegistration addEndpoint(String... paths) {
7071
this.wsHandler.addProtocolHandler(this.stompHandler);
71-
StompEndpointRegistration r = new StompEndpointRegistration(Arrays.asList(paths), this.wsHandler);
72-
r.setDefaultTaskScheduler(getDefaultTaskScheduler());
72+
StompEndpointRegistration r = new StompEndpointRegistration(
73+
Arrays.asList(paths), this.wsHandler, this.defaultSockJsTaskScheduler);
7374
this.registrations.add(r);
7475
return r;
7576
}
7677

77-
protected SubProtocolWebSocketHandler getSubProtocolWebSocketHandler() {
78-
return this.wsHandler;
79-
}
80-
81-
protected StompProtocolHandler getStompProtocolHandler() {
82-
return this.stompHandler;
83-
}
84-
8578
/**
8679
* Specify the order to use for the STOMP endpoint {@link HandlerMapping} relative to
8780
* other handler mappings configured in the Spring MVC configuration. The default
@@ -91,18 +84,6 @@ public void setOrder(int order) {
9184
this.order = order;
9285
}
9386

94-
protected int getOrder() {
95-
return this.order;
96-
}
97-
98-
protected void setDefaultTaskScheduler(TaskScheduler defaultTaskScheduler) {
99-
this.defaultTaskScheduler = defaultTaskScheduler;
100-
}
101-
102-
protected TaskScheduler getDefaultTaskScheduler() {
103-
return this.defaultTaskScheduler;
104-
}
105-
10687
/**
10788
* Returns a handler mapping with the mapped ViewControllers; or {@code null} in case of no registrations.
10889
*/

spring-messaging/src/main/java/org/springframework/messaging/simp/config/WebSocketMessageBrokerConfigurationSupport.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
3434
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
3535
import org.springframework.web.servlet.HandlerMapping;
36+
import org.springframework.web.socket.server.config.SockJsServiceRegistration;
3637

3738

3839
/**
@@ -54,9 +55,8 @@ public abstract class WebSocketMessageBrokerConfigurationSupport {
5455

5556
@Bean
5657
public HandlerMapping brokerWebSocketHandlerMapping() {
57-
StompEndpointRegistry registry =
58-
new StompEndpointRegistry(subProtocolWebSocketHandler(), userQueueSuffixResolver());
59-
registry.setDefaultTaskScheduler(brokerDefaultSockJsTaskScheduler());
58+
StompEndpointRegistry registry = new StompEndpointRegistry(
59+
subProtocolWebSocketHandler(), userQueueSuffixResolver(), brokerDefaultSockJsTaskScheduler());
6060
registerStompEndpoints(registry);
6161
return registry.getHandlerMapping();
6262
}
@@ -73,11 +73,14 @@ public MutableUserQueueSuffixResolver userQueueSuffixResolver() {
7373
return new SimpleUserQueueSuffixResolver();
7474
}
7575

76+
/**
77+
* The default TaskScheduler to use if none is configured via
78+
* {@link SockJsServiceRegistration#setTaskScheduler()}
79+
*/
7680
@Bean
7781
public ThreadPoolTaskScheduler brokerDefaultSockJsTaskScheduler() {
7882
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
7983
scheduler.setThreadNamePrefix("BrokerSockJS-");
80-
scheduler.setPoolSize(10);
8184
return scheduler;
8285
}
8386

@@ -97,9 +100,7 @@ public SubscribableChannel webSocketReplyChannel() {
97100
@Bean
98101
public ThreadPoolTaskExecutor webSocketChannelExecutor() {
99102
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
100-
executor.setCorePoolSize(4);
101-
executor.setCorePoolSize(8);
102-
executor.setThreadNamePrefix("MessageChannel-");
103+
executor.setThreadNamePrefix("BrokerWebSocketChannel-");
103104
return executor;
104105
}
105106

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2002-2013 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.messaging.simp;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.junit.After;
23+
import org.junit.Before;
24+
import org.junit.runners.Parameterized.Parameter;
25+
import org.springframework.context.Lifecycle;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.web.socket.client.WebSocketClient;
29+
import org.springframework.web.socket.server.DefaultHandshakeHandler;
30+
import org.springframework.web.socket.server.HandshakeHandler;
31+
import org.springframework.web.socket.server.RequestUpgradeStrategy;
32+
import org.springframework.web.socket.server.support.JettyRequestUpgradeStrategy;
33+
34+
35+
36+
/**
37+
* Base class for WebSocket integration tests.
38+
*
39+
* @author Rossen Stoyanchev
40+
*/
41+
public abstract class AbstractWebSocketIntegrationTests {
42+
43+
private static Map<Class<?>, Class<?>> upgradeStrategyConfigTypes = new HashMap<Class<?>, Class<?>>();
44+
45+
static {
46+
upgradeStrategyConfigTypes.put(JettyTestServer.class, JettyUpgradeStrategyConfig.class);
47+
}
48+
49+
@Parameter(0)
50+
public TestServer server;
51+
52+
@Parameter(1)
53+
public WebSocketClient webSocketClient;
54+
55+
56+
@Before
57+
public void setup() throws Exception {
58+
if (this.webSocketClient instanceof Lifecycle) {
59+
((Lifecycle) this.webSocketClient).start();
60+
}
61+
}
62+
63+
@After
64+
public void teardown() throws Exception {
65+
try {
66+
if (this.webSocketClient instanceof Lifecycle) {
67+
((Lifecycle) this.webSocketClient).stop();
68+
}
69+
}
70+
finally {
71+
this.server.stop();
72+
}
73+
}
74+
75+
protected String getWsBaseUrl() {
76+
return "ws://localhost:" + this.server.getPort();
77+
}
78+
79+
protected Class<?> getUpgradeStrategyConfigClass() {
80+
return upgradeStrategyConfigTypes.get(this.server.getClass());
81+
}
82+
83+
84+
static abstract class AbstractRequestUpgradeStrategyConfig {
85+
86+
@Bean
87+
public HandshakeHandler handshakeHandler() {
88+
return new DefaultHandshakeHandler(requestUpgradeStrategy());
89+
}
90+
91+
public abstract RequestUpgradeStrategy requestUpgradeStrategy();
92+
}
93+
94+
95+
@Configuration
96+
static class JettyUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
97+
98+
@Bean
99+
public RequestUpgradeStrategy requestUpgradeStrategy() {
100+
return new JettyRequestUpgradeStrategy();
101+
}
102+
}
103+
104+
}

0 commit comments

Comments
 (0)