嵌入式码头:在上下文中显示所有会话

Embedded jetty: display all sessions in context

我最近将一个使用嵌入式 Jetty 的项目从 6.1 版移到了 9.4 版。 这比我预期的要容易。现在只剩下一项功能需要迁移: 该项目有一个管理工具,它显示所有加载的上下文(它可以托管多个 Web 应用程序)并且对于每个上下文,它列出已注册的 HTTP 会话,包括会话属性等。 在 Jetty 6 中,它是这样的:

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();
              }
            }
          }
        }
      }
    }
  }
}

新的 Jetty 9 结构有点不同,但我能够访问所有上下文。但是在 Jetty 9 中,我无法从上下文中访问 SessionManager

wapp.getSessionHandler().getSessionManager();

不再起作用,SessionHandler 上的 getSessionManager() 方法不再存在。在搜索了几个小时之后,我没有找到任何方法从 WebAppContext 到该上下文中的 HTTPSession 实例。 有人知道怎么去吗?

无法访问活动列表 HttpSession

发生此更改是为了支持会话管理的持续改进,以处理分布式 SessionDataStores 和没有会话粘性的分布式服务器(例如许多云产品)

您最好在服务器端跟踪 HttpSessions 自己。

怎么做...

实施自定义会话侦听器。 确保它实现了 javax.servlet.http.HttpSessionListenerjavax.servlet.http.HttpSessionIdListener 以跟踪创建/销毁/更改的会话。 (可能还想实施 javax.servlet.ServletContextListener 以停止跟踪已销毁上下文中的会话)

确保在此实现中仅通过会话的 ID(以及它们所属的上下文)跟踪会话。 (不要持有对 HttpSession 对象的引用,否则你会搞乱 GC 和 WebApp 生命周期)。

示例:

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());
    }
}

以及将其正确添加到所有上下文的方法...

// 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);
        }
    }
}

然后使用这些追踪器...

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 stored Sessions.