Merge pull request #364 from mikesir87/ws-glassfish4-support

* mikesir87-ws-glassfish4-support:
  Added websocket upgrade support for GlassFish 4.0
master
Rossen Stoyanchev 11 years ago
commit f3611e767e
  1. 8
      spring-websocket/src/main/java/org/springframework/web/socket/server/DefaultHandshakeHandler.java
  2. 167
      spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractGlassFishRequestUpgradeStrategy.java
  3. 50
      spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFish40RequestUpgradeStrategy.java
  4. 136
      spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java

@ -62,6 +62,9 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
private static final boolean glassFishWsPresent = ClassUtils.isPresent(
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", HandshakeHandler.class.getClassLoader());
private static final boolean glassFish40WsPresent = ClassUtils.isPresent(
"org.glassfish.tyrus.server.TyrusEndpoint", HandshakeHandler.class.getClassLoader());
private final RequestUpgradeStrategy requestUpgradeStrategy;
@ -88,8 +91,13 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
className = "org.springframework.web.socket.server.support.JettyRequestUpgradeStrategy";
}
else if (glassFishWsPresent) {
if (glassFish40WsPresent) {
className = "org.springframework.web.socket.server.support.GlassFish40RequestUpgradeStrategy";
}
else {
className = "org.springframework.web.socket.server.support.GlassFishRequestUpgradeStrategy";
}
}
else {
throw new IllegalStateException("No suitable " + RequestUpgradeStrategy.class.getSimpleName());
}

@ -0,0 +1,167 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.support;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.Arrays;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import org.glassfish.tyrus.core.ComponentProviderService;
import org.glassfish.tyrus.core.EndpointWrapper;
import org.glassfish.tyrus.core.ErrorCollector;
import org.glassfish.tyrus.core.RequestContext;
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler;
import org.glassfish.tyrus.websockets.Connection;
import org.glassfish.tyrus.websockets.Version;
import org.glassfish.tyrus.websockets.WebSocketApplication;
import org.glassfish.tyrus.websockets.WebSocketEngine;
import org.glassfish.tyrus.websockets.WebSocketEngine.WebSocketHolderListener;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
import org.springframework.web.socket.server.endpoint.ServletServerContainerFactoryBean;
/**
* GlassFish support for upgrading a request during a WebSocket handshake. To modify
* properties of the underlying {@link javax.websocket.server.ServerContainer} you can use
* {@link ServletServerContainerFactoryBean} in XML configuration or if using Java
* configuration, access the container instance through the
* "javax.websocket.server.ServerContainer" ServletContext attribute.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractGlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
private final static Random random = new Random();
@Override
public String[] getSupportedVersions() {
return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions());
}
@Override
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException {
Assert.isTrue(request instanceof ServletServerHttpRequest);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
Assert.isTrue(response instanceof ServletServerHttpResponse);
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
WebSocketApplication webSocketApplication = createTyrusEndpoint(servletRequest, endpoint, selectedProtocol);
WebSocketEngine webSocketEngine = WebSocketEngine.getEngine();
try {
webSocketEngine.register(webSocketApplication);
}
catch (DeploymentException ex) {
throw new HandshakeFailureException("Failed to configure endpoint in GlassFish", ex);
}
try {
performUpgrade(servletRequest, servletResponse, request.getHeaders(), webSocketApplication);
}
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
finally {
webSocketEngine.unregister(webSocketApplication);
}
}
private boolean performUpgrade(HttpServletRequest request, HttpServletResponse response,
HttpHeaders headers, WebSocketApplication wsApp) throws IOException {
final TyrusHttpUpgradeHandler upgradeHandler;
try {
upgradeHandler = request.upgrade(TyrusHttpUpgradeHandler.class);
}
catch (ServletException e) {
throw new HandshakeFailureException("Unable to create UpgardeHandler", e);
}
Connection connection = createConnection(upgradeHandler, response);
RequestContext wsRequest = RequestContext.Builder.create()
.requestURI(URI.create(wsApp.getPath())).requestPath(wsApp.getPath())
.connection(connection).secure(request.isSecure()).build();
for (String header : headers.keySet()) {
wsRequest.getHeaders().put(header, headers.get(header));
}
return WebSocketEngine.getEngine().upgrade(connection, wsRequest, new WebSocketHolderListener() {
@Override
public void onWebSocketHolder(WebSocketEngine.WebSocketHolder webSocketHolder) {
upgradeHandler.setWebSocketHolder(webSocketHolder);
}
});
}
private WebSocketApplication createTyrusEndpoint(HttpServletRequest request,
Endpoint endpoint, String selectedProtocol) {
// shouldn't matter for processing but must be unique
String endpointPath = "/" + random.nextLong();
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol));
return createTyrusEndpoint(new EndpointWrapper(endpoint, endpointConfig,
ComponentProviderService.create(), null, "/", new ErrorCollector(),
endpointConfig.getConfigurator()));
}
/**
* Create the actual TyrusEndpoint
* @param endpoint The WebSocket endpoint
* @return The configured WebSocketApplication, most likely {@code TyrusEndpoint}
*/
abstract protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint);
private Connection createConnection(TyrusHttpUpgradeHandler handler, HttpServletResponse response) {
try {
String name = "org.glassfish.tyrus.servlet.ConnectionImpl";
Class<?> clazz = ClassUtils.forName(name, GlassFishRequestUpgradeStrategy.class.getClassLoader());
Constructor<?> constructor = clazz.getDeclaredConstructor(TyrusHttpUpgradeHandler.class, HttpServletResponse.class);
ReflectionUtils.makeAccessible(constructor);
return (Connection) constructor.newInstance(handler, response);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to instantiate GlassFish connection", ex);
}
}
}

