Skip to content

Commit 25ff34f

Browse files
committed
JettyRequestUpgradeStrategy implements Lifecycle
After this change JettyRequestUpgradeStrategy implements Lifecyle, which is used to init and cleanup the Jetty WebSocketServerFactory. Since a RequestUpgradeStrategy is typically created reflectively within DefaultHandshakeHandler, the Lifecycle events are propagated from the top, i.e. the Spring MVC HandlerMapping through the WebSocket/SockJsHttpRequestHandler. Issue: SPR-13140
1 parent b6b76ad commit 25ff34f

File tree

9 files changed

+282
-26
lines changed

9 files changed

+282
-26
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,22 @@
5959
import org.springframework.util.MimeTypeUtils;
6060
import org.springframework.util.StringUtils;
6161
import org.springframework.util.xml.DomUtils;
62-
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
6362
import org.springframework.web.socket.WebSocketHandler;
6463
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
6564
import org.springframework.web.socket.messaging.DefaultSimpUserRegistry;
6665
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
6766
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
6867
import org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler;
6968
import org.springframework.web.socket.server.support.OriginHandshakeInterceptor;
69+
import org.springframework.web.socket.server.support.WebSocketHandlerMapping;
7070
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
7171
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
7272

7373
/**
7474
* A {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that provides
7575
* the configuration for the {@code <websocket:message-broker/>} XML namespace element.
7676
*
77-
* <p>Registers a Spring MVC {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping}
77+
* <p>Registers a Spring MVC {@link org.springframework.web.servlet.HandlerMapping}
7878
* with order 1 to map HTTP WebSocket handshake requests from STOMP/WebSocket clients.
7979
*
8080
* <p>Registers the following {@link org.springframework.messaging.MessageChannel}s:
@@ -188,7 +188,7 @@ private RuntimeBeanReference registerUserRegistry(Element element, ParserContext
188188
private ManagedMap<String, Object> registerHandlerMapping(Element element,
189189
ParserContext context, Object source) {
190190

191-
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
191+
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(WebSocketHandlerMapping.class);
192192

193193
String orderAttribute = element.getAttribute("order");
194194
int order = orderAttribute.isEmpty() ? DEFAULT_MAPPING_ORDER : Integer.valueOf(orderAttribute);

spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@
2727
import org.springframework.util.MultiValueMap;
2828
import org.springframework.web.HttpRequestHandler;
2929
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
30-
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
3130
import org.springframework.web.socket.WebSocketHandler;
3231
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
3332
import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler;
3433
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
3534
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
35+
import org.springframework.web.socket.server.support.WebSocketHandlerMapping;
3636
import org.springframework.web.util.UrlPathHelper;
3737

3838
/**
3939
* A registry for STOMP over WebSocket endpoints that maps the endpoints with a
40-
* {@link SimpleUrlHandlerMapping} for use in Spring MVC.
40+
* {@link org.springframework.web.servlet.HandlerMapping} for use in Spring MVC.
4141
*
4242
* @author Rossen Stoyanchev
4343
* @author Artem Bilan
@@ -111,8 +111,9 @@ public StompWebSocketEndpointRegistration addEndpoint(String... paths) {
111111
}
112112

113113
/**
114-
* Set the order for the resulting {@link SimpleUrlHandlerMapping} relative to
115-
* other handler mappings configured in Spring MVC.
114+
* Set the order for the resulting
115+
* {@link org.springframework.web.servlet.HandlerMapping}
116+
* relative to other handler mappings configured in Spring MVC.
116117
* <p>The default value is 1.
117118
*/
118119
@Override
@@ -125,7 +126,7 @@ protected int getOrder() {
125126
}
126127

127128
/**
128-
* Set the UrlPathHelper to configure on the {@code SimpleUrlHandlerMapping}
129+
* Set the UrlPathHelper to configure on the {@code HandlerMapping}
129130
* used to map handshake requests.
130131
*/
131132
@Override
@@ -144,7 +145,8 @@ public WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler
144145
}
145146

