Monday, February 25, 2008

JSF Session Expiry Timeout Solution

With JSF, a clean Session expiry or timeout is not easy to implement. So, I would like to post a solution that you can integrate it as out-of-box with your JSF applications.


Step 1:



/**
* When the user session timedout,
* ({@link #sessionDestroyed(HttpSessionEvent)})method will be invoked.
* This method will make necessary cleanups (logging out user,
* updating db and audit logs, etc...)
* As a result; after this method, we will be in a clear
* and stable state. So nothing left to think about
* because session expired, user can do nothing after this point.
*
* Thanks to hturksoy
**/

public class MySessionListener implements HttpSessionListener {

public MySessionListener() {

}

public void sessionCreated(HttpSessionEvent event) {

System.out.println("Current Session created : "
+ event.getSession().getId()+ " at "+ new Date());

}

public void sessionDestroyed(HttpSessionEvent event) {

// get the destroying session...

HttpSession session = event.getSession();

System.out.println("Current Session destroyed :"
+ session.getId()+ " Logging out user...");

/*

* nobody can reach user data after this point because
* session is invalidated already.
* So, get the user data from session and save its
* logout information before losing it.
* User's redirection to the timeout page will be
* handled by the SessionTimeoutFilter.
*/

// Only if needed

try {

prepareLogoutInfoAndLogoutActiveUser(session);

} catch(Exception e) {

System.out.println("Error while logging out at session destroyed : "
+ e.getMessage());

}

}

/**
* Clean your logout operations.
*/

public void prepareLogoutInfoAndLogoutActiveUser(HttpSession httpSession) {

// Only if needed

}

}





Step 2:



/**
* When the session destroyed, MySessionListener will
* do necessary logout operations.
* Later, at the first request of client,
* this filter will be fired and redirect
* the user to the appropriate timeout page
* if the session is not valid.
*
* Thanks to hturksoy
*
*/

public class SessionTimeoutFilter implements Filter {

// This should be your default Home or Login page
// "login.seam" if you use Jboss Seam otherwise "login.jsf"
// "login.xhtml" or whatever

private String timeoutPage = "login.seam";

public void init(FilterConfig filterConfig) throws ServletException {

}

public void doFilter(ServletRequest request,
ServletResponse response, FilterChain filterChain) throws IOException,ServletException {

if ((request instanceof HttpServletRequest)
&& (response instanceof HttpServletResponse)) {

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

HttpServletResponse httpServletResponse = (HttpServletResponse) response;

// is session expire control required for this request?

if (isSessionControlRequiredForThisResource(httpServletRequest)) {

// is session invalid?

if (isSessionInvalid(httpServletRequest)) {

String timeoutUrl = httpServletRequest.getContextPath()
+ "/" + getTimeoutPage();

System.out.println("Session is invalid! redirecting to timeoutpage : " + timeoutUrl);

httpServletResponse.sendRedirect(timeoutUrl);

return;

}

}

}

filterChain.doFilter(request, response);

}

/*
* session shouldn't be checked for some pages.
* For example: for timeout page..
* Since we're redirecting to timeout page from this filter,
* if we don't disable session control for it,
* filter will again redirect to it
* and this will be result with an infinite loop...
*/

private boolean isSessionControlRequiredForThisResource(HttpServletRequest httpServletRequest) {

String requestPath = httpServletRequest.getRequestURI();

boolean controlRequired = !StringUtils.contains(requestPath, getTimeoutPage());

return controlRequired;

}

private boolean isSessionInvalid(HttpServletRequest httpServletRequest) {

boolean sessionInValid = (httpServletRequest.getRequestedSessionId() != null)

&& !httpServletRequest.isRequestedSessionIdValid();

return sessionInValid;

}

public void destroy() {

}

public String getTimeoutPage() {

return timeoutPage;

}

public void setTimeoutPage(String timeoutPage) {

this.timeoutPage = timeoutPage;

}

}


Step 3:



Web.xml



com.fpc.carconfig.session.MySessionListener




SessionTimeoutFilter

com.fpc.carconfig.session.SessionTimeoutFilter





SessionTimeoutFilter
*.seam
// Remember to use your correct URL pattern



// Thats it!!!


Step 4:



To check whether this solution works:
Change session timeout to 1 minute in web.xml like this:



1




Feel free to share your comments.

Check more technical articles and tutorials here:
http://techieexchange.wordpress.com