Our web application runs with JBoss 7.1.1 and Java (JPA2 and RichFaces4). At the moment the problem is that the application just gets stuck if the user is logged in but doesn’t do anything inside the application for some time (probably caused due to session timeout). Then the user has to load the web application again, which doesn’t look very professional.
Can you give me a hint, how an automatic logout could be implemented using the mentioned technologies?
[UPDATE]
I tried a lot of possibilities but none of them work properly. My thought is to implement a SessionTimeoutListener which knows when the session gets expired:
@Logged
@WebListener
@Named
public class SessionTimeoutListener implements HttpSessionListener
{
@Inject
private Logger logger;
@Override
public void sessionCreated(HttpSessionEvent event)
{
// not used.
}
@Override
public void sessionDestroyed(HttpSessionEvent event)
{
logger.info("Session destroyed.");
ApplicationContext.setAutoLoggedOut(true);
}
}
This works. But then all the problems appear: I cannot redirect from it because FacesContext is null in there. I cannot fire push events because I get some exceptions like NameNotFoundException or similar (I tried a lot but it seemed that firing events doesn’t work out of this too). Then I tried a4j:poll on ApplicationContext.isAutoLoggedOut() but this doesn’t work either, because if I execute poll events, the session would never expire. I always come to a dead end. If I could redirect somehow from SessionTimeoutListener, this would be the solution.
[POSSIBLE SOLUTION]
I’m now satisfied with a logout which is executed when I click on any button inside the view after session is expired. This current solution is only rudimentary and not applicable for production yet, but this solution works and I will build on it:
I use the upper SessionTimeoutListener. Moreover I use a PhaseListener which is being invoked after the SessionTimeoutListener, so if the session expires, the SessionTimeoutListener will be invoked and destroys the session but the SessionTimeoutPhaseListener still has the FacesContext. So I can redirect there to logout page:
public class SessionTimeoutPhaseListener implements PhaseListener
{
private static final long serialVersionUID = -8603272654541248512L;
@Override
public void beforePhase(PhaseEvent event)
{
//not used.
}
@Override
public void afterPhase(PhaseEvent event)
{
FacesContext facesContext = event.getFacesContext();
if (ApplicationContext.isAutoLoggedOut())
{
ApplicationContext.setAutoLoggedOut(false);
try
{
facesContext.getExternalContext().redirect("./logout.xhtml");
}
catch (IOException e)
{
}
}
}
@Override
public PhaseId getPhaseId()
{
return PhaseId.RESTORE_VIEW;
}
}
The ApplicationContext is a class with @ApplicationScoped which stores the boolean variables, but this has to be changed, because it would affect every user who currently works with the application. I think about some “ThreadLocal context” to solve that. I still have to distinguish between auto logout and manual logout. The listener is invoked in both cases. Although this solution works at the moment, the redirection in PhaseListener is also tricky, because it would be invoked over and over again by JSF (which causes redirection loop errors in browser), if I wouldn’t set “autoLoggedOut” to false…. as I said, only rudimentary, but using the PhaseListener is probably the only suitable solution.
If you want to handle the ViewExpiredException, you could add the following setting to your web.xml
Better yet, use a PhaseListener tio check if the User is still available in the Session.
If not – navigate to the login page.