【问题标题】:Embedded jetty: display all sessions in context嵌入式码头:在上下文中显示所有会话
【发布时间】:2019-06-21 06:39:05
【问题描述】:

我最近将一个使用嵌入式 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() 方法不再存在。在搜索了几个小时之后,我没有找到任何方法从 WebAppContextHTTPSession 在该上下文中的实例。 有谁知道去那里的方法吗?

【问题讨论】:

    标签: jetty embedded-jetty httpsession


    【解决方案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

    【讨论】:

    • 谢谢,当我在 Jetty 9 中查看新的会话管理网络时,我就预料到了这一点。我已经有一个会话监听器,我将在那里添加会话跟踪。非常感谢代码和注释!
    • 你可能也想走进 HandlerWrapper(就像 HandlerCollection 一样)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 2012-05-30
    • 1970-01-01
    • 2014-02-25
    • 2019-09-25
    • 2019-04-18
    • 2018-03-01
    相关资源
    最近更新 更多