diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/DefaultHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/DefaultHandshakeHandler.java index b5b26001b0..6c0bfa0e84 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/DefaultHandshakeHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/DefaultHandshakeHandler.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,7 +91,12 @@ public class DefaultHandshakeHandler implements HandshakeHandler { className = "org.springframework.web.socket.server.support.JettyRequestUpgradeStrategy"; } else if (glassFishWsPresent) { - className = "org.springframework.web.socket.server.support.GlassFishRequestUpgradeStrategy"; + 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()); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractGlassFishRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractGlassFishRequestUpgradeStrategy.java new file mode 100644 index 0000000000..6f0b783887 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractGlassFishRequestUpgradeStrategy.java @@ -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); + } + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFish40RequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFish40RequestUpgradeStrategy.java new file mode 100644 index 0000000000..ed2508e538 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFish40RequestUpgradeStrategy.java @@ -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); + } + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java index bd6cae902b..9756d50212 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/GlassFishRequestUpgradeStrategy.java @@ -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); } }