Skip to content

Commit f3611e7

Browse files
committed
Merge pull request #364 from mikesir87/ws-glassfish4-support
* mikesir87-ws-glassfish4-support: Added websocket upgrade support for GlassFish 4.0
2 parents c06ea3b + f1e929f commit f3611e7

File tree

4 files changed

+232
-131
lines changed

4 files changed

+232
-131
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
6262
private static final boolean glassFishWsPresent = ClassUtils.isPresent(
6363
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", HandshakeHandler.class.getClassLoader());
6464

65+
private static final boolean glassFish40WsPresent = ClassUtils.isPresent(
66+
"org.glassfish.tyrus.server.TyrusEndpoint", HandshakeHandler.class.getClassLoader());
67+
6568

6669
private final RequestUpgradeStrategy requestUpgradeStrategy;
6770

@@ -88,7 +91,12 @@ else if (jettyWsPresent) {
8891
className = "org.springframework.web.socket.server.support.JettyRequestUpgradeStrategy";
8992
}
9093
else if (glassFishWsPresent) {
91-
className = "org.springframework.web.socket.server.support.GlassFishRequestUpgradeStrategy";
94+
if (glassFish40WsPresent) {
95+
className = "org.springframework.web.socket.server.support.GlassFish40RequestUpgradeStrategy";
96+
}
97+
else {
98+
className = "org.springframework.web.socket.server.support.GlassFishRequestUpgradeStrategy";
99+
}
92100
}
93101
else {
94102
throw new IllegalStateException("No suitable " + RequestUpgradeStrategy.class.getSimpleName());
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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.web.socket.server.support;
18+
19+
import java.io.IOException;
20+
import java.lang.reflect.Constructor;
21+
import java.net.URI;
22+
import java.util.Arrays;
23+
import java.util.Random;
24+
25+
import javax.servlet.ServletException;
26+
import javax.servlet.http.HttpServletRequest;
27+
import javax.servlet.http.HttpServletResponse;
28+
import javax.websocket.DeploymentException;
29+
import javax.websocket.Endpoint;
30+
31+
import org.glassfish.tyrus.core.ComponentProviderService;
32+
import org.glassfish.tyrus.core.EndpointWrapper;
33+
import org.glassfish.tyrus.core.ErrorCollector;
34+
import org.glassfish.tyrus.core.RequestContext;
35+
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler;
36+
import org.glassfish.tyrus.websockets.Connection;
37+
import org.glassfish.tyrus.websockets.Version;
38+
import org.glassfish.tyrus.websockets.WebSocketApplication;
39+
import org.glassfish.tyrus.websockets.WebSocketEngine;
40+
import org.glassfish.tyrus.websockets.WebSocketEngine.WebSocketHolderListener;
41+
import org.springframework.http.HttpHeaders;
42+
import org.springframework.http.server.ServerHttpRequest;
43+
import org.springframework.http.server.ServerHttpResponse;
44+
import org.springframework.http.server.ServletServerHttpRequest;
45+
import org.springframework.http.server.ServletServerHttpResponse;
46+
import org.springframework.util.Assert;
47+
import org.springframework.util.ClassUtils;
48+
import org.springframework.util.ReflectionUtils;
49+
import org.springframework.util.StringUtils;
50+
import org.springframework.web.socket.server.HandshakeFailureException;
51+
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
52+
import org.springframework.web.socket.server.endpoint.ServletServerContainerFactoryBean;
53+
54+
55+
/**
56+
* GlassFish support for upgrading a request during a WebSocket handshake. To modify
57+
* properties of the underlying {@link javax.websocket.server.ServerContainer} you can use
58+
* {@link ServletServerContainerFactoryBean} in XML configuration or if using Java
59+
* configuration, access the container instance through the
60+
* "javax.websocket.server.ServerContainer" ServletContext attribute.
61+
*
62+
* @author Rossen Stoyanchev
63+
* @since 4.0
64+
*/
65+
public abstract class AbstractGlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
66+
67+
private final static Random random = new Random();
68+
69+
@Override
70+
public String[] getSupportedVersions() {
71+
return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions());
72+
}
73+
74+
@Override
75+
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
76+
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException {
77+
78+
Assert.isTrue(request instanceof ServletServerHttpRequest);
79+
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
80+
81+
Assert.isTrue(response instanceof ServletServerHttpResponse);
82+
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
83+
84+
WebSocketApplication webSocketApplication = createTyrusEndpoint(servletRequest, endpoint, selectedProtocol);
85+
WebSocketEngine webSocketEngine = WebSocketEngine.getEngine();
86+
87+
try {
88+
webSocketEngine.register(webSocketApplication);
89+
}
90+
catch (DeploymentException ex) {
91+
throw new HandshakeFailureException("Failed to configure endpoint in GlassFish", ex);
92+
}
93+
94+
try {
95+
performUpgrade(servletRequest, servletResponse, request.getHeaders(), webSocketApplication);
96+
}
97+
catch (IOException ex) {
98+
throw new HandshakeFailureException(
99+
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
100+
}
101+
finally {
102+
webSocketEngine.unregister(webSocketApplication);
103+
}
104+
}
105+
106+
private boolean performUpgrade(HttpServletRequest request, HttpServletResponse response,
107+
HttpHeaders headers, WebSocketApplication wsApp) throws IOException {
108+
109+
final TyrusHttpUpgradeHandler upgradeHandler;
110+
try {
111+
upgradeHandler = request.upgrade(TyrusHttpUpgradeHandler.class);
112+
}
113+
catch (ServletException e) {
114+
throw new HandshakeFailureException("Unable to create UpgardeHandler", e);
115+
}
116+
117+
Connection connection = createConnection(upgradeHandler, response);
118+
119+
RequestContext wsRequest = RequestContext.Builder.create()
120+
.requestURI(URI.create(wsApp.getPath())).requestPath(wsApp.getPath())
121+
.connection(connection).secure(request.isSecure()).build();
122+
123+
for (String header : headers.keySet()) {
124+
wsRequest.getHeaders().put(header, headers.get(header));
125+
}
126+
127+
return WebSocketEngine.getEngine().upgrade(connection, wsRequest, new WebSocketHolderListener() {
128+
@Override
129+
public void onWebSocketHolder(WebSocketEngine.WebSocketHolder webSocketHolder) {
130+
upgradeHandler.setWebSocketHolder(webSocketHolder);
131+
}
132+
});
133+
}
134+
135+
private WebSocketApplication createTyrusEndpoint(HttpServletRequest request,
136+
Endpoint endpoint, String selectedProtocol) {
137+
138+
// shouldn't matter for processing but must be unique
139+
String endpointPath = "/" + random.nextLong();
140+
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
141+
endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol));
142+
return createTyrusEndpoint(new EndpointWrapper(endpoint, endpointConfig,
143+
ComponentProviderService.create(), null, "/", new ErrorCollector(),
144+
endpointConfig.getConfigurator()));
145+
}
146+
147+
/**
148+
* Create the actual TyrusEndpoint
149+
* @param endpoint The WebSocket endpoint
150+
* @return The configured WebSocketApplication, most likely {@code TyrusEndpoint}
151+
*/
152+
abstract protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint);
153+
154+
private Connection createConnection(TyrusHttpUpgradeHandler handler, HttpServletResponse response) {
155+
try {
156+
String name = "org.glassfish.tyrus.servlet.ConnectionImpl";
157+
Class<?> clazz = ClassUtils.forName(name, GlassFishRequestUpgradeStrategy.class.getClassLoader());
158+
Constructor<?> constructor = clazz.getDeclaredConstructor(TyrusHttpUpgradeHandler.class, HttpServletResponse.class);
159+
ReflectionUtils.makeAccessible(constructor);
160+
return (Connection) constructor.newInstance(handler, response);
161+
}
162+
catch (Exception ex) {
163+
throw new IllegalStateException("Failed to instantiate GlassFish connection", ex);
164+
}
165+
}
166+
167+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.web.socket.server.support;
18+
19+
import java.lang.reflect.Constructor;
20+
21+
import org.glassfish.tyrus.core.EndpointWrapper;
22+
import org.glassfish.tyrus.spi.SPIEndpoint;
23+
import org.glassfish.tyrus.websockets.WebSocketApplication;
24+
import org.springframework.util.ClassUtils;
25+
26+
27+
/**
28+
* Extension of the {@link AbstractGlassFishRequestUpgradeStrategy} that provides support
29+
* for only GlassFish 4.0.
30+
*
31+
* @author Rossen Stoyanchev
32+
* @author Michael Irwin
33+
* @since 4.0
34+
*/
35+
public class GlassFish40RequestUpgradeStrategy extends AbstractGlassFishRequestUpgradeStrategy {
36+
37+
38+
protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint) {
39+
try {
40+
String name = "org.glassfish.tyrus.server.TyrusEndpoint";
41+
Class<?> clazz = ClassUtils.forName(name, this.getClass().getClassLoader());
42+
Constructor<?> constructor = clazz.getConstructor(SPIEndpoint.class);
43+
return (WebSocketApplication) constructor.newInstance(endpoint);
44+
}
45+
catch (ReflectiveOperationException exception) {
46+
throw new RuntimeException(exception);
47+
}
48+
}
49+
50+
}

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

