Reputation: 5315
I recently moved a project that uses an embedded Jetty from version 6.1 to 9.4. It was easier than I expected. Now only one feature is left to migrate: The project has an administration tool, that shows all the loaded contexts (it can host more than one web applicaton) and for each context, it lists the registered HTTP sessions, including session attributes etc. In Jetty 6, it goes something like this:
Handler[] handlers = jettyServer.getHandlers();
for (Handler h : handlers) {
if (h instanceof ContextHandlerCollection) {
ContextHandlerCollection ch = (ContextHandlerCollection) h;
Handler[] contexts = ch.getHandlers();
for (Handler context : contexts) {
if (context instanceof WebAppContext) {
WebAppContext wapp = (WebAppContext) context;
// Here, I am stuck in migrating this to jetty 9:
SessionManager sm = wapp.getSessionHandler().getSessionManager();
if (sm instanceof HashSessionManager) {
HashSessionManager hsm = (HashSessionManager) sm;
Map<?,?> sessions = hsm.getSessionMap();
if (sessions != null) {
for (Map.Entry<?,?> entry : sessions.entrySet()) {
if (entry.getValue() instanceof HttpSession) {
HttpSession s = (HttpSession) entry.getValue();
}
}
}
}
}
}
}
}
The new Jetty 9 structure is a bit different, but I was able to access all the contexts. But in Jetty 9, I am unable to access the SessionManager
from the context.
wapp.getSessionHandler().getSessionManager();
Does not work anymore, the getSessionManager()
method on SessionHandler
is not there anymore. And after searching for hours, I did not find any way to get from a WebAppContext
to the HTTPSession
instances in that context.
Does anyone know a way to get there?
Upvotes: 4
Views: 1837
Reputation: 49462
There's no access to the list of active HttpSession
.
This change occurred to support continual improvement of Session management to handle distributed SessionDataStores
and distributed servers without Session stickiness (such as many cloud offerings)
You would be better off tracking the HttpSessions
yourself at the server side.
How to do this ...
Implement a custom Session Listener.
Make sure it implements both javax.servlet.http.HttpSessionListener
and javax.servlet.http.HttpSessionIdListener
to track the Sessions that are created / destroyed / changed. (might also want to implement javax.servlet.ServletContextListener
to stop tracking for sessions on destroyed contexts)
Make sure you only track the sessions by their IDs (and optionally what context they belong to) in this implementation. (don't hold a reference to the HttpSession object, or you'll mess up the GC and WebApp lifecycle).
Example:
public static class SessionTracker implements HttpSessionListener,
HttpSessionIdListener, ServletContextListener
{
private final Server server;
private final SessionHandler sessionHandler;
private String contextPath;
private HashSet<String> sessionIds = new HashSet<>();
public SessionTracker(Server server, SessionHandler sessionHandler)
{
this.server = server;
this.sessionHandler = sessionHandler;
this.sessionHandler.addEventListener(this);
}
public String getContextPath()
{
return contextPath;
}
public SessionHandler getSessionHandler()
{
return sessionHandler;
}
public HashSet<String> getSessionIds()
{
return sessionIds;
}
@Override
public void contextInitialized(ServletContextEvent sce)
{
contextPath = sce.getServletContext().getContextPath();
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
Collection<SessionTracker> trackers = this.server.getBeans(SessionTracker.class);
trackers.removeIf((tracker) -> tracker.getContextPath().equals(sce.getServletContext().getContextPath()));
}
@Override
public void sessionCreated(HttpSessionEvent se)
{
sessionIds.add(se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se)
{
sessionIds.remove(se.getSession().getId());
}
@Override
public void sessionIdChanged(HttpSessionEvent event, String oldSessionId)
{
sessionIds.add(oldSessionId);
sessionIds.add(event.getSession().getId());
}
}
And the way to add this to all contexts properly ...
// After server.start() somewhere.
addSessionTracker(server.getHandler());
private static void addSessionTracker(Handler handler)
{
if (handler == null)
{
return; // skip
}
if (handler instanceof HandlerCollection)
{
HandlerCollection handlers = (HandlerCollection) handler;
for (Handler child : handlers.getHandlers())
{
addSessionTracker(child);
}
}
else
{
if (handler instanceof ServletContextHandler)
{
ServletContextHandler context = (ServletContextHandler) handler;
SessionHandler sessionHandler = context.getSessionHandler();
new SessionTracker(handler.getServer(), sessionHandler);
}
}
}
Then to use these trackers ...
private static void doSomethingWithSessionTracker(Server server)
{
Collection<SessionTracker> trackers = server.getBeans(SessionTracker.class);
trackers.forEach((tracker) -> {
tracker.getSessionIds().forEach((sessionId) -> {
Session session = tracker.getSessionHandler().getSession(sessionId);
// Do something with Session.
});
});
}
Note that the above will only show you the Session being tracked on that server, if you have multiple servers, or a distributed Session store, you wont see the Sessions from other servers. If this is important to you, consider writing your own
SessionDataStore
that lets you access the storedSessions
.
Upvotes: 5