146147
/**
147-
* Return a handler mapping with the mapped ViewControllers; or {@code null} in case of no registrations.
148+
* Return a handler mapping with the mapped ViewControllers; or {@code null}
149+
* in case of no registrations.
148150
*/
149151
public AbstractHandlerMapping getHandlerMapping() {
150152
Map<String, Object> urlMap = new LinkedHashMap<String, Object>();
@@ -156,7 +158,7 @@ public AbstractHandlerMapping getHandlerMapping() {
156158
}
157159
}
158160
}
159-
SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
161+
WebSocketHandlerMapping hm = new WebSocketHandlerMapping();
160162
hm.setUrlMap(urlMap);
161163
hm.setOrder(this.order);
162164
if (this.urlPathHelper != null) {

spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.util.ArrayList;
2222
import java.util.List;
2323
import java.util.Map;
24+
2425
import javax.servlet.http.HttpServletRequest;
2526
import javax.servlet.http.HttpServletResponse;
2627

@@ -34,6 +35,7 @@
3435
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
3536
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
3637

38+
import org.springframework.context.Lifecycle;
3739
import org.springframework.core.NamedThreadLocal;
3840
import org.springframework.http.server.ServerHttpRequest;
3941
import org.springframework.http.server.ServerHttpResponse;
@@ -57,7 +59,7 @@
5759
* @author Rossen Stoyanchev
5860
* @since 4.0
5961
*/
60-
public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
62+
public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Lifecycle {
6163

6264
private static final ThreadLocal<WebSocketHandlerContainer> wsContainerHolder =
6365
new NamedThreadLocal<WebSocketHandlerContainer>("WebSocket Handler Container");
@@ -67,6 +69,8 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
6769

6870
private volatile List<WebSocketExtension> supportedExtensions;
6971

72+
private volatile boolean running = false;
73+
7074

7175
/**
7276
* Default constructor that creates {@link WebSocketServerFactory} through its default
@@ -90,6 +94,7 @@ public Object createWebSocket(ServletUpgradeRequest request, ServletUpgradeRespo
9094
// Cast to avoid infinite recursion
9195
return createWebSocket((UpgradeRequest) request, (UpgradeResponse) response);
9296
}
97+
9398
// For Jetty 9.0.x
9499
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
95100
WebSocketHandlerContainer container = wsContainerHolder.get();
@@ -99,12 +104,6 @@ public Object createWebSocket(UpgradeRequest request, UpgradeResponse response)
99104
return container.getHandler();
100105
}
101106
});
102-
try {
103-
this.factory.init();
104-
}
105-
catch (Exception ex) {
106-
throw new IllegalStateException("Unable to initialize Jetty WebSocketServerFactory", ex);
107-
}
108107
}
109108

110109

@@ -129,6 +128,33 @@ private List<WebSocketExtension> getWebSocketExtensions() {
129128
return result;
130129
}
131130

131+
@Override
132+
public boolean isRunning() {
133+
return this.running;
134+
}
135+
136+
137+
@Override
138+
public void start() {
139+
if (!isRunning()) {
140+
this.running = true;
141+
try {
142+
this.factory.init();
143+
}
144+
catch (Exception ex) {
145+
throw new IllegalStateException("Unable to initialize Jetty WebSocketServerFactory", ex);
146+
}
147+
}
148+
}
149+
150+
@Override
151+
public void stop() {
152+
if (isRunning()) {
153+
this.running = false;
154+
this.factory.cleanup();
155+
}
156+
}
157+
132158
@Override
133159
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
134160
String selectedProtocol, List<WebSocketExtension> selectedExtensions, Principal user,

spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
3030

31+
import org.springframework.context.Lifecycle;
3132
import org.springframework.http.HttpMethod;
3233
import org.springframework.http.HttpStatus;
3334
import org.springframework.http.server.ServerHttpRequest;
@@ -59,7 +60,7 @@
5960
* @author Rossen Stoyanchev
6061
* @since 4.0
6162
*/
62-
public class DefaultHandshakeHandler implements HandshakeHandler {
63+
public class DefaultHandshakeHandler implements HandshakeHandler, Lifecycle {
6364

6465
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
6566

@@ -86,6 +87,8 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
8687

8788
private final List<String> supportedProtocols = new ArrayList<String>();
8889

90+
private volatile boolean running = false;
91+
8992

9093
/**
9194
* Default constructor that autodetects and instantiates a
@@ -161,6 +164,31 @@ public String[] getSupportedProtocols() {
161164
return this.supportedProtocols.toArray(new String[this.supportedProtocols.size()]);
162165
}
163166

167+
@Override
168+
public boolean isRunning() {
169+
return this.running;
170+
}
171+
172+
@Override
173+
public void start() {
174+
if (!isRunning()) {
175+
this.running = true;
176+
if (this.requestUpgradeStrategy instanceof Lifecycle) {
177+
((Lifecycle) this.requestUpgradeStrategy).start();
178+
}
179+
}
180+
}
181+
182+
@Override
183+
public void stop() {
184+
if (isRunning()) {
185+
this.running = false;
186+
if (this.requestUpgradeStrategy instanceof Lifecycle) {
187+
((Lifecycle) this.requestUpgradeStrategy).stop();
188+
}
189+
}
190+
}
191+
164192

165193
@Override
166194
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2002-2015 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+
package org.springframework.web.socket.server.support;
17+
18+
import org.springframework.context.Lifecycle;
19+
import org.springframework.context.SmartLifecycle;
20+
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
21+
22+
/**
23+
* An extension of {@link SimpleUrlHandlerMapping} that is also a
24+
* {@link SmartLifecycle} container and propagates start and stop calls to any
25+
* handlers that implement {@link Lifecycle}. The handlers are typically expected
26+
* to be {@code WebSocketHttpRequestHandler} or {@code SockJsHttpRequestHandler}.
27+
*
28+
* @author Rossen Stoyanchev
29+
* @since 4.2
30+
*/
31+
public class WebSocketHandlerMapping extends SimpleUrlHandlerMapping implements SmartLifecycle {
32+
33+
private volatile boolean running = false;
34+
35+
36+
@Override
37+
public boolean isAutoStartup() {
38+
return true;
39+
}
40+
41+
@Override
42+
public boolean isRunning() {
43+
return this.running;
44+
}
45+
46+
@Override
47+
public int getPhase() {
48+
return Integer.MAX_VALUE;
49+
}
50+
51+
@Override
52+
public void start() {
53+
if (!isRunning()) {
54+
this.running = true;
55+
for (Object handler : getUrlMap().values()) {
56+
if (handler instanceof Lifecycle) {
57+
((Lifecycle) handler).start();
58+
}
59+
}
60+
}
61+
}
62+
63+
@Override
64+
public void stop() {
65+
if (isRunning()) {
66+
this.running = false;
67+
for (Object handler : getUrlMap().values()) {
68+
if (handler instanceof Lifecycle) {
69+
((Lifecycle) handler).stop();
70+
}
71+
}
72+
}
73+
}
74+
75+
@Override
76+
public void stop(Runnable callback) {
77+
stop();
78+
callback.run();
79+
}
80+
81+
}

0 commit comments

Comments
 (0)