Lines changed: 6 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -16,148 +16,24 @@
1616

1717
package org.springframework.web.socket.server.support;
1818

19-
import java.io.IOException;
20-
import java.lang.reflect.Constructor;
21-
import java.net.URI;
22-
import java.util.Arrays;
23-
import java.util.Random;
24-
25-
import javax.servlet.ServletException;
26-
import javax.servlet.http.HttpServletRequest;
27-
import javax.servlet.http.HttpServletResponse;
28-
import javax.websocket.DeploymentException;
29-
import javax.websocket.Endpoint;
30-
31-
import org.glassfish.tyrus.core.ComponentProviderService;
3219
import org.glassfish.tyrus.core.EndpointWrapper;
33-
import org.glassfish.tyrus.core.ErrorCollector;
34-
import org.glassfish.tyrus.core.RequestContext;
3520
import org.glassfish.tyrus.core.TyrusEndpoint;
36-
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler;
37-
import org.glassfish.tyrus.websockets.Connection;
38-
import org.glassfish.tyrus.websockets.Version;
3921
import org.glassfish.tyrus.websockets.WebSocketApplication;
40-
import org.glassfish.tyrus.websockets.WebSocketEngine;
41-
import org.glassfish.tyrus.websockets.WebSocketEngine.WebSocketHolderListener;
42-
import org.springframework.http.HttpHeaders;
43-
import org.springframework.http.server.ServerHttpRequest;
44-
import org.springframework.http.server.ServerHttpResponse;
45-
import org.springframework.http.server.ServletServerHttpRequest;
46-
import org.springframework.http.server.ServletServerHttpResponse;
47-
import org.springframework.util.Assert;
48-
import org.springframework.util.ClassUtils;
49-
import org.springframework.util.ReflectionUtils;
50-
import org.springframework.util.StringUtils;
51-
import org.springframework.web.socket.server.HandshakeFailureException;
52-
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
53-
import org.springframework.web.socket.server.endpoint.ServletServerContainerFactoryBean;
5422

