diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java index 1b8ef91a8e2..8ec0ca5a17d 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/deploy/WebSocketServerContainerInitializer.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.server.deploy; import java.util.HashSet; import java.util.Set; + import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -30,12 +31,18 @@ import javax.websocket.server.ServerApplicationConfig; import javax.websocket.server.ServerEndpoint; import javax.websocket.server.ServerEndpointConfig; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.TypeUtil; 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.jsr356.server.ServerContainer; +import org.eclipse.jetty.websocket.server.DefaultMappedWebSocketCreator; +import org.eclipse.jetty.websocket.server.MappedWebSocketCreator; +import org.eclipse.jetty.websocket.server.WebSocketServerFactory; import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter; @HandlesTypes( @@ -43,28 +50,87 @@ import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter; public class WebSocketServerContainerInitializer implements ServletContainerInitializer { public static final String ENABLE_KEY = "org.eclipse.jetty.websocket.jsr356"; + public static final String ADD_DYNAMIC_FILTER_KEY = "org.eclipse.jetty.websocket.jsr356.addDynamicFilter"; private static final Logger LOG = Log.getLogger(WebSocketServerContainerInitializer.class); - + /** - * Jetty Native approach. - *

- * Note: this will add the Upgrade filter to the existing list, with no regard for order. It will just be tacked onto the end of the list. + * Test a ServletContext for {@code init-param} or {@code attribute} at {@code keyName} for + * true or false setting that determines if the specified feature is enabled (or not). + * + * @param context the context to search + * @param keyName the key name + * @param defValue the default value, if the value is not specified in the context + * @return the value for the feature key + */ + public static boolean isEnabledViaContext(ServletContext context, String keyName, boolean defValue) + { + // Try context parameters first + String cp = context.getInitParameter(keyName); + + if(cp != null) + { + if (TypeUtil.isTrue(cp)) + { + return true; + } + + if (TypeUtil.isFalse(cp)) + { + return false; + } + + return defValue; + } + + // Next, try attribute on context + Object enable = context.getAttribute(ENABLE_KEY); + + if(enable != null) + { + if (TypeUtil.isTrue(enable)) + { + return true; + } + + if (TypeUtil.isFalse(enable)) + { + return false; + } + } + + return defValue; + } + + /** + * Embedded Jetty approach for non-bytecode scanning. */ public static ServerContainer configureContext(ServletContextHandler context) throws ServletException { - // Create Filter - WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context); + // Create Basic components + WebSocketPolicy policy = WebSocketPolicy.newServerPolicy(); + ByteBufferPool bufferPool = new MappedByteBufferPool(); + MappedWebSocketCreator creator = new DefaultMappedWebSocketCreator(); + WebSocketServerFactory factory = new WebSocketServerFactory(policy, bufferPool); // Create the Jetty ServerContainer implementation - ServerContainer jettyContainer = new ServerContainer(filter,filter.getFactory(),context.getServer().getThreadPool()); + ServerContainer jettyContainer = new ServerContainer(creator,factory,context.getServer().getThreadPool()); context.addBean(jettyContainer); + + context.setAttribute(WebSocketUpgradeFilter.CREATOR_KEY, creator); + context.setAttribute(WebSocketUpgradeFilter.FACTORY_KEY, factory); // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer); - + + // Create Filter + if(isEnabledViaContext(context.getServletContext(), ADD_DYNAMIC_FILTER_KEY, true)) + { + WebSocketUpgradeFilter.configureContext(context); + } + return jettyContainer; } - + /** * @deprecated use {@link #configureContext(ServletContextHandler)} instead */ @@ -74,56 +140,10 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit return configureContext(jettyContext); } - private boolean isEnabled(Set> c, ServletContext context) - { - // Try context parameters first - String cp = context.getInitParameter(ENABLE_KEY); - if(TypeUtil.isTrue(cp)) - { - // forced on - return true; - } - - if(TypeUtil.isFalse(cp)) - { - // forced off - LOG.warn("JSR-356 support disabled via parameter on context {} - {}",context.getContextPath(),context); - return false; - } - - // Next, try attribute on context - Object enable = context.getAttribute(ENABLE_KEY); - - if(TypeUtil.isTrue(enable)) - { - // forced on - return true; - } - - if (TypeUtil.isFalse(enable)) - { - // forced off - LOG.warn("JSR-356 support disabled via attribute on context {} - {}",context.getContextPath(),context); - return false; - } - - // if not forced on or off, determine behavior based on annotations. - if (c.isEmpty()) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("No JSR-356 annotations or interfaces discovered. JSR-356 support disabled",context.getContextPath(),context); - } - return false; - } - - return true; - } - @Override public void onStartup(Set> c, ServletContext context) throws ServletException { - if(!isEnabled(c,context)) + if(!isEnabledViaContext(context, ENABLE_KEY, true)) { return; } @@ -146,18 +166,27 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit try { Thread.currentThread().setContextClassLoader(context.getClassLoader()); - + // Create the Jetty ServerContainer implementation ServerContainer jettyContainer = configureContext(jettyContext); // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer); + if (c.isEmpty()) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("No JSR-356 annotations or interfaces discovered"); + } + return; + } + if (LOG.isDebugEnabled()) { LOG.debug("Found {} classes",c.size()); } - + // Now process the incoming classes Set> discoveredExtendedEndpoints = new HashSet<>(); Set> discoveredAnnotatedEndpoints = new HashSet<>(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AltFilterTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AltFilterTest.java new file mode 100644 index 00000000000..6c0025e0e18 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AltFilterTest.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.jsr356.server; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.net.URI; +import java.util.Queue; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.toolchain.test.TestingDir; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule; +import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoSocket; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +/** + * Testing the use of an alternate {@link org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter} + * defined in the WEB-INF/web.xml + */ +public class AltFilterTest +{ + @Rule + public TestingDir testdir = new TestingDir(); + + @Rule + public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test"); + + @Test + public void testEcho() throws Exception + { + WSServer wsb = new WSServer(testdir,"app"); + wsb.copyWebInf("alt-filter-web.xml"); + // the endpoint (extends javax.websocket.Endpoint) + wsb.copyClass(BasicEchoSocket.class); + + try + { + wsb.start(); + URI uri = wsb.getServerBaseURI(); + + WebAppContext webapp = wsb.createWebAppContext(); + wsb.deployWebapp(webapp); + + FilterHolder filterWebXml = webapp.getServletHandler().getFilter("wsuf-test"); + assertThat("Filter[wsuf-test]", filterWebXml, notNullValue()); + + FilterHolder filterSCI = webapp.getServletHandler().getFilter("Jetty_WebSocketUpgradeFilter"); + assertThat("Filter[Jetty_WebSocketUpgradeFilter]", filterSCI, nullValue()); + + WebSocketClient client = new WebSocketClient(bufferPool); + try + { + client.start(); + JettyEchoSocket clientEcho = new JettyEchoSocket(); + Future future = client.connect(clientEcho,uri.resolve("echo")); + // wait for connect + future.get(1,TimeUnit.SECONDS); + clientEcho.sendMessage("Hello Echo"); + Queue msgs = clientEcho.awaitMessages(1); + Assert.assertEquals("Expected message","Hello Echo",msgs.poll()); + } + finally + { + client.stop(); + } + } + finally + { + wsb.stop(); + } + } +} diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ExtensionStackProcessingTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ExtensionStackProcessingTest.java index a14daef82e3..89e0498914d 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ExtensionStackProcessingTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/ExtensionStackProcessingTest.java @@ -58,21 +58,18 @@ public class ExtensionStackProcessingTest { private Server server; private ServerConnector connector; - private ExtensionFactory serverExtensionFactory; private WebSocketContainer client; - + private ServletContextHandler servletContextHandler; + @Before public void prepare() throws Exception { server = new Server(); connector = new ServerConnector(server); server.addConnector(connector); - - ServletContextHandler context = new ServletContextHandler(server, "/", true, false); - ServerContainer container = WebSocketServerContainerInitializer.configureContext(context); - - WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)context.getAttribute(WebSocketUpgradeFilter.class.getName()); - serverExtensionFactory = filter.getFactory().getExtensionFactory(); + + servletContextHandler = new ServletContextHandler(server, "/", true, false); + ServerContainer container = WebSocketServerContainerInitializer.configureContext(servletContextHandler); ServerEndpointConfig config = ServerEndpointConfig.Builder.create(BasicEchoEndpoint.class, "/").build(); container.addEndpoint(config); @@ -88,11 +85,18 @@ public class ExtensionStackProcessingTest { server.stop(); } + + private void assumeDeflateFrameAvailable() + { + WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)servletContextHandler.getAttribute(WebSocketUpgradeFilter.class.getName()); + ExtensionFactory serverExtensionFactory = filter.getFactory().getExtensionFactory(); + Assume.assumeTrue("Server has permessage-deflate extension registered",serverExtensionFactory.isAvailable("permessage-deflate")); + } @Test public void testDeflateFrameExtension() throws Exception { - Assume.assumeTrue("Server has deflate-frame extension registered",serverExtensionFactory.isAvailable("deflate-frame")); + assumeDeflateFrameAvailable(); ClientEndpointConfig config = ClientEndpointConfig.Builder.create() .extensions(Arrays.asList(new JsrExtension("deflate-frame"))) @@ -140,7 +144,7 @@ public class ExtensionStackProcessingTest @Test public void testPerMessageDeflateExtension() throws Exception { - Assume.assumeTrue("Server has permessage-deflate extension registered",serverExtensionFactory.isAvailable("permessage-deflate")); + assumeDeflateFrameAvailable(); ClientEndpointConfig config = ClientEndpointConfig.Builder.create() .extensions(Arrays.asList(new JsrExtension("permessage-deflate"))) diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java index e974ea7e152..ccf65963701 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/WSServer.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.websocket.jsr356.server; +import static org.hamcrest.Matchers.notNullValue; + import java.io.File; import java.io.IOException; import java.net.MalformedURLException; @@ -47,8 +49,6 @@ import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebXmlConfiguration; import org.junit.Assert; -import static org.hamcrest.Matchers.notNullValue; - /** * Utility to build out exploded directory WebApps, in the /target/tests/ directory, for testing out servers that use javax.websocket endpoints. *

@@ -151,6 +151,11 @@ public class WSServer { return serverUri; } + + public Server getServer() + { + return server; + } public File getWebAppDir() { diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointConfigContextListener.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointConfigContextListener.java index c56585a5eb5..6b2a6b38bf6 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointConfigContextListener.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/samples/echo/BasicEchoEndpointConfigContextListener.java @@ -25,7 +25,7 @@ import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; /** - * Example of adding a server WebSocket (extending {@link Endpoint}) programmatically via config + * Example of adding a server WebSocket (extending {@link javax.websocket.Endpoint}) programmatically via config */ public class BasicEchoEndpointConfigContextListener implements ServletContextListener { diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/resources/alt-filter-web.xml b/jetty-websocket/javax-websocket-server-impl/src/test/resources/alt-filter-web.xml new file mode 100644 index 00000000000..24c506eb389 --- /dev/null +++ b/jetty-websocket/javax-websocket-server-impl/src/test/resources/alt-filter-web.xml @@ -0,0 +1,23 @@ + + + + + org.eclipse.jetty.websocket.jsr356.addDynamicFilter + false + + + + wsuf-test + org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter + + + + wsuf-test + /echo/* + + diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/DefaultMappedWebSocketCreator.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/DefaultMappedWebSocketCreator.java new file mode 100644 index 00000000000..fcbd5419703 --- /dev/null +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/DefaultMappedWebSocketCreator.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.server; + +import org.eclipse.jetty.websocket.server.pathmap.PathMappings; +import org.eclipse.jetty.websocket.server.pathmap.PathSpec; +import org.eclipse.jetty.websocket.servlet.WebSocketCreator; + +public class DefaultMappedWebSocketCreator implements MappedWebSocketCreator +{ + private final PathMappings mappings = new PathMappings<>(); + + @Override + public void addMapping(PathSpec spec, WebSocketCreator creator) + { + this.mappings.put(spec, creator); + } + + @Override + public PathMappings getMappings() + { + return this.mappings; + } +} 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 6a0829f52a2..1178c35c578 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 @@ -57,48 +57,35 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter { public static final String CONTEXT_ATTRIBUTE_KEY = "contextAttributeKey"; private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class); - + public static WebSocketUpgradeFilter configureContext(ServletContextHandler context) throws ServletException { // Prevent double configure - WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)context.getAttribute(WebSocketUpgradeFilter.class.getName()); + WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter) context.getAttribute(WebSocketUpgradeFilter.class.getName()); if (filter != null) { return filter; } - - // Use WEB-INF/web.xml instantiated version, if present - FilterHolder filters[] = context.getServletHandler().getFilters(); - for (FilterHolder registeredFilter: filters) - { - if (WebSocketUpgradeFilter.class.isAssignableFrom(registeredFilter.getClass())) - { - WebSocketUpgradeFilter wsuf = (WebSocketUpgradeFilter) registeredFilter.getFilter(); - // need async supported - context.setAttribute(WebSocketUpgradeFilter.class.getName(), wsuf); - return wsuf; - } - } // Dynamically add filter filter = new WebSocketUpgradeFilter(); filter.setToAttribute(context, WebSocketUpgradeFilter.class.getName()); - + String name = "Jetty_WebSocketUpgradeFilter"; String pathSpec = "/*"; EnumSet dispatcherTypes = EnumSet.of(DispatcherType.REQUEST); - + FilterHolder fholder = new FilterHolder(filter); fholder.setName(name); fholder.setAsyncSupported(true); - fholder.setInitParameter(CONTEXT_ATTRIBUTE_KEY,WebSocketUpgradeFilter.class.getName()); - context.addFilter(fholder,pathSpec,dispatcherTypes); - + fholder.setInitParameter(CONTEXT_ATTRIBUTE_KEY, WebSocketUpgradeFilter.class.getName()); + context.addFilter(fholder, pathSpec, dispatcherTypes); + if (LOG.isDebugEnabled()) { - LOG.debug("Adding [{}] {} mapped to {} to {}",name,filter,pathSpec,context); + LOG.debug("Adding [{}] {} mapped to {} to {}", name, filter, pathSpec, context); } - + return filter; } @@ -109,12 +96,12 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter public static WebSocketUpgradeFilter configureContext(ServletContext context) throws ServletException { ContextHandler handler = ContextHandler.getContextHandler(context); - + if (handler == null) { throw new ServletException("Not running on Jetty, WebSocket support unavailable"); } - + if (!(handler instanceof ServletContextHandler)) { throw new ServletException("Not running in Jetty ServletContextHandler, WebSocket support via " + WebSocketUpgradeFilter.class.getName() + " unavailable"); @@ -122,40 +109,41 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter return configureContext((ServletContextHandler) handler); } - - private final WebSocketServerFactory factory; - private final PathMappings pathmap = new PathMappings<>(); + + public static final String CREATOR_KEY = "org.eclipse.jetty.websocket.server.creator"; + public static final String FACTORY_KEY = "org.eclipse.jetty.websocket.server.factory"; + + private final WebSocketPolicy policy; + private final ByteBufferPool bufferPool; + private WebSocketServerFactory factory; + private MappedWebSocketCreator mappedWebSocketCreator; private String fname; private boolean alreadySetToAttribute = false; - + public WebSocketUpgradeFilter() { - this(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool()); + this(WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool()); } - + public WebSocketUpgradeFilter(WebSocketPolicy policy, ByteBufferPool bufferPool) { - factory = new WebSocketServerFactory(policy,bufferPool); - addBean(factory,true); + this.policy = policy; + this.bufferPool = bufferPool; } - + @Override public void addMapping(PathSpec spec, WebSocketCreator creator) { - // TODO: ideally should throw warning/error if attempting to map to something - // not covered by the filter pathSpec/url-patterns. But how do we get the filter mappings - // in a sane way? and then apply them to the PathSpec object properly? - pathmap.put(spec,creator); + mappedWebSocketCreator.addMapping(spec, creator); } - + @Override public void destroy() { factory.cleanup(); - pathmap.reset(); super.destroy(); } - + @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -163,10 +151,10 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter { // no factory, cannot operate LOG.debug("WebSocketUpgradeFilter is not operational - no WebSocketServletFactory configured"); - chain.doFilter(request,response); + chain.doFilter(request, response); return; } - + try { HttpServletRequest httpreq = (HttpServletRequest) request; @@ -178,7 +166,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter chain.doFilter(request, response); return; } - + // Since this is a filter, we need to be smart about determining the target path String contextPath = httpreq.getContextPath(); String target = httpreq.getRequestURI(); @@ -186,37 +174,37 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter { target = target.substring(contextPath.length()); } - - MappedResource resource = pathmap.getMatch(target); + + MappedResource resource = mappedWebSocketCreator.getMappings().getMatch(target); if (resource == null) { if (LOG.isDebugEnabled()) { LOG.debug("WebSocket Upgrade on {} has no associated endpoint", target); - LOG.debug("PathMappings: {}", pathmap.dump()); + LOG.debug("PathMappings: {}", mappedWebSocketCreator.getMappings().dump()); } // no match. chain.doFilter(request, response); return; } - if(LOG.isDebugEnabled()) + if (LOG.isDebugEnabled()) { LOG.debug("WebSocket Upgrade detected on {} for endpoint {}", target, resource); } - + WebSocketCreator creator = resource.getResource(); - + // Store PathSpec resource mapping as request attribute httpreq.setAttribute(PathSpec.class.getName(), resource.getPathSpec()); - + // We have an upgrade request if (factory.acceptWebSocket(creator, httpreq, httpresp)) { // We have a socket instance created return; } - + // If we reach this point, it means we had an incoming request to upgrade // but it was either not a proper websocket upgrade, or it was possibly rejected // due to incoming request constraints (controlled by WebSocketCreator) @@ -234,69 +222,82 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter LOG.debug("Not a HttpServletRequest, skipping WebSocketUpgradeFilter"); } } - + // not an Upgrade request - chain.doFilter(request,response); + chain.doFilter(request, response); } - + @Override public String dump() { return ContainerLifeCycle.dump(this); } - + @Override public void dump(Appendable out, String indent) throws IOException { - out.append(indent).append(" +- pathmap=").append(pathmap.toString()).append("\n"); - pathmap.dump(out,indent + " "); + out.append(indent).append(" +- pathmap=").append(mappedWebSocketCreator.toString()).append("\n"); + mappedWebSocketCreator.getMappings().dump(out, indent + " "); } - + public WebSocketServerFactory getFactory() { return factory; } - + @ManagedAttribute(value = "mappings", readonly = true) @Override public PathMappings getMappings() { - return pathmap; + return mappedWebSocketCreator.getMappings(); } - + @Override public void init(FilterConfig config) throws ServletException { fname = config.getFilterName(); - + try { - WebSocketPolicy policy = factory.getPolicy(); - + mappedWebSocketCreator = (MappedWebSocketCreator) config.getServletContext().getAttribute(CREATOR_KEY); + if (mappedWebSocketCreator == null) + { + mappedWebSocketCreator = new DefaultMappedWebSocketCreator(); + } + + factory = (WebSocketServerFactory) config.getServletContext().getAttribute(FACTORY_KEY); + if (factory == null) + { + factory = new WebSocketServerFactory(policy, bufferPool); + } + addBean(factory, true); + + // TODO: Policy isn't from attributes + String max = config.getInitParameter("maxIdleTime"); if (max != null) { - policy.setIdleTimeout(Long.parseLong(max)); + factory.getPolicy().setIdleTimeout(Long.parseLong(max)); } - + max = config.getInitParameter("maxTextMessageSize"); if (max != null) { - policy.setMaxTextMessageSize(Integer.parseInt(max)); + factory.getPolicy().setMaxTextMessageSize(Integer.parseInt(max)); } - + max = config.getInitParameter("maxBinaryMessageSize"); if (max != null) { - policy.setMaxBinaryMessageSize(Integer.parseInt(max)); + factory.getPolicy().setMaxBinaryMessageSize(Integer.parseInt(max)); } - + max = config.getInitParameter("inputBufferSize"); if (max != null) { - policy.setInputBufferSize(Integer.parseInt(max)); + factory.getPolicy().setInputBufferSize(Integer.parseInt(max)); } - + String key = config.getInitParameter(CONTEXT_ATTRIBUTE_KEY); if (key == null) { @@ -305,7 +306,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter } setToAttribute(config.getServletContext(), key); - + factory.start(); } catch (Exception x) @@ -316,45 +317,45 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter private void setToAttribute(ServletContextHandler context, String key) throws ServletException { - if(alreadySetToAttribute) + if (alreadySetToAttribute) { return; } if (context.getAttribute(key) != null) { - throw new ServletException(WebSocketUpgradeFilter.class.getName() + + throw new ServletException(WebSocketUpgradeFilter.class.getName() + " is defined twice for the same context attribute key '" + key - + "'. Make sure you have different init-param '" + + + "'. Make sure you have different init-param '" + CONTEXT_ATTRIBUTE_KEY + "' values set"); } - context.setAttribute(key,this); - + context.setAttribute(key, this); + alreadySetToAttribute = true; } - + public void setToAttribute(ServletContext context, String key) throws ServletException { - if(alreadySetToAttribute) + if (alreadySetToAttribute) { return; } if (context.getAttribute(key) != null) { - throw new ServletException(WebSocketUpgradeFilter.class.getName() + + throw new ServletException(WebSocketUpgradeFilter.class.getName() + " is defined twice for the same context attribute key '" + key - + "'. Make sure you have different init-param '" + + + "'. Make sure you have different init-param '" + CONTEXT_ATTRIBUTE_KEY + "' values set"); } - context.setAttribute(key,this); - + context.setAttribute(key, this); + alreadySetToAttribute = true; } - + @Override public String toString() { - return String.format("%s[factory=%s,pathmap=%s]",this.getClass().getSimpleName(),factory,pathmap); + return String.format("%s[factory=%s,creator=%s]", this.getClass().getSimpleName(), factory, mappedWebSocketCreator); } }