Issue #6277 Better handling of exceptions thrown in sessionDestroyed (#6278) (#6279)

* Issue #6277 Better handling of exceptions thrown in sessionDestroyed

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2021-05-16 17:32:49 +10:00 committed by GitHub
parent edcaf70d9a
commit 087f486b44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 19 deletions

View File

@ -498,10 +498,7 @@ public class Session implements SessionHandler.SessionIf
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
if (isInvalid()) checkValidForRead();
{
throw new IllegalStateException("Session not valid");
}
return _sessionData.getLastAccessed(); return _sessionData.getLastAccessed();
} }
} }
@ -947,14 +944,18 @@ public class Session implements SessionHandler.SessionIf
// do the invalidation // do the invalidation
_handler.callSessionDestroyedListeners(this); _handler.callSessionDestroyedListeners(this);
} }
catch (Exception e)
{
LOG.warn("Error during Session destroy listener", e);
}
finally finally
{ {
// call the attribute removed listeners and finally mark it // call the attribute removed listeners and finally mark it
// as invalid // as invalid
finishInvalidate(); finishInvalidate();
// tell id mgr to remove sessions with same id from all contexts
_handler.getSessionIdManager().invalidateAll(_sessionData.getId());
} }
// tell id mgr to remove sessions with same id from all contexts
_handler.getSessionIdManager().invalidateAll(_sessionData.getId());
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -31,16 +31,18 @@ public class TestHttpSessionListener implements HttpSessionListener
public List<String> createdSessions = new ArrayList<>(); public List<String> createdSessions = new ArrayList<>();
public List<String> destroyedSessions = new ArrayList<>(); public List<String> destroyedSessions = new ArrayList<>();
public boolean accessAttribute = false; public boolean accessAttribute = false;
public Exception ex = null; public boolean lastAccessTime = false;
public Exception attributeException = null;
public Exception accessTimeException = null;
public TestHttpSessionListener(boolean access) public TestHttpSessionListener(boolean accessAttribute, boolean lastAccessTime)
{ {
accessAttribute = access; this.accessAttribute = accessAttribute;
this.lastAccessTime = lastAccessTime;
} }
public TestHttpSessionListener() public TestHttpSessionListener()
{ {
accessAttribute = false;
} }
public void sessionDestroyed(HttpSessionEvent se) public void sessionDestroyed(HttpSessionEvent se)
@ -54,7 +56,19 @@ public class TestHttpSessionListener implements HttpSessionListener
} }
catch (Exception e) catch (Exception e)
{ {
ex = e; attributeException = e;
}
}
if (lastAccessTime)
{
try
{
se.getSession().getLastAccessedTime();
}
catch (Exception e)
{
accessTimeException = e;
} }
} }
} }

View File

@ -35,9 +35,9 @@ public class TestHttpSessionListenerWithWebappClasses extends TestHttpSessionLis
super(); super();
} }
public TestHttpSessionListenerWithWebappClasses(boolean access) public TestHttpSessionListenerWithWebappClasses(boolean attribute, boolean lastAccessTime)
{ {
super(access); super(attribute, lastAccessTime);
} }
@Override @Override
@ -52,7 +52,7 @@ public class TestHttpSessionListenerWithWebappClasses extends TestHttpSessionLis
} }
catch (Exception cnfe) catch (Exception cnfe)
{ {
ex = cnfe; attributeException = cnfe;
} }
super.sessionDestroyed(se); super.sessionDestroyed(se);
} }

View File

