From 70247d74d943e178d91e87f5dd2959a0b927bcbe Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 29 Nov 2016 15:46:03 -0700 Subject: [PATCH] Issue #1124 - Fixing up merge issues + Making WebSocketServletFactory always load a new WebSocketServerFactory + Making WebSocketServerFactory need a ServletContext to construct it, if appropriate (the WebSocketHandler approach doesn't use a ServletContext) + NativeWebSocketConfiguration is now a bean of ServerContainer + Removed WebSocketServletFactory.init(ServletContext) method + Renamed WebSocketServletFactory.init() to .start() + Renamed WebSocketServletFactory.cleanup() to .stop() + CDI & Websocket now works + Using a ServletContextListener now works + DecoderFactory and EncoderFactory now work --- .../websocket/jsr356/ClientContainer.java | 2 +- .../websocket/jsr356/DecoderFactory.java | 6 + .../jsr356/server/ServerContainer.java | 1 + .../jsr356/server/OnPartialTest.java | 2 + .../jsr356/server/SessionTrackingTest.java | 2 +- .../scopes/WebSocketContainerScope.java | 24 +- .../server/NativeWebSocketConfiguration.java | 6 +- ...eWebSocketServletContainerInitializer.java | 2 +- .../websocket/server/WebSocketHandler.java | 18 +- .../server/WebSocketServerFactory.java | 316 +++++++++--------- .../server/WebSocketUpgradeFilter.java | 10 +- .../WebSocketUpgradeHandlerWrapper.java | 9 +- .../websocket/server/InfoContextListener.java | 2 +- .../server/WebSocketUpgradeFilterTest.java | 2 +- .../websocket/servlet/WebSocketServlet.java | 18 +- .../servlet/WebSocketServletFactory.java | 93 +++--- 16 files changed, 267 insertions(+), 246 deletions(-) diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java index 2164bdded80..46235eaeaa2 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java @@ -190,7 +190,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont protected void doStart() throws Exception { super.doStart(); - + // Initialize the default decoder / encoder factories EmptyClientEndpointConfig empty = new EmptyClientEndpointConfig(); this.decoderFactory.init(empty); diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java index 38f7a7f84fe..92e90f1c450 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/DecoderFactory.java @@ -166,6 +166,12 @@ public class DecoderFactory implements Configurable { LOG.debug("init({})",config); } + + if(!containerScope.isRunning()) + { + throw new RuntimeException(containerScope.getClass().getName() + " is not running yet"); + } + // Instantiate all declared decoders for (DecoderMetadata metadata : metadatas) { diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java index bd81e04b921..ee782b30155 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java @@ -59,6 +59,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket. eventDriverFactory.addImplementation(new JsrServerEndpointImpl()); eventDriverFactory.addImplementation(new JsrServerExtendsEndpointImpl()); this.configuration.getFactory().addSessionFactory(new JsrSessionFactory(this)); + addBean(this.configuration); } public EndpointInstance newClientEndpointInstance(Object endpoint, ServerEndpointConfig config, String path) diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java index 6628a026ad4..f45be2e08f8 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnPartialTest.java @@ -85,6 +85,8 @@ public class OnPartialTest URI requestURI = URI.create("ws://localhost/" + id); DummyConnection connection = new DummyConnection(); ClientContainer container = new ClientContainer(); + container.start(); + @SuppressWarnings("resource") JsrSession session = new JsrSession(container,id,requestURI,driver,connection); session.setPolicy(policy); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java index ca0cd2b00f5..7dac2a71a70 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTrackingTest.java @@ -67,7 +67,7 @@ public class SessionTrackingTest serverContainer = WebSocketServerContainerInitializer.configureContext(servletContextHandler); serverContainer.addEndpoint(EchoSocket.class); - wsServerFactory = serverContainer.getBean(WebSocketServerFactory.class); + wsServerFactory = serverContainer.getWebSocketServerFactory(); server.start(); diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java index aacaf85ab22..416598b2385 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketContainerScope.java @@ -36,48 +36,54 @@ public interface WebSocketContainerScope * * @return the buffer pool (never null) */ - public ByteBufferPool getBufferPool(); + ByteBufferPool getBufferPool(); /** * Executor in use by the container. * * @return the Executor in use by the container. */ - public Executor getExecutor(); + Executor getExecutor(); /** * Object Factory used to create objects. * * @return Object Factory used to create instances of objects. */ - public DecoratedObjectFactory getObjectFactory(); + DecoratedObjectFactory getObjectFactory(); /** * The policy the container is running on. * * @return the websocket policy */ - public WebSocketPolicy getPolicy(); + WebSocketPolicy getPolicy(); /** * The SslContextFactory in use by the container. * * @return the SslContextFactory in use by the container (can be null if no SSL context is defined) */ - public SslContextFactory getSslContextFactory(); - + SslContextFactory getSslContextFactory(); + + /** + * Test for if the container has been started. + * + * @return true if container is started and running + */ + boolean isRunning(); + /** * A Session has been opened * * @param session the session that was opened */ - public void onSessionOpened(WebSocketSession session); + void onSessionOpened(WebSocketSession session); /** * A Session has been closed * * @param session the session that was closed */ - public void onSessionClosed(WebSocketSession session); - + void onSessionClosed(WebSocketSession session); } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java index 423b29cb8bd..aac64ec26ba 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketConfiguration.java @@ -20,6 +20,8 @@ package org.eclipse.jetty.websocket.server; import java.io.IOException; +import javax.servlet.ServletContext; + import org.eclipse.jetty.http.pathmap.MappedResource; import org.eclipse.jetty.http.pathmap.PathMappings; import org.eclipse.jetty.http.pathmap.PathSpec; @@ -44,9 +46,9 @@ public class NativeWebSocketConfiguration extends ContainerLifeCycle implements private final WebSocketServerFactory factory; private final PathMappings mappings = new PathMappings<>(); - public NativeWebSocketConfiguration() + public NativeWebSocketConfiguration(ServletContext context) { - this(new WebSocketServerFactory()); + this(new WebSocketServerFactory(context)); } public NativeWebSocketConfiguration(WebSocketServerFactory webSocketServerFactory) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java index 87c48e6ac0e..3e9686512c5 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/NativeWebSocketServletContainerInitializer.java @@ -33,7 +33,7 @@ public class NativeWebSocketServletContainerInitializer implements ServletContai NativeWebSocketConfiguration configuration = (NativeWebSocketConfiguration) context.getAttribute(KEY); if (configuration == null) { - configuration = new NativeWebSocketConfiguration(); + configuration = new NativeWebSocketConfiguration(context); context.setAttribute(KEY, configuration); } return configuration; diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java index e59f5b24731..70dba86ebcd 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java @@ -53,8 +53,9 @@ public abstract class WebSocketHandler extends HandlerWrapper factory.register(websocketPojo); } } - - private final WebSocketServletFactory webSocketFactory; + + private final ByteBufferPool bufferPool; + private WebSocketServletFactory webSocketFactory; public WebSocketHandler() { @@ -63,10 +64,7 @@ public abstract class WebSocketHandler extends HandlerWrapper public WebSocketHandler(ByteBufferPool bufferPool) { - WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); - configurePolicy(policy); - webSocketFactory = new WebSocketServerFactory(policy, bufferPool); - addBean(webSocketFactory); + this.bufferPool = bufferPool; } public abstract void configure(WebSocketServletFactory factory); @@ -79,12 +77,18 @@ public abstract class WebSocketHandler extends HandlerWrapper @Override protected void doStart() throws Exception { + WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); + configurePolicy(policy); + webSocketFactory = new WebSocketServerFactory(policy, getServer().getThreadPool(), bufferPool); + addBean(webSocketFactory); configure(webSocketFactory); super.doStart(); } - + public WebSocketServletFactory getWebSocketFactory() { + if (!isRunning()) + throw new IllegalStateException("Not Started yet"); return webSocketFactory; } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index 9b0dc7859a6..49f9b5f6cd4 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -28,12 +28,12 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.servlet.ServletContext; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -48,7 +48,6 @@ import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.component.ContainerLifeCycle; @@ -83,7 +82,7 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator, WebSocketContainerScope, WebSocketServletFactory { private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class); - + private final ClassLoader contextClassloader; private final Map handshakes = new HashMap<>(); /** @@ -96,47 +95,58 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc private final EventDriverFactory eventDriverFactory; private final ByteBufferPool bufferPool; private final WebSocketExtensionFactory extensionFactory; + private ServletContext context; // can be null when this factory is used from WebSocketHandler private Executor executor; private List sessionFactories; private WebSocketCreator creator; private List> registeredSocketClasses; private DecoratedObjectFactory objectFactory; - - public WebSocketServerFactory() + + public WebSocketServerFactory(ServletContext context) { - this(WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool()); + this(context, WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool()); } - - public WebSocketServerFactory(WebSocketPolicy policy) + + public WebSocketServerFactory(ServletContext context, ByteBufferPool bufferPool) { - this(policy, new MappedByteBufferPool()); + this(context, WebSocketPolicy.newServerPolicy(), bufferPool); } - - public WebSocketServerFactory(ByteBufferPool bufferPool) + + /** + * Entry point for {@link org.eclipse.jetty.websocket.servlet.WebSocketServletFactory.Loader} + * + * @param context the servlet context + * @param policy the policy to use + */ + public WebSocketServerFactory(ServletContext context, WebSocketPolicy policy) { - this(WebSocketPolicy.newServerPolicy(), bufferPool); + this(context, policy, new MappedByteBufferPool()); } - - public WebSocketServerFactory(WebSocketPolicy policy, ByteBufferPool bufferPool) + + public WebSocketServerFactory(ServletContext context, WebSocketPolicy policy, ByteBufferPool bufferPool) { + Objects.requireNonNull(context, ServletContext.class.getName()); + + this.context = context; + handshakes.put(HandshakeRFC6455.VERSION, new HandshakeRFC6455()); - + addBean(scheduler); addBean(bufferPool); - + this.contextClassloader = Thread.currentThread().getContextClassLoader(); - + this.registeredSocketClasses = new ArrayList<>(); - + this.defaultPolicy = policy; this.eventDriverFactory = new EventDriverFactory(defaultPolicy); this.bufferPool = bufferPool; this.extensionFactory = new WebSocketExtensionFactory(this); - + this.sessionFactories = new ArrayList<>(); this.sessionFactories.add(new WebSocketSessionFactory(this)); this.creator = this; - + // Create supportedVersions List versions = new ArrayList<>(); for (int v : handshakes.keySet()) @@ -155,23 +165,72 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } supportedVersions = rv.toString(); } - + + /** + * Protected entry point for {@link WebSocketHandler} + * + * @param policy the policy to use + * @param executor the executor to use + * @param bufferPool the buffer pool to use + */ + protected WebSocketServerFactory(WebSocketPolicy policy, Executor executor, ByteBufferPool bufferPool) + { + this.objectFactory = new DecoratedObjectFactory(); + this.executor = executor; + + handshakes.put(HandshakeRFC6455.VERSION, new HandshakeRFC6455()); + + addBean(scheduler); + addBean(bufferPool); + + this.contextClassloader = Thread.currentThread().getContextClassLoader(); + + this.registeredSocketClasses = new ArrayList<>(); + + this.defaultPolicy = policy; + this.eventDriverFactory = new EventDriverFactory(defaultPolicy); + this.bufferPool = bufferPool; + this.extensionFactory = new WebSocketExtensionFactory(this); + + this.sessionFactories = new ArrayList<>(); + this.sessionFactories.add(new WebSocketSessionFactory(this)); + this.creator = this; + + // Create supportedVersions + List versions = new ArrayList<>(); + for (int v : handshakes.keySet()) + { + versions.add(v); + } + Collections.sort(versions, Collections.reverseOrder()); // newest first + StringBuilder rv = new StringBuilder(); + for (int v : versions) + { + if (rv.length() > 0) + { + rv.append(", "); + } + rv.append(v); + } + supportedVersions = rv.toString(); + } + public void addSessionListener(WebSocketSession.Listener listener) { listeners.add(listener); } - + public void removeSessionListener(WebSocketSession.Listener listener) { listeners.remove(listener); } - + @Override public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException { return acceptWebSocket(getCreator(), request, response); } - + @Override public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException { @@ -179,32 +238,32 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc try { Thread.currentThread().setContextClassLoader(contextClassloader); - + // Create Servlet Specific Upgrade Request/Response objects ServletUpgradeRequest sockreq = new ServletUpgradeRequest(request); ServletUpgradeResponse sockresp = new ServletUpgradeResponse(response); - + Object websocketPojo = creator.createWebSocket(sockreq, sockresp); - + // Handle response forbidden (and similar paths) if (sockresp.isCommitted()) { return false; } - + if (websocketPojo == null) { // no creation, sorry sockresp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Endpoint Creation Failed"); return false; } - + // Allow Decorators to do their thing websocketPojo = getObjectFactory().decorate(websocketPojo); - + // Get the original HTTPConnection - HttpConnection connection = (HttpConnection)request.getAttribute("org.eclipse.jetty.server.HttpConnection"); - + HttpConnection connection = (HttpConnection) request.getAttribute("org.eclipse.jetty.server.HttpConnection"); + // Send the upgrade EventDriver driver = eventDriverFactory.wrap(websocketPojo); return upgrade(connection, sockreq, sockresp, driver); @@ -218,7 +277,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc Thread.currentThread().setContextClassLoader(old); } } - + public void addSessionFactory(SessionFactory sessionFactory) { if (sessionFactories.contains(sessionFactory)) @@ -227,33 +286,20 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } this.sessionFactories.add(sessionFactory); } - + @Override - public void cleanup() + public WebSocketServletFactory createFactory(ServletContext context, WebSocketPolicy policy) { - try - { - this.stop(); - } - catch (Exception e) - { - LOG.warn(e); - } + return new WebSocketServerFactory(context, policy, bufferPool); } - - @Override - public WebSocketServletFactory createFactory(WebSocketPolicy policy) - { - return new WebSocketServerFactory(policy, bufferPool); - } - + private WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection) { if (websocket == null) { throw new InvalidWebSocketException("Unable to create Session from null websocket"); } - + for (SessionFactory impl : sessionFactories) { if (impl.supports(websocket)) @@ -268,10 +314,10 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } } } - + throw new InvalidWebSocketException("Unable to create Session: unrecognized internal EventDriver type: " + websocket.getClass().getName()); } - + /** * Default Creator logic */ @@ -282,12 +328,12 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc { throw new WebSocketException("No WebSockets have been registered with the factory. Cannot use default implementation of WebSocketCreator."); } - + if (registeredSocketClasses.size() > 1) { LOG.warn("You have registered more than 1 websocket object, and are using the default WebSocketCreator! Using first registered websocket."); } - + Class firstClass = registeredSocketClasses.get(0); try { @@ -298,63 +344,76 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc throw new WebSocketException("Unable to create instance of " + firstClass, e); } } - + @Override protected void doStart() throws Exception { - if(this.objectFactory == null) + if(this.objectFactory == null && context != null) { - this.objectFactory = new DecoratedObjectFactory(); + this.objectFactory = (DecoratedObjectFactory) context.getAttribute(DecoratedObjectFactory.ATTR); + if (this.objectFactory == null) + { + throw new RuntimeException("Unable to find required ServletContext attribute: " + DecoratedObjectFactory.ATTR); + } } - + + if(this.executor == null && context != null) + { + ContextHandler contextHandler = ContextHandler.getContextHandler(context); + this.executor = contextHandler.getServer().getThreadPool(); + } + + Objects.requireNonNull(this.objectFactory, DecoratedObjectFactory.class.getName()); + Objects.requireNonNull(this.executor, Executor.class.getName()); + super.doStart(); } - + @Override public ByteBufferPool getBufferPool() { return this.bufferPool; } - + @Override public WebSocketCreator getCreator() { return this.creator; } - + @Override public Executor getExecutor() { return this.executor; } - + public DecoratedObjectFactory getObjectFactory() { return objectFactory; } - + public EventDriverFactory getEventDriverFactory() { return eventDriverFactory; } - + @Override public ExtensionFactory getExtensionFactory() { return extensionFactory; } - + public Collection getOpenSessions() { return getBeans(WebSocketSession.class); } - + @Override public WebSocketPolicy getPolicy() { return defaultPolicy; } - + @Override public SslContextFactory getSslContextFactory() { @@ -363,53 +422,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc */ return null; } - - public void init(ServletContextHandler context) throws ServletException - { - this.objectFactory = (DecoratedObjectFactory)context.getServletContext().getAttribute(DecoratedObjectFactory.ATTR); - if (this.objectFactory == null) - { - this.objectFactory = new DecoratedObjectFactory(); - } - - this.executor = context.getServer().getThreadPool(); - } - - @Override - public void init(ServletContext context) throws ServletException - { - // Setup ObjectFactory - this.objectFactory = (DecoratedObjectFactory)context.getAttribute(DecoratedObjectFactory.ATTR); - if (this.objectFactory == null) - { - this.objectFactory = new DecoratedObjectFactory(); - } - - // Validate Environment - ContextHandler handler = ContextHandler.getContextHandler(context); - - if (handler == null) - { - throw new ServletException("Not running on Jetty, WebSocket support unavailable"); - } - - this.executor = handler.getServer().getThreadPool(); - - try - { - // start lifecycle - start(); - } - catch (ServletException e) - { - throw e; - } - catch (Exception e) - { - throw new ServletException(e); - } - } - + @Override public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response) { @@ -434,7 +447,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc // no "Connection: upgrade" header present. return false; } - + // Test for "Upgrade" token boolean foundUpgradeToken = false; Iterator iter = QuoteUtil.splitAt(connection, ","); @@ -447,7 +460,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc break; } } - + if (!foundUpgradeToken) { return false; @@ -458,30 +471,30 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc // not a "GET" request (not a websocket upgrade) return false; } - + if (!"HTTP/1.1".equals(request.getProtocol())) { LOG.debug("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])"); return false; } - + return true; } - + @Override public void onSessionOpened(WebSocketSession session) { addManaged(session); notifySessionListeners(listener -> listener.onOpened(session)); } - + @Override public void onSessionClosed(WebSocketSession session) { removeBean(session); notifySessionListeners(listener -> listener.onClosed(session)); } - + private void notifySessionListeners(Consumer consumer) { for (WebSocketSession.Listener listener : listeners) @@ -496,30 +509,29 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } } } - + @Override public void register(Class websocketPojo) { registeredSocketClasses.add(websocketPojo); } - + @Override public void setCreator(WebSocketCreator creator) { this.creator = creator; } - + /** * Upgrade the request/response to a WebSocket Connection. *