5523

5624
/**
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.
25+
* Extension of the {@link AbstractGlassFishRequestUpgradeStrategy} that provides support
26+
* for GlassFish 4.0.1 and beyond.
6227
*
6328
* @author Rossen Stoyanchev
29+
* @author Michael Irwin
6430
* @since 4.0
6531
*/
66-
public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
67-
68-
private final static Random random = new Random();
69-
70-
71-
@Override
72-
public String[] getSupportedVersions() {
73-
return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions());
74-
}
32+
public class GlassFishRequestUpgradeStrategy extends AbstractGlassFishRequestUpgradeStrategy {
7533

7634
@Override
77-
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
78-
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException {
79-
80-
Assert.isTrue(request instanceof ServletServerHttpRequest);
81-
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
82-
83-
Assert.isTrue(response instanceof ServletServerHttpResponse);
84-
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
85-
86-
WebSocketApplication webSocketApplication = createTyrusEndpoint(servletRequest, endpoint, selectedProtocol);
87-
WebSocketEngine webSocketEngine = WebSocketEngine.getEngine();
88-
89-
try {
90-
webSocketEngine.register(webSocketApplication);
91-
}
92-
catch (DeploymentException ex) {
93-
throw new HandshakeFailureException("Failed to configure endpoint in GlassFish", ex);
94-
}
95-
96-
try {
97-
performUpgrade(servletRequest, servletResponse, request.getHeaders(), webSocketApplication);
98-
}
99-
catch (IOException ex) {
100-
throw new HandshakeFailureException(
101-
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
102-
}
103-
finally {
104-
webSocketEngine.unregister(webSocketApplication);
105-
}
106-
}
107-
108-
private boolean performUpgrade(HttpServletRequest request, HttpServletResponse response,
109-
HttpHeaders headers, WebSocketApplication wsApp) throws IOException {
110-
111-
final TyrusHttpUpgradeHandler upgradeHandler;
112-
try {
113-
upgradeHandler = request.upgrade(TyrusHttpUpgradeHandler.class);
114-
}
115-
catch (ServletException e) {
116-
throw new HandshakeFailureException("Unable to create UpgardeHandler", e);
117-
}
118-
119-
Connection connection = createConnection(upgradeHandler, response);
120-
121-
RequestContext wsRequest = RequestContext.Builder.create()
122-
.requestURI(URI.create(wsApp.getPath())).requestPath(wsApp.getPath())
123-
.connection(connection).secure(request.isSecure()).build();
124-
125-
for (String header : headers.keySet()) {
126-
wsRequest.getHeaders().put(header, headers.get(header));
127-
}
128-
129-
return WebSocketEngine.getEngine().upgrade(connection, wsRequest, new WebSocketHolderListener() {
130-
@Override
131-
public void onWebSocketHolder(WebSocketEngine.WebSocketHolder webSocketHolder) {
132-
upgradeHandler.setWebSocketHolder(webSocketHolder);
133-
}
134-
});
135-
}
136-
137-
private WebSocketApplication createTyrusEndpoint(HttpServletRequest request,
138-
Endpoint endpoint, String selectedProtocol) {
139-
140-
// shouldn't matter for processing but must be unique
141-
String endpointPath = "/" + random.nextLong();
142-
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
143-
endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol));
144-
145-
return new TyrusEndpoint(new EndpointWrapper(endpoint, endpointConfig,
146-
ComponentProviderService.create(), null, "/", new ErrorCollector(),
147-
endpointConfig.getConfigurator()));
148-
}
149-
150-
private Connection createConnection(TyrusHttpUpgradeHandler handler, HttpServletResponse response) {
151-
try {
152-
String name = "org.glassfish.tyrus.servlet.ConnectionImpl";
153-
Class<?> clazz = ClassUtils.forName(name, GlassFishRequestUpgradeStrategy.class.getClassLoader());
154-
Constructor<?> constructor = clazz.getDeclaredConstructor(TyrusHttpUpgradeHandler.class, HttpServletResponse.class);
155-
ReflectionUtils.makeAccessible(constructor);
156-
return (Connection) constructor.newInstance(handler, response);
157-
}
158-
catch (Exception ex) {
159-
throw new IllegalStateException("Failed to instantiate GlassFish connection", ex);
160-
}
35+
protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint) {
36+
return new TyrusEndpoint(endpoint);
16137
}
16238

16339
}

0 commit comments

Comments
 (0)