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

时间:2019-01-28 09:31:58

标签: jetty embedded-jetty httpsession

我最近将一个使用嵌入式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();

不再起作用,getSessionManager()上的SessionHandler方法不再存在。经过数小时的搜索,在这种情况下,我没有找到从WebAppContextHTTPSession实例的任何方法。 有人知道到达那里的方法吗?

1 个答案:

答案 0 :(得分:1)

无法访问有效的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.
        });
    });
}
  

请注意,以上内容仅会向您显示该服务器上正在跟踪的会话,如果您有多个服务器或分布式会话存储,则不会看到来自其他服务器的会话。   如果这对您很重要,请考虑编写自己的SessionDataStore,以便您访问存储的Sessions