* This method will not normally return, but will instead throw a UpgradeConnectionException, to exit HTTP handling and initiate WebSocket handling of the * connection. * - * @param http the raw http connection - * @param request The request to upgrade + * @param http the raw http connection + * @param request The request to upgrade * @param response The response to upgrade - * @param driver The websocket handler implementation to use - * @throws IOException + * @param driver The websocket handler implementation to use */ private boolean upgrade(HttpConnection http, ServletUpgradeRequest request, ServletUpgradeResponse response, EventDriver driver) throws IOException { @@ -531,14 +543,14 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc { throw new IllegalStateException("Not a 'HTTP/1.1' request"); } - + int version = request.getHeaderInt("Sec-WebSocket-Version"); if (version < 0) { // Old pre-RFC version specifications (header not present in RFC-6455) version = request.getHeaderInt("Sec-WebSocket-Draft"); } - + WebSocketHandshake handshaker = handshakes.get(version); if (handshaker == null) { @@ -563,14 +575,14 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } warn.append(": [").append(supportedVersions).append("]"); LOG.warn(warn.toString()); - + // Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol // Using the examples as outlined response.setHeader("Sec-WebSocket-Version", supportedVersions); response.sendError(HttpStatus.BAD_REQUEST_400, "Unsupported websocket version specification"); return false; } - + // Initialize / Negotiate Extensions ExtensionStack extensionStack = new ExtensionStack(getExtensionFactory()); // The JSR allows for the extensions to be pre-negotiated, filtered, etc... @@ -585,26 +597,26 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc // Use raw extension list from request extensionStack.negotiate(request.getExtensions()); } - + // Get original HTTP connection EndPoint endp = http.getEndPoint(); Connector connector = http.getConnector(); Executor executor = connector.getExecutor(); ByteBufferPool bufferPool = connector.getByteBufferPool(); - + // Setup websocket connection AbstractWebSocketConnection wsConnection = new WebSocketServerConnection(endp, executor, scheduler, driver.getPolicy(), bufferPool); - + extensionStack.setPolicy(driver.getPolicy()); extensionStack.configure(wsConnection.getParser()); extensionStack.configure(wsConnection.getGenerator()); - + if (LOG.isDebugEnabled()) { LOG.debug("HttpConnection: {}", http); LOG.debug("WebSocketConnection: {}", wsConnection); } - + // Setup Session WebSocketSession session = createSession(request.getRequestURI(), driver, wsConnection); session.setPolicy(driver.getPolicy()); @@ -613,51 +625,51 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc response.setExtensions(extensionStack.getNegotiatedExtensions()); session.setUpgradeResponse(response); wsConnection.addListener(session); - + // Setup Incoming Routing wsConnection.setNextIncomingFrames(extensionStack); extensionStack.setNextIncoming(session); - + // Setup Outgoing Routing session.setOutgoingHandler(extensionStack); extensionStack.setNextOutgoing(wsConnection); - + // Start Components session.addManaged(extensionStack); this.addManaged(session); - + if (session.isFailed()) { throw new IOException("Session failed to start"); } - + // Tell jetty about the new upgraded connection request.setServletAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE, wsConnection); - + if (LOG.isDebugEnabled()) LOG.debug("Handshake Response: {}", handshaker); - + if (getSendServerVersion(connector)) - response.setHeader("Server",HttpConfiguration.SERVER_VERSION); - + response.setHeader("Server", HttpConfiguration.SERVER_VERSION); + // Process (version specific) handshake response handshaker.doHandshakeResponse(request, response); - + if (LOG.isDebugEnabled()) LOG.debug("Websocket upgrade {} {} {} {}", request.getRequestURI(), version, response.getAcceptedSubProtocol(), wsConnection); - + return true; } - + private boolean getSendServerVersion(Connector connector) { ConnectionFactory connFactory = connector.getConnectionFactory(HttpVersion.HTTP_1_1.asString()); if (connFactory == null) return false; - + if (connFactory instanceof HttpConnectionFactory) { - HttpConfiguration httpConf = ((HttpConnectionFactory)connFactory).getHttpConfiguration(); + HttpConfiguration httpConf = ((HttpConnectionFactory) connFactory).getHttpConfiguration(); if (httpConf != null) return httpConf.getSendServerVersion(); } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java index e9fd1db5624..039ce962506 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java @@ -34,7 +34,6 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.pathmap.MappedResource; import org.eclipse.jetty.http.pathmap.PathSpec; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; @@ -45,7 +44,6 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @@ -55,11 +53,9 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @ManagedObject("WebSocket Upgrade Filter") public class WebSocketUpgradeFilter extends AbstractLifeCycle implements Filter, MappedWebSocketCreator, Dumpable { + private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class); public static final String CONTEXT_ATTRIBUTE_KEY = "contextAttributeKey"; public static final String CONFIG_ATTRIBUTE_KEY = "configAttributeKey"; - private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class); - private boolean localMapper; - private boolean localFactory; public static WebSocketUpgradeFilter configureContext(ServletContextHandler context) throws ServletException { @@ -122,9 +118,9 @@ public class WebSocketUpgradeFilter extends AbstractLifeCycle implements Filter, // do nothing } - public WebSocketUpgradeFilter(WebSocketPolicy policy, ByteBufferPool bufferPool) + public WebSocketUpgradeFilter(WebSocketServerFactory factory) { - this(new NativeWebSocketConfiguration(new WebSocketServerFactory(policy, bufferPool))); + this(new NativeWebSocketConfiguration(factory)); } public WebSocketUpgradeFilter(NativeWebSocketConfiguration configuration) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java index 0797114d3b0..83ec7273ac4 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java @@ -30,20 +30,21 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; +import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; public class WebSocketUpgradeHandlerWrapper extends HandlerWrapper implements MappedWebSocketCreator { private NativeWebSocketConfiguration configuration; - public WebSocketUpgradeHandlerWrapper() + public WebSocketUpgradeHandlerWrapper(ServletContextHandler context) { - this(new MappedByteBufferPool()); + this(context, new MappedByteBufferPool()); } - public WebSocketUpgradeHandlerWrapper(ByteBufferPool bufferPool) + public WebSocketUpgradeHandlerWrapper(ServletContextHandler context, ByteBufferPool bufferPool) { - this.configuration = new NativeWebSocketConfiguration(new WebSocketServerFactory(bufferPool)); + this.configuration = new NativeWebSocketConfiguration(new WebSocketServerFactory(context.getServletContext(), bufferPool)); } @Override diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java index 0e2e7a46d13..cf880774b96 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/InfoContextListener.java @@ -31,7 +31,7 @@ public class InfoContextListener implements WebSocketCreator, ServletContextList @Override public void contextInitialized(ServletContextEvent sce) { - NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(); + NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(sce.getServletContext()); configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024); configuration.addMapping(new ServletPathSpec("/info/*"), this); sce.getServletContext().setAttribute(NativeWebSocketConfiguration.class.getName(), configuration); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java index 14a55761790..f08e417ef63 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilterTest.java @@ -163,7 +163,7 @@ public class WebSocketUpgradeFilterTest server.setHandler(context); context.addFilter(WebSocketUpgradeFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); - NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(); + NativeWebSocketConfiguration configuration = new NativeWebSocketConfiguration(context.getServletContext()); configuration.getFactory().getPolicy().setMaxTextMessageSize(10 * 1024 * 1024); configuration.addMapping(new ServletPathSpec("/info/*"), infoCreator); context.getServletContext().setAttribute(NativeWebSocketConfiguration.class.getName(), configuration); diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java index bf99fc1e02f..d233cfd480f 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java @@ -90,7 +90,14 @@ public abstract class WebSocketServlet extends HttpServlet @Override public void destroy() { - factory.cleanup(); + try + { + factory.stop(); + } + catch (Exception ignore) + { + // ignore; + } } /** @@ -126,14 +133,13 @@ public abstract class WebSocketServlet extends HttpServlet { policy.setInputBufferSize(Integer.parseInt(max)); } - - factory = WebSocketServletFactory.Loader.create(policy); + + ServletContext ctx = getServletContext(); + factory = WebSocketServletFactory.Loader.load(ctx, policy); configure(factory); - ServletContext ctx = getServletContext(); - - factory.init(ctx); + factory.start(); ctx.setAttribute(WebSocketServletFactory.class.getName(),factory); } diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java index 80046b2bcad..c7f6de5d382 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.java @@ -19,8 +19,8 @@ package org.eclipse.jetty.websocket.servlet; import java.io.IOException; -import java.util.Iterator; -import java.util.ServiceLoader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -35,77 +35,62 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory; */ public interface WebSocketServletFactory { - public static class Loader + class Loader { - private static WebSocketServletFactory INSTANCE; - - public static WebSocketServletFactory create(WebSocketPolicy policy) throws ClassNotFoundException, InstantiationException, IllegalAccessException + final static String DEFAULT_IMPL = "org.eclipse.jetty.websocket.server.WebSocketServerFactory"; + + public static WebSocketServletFactory load(ServletContext ctx, WebSocketPolicy policy) { - return load().createFactory(policy); - } - - public static WebSocketServletFactory load() throws ClassNotFoundException, InstantiationException, IllegalAccessException - { - if (INSTANCE != null) + try { - return INSTANCE; + Class wsClazz = + (Class) Class.forName(DEFAULT_IMPL); + Constructor ctor = wsClazz.getDeclaredConstructor(new Class[]{ServletContext.class, WebSocketPolicy.class}); + return ctor.newInstance(ctx, policy); } - WebSocketServletFactory baseFactory; - Iterator factories = ServiceLoader.load(WebSocketServletFactory.class).iterator(); - - if (factories.hasNext()) + catch (ClassNotFoundException e) { - baseFactory = factories.next(); + throw new RuntimeException("Unable to load " + DEFAULT_IMPL, e); } - else + catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { - // Load the default class if ServiceLoader mechanism isn't valid in this environment. (such as OSGi) - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - @SuppressWarnings("unchecked") - Class wssf = (Class)loader - .loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory"); - baseFactory = wssf.newInstance(); + throw new RuntimeException("Unable to instantiate " + DEFAULT_IMPL, e); } - - INSTANCE = baseFactory; - return INSTANCE; } } - - public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException; - - public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException; - - public void cleanup(); - - public WebSocketServletFactory createFactory(WebSocketPolicy policy); - - public abstract WebSocketCreator getCreator(); - - public abstract ExtensionFactory getExtensionFactory(); - + + boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException; + + boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException; + + void start() throws Exception; + void stop() throws Exception; + + WebSocketServletFactory createFactory(ServletContext context, WebSocketPolicy policy); + + WebSocketCreator getCreator(); + + ExtensionFactory getExtensionFactory(); + /** * Get the base policy in use for WebSockets. *

* Note: individual WebSocket implementations can override some of the values in here by using the {@link WebSocket @WebSocket} annotation. - * + * * @return the base policy */ - public WebSocketPolicy getPolicy(); - - public void init(ServletContext servletContext) throws Exception; - - public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response); - + WebSocketPolicy getPolicy(); + + boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response); + /** * Register a websocket class pojo with the default {@link WebSocketCreator}. *

* Note: only required if using the default {@link WebSocketCreator} provided by this factory. - * - * @param websocketPojo - * the class to instantiate for each incoming websocket upgrade request. + * + * @param websocketPojo the class to instantiate for each incoming websocket upgrade request. */ - public void register(Class websocketPojo); - - public abstract void setCreator(WebSocketCreator creator); + void register(Class websocketPojo); + + void setCreator(WebSocketCreator creator); }