FacesContext is only available in the stream serving the HTTP request initiated by the web browser that called FacesServlet . During a session, destruction does not necessarily mean an HTTP request. Sessions are usually destroyed by the background thread controlled by the container. This does not trigger an HTTP request through FacesServlet . Therefore, you should not expect that FacesContext will always be present during the destroy session. Only when you call session.invalidate() inside a JSF managed bean is FacesContext available.
If your managed application is controlled by a bean, controlled by JSF @ManagedBean , then itβs good to know that JSF stores it under covers as a ServletContext attribute. ServletContext in turn, is available in the HttpSession#getServletContext() session listener.
So this should do:
@Override public void sessionDestroyed(HttpSessionEvent se) { HttpSession session = se.getSession(); User user = userService.findBySessionId(session.getId()); ApplicationScopedBean appBean = (ApplicationScopedBean) session.getServletContext().getAttribute("appBean"); appBean.getConnectedUsers().remove(user); }
If you use a container compatible with Servlet 3.0, the alternative is to simply let your application have a bean to implement the HttpSessionListener and register itself as such when building. This way you have a direct reference to the connectedUsers property.
@ManagedBean @ApplicationScoped public class AppBean implements HttpSessionListener { public AppBean() { ServletContext context = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext(); context.addListener(this); } @Override public void sessionDestroyed(HttpSessionEvent se) { HttpSession session = se.getSession(); User user = userService.findBySessionId(session.getId()); connectedUsers.remove(user); }
Again, another alternative is to save the User in the session area as a session-controlled bean. You can then use the @PreDestroy annotation to mark the method that should be called when the session is destroyed.
@ManagedBean @SessionScoped public class User { @ManagedProperty("#{appBean}") private AppBean appBean; @PreDestroy public void destroy() { appBean.getConnectedUsers().remove(this); }
This has the added benefit that User is in an EL context, available as #{user} .
source share