@ -0,0 +1,50 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.support;
import java.lang.reflect.Constructor;
import org.glassfish.tyrus.core.EndpointWrapper;
import org.glassfish.tyrus.spi.SPIEndpoint;
import org.glassfish.tyrus.websockets.WebSocketApplication;
import org.springframework.util.ClassUtils;
/**
* Extension of the {@link AbstractGlassFishRequestUpgradeStrategy} that provides support
* for only GlassFish 4.0.
*
* @author Rossen Stoyanchev
* @author Michael Irwin
* @since 4.0
*/
public class GlassFish40RequestUpgradeStrategy extends AbstractGlassFishRequestUpgradeStrategy {
protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint) {
try {
String name = "org.glassfish.tyrus.server.TyrusEndpoint";
Class<?> clazz = ClassUtils.forName(name, this.getClass().getClassLoader());
Constructor<?> constructor = clazz.getConstructor(SPIEndpoint.class);
return (WebSocketApplication) constructor.newInstance(endpoint);
}
catch (ReflectiveOperationException exception) {
throw new RuntimeException(exception);
}
}
}

@ -16,148 +16,24 @@
package org.springframework.web.socket.server.support;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.Arrays;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import org.glassfish.tyrus.core.ComponentProviderService;
import org.glassfish.tyrus.core.EndpointWrapper;
import org.glassfish.tyrus.core.ErrorCollector;
import org.glassfish.tyrus.core.RequestContext;
import org.glassfish.tyrus.core.TyrusEndpoint;
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler;
import org.glassfish.tyrus.websockets.Connection;
import org.glassfish.tyrus.websockets.Version;
import org.glassfish.tyrus.websockets.WebSocketApplication;
import org.glassfish.tyrus.websockets.WebSocketEngine;
import org.glassfish.tyrus.websockets.WebSocketEngine.WebSocketHolderListener;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
import org.springframework.web.socket.server.endpoint.ServletServerContainerFactoryBean;
/**
* GlassFish support for upgrading a request during a WebSocket handshake. To modify
* properties of the underlying {@link javax.websocket.server.ServerContainer} you can use
* {@link ServletServerContainerFactoryBean} in XML configuration or if using Java
* configuration, access the container instance through the
* "javax.websocket.server.ServerContainer" ServletContext attribute.
* Extension of the {@link AbstractGlassFishRequestUpgradeStrategy} that provides support
* for GlassFish 4.0.1 and beyond.
*
* @author Rossen Stoyanchev
* @author Michael Irwin
* @since 4.0
*/
public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
private final static Random random = new Random();
@Override
public String[] getSupportedVersions() {
return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions());
}
public class GlassFishRequestUpgradeStrategy extends AbstractGlassFishRequestUpgradeStrategy {
@Override
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException {
Assert.isTrue(request instanceof ServletServerHttpRequest);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
Assert.isTrue(response instanceof ServletServerHttpResponse);
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
WebSocketApplication webSocketApplication = createTyrusEndpoint(servletRequest, endpoint, selectedProtocol);
WebSocketEngine webSocketEngine = WebSocketEngine.getEngine();
try {
webSocketEngine.register(webSocketApplication);
}
catch (DeploymentException ex) {
throw new HandshakeFailureException("Failed to configure endpoint in GlassFish", ex);
}
try {
performUpgrade(servletRequest, servletResponse, request.getHeaders(), webSocketApplication);
}
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
finally {
webSocketEngine.unregister(webSocketApplication);
}
}
private boolean performUpgrade(HttpServletRequest request, HttpServletResponse response,
HttpHeaders headers, WebSocketApplication wsApp) throws IOException {
final TyrusHttpUpgradeHandler upgradeHandler;
try {
upgradeHandler = request.upgrade(TyrusHttpUpgradeHandler.class);
}
catch (ServletException e) {
throw new HandshakeFailureException("Unable to create UpgardeHandler", e);
}
Connection connection = createConnection(upgradeHandler, response);
RequestContext wsRequest = RequestContext.Builder.create()
.requestURI(URI.create(wsApp.getPath())).requestPath(wsApp.getPath())
.connection(connection).secure(request.isSecure()).build();
for (String header : headers.keySet()) {
wsRequest.getHeaders().put(header, headers.get(header));
}
return WebSocketEngine.getEngine().upgrade(connection, wsRequest, new WebSocketHolderListener() {
@Override
public void onWebSocketHolder(WebSocketEngine.WebSocketHolder webSocketHolder) {
upgradeHandler.setWebSocketHolder(webSocketHolder);
}
});
}
private WebSocketApplication createTyrusEndpoint(HttpServletRequest request,
Endpoint endpoint, String selectedProtocol) {
// shouldn't matter for processing but must be unique
String endpointPath = "/" + random.nextLong();
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol));
return new TyrusEndpoint(new EndpointWrapper(endpoint, endpointConfig,
ComponentProviderService.create(), null, "/", new ErrorCollector(),
endpointConfig.getConfigurator()));
}
private Connection createConnection(TyrusHttpUpgradeHandler handler, HttpServletResponse response) {
try {
String name = "org.glassfish.tyrus.servlet.ConnectionImpl";
Class<?> clazz = ClassUtils.forName(name, GlassFishRequestUpgradeStrategy.class.getClassLoader());
Constructor<?> constructor = clazz.getDeclaredConstructor(TyrusHttpUpgradeHandler.class, HttpServletResponse.class);
ReflectionUtils.makeAccessible(constructor);
return (Connection) constructor.newInstance(handler, response);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to instantiate GlassFish connection", ex);
}
protected WebSocketApplication createTyrusEndpoint(EndpointWrapper endpoint) {
return new TyrusEndpoint(endpoint);
}
}

Loading…
Cancel
Save