From dc81cda742b354138fd3f78c815aa9d81ae2e078 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Mar 2015 10:27:07 -0700 Subject: [PATCH] Using Jetty component listeners for CDI --- .../cdi/servlet/JettyWeldInitializer.java | 2 - .../websocket/WebSocketCdiInitializer.java | 74 ++++++++++ .../cdi/websocket/WebSocketCdiListener.java | 138 ++++++++++++++++++ .../cdi/websocket/WebSocketScopeContext.java | 4 +- .../javax.servlet.ServletContainerInitializer | 1 + .../cdi/websocket/basicapp/BasicAppTest.java | 1 + .../cdi/websocket/cdiapp/CdiAppTest.java | 32 +++- .../jetty/cdi/websocket/cdiapp/DataMaker.java | 7 + .../cdi/websocket/cdiapp/EchoSocket.java | 57 ++++++++ .../cdi/websocket/cdiapp/InfoSocket.java | 6 +- .../websocket/jsr356/ClientContainer.java | 26 ---- .../jsr356/annotations/JsrEvents.java | 1 + .../websocket/client/WebSocketClient.java | 31 ---- .../client/io/UpgradeConnection.java | 4 +- .../common/scopes/SimpleContainerScope.java | 27 ---- .../scopes/WebSocketContainerScope.java | 27 ---- .../common/scopes/WebSocketScopeEvents.java | 99 ------------- .../ThreadClassLoaderScope.java} | 39 ++--- .../util/ThreadClassLoaderScopeTest.java | 78 ++++++++++ .../server/WebSocketServerFactory.java | 30 ---- 20 files changed, 418 insertions(+), 266 deletions(-) create mode 100644 jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java create mode 100644 jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java create mode 100644 jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer create mode 100644 jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java delete mode 100644 jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeEvents.java rename jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/{scopes/WebSocketScopeListener.java => util/ThreadClassLoaderScope.java} (54%) create mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScopeTest.java diff --git a/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java index 08694d23100..ac243cc4141 100644 --- a/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java +++ b/jetty-cdi/cdi-servlet/src/main/java/org/eclipse/jetty/cdi/servlet/JettyWeldInitializer.java @@ -57,8 +57,6 @@ public class JettyWeldInitializer public static void initContext(ContextHandler handler) throws NamingException { - // TODO: handler.addLifeCycleListener(new WebSocketServerLifecycleListener(handler)); - // Add context specific weld container reference. // See https://issues.jboss.org/browse/WELD-1710 // and https://github.com/weld/core/blob/2.2.5.Final/environments/servlet/core/src/main/java/org/jboss/weld/environment/servlet/WeldServletLifecycle.java#L244-L253 diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java new file mode 100644 index 00000000000..e0a5bb742b1 --- /dev/null +++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiInitializer.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.cdi.websocket; + +import java.util.Set; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.websocket.common.util.ThreadClassLoaderScope; + +public class WebSocketCdiInitializer implements ServletContainerInitializer +{ + public static void configureContext(ServletContextHandler context) throws ServletException + { + try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader())) + { + addListeners(context); + } + } + + @Override + public void onStartup(Set> c, ServletContext context) throws ServletException + { + ContextHandler handler = ContextHandler.getContextHandler(context); + + if (handler == null) + { + throw new ServletException("Not running on Jetty, WebSocket+CDI support unavailable"); + } + + if (!(handler instanceof ServletContextHandler)) + { + throw new ServletException("Not running in Jetty ServletContextHandler, WebSocket+CDI support unavailable"); + } + + ServletContextHandler jettyContext = (ServletContextHandler)handler; + try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader())) + { + addListeners(jettyContext); + } + } + + private static void addListeners(ContainerLifeCycle container) + { + WebSocketCdiListener listener = new WebSocketCdiListener(container); + + if (listener != null) + { + container.addLifeCycleListener(listener); + container.addEventListener(listener); + } + } +} diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java new file mode 100644 index 00000000000..bee9002d385 --- /dev/null +++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketCdiListener.java @@ -0,0 +1,138 @@ +package org.eclipse.jetty.cdi.websocket; + +import java.util.Set; + +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.CDI; + +import org.eclipse.jetty.cdi.core.AnyLiteral; +import org.eclipse.jetty.cdi.core.ScopedInstance; +import org.eclipse.jetty.util.component.Container; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; +import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope; + +public class WebSocketCdiListener implements LifeCycle.Listener, Container.InheritedListener +{ + private static final Logger LOG = Log.getLogger(WebSocketCdiListener.class); + private final ContainerLifeCycle container; + + public WebSocketCdiListener(ContainerLifeCycle container) + { + this.container = container; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static ScopedInstance newInstance(Class clazz) + { + BeanManager bm = CDI.current().getBeanManager(); + + ScopedInstance sbean = new ScopedInstance(); + Set> beans = bm.getBeans(clazz,AnyLiteral.INSTANCE); + if (beans.size() > 0) + { + sbean.bean = beans.iterator().next(); + sbean.creationalContext = bm.createCreationalContext(sbean.bean); + sbean.instance = bm.getReference(sbean.bean,clazz,sbean.creationalContext); + return sbean; + } + else + { + throw new RuntimeException(String.format("Can't find class %s",clazz)); + } + } + + private ScopedInstance wsScope; + + private synchronized ScopedInstance getWebSocketScope() + { + if (wsScope == null) + { + wsScope = newInstance(WebSocketScopeContext.class); + } + return wsScope; + } + + @Override + public void lifeCycleStarting(LifeCycle event) + { + if (event instanceof WebSocketContainerScope) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("starting websocket container [{}]", event); + } + getWebSocketScope().instance.begin(); + } + else if (event instanceof WebSocketSessionScope) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("starting websocket session [{}]", event); + } + getWebSocketScope().instance.setSession((Session)event); + } + } + + @Override + public void lifeCycleStarted(LifeCycle event) + { + } + + @Override + public void lifeCycleFailure(LifeCycle event, Throwable cause) + { + } + + @Override + public void lifeCycleStopping(LifeCycle event) + { + } + + @Override + public void lifeCycleStopped(LifeCycle event) + { + if (event instanceof WebSocketContainerScope) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("stopped websocket container [{}]", event); + } + getWebSocketScope().instance.end(); + } + else if (event == container) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("stopped parent container [{}]", event); + } + if (wsScope != null) + { + wsScope.destroy(); + } + } + } + + @Override + public void beanAdded(Container parent, Object child) + { + if (child instanceof LifeCycle) + { + ((LifeCycle)child).addLifeCycleListener(this); + } + } + + @Override + public void beanRemoved(Container parent, Object child) + { + if (child instanceof LifeCycle) + { + ((LifeCycle)child).removeLifeCycleListener(this); + } + } +} diff --git a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java index 561e6fb0437..111a513a2de 100644 --- a/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java +++ b/jetty-cdi/cdi-websocket/src/main/java/org/eclipse/jetty/cdi/websocket/WebSocketScopeContext.java @@ -52,7 +52,7 @@ public class WebSocketScopeContext public void begin() { - LOG.debug("begin()"); + LOG.debug("begin()", new Throwable("Trace")); if (state.get() != null) { throw new IllegalAccessError("Already in WebSocketScope"); @@ -102,7 +102,7 @@ public class WebSocketScopeContext public void setSession(Session sess) { - LOG.debug("setSession()"); + LOG.debug("setSession({})",sess); jettySessionProducer.setSession(sess); if(sess instanceof javax.websocket.Session) { diff --git a/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..f527270e524 --- /dev/null +++ b/jetty-cdi/cdi-websocket/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.eclipse.jetty.cdi.websocket.WebSocketCdiInitializer \ No newline at end of file diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java index e531f2aefa7..cd9b2354d4b 100644 --- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java +++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/basicapp/BasicAppTest.java @@ -49,6 +49,7 @@ public class BasicAppTest private static final Logger LOG = Log.getLogger(BasicAppTest.class); private static Server server; + @SuppressWarnings("unused") private static URI serverHttpURI; private static URI serverWebsocketURI; diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java index 26c89d11404..f6d984973e3 100644 --- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java +++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/CdiAppTest.java @@ -29,6 +29,7 @@ import javax.websocket.server.ServerContainer; import org.eclipse.jetty.cdi.servlet.EmbeddedCdiHandler; import org.eclipse.jetty.cdi.websocket.CheckSocket; +import org.eclipse.jetty.cdi.websocket.WebSocketCdiInitializer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; @@ -60,6 +61,7 @@ public class CdiAppTest server.addConnector(connector); EmbeddedCdiHandler context = new EmbeddedCdiHandler(); + WebSocketCdiInitializer.configureContext(context); File baseDir = MavenTestingUtils.getTestResourcesDir(); @@ -69,6 +71,7 @@ public class CdiAppTest // Add some websockets ServerContainer container = WebSocketServerContainerInitializer.configureContext(context); + container.addEndpoint(EchoSocket.class); container.addEndpoint(InfoSocket.class); server.start(); @@ -120,7 +123,7 @@ public class CdiAppTest } @Test - public void testWebSocketInfo() throws Exception + public void testWebSocket_Info_FieldPresence() throws Exception { WebSocketClient client = new WebSocketClient(); try @@ -150,4 +153,31 @@ public class CdiAppTest client.stop(); } } + + @Test + public void testWebSocket_Info_DataFromCdi() throws Exception + { + WebSocketClient client = new WebSocketClient(); + try + { + client.start(); + CheckSocket socket = new CheckSocket(); + client.connect(socket,serverWebsocketURI.resolve("/cdi-info")); + + socket.awaitOpen(2,TimeUnit.SECONDS); + socket.sendText("data|stuff"); + socket.close(StatusCode.NORMAL,"Test complete"); + socket.awaitClose(2,TimeUnit.SECONDS); + + assertThat("Messages received",socket.getTextMessages().size(),is(1)); + String response = socket.getTextMessages().poll(); + System.err.println(response); + + assertThat("Message[0]",response,containsString("Hello there data")); + } + finally + { + client.stop(); + } + } } diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java index 0d6cbcc8dee..cd780733296 100644 --- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java +++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/DataMaker.java @@ -20,15 +20,22 @@ package org.eclipse.jetty.cdi.websocket.cdiapp; import javax.inject.Inject; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.api.Session; public class DataMaker { + private static final Logger LOG = Log.getLogger(DataMaker.class); + @Inject private Session session; public void processMessage(String msg) { + LOG.debug(".processMessage({})",msg); + LOG.debug("session = {}",session); + session.getRemote().sendStringByFuture("Hello there " + msg); } } diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java new file mode 100644 index 00000000000..264b2652916 --- /dev/null +++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/EchoSocket.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.cdi.websocket.cdiapp; + +import javax.websocket.CloseReason; +import javax.websocket.OnClose; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +@ServerEndpoint("/echo") +public class EchoSocket +{ + private static final Logger LOG = Log.getLogger(EchoSocket.class); + @SuppressWarnings("unused") + private Session session; + + @OnOpen + public void onOpen(Session session) + { + LOG.debug("onOpen(): {}",session); + this.session = session; + } + + @OnClose + public void onClose(CloseReason close) + { + LOG.debug("onClose(): {}",close); + this.session = null; + } + + @OnMessage + public String onMessage(String msg) + { + return msg; + } +} diff --git a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java index 31cf6cb44c2..e724edd8f74 100644 --- a/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java +++ b/jetty-cdi/cdi-websocket/src/test/java/org/eclipse/jetty/cdi/websocket/cdiapp/InfoSocket.java @@ -69,8 +69,10 @@ public class InfoSocket { StringWriter str = new StringWriter(); PrintWriter out = new PrintWriter(str); + + String args[] = msg.split("\\|"); - switch (msg) + switch (args[0]) { case "info": out.printf("websocketSession is %s%n",asPresent(session)); @@ -78,7 +80,7 @@ public class InfoSocket out.printf("servletContext is %s%n",asPresent(servletContext)); break; case "data": - dataMaker.processMessage(msg); + dataMaker.processMessage(args[1]); break; } 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 0cefc85d530..51569e256ec 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 @@ -22,7 +22,6 @@ import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -59,8 +58,6 @@ import org.eclipse.jetty.websocket.common.SessionListener; import org.eclipse.jetty.websocket.common.WebSocketSession; import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope; import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeEvents; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeListener; import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner; import org.eclipse.jetty.websocket.jsr356.client.AnnotatedClientEndpointMetadata; import org.eclipse.jetty.websocket.jsr356.client.EmptyClientEndpointConfig; @@ -119,12 +116,6 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont ShutdownThread.register(this); } - @Override - public void addScopeListener(WebSocketScopeListener listener) - { - this.scopeDelegate.addScopeListener(listener); - } - private Session connect(EndpointInstance instance, URI path) throws IOException { Objects.requireNonNull(instance,"EndpointInstance cannot be null"); @@ -351,17 +342,6 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont return scopeDelegate.getPolicy(); } - public WebSocketScopeEvents getScopeEvents() - { - return scopeDelegate.getScopeEvents(); - } - - @Override - public List getScopeListeners() - { - return scopeDelegate.getScopeListeners(); - } - @Override public SslContextFactory getSslContextFactory() { @@ -426,12 +406,6 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont } } - @Override - public void removeScopeListener(WebSocketScopeListener listener) - { - scopeDelegate.removeScopeListener(listener); - } - @Override public void setAsyncSendTimeout(long ms) { diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java index 745daabe0f3..3891e760e0f 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/annotations/JsrEvents.java @@ -148,6 +148,7 @@ public class JsrEvents { if (onError == null) { + LOG.warn("Unable to report throwable to websocket (no @OnError handler declared): " + websocket.getClass().getName(), cause); return; } onError.call(websocket,cause); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index 7200921da5f..399257d23e4 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -24,7 +24,6 @@ import java.net.SocketAddress; import java.net.URI; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.Executor; @@ -61,8 +60,6 @@ import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.common.events.EventDriverFactory; import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory; import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeEvents; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeListener; /** * WebSocketClient provides a means of establishing connections to remote websocket endpoints. @@ -74,7 +71,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen private final WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); private final SslContextFactory sslContextFactory; private final WebSocketExtensionFactory extensionRegistry; - private WebSocketScopeEvents scopeEvents = new WebSocketScopeEvents(); private boolean daemon = false; private EventDriverFactory eventDriverFactory; private SessionFactory sessionFactory; @@ -150,12 +146,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen addBean(this.bufferPool); } - @Override - public void addScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.addScopeListener(listener); - } - public Future connect(Object websocket, URI toUri) throws IOException { ClientUpgradeRequest request = new ClientUpgradeRequest(toUri); @@ -291,8 +281,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen super.doStart(); - scopeEvents.fireContainerActivated(this); - if (LOG.isDebugEnabled()) LOG.debug("Started {}",this); } @@ -316,8 +304,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen super.doStop(); - scopeEvents.fireContainerDeactivated(this); - if (LOG.isDebugEnabled()) LOG.debug("Stopped {}",this); } @@ -453,17 +439,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen return scheduler; } - public WebSocketScopeEvents getScopeEvents() - { - return scopeEvents; - } - - @Override - public List getScopeListeners() - { - return this.scopeEvents.getScopeListeners(); - } - public SessionFactory getSessionFactory() { return sessionFactory; @@ -531,12 +506,6 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen LOG.debug("Session Opened: {}",session); } - @Override - public void removeScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.removeScopeListener(listener); - } - public void setAsyncWriteTimeout(long ms) { this.policy.setAsyncWriteTimeout(ms); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java index 530c010b926..b8a20c3efb2 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/UpgradeConnection.java @@ -275,7 +275,9 @@ public class UpgradeConnection extends AbstractConnection // Validate Response Status Code if (response.getStatusCode() != SWITCHING_PROTOCOLS) { - throw new UpgradeException(request.getRequestURI(),response.getStatusCode(),"Didn't switch protocols"); + // TODO: use jetty-http and org.eclipse.jetty.http.HttpStatus for more meaningful exception messages + throw new UpgradeException(request.getRequestURI(),response.getStatusCode(),"Didn't switch protocols, expected status <" + SWITCHING_PROTOCOLS + + ">, but got <" + response.getStatusCode() + ">"); } // Validate Connection header diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java index 935583e5f62..f407f8bafd9 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/SimpleContainerScope.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.websocket.common.scopes; -import java.util.List; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; @@ -36,7 +35,6 @@ public class SimpleContainerScope extends ContainerLifeCycle implements WebSocke private final WebSocketPolicy policy; private Executor executor; private SslContextFactory sslContextFactory; - private WebSocketScopeEvents scopeEvents = new WebSocketScopeEvents(); public SimpleContainerScope(WebSocketPolicy policy) { @@ -65,14 +63,12 @@ public class SimpleContainerScope extends ContainerLifeCycle implements WebSocke protected void doStart() throws Exception { super.doStart(); - scopeEvents.fireContainerActivated(this); } @Override protected void doStop() throws Exception { super.doStop(); - scopeEvents.fireContainerDeactivated(this); } @Override @@ -109,27 +105,4 @@ public class SimpleContainerScope extends ContainerLifeCycle implements WebSocke { this.sslContextFactory = sslContextFactory; } - - public WebSocketScopeEvents getScopeEvents() - { - return scopeEvents; - } - - @Override - public void addScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.addScopeListener(listener); - } - - @Override - public void removeScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.removeScopeListener(listener); - } - - @Override - public List getScopeListeners() - { - return this.scopeEvents.getScopeListeners(); - } } 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 1f65f1bc263..acc611f7784 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 @@ -18,7 +18,6 @@ package org.eclipse.jetty.websocket.common.scopes; -import java.util.List; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; @@ -31,32 +30,6 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy; */ public interface WebSocketContainerScope { - /** - * Add a {@link WebSocketScopeListener} for this container. - * - * @param listener - * the listener to receive container scope events - */ - public void addScopeListener(WebSocketScopeListener listener); - - /** - * Remove a {@link WebSocketScopeListener} from this container - * - * @param listener - * the listener to receive websocket scope events - */ - public void removeScopeListener(WebSocketScopeListener listener); - - /** - * Get the current list of {@link WebSocketScopeListener}s for this container - */ - public List getScopeListeners(); - - /** - * Get the WebSocketScopeEvents handler, for firing {@link WebSocketScopeListener} events. - */ - public WebSocketScopeEvents getScopeEvents(); - /** * The configured Container Buffer Pool. * diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeEvents.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeEvents.java deleted file mode 100644 index 6294aa3bc2e..00000000000 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeEvents.java +++ /dev/null @@ -1,99 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2015 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.common.scopes; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class WebSocketScopeEvents -{ - private List scopeListeners; - - public void addScopeListener(WebSocketScopeListener listener) - { - if (scopeListeners == null) - { - scopeListeners = new ArrayList<>(); - } - scopeListeners.add(listener); - } - - public void fireContainerActivated(WebSocketContainerScope container) - { - if (scopeListeners != null) - { - for (WebSocketScopeListener listener : scopeListeners) - { - listener.onWebSocketContainerActivated(container); - } - } - } - - public void fireContainerDeactivated(WebSocketContainerScope container) - { - if (scopeListeners != null) - { - for (WebSocketScopeListener listener : scopeListeners) - { - listener.onWebSocketContainerDeactivated(container); - } - } - } - - public void fireSessionActivated(WebSocketSessionScope session) - { - if (scopeListeners != null) - { - for (WebSocketScopeListener listener : scopeListeners) - { - listener.onWebSocketSessionActivated(session); - } - } - } - - public void fireSessionDeactivated(WebSocketSessionScope session) - { - if (scopeListeners != null) - { - for (WebSocketScopeListener listener : scopeListeners) - { - listener.onWebSocketSessionDeactivated(session); - } - } - } - - public List getScopeListeners() - { - if (scopeListeners == null) - { - return Collections.emptyList(); - } - return Collections.unmodifiableList(scopeListeners); - } - - public void removeScopeListener(WebSocketScopeListener listener) - { - if (scopeListeners == null) - { - return; - } - scopeListeners.remove(listener); - } -} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeListener.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScope.java similarity index 54% rename from jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeListener.java rename to jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScope.java index 5aca114e9b3..5322583333f 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/scopes/WebSocketScopeListener.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScope.java @@ -16,27 +16,30 @@ // ======================================================================== // -package org.eclipse.jetty.websocket.common.scopes; +package org.eclipse.jetty.websocket.common.util; -public interface WebSocketScopeListener +import java.io.Closeable; + +public class ThreadClassLoaderScope implements Closeable { - /** - * A WebSocket Container scope was created / activated - */ - void onWebSocketContainerActivated(WebSocketContainerScope scope); + private final ClassLoader old; + private final ClassLoader scopedClassLoader; - /** - * A WebSocket Container scope was stopped / deactivated - */ - void onWebSocketContainerDeactivated(WebSocketContainerScope scope); + public ThreadClassLoaderScope(ClassLoader cl) + { + old = Thread.currentThread().getContextClassLoader(); + scopedClassLoader = cl; + Thread.currentThread().setContextClassLoader(scopedClassLoader); + } - /** - * A WebSocket Session scope was created / activated - */ - void onWebSocketSessionActivated(WebSocketSessionScope scope); + @Override + public void close() + { + Thread.currentThread().setContextClassLoader(old); + } - /** - * A WebSocket Session scope was stopped / deactivated - */ - void onWebSocketSessionDeactivated(WebSocketSessionScope scope); + public ClassLoader getScopedClassLoader() + { + return scopedClassLoader; + } } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScopeTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScopeTest.java new file mode 100644 index 00000000000..28fe50efa2d --- /dev/null +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ThreadClassLoaderScopeTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.common.util; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.Test; + +public class ThreadClassLoaderScopeTest +{ + private static class ClassLoaderFoo extends URLClassLoader + { + public ClassLoaderFoo() + { + super(new URL[0]); + } + } + + private static class ClassLoaderBar extends URLClassLoader + { + public ClassLoaderBar() + { + super(new URL[0]); + } + } + + @Test + public void testNormal() + { + try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(new ClassLoaderFoo())) + { + assertThat("ClassLoader in scope",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderFoo.class)); + assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderFoo.class)); + } + assertThat("ClassLoader after scope",Thread.currentThread().getContextClassLoader(),not(instanceOf(ClassLoaderFoo.class))); + } + + @Test + public void testWithException() + { + try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(new ClassLoaderBar())) + { + assertThat("ClassLoader in 'scope'",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderBar.class)); + assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderBar.class)); + try (ThreadClassLoaderScope inner = new ThreadClassLoaderScope(new ClassLoaderFoo())) + { + assertThat("ClassLoader in 'inner'",Thread.currentThread().getContextClassLoader(),instanceOf(ClassLoaderFoo.class)); + assertThat("Scoped ClassLoader",scope.getScopedClassLoader(),instanceOf(ClassLoaderFoo.class)); + throw new RuntimeException("Intention exception"); + } + } + catch (Throwable ignore) + { + /* ignore */ + } + assertThat("ClassLoader after 'scope'",Thread.currentThread().getContextClassLoader(),not(instanceOf(ClassLoaderBar.class))); + } +} 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 62d57381649..7f8fd830afe 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 @@ -66,8 +66,6 @@ import org.eclipse.jetty.websocket.common.events.EventDriverFactory; import org.eclipse.jetty.websocket.common.extensions.ExtensionStack; import org.eclipse.jetty.websocket.common.extensions.WebSocketExtensionFactory; import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeEvents; -import org.eclipse.jetty.websocket.common.scopes.WebSocketScopeListener; import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; @@ -97,7 +95,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc private WebSocketCreator creator; private List> registeredSocketClasses; private DecoratedObjectFactory objectFactory; - private WebSocketScopeEvents scopeEvents = new WebSocketScopeEvents(); public WebSocketServerFactory() { @@ -209,12 +206,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } } - @Override - public void addScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.addScopeListener(listener); - } - public void addSessionFactory(SessionFactory sessionFactory) { if (sessionFactories.contains(sessionFactory)) @@ -325,8 +316,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc } super.doStart(); - - scopeEvents.fireContainerActivated(this); } @Override @@ -334,8 +323,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc { shutdownAllConnections(); super.doStop(); - - scopeEvents.fireContainerDeactivated(this); } @Override @@ -383,17 +370,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc return defaultPolicy; } - public WebSocketScopeEvents getScopeEvents() - { - return scopeEvents; - } - - @Override - public List getScopeListeners() - { - return this.scopeEvents.getScopeListeners(); - } - @Override public SslContextFactory getSslContextFactory() { @@ -540,12 +516,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc registeredSocketClasses.add(websocketPojo); } - @Override - public void removeScopeListener(WebSocketScopeListener listener) - { - this.scopeEvents.removeScopeListener(listener); - } - @Override public void setCreator(WebSocketCreator creator) {