@ -58,6 +58,7 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -92,7 +93,7 @@ public class SessionListenerTest
TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, TestServer server = new TestServer(0, inactivePeriod, scavengePeriod,
cacheFactory, storeFactory); cacheFactory, storeFactory);
ServletContextHandler context = server.addContext(contextPath); ServletContextHandler context = server.addContext(contextPath);
TestHttpSessionListener listener = new TestHttpSessionListener(true); TestHttpSessionListener listener = new TestHttpSessionListener(true, true);
context.getSessionHandler().addEventListener(listener); context.getSessionHandler().addEventListener(listener);
TestServlet servlet = new TestServlet(); TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet); ServletHolder holder = new ServletHolder(servlet);
@ -136,6 +137,72 @@ public class SessionListenerTest
LifeCycle.stop(server); LifeCycle.stop(server);
} }
} }
/**
* Test that if a session listener throws an exception during sessionDestroyed the session is still invalidated
*/
@Test
public void testListenerWithInvalidationException() throws Exception
{
String contextPath = "";
String servletMapping = "/server";
int inactivePeriod = 6;
int scavengePeriod = -1;
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
TestSessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory();
storeFactory.setGracePeriodSec(scavengePeriod);
TestServer server = new TestServer(0, inactivePeriod, scavengePeriod,
cacheFactory, storeFactory);
ServletContextHandler context = server.addContext(contextPath);
ThrowingSessionListener listener = new ThrowingSessionListener();
context.getSessionHandler().addEventListener(listener);
TestServlet servlet = new TestServlet();
ServletHolder holder = new ServletHolder(servlet);
context.addServlet(holder, servletMapping);
try
{
server.start();
int port1 = server.getPort();
HttpClient client = new HttpClient();
client.start();
try
{
String url = "http://localhost:" + port1 + contextPath + servletMapping;
// Create the session
ContentResponse response1 = client.GET(url + "?action=init");
assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
String sessionCookie = response1.getHeaders().get("Set-Cookie");
assertNotNull(sessionCookie);
assertTrue(TestServlet.bindingListener.bound);
String sessionId = TestServer.extractSessionId(sessionCookie);
// Make a request which will invalidate the existing session
Request request2 = client.newRequest(url + "?action=test");
ContentResponse response2 = request2.send();
assertEquals(HttpServletResponse.SC_OK, response2.getStatus());
assertTrue(TestServlet.bindingListener.unbound);
//check session no longer exists
assertFalse(context.getSessionHandler().getSessionCache().contains(sessionId));
assertFalse(context.getSessionHandler().getSessionCache().getSessionDataStore().exists(sessionId));
}
finally
{
LifeCycle.stop(client);
}
}
finally
{
LifeCycle.stop(server);
}
}
/** /**
* Test that listeners are called when a session expires * Test that listeners are called when a session expires
@ -177,7 +244,7 @@ public class SessionListenerTest
ServletContextHandler context = server1.addContext(contextPath); ServletContextHandler context = server1.addContext(contextPath);
context.setClassLoader(contextClassLoader); context.setClassLoader(contextClassLoader);
context.addServlet(holder, servletMapping); context.addServlet(holder, servletMapping);
TestHttpSessionListener listener = new TestHttpSessionListenerWithWebappClasses(true); TestHttpSessionListener listener = new TestHttpSessionListenerWithWebappClasses(true, true);
context.getSessionHandler().addEventListener(listener); context.getSessionHandler().addEventListener(listener);
try try
@ -206,7 +273,8 @@ public class SessionListenerTest
assertThat(sessionId, is(in(listener.destroyedSessions))); assertThat(sessionId, is(in(listener.destroyedSessions)));
assertNull(listener.ex); assertNull(listener.attributeException);
assertNull(listener.accessTimeException);
} }
finally finally
{ {
@ -241,7 +309,7 @@ public class SessionListenerTest
ServletHolder holder = new ServletHolder(servlet); ServletHolder holder = new ServletHolder(servlet);
ServletContextHandler context = server1.addContext(contextPath); ServletContextHandler context = server1.addContext(contextPath);
context.addServlet(holder, servletMapping); context.addServlet(holder, servletMapping);
TestHttpSessionListener listener = new TestHttpSessionListener(); TestHttpSessionListener listener = new TestHttpSessionListener(true, true);
context.getSessionHandler().addEventListener(listener); context.getSessionHandler().addEventListener(listener);
@ -276,7 +344,8 @@ public class SessionListenerTest
assertTrue(listener.destroyedSessions.contains("1234")); assertTrue(listener.destroyedSessions.contains("1234"));
assertNull(listener.ex); assertNull(listener.attributeException);
assertNull(listener.accessTimeException);
} }
finally finally
{ {
@ -301,6 +370,22 @@ public class SessionListenerTest
{ {
} }
} }
public static class ThrowingSessionListener implements HttpSessionListener
{
@Override
public void sessionCreated(HttpSessionEvent se)
{
}
@Override
public void sessionDestroyed(HttpSessionEvent se)
{
throw new IllegalStateException("Exception during sessionDestroyed");
}
}
@Test @Test
public void testSessionListeners() public void testSessionListeners()