我最近将一个使用嵌入式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
方法不再存在。经过数小时的搜索,在这种情况下,我没有找到从WebAppContext
到HTTPSession
实例的任何方法。
有人知道到达那里的方法吗?
答案 0 :(得分:1)
无法访问有效的HttpSession
列表。
发生此更改是为了支持持续改进会话管理,以处理分布式SessionDataStores
和没有会话粘性的分布式服务器(例如许多云产品)
最好在服务器端自己跟踪HttpSessions
。
如何执行此操作...
实施自定义会话监听器。
确保它同时实现javax.servlet.http.HttpSessionListener
和javax.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
。