diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java index 2cbbb90ed2..1b2af53107 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.reflect.Method; import java.security.Principal; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -72,7 +73,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv private WebSocketPolicy policy; @Nullable - private WebSocketServerFactory factory; + private volatile WebSocketServerFactory factory; @Nullable private ServletContext servletContext; @@ -122,17 +123,20 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv if (!isRunning()) { this.running = true; try { - if (this.factory == null) { - this.factory = new WebSocketServerFactory(this.servletContext, this.policy); + WebSocketServerFactory factory = this.factory; + if (factory == null) { + Assert.state(this.servletContext != null, "No ServletContext set"); + factory = new WebSocketServerFactory(this.servletContext, this.policy); + this.factory = factory; } - this.factory.setCreator((request, response) -> { + factory.setCreator((request, response) -> { WebSocketHandlerContainer container = containerHolder.get(); Assert.state(container != null, "Expected WebSocketHandlerContainer"); response.setAcceptedSubProtocol(container.getSelectedProtocol()); response.setExtensions(container.getExtensionConfigs()); return container.getHandler(); }); - this.factory.start(); + factory.start(); } catch (Throwable ex) { throw new IllegalStateException("Unable to start Jetty WebSocketServerFactory", ex); @@ -144,9 +148,10 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv public void stop() { if (isRunning()) { this.running = false; - if (this.factory != null) { + WebSocketServerFactory factory = this.factory; + if (factory != null) { try { - this.factory.stop(); + factory.stop(); } catch (Throwable ex) { throw new IllegalStateException("Unable to stop Jetty WebSocketServerFactory", ex); @@ -168,10 +173,12 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv @Override public List getSupportedExtensions(ServerHttpRequest request) { - if (this.supportedExtensions == null) { - this.supportedExtensions = buildWebSocketExtensions(); + List extensions = this.supportedExtensions; + if (extensions == null) { + extensions = buildWebSocketExtensions(); + this.supportedExtensions = extensions; } - return this.supportedExtensions; + return extensions; } private List buildWebSocketExtensions() { @@ -185,8 +192,10 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv @SuppressWarnings({"unchecked", "deprecation"}) private Set getExtensionNames() { + WebSocketServerFactory factory = this.factory; + Assert.state(factory != null, "No WebSocketServerFactory available"); try { - return this.factory.getAvailableExtensionNames(); + return factory.getAvailableExtensionNames(); } catch (IncompatibleClassChangeError ex) { // Fallback for versions prior to 9.4.21: @@ -194,7 +203,8 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv // 9.4.21.v20190926: ExtensionFactory (interface -> abstract class) + deprecated Class clazz = org.eclipse.jetty.websocket.api.extensions.ExtensionFactory.class; Method method = ClassUtils.getMethod(clazz, "getExtensionNames"); - return (Set) ReflectionUtils.invokeMethod(method, this.factory.getExtensionFactory()); + Set result = (Set) ReflectionUtils.invokeMethod(method, factory.getExtensionFactory()); + return (result != null ? result : Collections.emptySet()); } } @@ -209,7 +219,9 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required"); HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse(); - Assert.isTrue(this.factory.isUpgradeRequest(servletRequest, servletResponse), "Not a WebSocket handshake"); + WebSocketServerFactory factory = this.factory; + Assert.state(factory != null, "No WebSocketServerFactory available"); + Assert.isTrue(factory.isUpgradeRequest(servletRequest, servletResponse), "Not a WebSocket handshake"); JettyWebSocketSession session = new JettyWebSocketSession(attributes, user); JettyWebSocketHandlerAdapter handlerAdapter = new JettyWebSocketHandlerAdapter(wsHandler, session); @@ -219,7 +231,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv try { containerHolder.set(container); - this.factory.acceptWebSocket(servletRequest, servletResponse); + factory.acceptWebSocket(servletRequest, servletResponse); } catch (IOException ex) { throw new HandshakeFailureException( @@ -235,12 +247,13 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv private final JettyWebSocketHandlerAdapter handler; + @Nullable private final String selectedProtocol; private final List extensionConfigs; - public WebSocketHandlerContainer( - JettyWebSocketHandlerAdapter handler, String protocol, List extensions) { + public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter handler, + @Nullable String protocol, List extensions) { this.handler = handler; this.selectedProtocol = protocol; @@ -259,6 +272,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Serv return this.handler; } + @Nullable public String getSelectedProtocol() { return this.selectedProtocol; }