diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 0165eda509c..c8397ee7570 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -370,11 +370,22 @@ public class SessionHandler extends ScopedHandler if (_sessionListeners!=null) { - HttpSessionEvent event=new HttpSessionEvent(session); - for (int i = _sessionListeners.size()-1; i>=0; i--) + //We annoint the calling thread with + //the webapp's classloader because the calling thread may + //come from the scavenger, rather than a request thread + Runnable r = new Runnable() { - _sessionListeners.get(i).sessionDestroyed(event); - } + @Override + public void run () + { + HttpSessionEvent event=new HttpSessionEvent(session); + for (int i = _sessionListeners.size()-1; i>=0; i--) + { + _sessionListeners.get(i).sessionDestroyed(event); + } + } + }; + _sessionContext.run(r); } } diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListenerWithWebappClasses.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListenerWithWebappClasses.java new file mode 100644 index 00000000000..9884a214161 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListenerWithWebappClasses.java @@ -0,0 +1,61 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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.server.session; + +import javax.servlet.http.HttpSessionEvent; + +/** + * TestHttpSessionListenerWithWebappClasses + * + * A session listener class that checks that sessionDestroyed + * events can reference classes known only to the webapp, ie + * that the calling thread has been correctly annointed with + * the webapp loader. + * + */ +public class TestHttpSessionListenerWithWebappClasses extends TestHttpSessionListener +{ + public TestHttpSessionListenerWithWebappClasses() + { + super(); + } + + public TestHttpSessionListenerWithWebappClasses(boolean access) + { + super(access); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + //try loading a class that is known only to the webapp + //to test that the calling thread has been properly + //annointed with the webapp's classloader + try + { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass("Foo"); + } + catch (Exception cnfe) + { + ex=cnfe; + } + super.sessionDestroyed(se); + } + +} diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java index 7769ff0b3d6..11f8155bdda 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java @@ -18,9 +18,14 @@ package org.eclipse.jetty.server.session; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; import java.net.HttpCookie; +import java.net.URL; +import java.net.URLClassLoader; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -35,6 +40,8 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -121,13 +128,27 @@ public class SessionListenerTest /** - * Test that listeners are called when a session expires. + * Test that listeners are called when a session expires + * and that the listener is able to access webapp classes. * * @throws Exception */ @Test public void testSessionExpiresWithListener() throws Exception { + //Use a class that would only be known to the webapp classloader + InputStream foostream = Thread.currentThread().getContextClassLoader().getResourceAsStream("Foo.clazz"); + File foodir = new File (MavenTestingUtils.getTargetDir(), "foo"); + foodir.mkdirs(); + File fooclass = new File (foodir, "Foo.class"); + IO.copy(foostream, new FileOutputStream(fooclass)); + + assertTrue(fooclass.exists()); + assertTrue(fooclass.length() != 0); + + URL[] foodirUrls = new URL[]{foodir.toURI().toURL()}; + URLClassLoader contextClassLoader = new URLClassLoader(foodirUrls, Thread.currentThread().getContextClassLoader()); + String contextPath = "/"; String servletMapping = "/server"; int inactivePeriod = 3; @@ -143,8 +164,9 @@ public class SessionListenerTest TestServlet servlet = new TestServlet(); ServletHolder holder = new ServletHolder(servlet); ServletContextHandler context = server1.addContext(contextPath); + context.setClassLoader(contextClassLoader); context.addServlet(holder, servletMapping); - TestHttpSessionListener listener = new TestHttpSessionListener(true); + TestHttpSessionListener listener = new TestHttpSessionListenerWithWebappClasses(true); context.getSessionHandler().addEventListener(listener); server1.start();