Skip to content

Commit a514305

Browse files
committed
Add configurability for underlying WebSocket engine
Issue: SPR-10844
1 parent ccaa101 commit a514305

19 files changed

+219
-69
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSesssion.java

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@
3030
/**
3131
* An abstract base class for implementations of {@link WebSocketSession}.
3232
*
33+
* @param T the type of the native (or delegate) WebSocket session
34+
*
3335
* @author Rossen Stoyanchev
3436
* @since 4.0
3537
*/
36-
public abstract class AbstractWebSocketSesssion<T> implements DelegatingWebSocketSession<T> {
38+
public abstract class AbstractWebSocketSesssion<T> implements WebSocketSession, NativeWebSocketSession {
3739

3840
protected final Log logger = LogFactory.getLog(getClass());
3941

40-
private T delegateSession;
42+
private T nativeSession;
4143

4244
private final Map<String, Object> handshakeAttributes;
4345

@@ -58,28 +60,35 @@ public Map<String, Object> getHandshakeAttributes() {
5860
return this.handshakeAttributes;
5961
}
6062

61-
/**
62-
* @return the WebSocket session to delegate to
63-
*/
64-
public T getDelegateSession() {
65-
return this.delegateSession;
63+
@Override
64+
public T getNativeSession() {
65+
return this.nativeSession;
6666
}
6767

68-
68+
@SuppressWarnings("unchecked")
6969
@Override
70-
public void afterSessionInitialized(T session) {
70+
public <R> R getNativeSession(Class<R> requiredType) {
71+
if (requiredType != null) {
72+
if (requiredType.isInstance(this.nativeSession)) {
73+
return (R) this.nativeSession;
74+
}
75+
}
76+
return null;
77+
}
78+
79+
public void initializeNativeSession(T session) {
7180
Assert.notNull(session, "session must not be null");
72-
this.delegateSession = session;
81+
this.nativeSession = session;
7382
}
7483

75-
protected final void checkDelegateSessionInitialized() {
76-
Assert.state(this.delegateSession != null, "WebSocket session is not yet initialized");
84+
protected final void checkNativeSessionInitialized() {
85+
Assert.state(this.nativeSession != null, "WebSocket session is not yet initialized");
7786
}
7887

7988
@Override
8089
public final void sendMessage(WebSocketMessage message) throws IOException {
8190

82-
checkDelegateSessionInitialized();
91+
checkNativeSessionInitialized();
8392
Assert.isTrue(isOpen(), "Cannot send message after connection closed.");
8493

8594
if (logger.isTraceEnabled()) {
@@ -109,7 +118,7 @@ public final void close() throws IOException {
109118

110119
@Override
111120
public final void close(CloseStatus status) throws IOException {
112-
checkDelegateSessionInitialized();
121+
checkNativeSessionInitialized();
113122
if (logger.isDebugEnabled()) {
114123
logger.debug("Closing " + this);
115124
}

spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketHandlerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public JettyWebSocketHandlerAdapter(WebSocketHandler webSocketHandler, JettyWebS
5454
@Override
5555
public void onWebSocketConnect(Session session) {
5656
try {
57-
this.wsSession.afterSessionInitialized(session);
57+
this.wsSession.initializeNativeSession(session);
5858
this.webSocketHandler.afterConnectionEstablished(this.wsSession);
5959
}
6060
catch (Throwable t) {

spring-websocket/src/main/java/org/springframework/web/socket/adapter/JettyWebSocketSession.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,22 @@ public JettyWebSocketSession(Principal principal, Map<String, Object> handshakeA
5858

5959
@Override
6060
public String getId() {
61-
checkDelegateSessionInitialized();
62-
return ObjectUtils.getIdentityHexString(getDelegateSession());
61+
checkNativeSessionInitialized();
62+
return ObjectUtils.getIdentityHexString(getNativeSession());
6363
}
6464

6565
@Override
6666
public URI getUri() {
67-
checkDelegateSessionInitialized();
68-
return getDelegateSession().getUpgradeRequest().getRequestURI();
67+
checkNativeSessionInitialized();
68+
return getNativeSession().getUpgradeRequest().getRequestURI();
6969
}
7070

7171
@Override
7272
public HttpHeaders getHandshakeHeaders() {
73-
checkDelegateSessionInitialized();
73+
checkNativeSessionInitialized();
7474
if (this.headers == null) {
7575
this.headers = new HttpHeaders();
76-
this.headers.putAll(getDelegateSession().getUpgradeRequest().getHeaders());
76+
this.headers.putAll(getNativeSession().getUpgradeRequest().getHeaders());
7777
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
7878
}
7979
return this.headers;
@@ -86,40 +86,40 @@ public Principal getPrincipal() {
8686

8787
@Override
8888
public InetSocketAddress getLocalAddress() {
89-
checkDelegateSessionInitialized();
90-
return getDelegateSession().getLocalAddress();
89+
checkNativeSessionInitialized();
90+
return getNativeSession().getLocalAddress();
9191
}
9292

9393
@Override
9494
public InetSocketAddress getRemoteAddress() {
95-
checkDelegateSessionInitialized();
96-
return getDelegateSession().getRemoteAddress();
95+
checkNativeSessionInitialized();
96+
return getNativeSession().getRemoteAddress();
9797
}
9898

9999
@Override
100100
public String getAcceptedProtocol() {
101-
checkDelegateSessionInitialized();
102-
return getDelegateSession().getUpgradeResponse().getAcceptedSubProtocol();
101+
checkNativeSessionInitialized();
102+
return getNativeSession().getUpgradeResponse().getAcceptedSubProtocol();
103103
}
104104

105105
@Override
106106
public boolean isOpen() {
107-
return ((getDelegateSession() != null) && getDelegateSession().isOpen());
107+
return ((getNativeSession() != null) && getNativeSession().isOpen());
108108
}
109109

110110
@Override
111111
protected void sendTextMessage(TextMessage message) throws IOException {
112-
getDelegateSession().getRemote().sendString(message.getPayload());
112+
getNativeSession().getRemote().sendString(message.getPayload());
113113
}
114114

115115
@Override
116116
protected void sendBinaryMessage(BinaryMessage message) throws IOException {
117-
getDelegateSession().getRemote().sendBytes(message.getPayload());
117+
getNativeSession().getRemote().sendBytes(message.getPayload());
118118
}
119119

120120
@Override
121121
protected void closeInternal(CloseStatus status) throws IOException {
122-
getDelegateSession().close(status.getCode(), status.getReason());
122+
getNativeSession().close(status.getCode(), status.getReason());
123123
}
124124

125125
}
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,26 @@
2020

2121

2222
/**
23-
* A contract for a {@link WebSocketSession} that delegates to another WebSocket session
24-
* (e.g. a native session).
25-
*
26-
* @param T the type of the delegate WebSocket session
23+
* A {@link WebSocketSession} that exposes the underlying, native WebSocketSession
24+
* through a getter.
2725
*
2826
* @author Rossen Stoyanchev
2927
* @since 4.0
3028
*/
31-
public interface DelegatingWebSocketSession<T> extends WebSocketSession {
29+
public interface NativeWebSocketSession extends WebSocketSession {
3230

3331

3432
/**
35-
* Invoked when the delegate WebSocket session has been initialized.
33+
* Return the underlying native WebSocketSession, if available.
34+
* @return the native session or {@code null}
3635
*/
37-
void afterSessionInitialized(T session);
36+
Object getNativeSession();
3837

38+
/**
39+
* Return the underlying native WebSocketSession, if available.
40+
* @param requiredType the required type of the session
41+
* @return the native session of the required type or {@code null}
42+
*/
43+
<T> T getNativeSession(Class<T> requiredType);
3944

4045
}

spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketHandlerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public StandardWebSocketHandlerAdapter(WebSocketHandler handler, StandardWebSock
5858
@Override
5959
public void onOpen(final javax.websocket.Session session, EndpointConfig config) {
6060

61-
this.wsSession.afterSessionInitialized(session);
61+
this.wsSession.initializeNativeSession(session);
6262

6363
if (this.handler.supportsPartialMessages()) {
6464
session.addMessageHandler(new MessageHandler.Partial<String>() {

spring-websocket/src/main/java/org/springframework/web/socket/adapter/StandardWebSocketSession.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@ public StandardWebSocketSession(HttpHeaders handshakeHeaders, Map<String, Object
6868

6969
@Override
7070
public String getId() {
71-
checkDelegateSessionInitialized();
72-
return getDelegateSession().getId();
71+
checkNativeSessionInitialized();
72+
return getNativeSession().getId();
7373
}
7474

7575
@Override
7676
public URI getUri() {
77-
checkDelegateSessionInitialized();
78-
return getDelegateSession().getRequestURI();
77+
checkNativeSessionInitialized();
78+
return getNativeSession().getRequestURI();
7979
}
8080

8181
@Override
@@ -85,8 +85,8 @@ public HttpHeaders getHandshakeHeaders() {
8585

8686
@Override
8787
public Principal getPrincipal() {
88-
checkDelegateSessionInitialized();
89-
return getDelegateSession().getUserPrincipal();
88+
checkNativeSessionInitialized();
89+
return getNativeSession().getUserPrincipal();
9090
}
9191

9292
@Override
@@ -101,29 +101,29 @@ public InetSocketAddress getRemoteAddress() {
101101

102102
@Override
103103
public String getAcceptedProtocol() {
104-
checkDelegateSessionInitialized();
105-
String protocol = getDelegateSession().getNegotiatedSubprotocol();
104+
checkNativeSessionInitialized();
105+
String protocol = getNativeSession().getNegotiatedSubprotocol();
106106
return StringUtils.isEmpty(protocol)? null : protocol;
107107
}
108108

109109
@Override
110110
public boolean isOpen() {
111-
return ((getDelegateSession() != null) && getDelegateSession().isOpen());
111+
return ((getNativeSession() != null) && getNativeSession().isOpen());
112112
}
113113

114114
@Override
115115
protected void sendTextMessage(TextMessage message) throws IOException {
116-
getDelegateSession().getBasicRemote().sendText(message.getPayload(), message.isLast());
116+
getNativeSession().getBasicRemote().sendText(message.getPayload(), message.isLast());
117117
}
118118

119119
@Override
120120
protected void sendBinaryMessage(BinaryMessage message) throws IOException {
121-
getDelegateSession().getBasicRemote().sendBinary(message.getPayload(), message.isLast());
121+
getNativeSession().getBasicRemote().sendBinary(message.getPayload(), message.isLast());
122122
}
123123

124124
@Override
125125
protected void closeInternal(CloseStatus status) throws IOException {
126-
getDelegateSession().close(new CloseReason(CloseCodes.getCloseCode(status.getCode()), status.getReason()));
126+
getNativeSession().close(new CloseReason(CloseCodes.getCloseCode(status.getCode()), status.getReason()));
127127
}
128128

129129
}

spring-websocket/src/main/java/org/springframework/web/socket/client/endpoint/StandardWebSocketClient.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,30 @@ public class StandardWebSocketClient extends AbstractWebSocketClient {
5252
private final WebSocketContainer webSocketContainer;
5353

5454

55+
/**
56+
* Default constructor that calls {@code ContainerProvider.getWebSocketContainer()} to
57+
* obtain a {@link WebSocketContainer} instance.
58+
*/
5559
public StandardWebSocketClient() {
5660
this.webSocketContainer = ContainerProvider.getWebSocketContainer();
5761
}
5862

63+
/**
64+
* Constructor that accepts a pre-configured {@link WebSocketContainer} instance. If
65+
* using XML configuration see {@link WebSocketContainerFactoryBean}. In Java
66+
* configuration use {@code ContainerProvider.getWebSocketContainer()} to obtain
67+
* a container instance.
68+
*/
5969
public StandardWebSocketClient(WebSocketContainer webSocketContainer) {
6070
Assert.notNull(webSocketContainer, "webSocketContainer must not be null");
6171
this.webSocketContainer = webSocketContainer;
6272
}
6373

6474

6575
@Override
66-
protected WebSocketSession doHandshakeInternal(WebSocketHandler webSocketHandler, HttpHeaders headers,
67-
URI uri, List<String> protocols, Map<String, Object> handshakeAttributes)
68-
throws WebSocketConnectFailureException {
76+
protected WebSocketSession doHandshakeInternal(WebSocketHandler webSocketHandler,
77+
HttpHeaders headers, URI uri, List<String> protocols,
78+
Map<String, Object> handshakeAttributes) throws WebSocketConnectFailureException {
6979

7080
int port = getPort(uri);
7181
InetSocketAddress localAddress = new InetSocketAddress(getLocalHost(), port);

spring-websocket/src/main/java/org/springframework/web/socket/client/jetty/JettyWebSocketClient.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323

2424
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
25+
import org.eclipse.jetty.websocket.client.WebSocketClient;
2526
import org.springframework.context.SmartLifecycle;
2627
import org.springframework.http.HttpHeaders;
2728
import org.springframework.web.socket.WebSocketHandler;
@@ -51,12 +52,22 @@ public class JettyWebSocketClient extends AbstractWebSocketClient implements Sma
5152
private final Object lifecycleMonitor = new Object();
5253

5354

55+
/**
56+
* Default constructor that creates an instance of
57+
* {@link org.eclipse.jetty.websocket.client.WebSocketClient} with default settings.
58+
*/
5459
public JettyWebSocketClient() {
5560
this.client = new org.eclipse.jetty.websocket.client.WebSocketClient();
5661
}
5762

63+
/**
64+
* Constructor that accepts a pre-configured {@link WebSocketClient}.
65+
*/
66+
public JettyWebSocketClient(WebSocketClient client) {
67+
super();
68+
this.client = client;
69+
}
5870

59-
// TODO: configure Jetty WebSocketClient properties
6071

6172
public void setAutoStartup(boolean autoStartup) {
6273
this.autoStartup = autoStartup;

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@
5050
import org.springframework.util.StringUtils;
5151
import org.springframework.web.socket.server.HandshakeFailureException;
5252
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
53+
import org.springframework.web.socket.server.endpoint.ServletServerContainerFactoryBean;
5354

5455

5556
/**
56-
* GlassFish support for upgrading a request during a WebSocket handshake.
57+
* GlassFish support for upgrading a request during a WebSocket handshake. To modify
58+
* properties of the underlying {@link javax.websocket.server.ServerContainer} you can use
59+
* {@link ServletServerContainerFactoryBean} in XML configuration or if using Java
60+
* configuration, access the container instance through the
61+
* "javax.websocket.server.ServerContainer" ServletContext attribute.
5762
*
5863
* @author Rossen Stoyanchev
5964
* @since 4.0

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import org.eclipse.jetty.websocket.api.UpgradeRequest;
2626
import org.eclipse.jetty.websocket.api.UpgradeResponse;
27+
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
2728
import org.eclipse.jetty.websocket.server.HandshakeRFC6455;
2829
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
2930
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
@@ -55,10 +56,19 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
5556

5657

5758
/**
58-
* Default constructor.
59+
* Default constructor that creates {@link WebSocketServerFactory} through its default
60+
* constructor thus using a default {@link WebSocketPolicy}.
5961
*/
6062
public JettyRequestUpgradeStrategy() {
61-
this.factory = new WebSocketServerFactory();
63+
this(new WebSocketServerFactory());
64+
}
65+
66+
/**
67+
* A constructor accepting a {@link WebSocketServerFactory}. This may be useful for
68+
* modifying the factory's {@link WebSocketPolicy} via
69+
* {@link WebSocketServerFactory#getPolicy()}.
70+
*/
71+
public JettyRequestUpgradeStrategy(WebSocketServerFactory factory) {
6272
this.factory.setCreator(new WebSocketCreator() {
6373
@Override
6474
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {

0 commit comments

Comments
 (0)