@ -16,14 +16,14 @@
package org.springframework.web.socket.server.standard ;
import java.io.IOException ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.Method ;
import java.net.URI ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;
import java.util.Map ;
import java.util.Random ;
import javax.servlet.ServletException ;
import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse ;
import javax.websocket.DeploymentException ;
@ -32,16 +32,19 @@ import javax.websocket.Extension;
import javax.websocket.WebSocketContainer ;
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.TyrusEndpointWrapper ;
import org.glassfish.tyrus.core.TyrusUpgradeResponse ;
import org.glassfish.tyrus.core.TyrusWebSocketEngine ;
import org.glassfish.tyrus.core.Utils ;
import org.glassfish.tyrus.core.Version ;
import org.glassfish.tyrus.core.cluster.ClusterContext ;
import org.glassfish.tyrus.core.monitoring.EndpointEventListener ;
import org.glassfish.tyrus.server.TyrusServerContainer ;
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler ;
import org.glassfish.tyrus.spi.SPIEndpoint ;
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.spi.WebSocketEngine.UpgradeInfo ;
import org.glassfish.tyrus.spi.Writer ;
import org.springframework.http.HttpHeaders ;
import org.springframework.http.server.ServerHttpRequest ;
import org.springframework.http.server.ServerHttpResponse ;
@ -51,7 +54,7 @@ import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.server.HandshakeFailureException ;
/ * *
* { @code RequestUpgradeStrategy } that provides support for GlassFish 4 and beyond .
* A WebSocket request upgrade strategy for GlassFish 4 . 0 . 1 and beyond .
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
@ -60,43 +63,31 @@ import org.springframework.web.socket.server.HandshakeFailureException;
* /
public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
private final static Random random = new Random ( ) ;
private static final Random random = new Random ( ) ;
private static final Constructor < ? > tyrusConnectionConstructor ;
private static final Constructor < ? > tyrusEndpointConstructor ;
private static final Constructor < ? > tyrusServletWriterConstructor ;
private static final Method endpointRegistrationMethod ;
static {
ClassLoader cl = GlassFishRequestUpgradeStrategy . class . getClassLoader ( ) ;
try {
// Tyrus ConnectionImpl is package-visible only
Class < ? > tyrusConnectionClass = cl . loadClass ( "org.glassfish.tyrus.servlet.ConnectionImpl" ) ;
tyrusConnectionConstructor = tyrusConnectionClass . getDeclaredConstructor (
TyrusHttpUpgradeHandler . class , HttpServletResponse . class ) ;
ReflectionUtils . makeAccessible ( tyrusConnectionConstructor ) ;
// TyrusEndpoint package location differs between GlassFish 4.0.0 and 4.0.1
Class < ? > tyrusEndpointClass ;
try {
tyrusEndpointClass = cl . loadClass ( "org.glassfish.tyrus.core.TyrusEndpoint" ) ;
}
catch ( ClassNotFoundException ex ) {
try {
tyrusEndpointClass = cl . loadClass ( "org.glassfish.tyrus.server.TyrusEndpoint" ) ;
}
catch ( ClassNotFoundException ex2 ) {
// Propagate original exception for newer version of the class
throw ex ;
}
}
tyrusEndpointConstructor = tyrusEndpointClass . getConstructor ( SPIEndpoint . class ) ;
ClassLoader classLoader = GlassFishRequestUpgradeStrategy . class . getClassLoader ( ) ;
Class < ? > type = classLoader . loadClass ( "org.glassfish.tyrus.servlet.TyrusServletWriter" ) ;
tyrusServletWriterConstructor = type . getDeclaredConstructor ( TyrusHttpUpgradeHandler . class ) ;
ReflectionUtils . makeAccessible ( tyrusServletWriterConstructor ) ;
Class < ? > endpointType = TyrusEndpointWrapper . class ;
endpointRegistrationMethod = TyrusWebSocketEngine . class . getDeclaredMethod ( "register" , endpointType ) ;
ReflectionUtils . makeAccessible ( endpointRegistrationMethod ) ;
}
catch ( Exception ex ) {
throw new IllegalStateException ( "No compatible Tyrus version found" , ex ) ;
}
}
private final ComponentProviderService componentProviderService = ComponentProviderService . create ( ) ;
@Override
public String [ ] getSupportedVersions ( ) {
@ -114,99 +105,97 @@ public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStra
@Override
public void upgradeInternal ( ServerHttpRequest request , ServerHttpResponse response ,
String selectedProtocol , List < Extension > selectedExtensions ,
Endpoint endpoint ) throws HandshakeFailureException {
String subProtocol , List < Extension > extensions , Endpoint endpoint ) throws HandshakeFailureException {
HttpServletRequest servletRequest = getHttpServletRequest ( request ) ;
HttpServletResponse servletResponse = getHttpServletResponse ( response ) ;
WebSocketApplication webSocketApplication = createTyrusEndpoint ( endpoint , selectedProtocol , selectedExtensions ) ;
WebSocketEngine webSocketEngine = WebSocketEngine . getEngine ( ) ;
TyrusServerContainer serverContainer = ( TyrusServerContainer ) getContainer ( servletRequest ) ;
TyrusWebSocketEngine engine = ( TyrusWebSocketEngine ) serverContainer . getWebSocketEngine ( ) ;
TyrusEndpointWrapper tyrusEndpoint = null ;
try {
webSocketEngine . register ( webSocketApplication ) ;
}
catch ( DeploymentException ex ) {
throw new HandshakeFailureException ( "Failed to configure endpoint in GlassFish" , ex ) ;
}
try {
performUpgrade ( servletRequest , servletResponse , request . getHeaders ( ) , webSocketApplication ) ;
tyrusEndpoint = createTyrusEndpoint ( endpoint , subProtocol , extensions , serverContainer ) ;
endpointRegistrationMethod . invoke ( engine , tyrusEndpoint ) ;
String endpointPath = tyrusEndpoint . getEndpointPath ( ) ;
HttpHeaders headers = request . getHeaders ( ) ;
RequestContext requestContext = createRequestContext ( servletRequest , endpointPath , headers ) ;
TyrusUpgradeResponse upgradeResponse = new TyrusUpgradeResponse ( ) ;
UpgradeInfo upgradeInfo = engine . upgrade ( requestContext , upgradeResponse ) ;
switch ( upgradeInfo . getStatus ( ) ) {
case SUCCESS :
TyrusHttpUpgradeHandler handler = servletRequest . upgrade ( TyrusHttpUpgradeHandler . class ) ;
Writer servletWriter = createTyrusServletWriter ( handler ) ;
handler . preInit ( upgradeInfo , servletWriter , servletRequest . getUserPrincipal ( ) ! = null ) ;
servletResponse . setStatus ( upgradeResponse . getStatus ( ) ) ;
for ( Map . Entry < String , List < String > > entry : upgradeResponse . getHeaders ( ) . entrySet ( ) ) {
servletResponse . addHeader ( entry . getKey ( ) , Utils . getHeaderFromList ( entry . getValue ( ) ) ) ;
}
servletResponse . flushBuffer ( ) ;
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "Successful upgrade uri=" + servletRequest . getRequestURI ( ) +
", response headers=" + upgradeResponse . getHeaders ( ) ) ;
}
break ;
case HANDSHAKE_FAILED :
// Should never happen
throw new HandshakeFailureException ( "Unexpected handshake failure: " + request . getURI ( ) ) ;
case NOT_APPLICABLE :
// Should never happen
throw new HandshakeFailureException ( "Unexpected handshake mapping failure: " + request . getURI ( ) ) ;
}
}
catch ( IOException ex ) {
throw new HandshakeFailureException (
"Response update failed during upgrade to WebSocket, uri=" + request . getURI ( ) , ex ) ;
catch ( Exception ex ) {
throw new HandshakeFailureException ( "Error during handshake: " + 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 ex ) {
throw new HandshakeFailureException ( "Unable to create TyrusHttpUpgradeHandler" , ex ) ;
}
Connection connection = createConnection ( upgradeHandler , response ) ;
RequestContext requestContext = RequestContext . Builder . create ( ) .
requestURI ( URI . create ( wsApp . getPath ( ) ) ) . requestPath ( wsApp . getPath ( ) ) .
userPrincipal ( request . getUserPrincipal ( ) ) .
connection ( connection ) . secure ( request . isSecure ( ) ) . build ( ) ;
for ( String header : headers . keySet ( ) ) {
requestContext . getHeaders ( ) . put ( header , headers . get ( header ) ) ;
if ( tyrusEndpoint ! = null ) {
engine . unregister ( tyrusEndpoint ) ;
}
}
boolean upgraded = WebSocketEngine . getEngine ( ) . upgrade ( connection , requestContext ,
new WebSocketEngine . WebSocketHolderListener ( ) {
@Override
public void onWebSocketHolder ( WebSocketEngine . WebSocketHolder webSocketHolder ) {
upgradeHandler . setWebSocketHolder ( webSocketHolder ) ;
}
} ) ;
// GlassFish bug ?? (see same line in TyrusServletFilter.doFilter)
response . flushBuffer ( ) ;
return upgraded ;
}
private WebSocketApplication createTyrusEndpoint ( Endpoint endpoint , String selectedP rotocol,
List < Extension > selectedExtensions ) {
private TyrusEndpointWrapper createTyrusEndpoint ( Endpoint endpoint , String protocol ,
List < Extension > extensions , WebSocketContainer container ) throws DeploymentException {
// Shouldn't matter for processing but must be unique
String endpointPath = "/" + random . nextLong ( ) ;
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration ( endpointPath , endpoint ) ;
endpointConfig . setSubprotocols ( Arrays . asList ( selectedProtocol ) ) ;
endpointConfig . setExtensions ( selectedExtensions ) ;
return createTyrusEndpoint ( new EndpointWrapper ( endpoint , endpointConfig ,
ComponentProviderService . create ( ) , null , "/" , new ErrorCollector ( ) ,
endpointConfig . getConfigurator ( ) ) ) ;
endpointConfig . setSubprotocols ( Arrays . asList ( protocol ) ) ;
endpointConfig . setExtensions ( extensions ) ;
TyrusEndpointWrapper . SessionListener sessionListener = new TyrusEndpointWrapper . SessionListener ( ) { } ;
ClusterContext clusterContext = null ;
EndpointEventListener eventListener = EndpointEventListener . NO_OP ;
return new TyrusEndpointWrapper ( endpoint , endpointConfig , this . componentProviderService ,
container , "/" , endpointConfig . getConfigurator ( ) , sessionListener , clusterContext , eventListener ) ;
}
private Connection createConnection ( TyrusHttpUpgradeHandler handler , HttpServletResponse response ) {
try {
return ( Connection ) tyrusConnectionConstructor . newInstance ( handler , response ) ;
}
catch ( Exception ex ) {
throw new IllegalStateException ( "Failed to create GlassFish connection" , ex ) ;
private RequestContext createRequestContext ( HttpServletRequest request , String endpointPath , HttpHeaders headers ) {
RequestContext context =
RequestContext . Builder . create ( )
. requestURI ( URI . create ( endpointPath ) )
. userPrincipal ( request . getUserPrincipal ( ) )
. secure ( request . isSecure ( ) )
. remoteAddr ( request . getRemoteAddr ( ) )
. build ( ) ;
for ( String header : headers . keySet ( ) ) {
context . getHeaders ( ) . put ( header , headers . get ( header ) ) ;
}
return context ;
}
protected WebSocketApplication createTyrusEndpoint ( EndpointWrapper endpoint ) {
private Writer createTyrusServletWriter ( TyrusHttpUpgradeHandler handler ) {
try {
return ( WebSocketApplication ) tyrusEndpoint Constructor . newInstance ( endpoint ) ;
return ( Writer ) tyrusServletWriter Constructor . newInstance ( handler ) ;
}
catch ( Exception ex ) {
throw new IllegalStateException ( "Failed to create GlassFish endpoint ", ex ) ;
throw new HandshakeFailureException ( "Failed to instantiate TyrusServletWriter ", ex ) ;
}
}