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

27 comments:

iroh said...

Wow -- it worked great! Thanks a bunch for posting. Saved me a lot of thinking.

Anonymous said...

nice job, but i did have some problems with the session check method in the filter:

private boolean isSessionInvalid(HttpServletRequest httpServletRequest) {
boolean sessionInValid = httpServletRequest.getRequestedSessionId() == null
|| !httpServletRequest.isRequestedSessionIdValid();
return sessionInValid;
}

this one does work better.
/daniel

Anonymous said...

Hmmm...I tried this implementation (with the update from the previous comment) with this result:
-navigate to the site --> redirected to timeout page!
-navigate again to the site --> success!
-let session timeout and navigate in the site --> new session created...no timeout page!

Thoughts?

Dinesh said...

Hi,
Everything is OK, but I want to automatically display timeout page on session time out not on first request.

Regards,
Dinesh Gupta
dinesh.gupta01@hotmail.com

Ramya said...

Hi this is working...... But for popup it displays the login page within popup where in i need to close that and display the login in the parent window.. any idea how do we do this.......

Anonymous said...

I can't get this to work correctly at all.
Could we get an update from the author that addresses the above complaints?

TechieExchange said...

Hi,
Please let me know what your problems are. Post your exact problem. I would suggest you to try it step-by-step again. If still you could not make up, then post your problem.

Regards,
Techieexchange
http://techieexchange.wordpress.com/

Asha said...

I am also facing same problem, the first request is redirecting to timeout page!

Any thoughts?

Asha said...

I have created a filter like yours, but my application is not able to discern the first request, that has no session created yet, from a request that is made after session was destroyed. That causes my application to redirect to timeout jsp the first request i make to it, so i can't enter in my application.
How do you discern first request in your set up?

Anonymous said...

well,I tested your code but it did not work.

Here is how I tested.

I created a jsp with a textbox "name", and the user input name will be stored in session and retrieved later.

I opened the page and entered a name, which was stored in session. Then I waited for 2 minutes. At that moment, from the debugger, I can see the sessin attrubute is 0.
I assume then the filter will be called and the application will be directed to timeout page. However, the filter was not called at all. The application checked the seesion was empty and just throw a nullpointer exception.

Could you please explain why this happen?

Lincoln said...

You could set a cookie and check for that cookie to see if the session should be re-created or if the user needs to re-authenticate.

Lincoln

dacalty said...

hi.... from ur post i guess u must be a master in JSF... am using JSF rad 7.0...i am not sure about much of the concepts...when i follow yours my session is getting expired but again when i click on any link ,a new session is being created........ i dont know wat......actually my prob is during online examination after certain time answering facility should be blocked...say after 3 hrs his session should expire saying EXAM time out.... can u help me out!!!!!!!!!!

TechieExchange said...

@dacalty
Please try to understand Session Timeout. If user has no activity within a certain time limit (defined in web.xml), then the Session gets automatically expired and times out, which is different from pushing manual timeout after 3 hrs. for manual timeout you have to try someother way.

dacalty said...

hey i have worked with the session's automatic timeout for max inactive time.... and manual time out for exams by using getcurrent time and comparing .... not sure whether its an optimal way(is tat ok????)....
anyways i cant trigger tat event(getting current time) or others without a user action....i want it to be performed automatically.....trying to work on it....
and in Your code... my no function in filter is getting called...can u explain me why....anyways i solved it thru session class and used session.isnew() to detect whethr a new session.... should i use the filter compulsorily.....


thanks

prem

Anonymous said...

in response to the postings who were getting the timeout page on the initial request, you can modify the isSessionControlRequiredForThisResource() method as follows:

private boolean isSessionControlRequiredForThisResource(
HttpServletRequest httpServletRequest) {
String requestPath = httpServletRequest.getRequestURI();
boolean targetIsTimeoutPage
= StringUtils.contains(requestPath, getTimeoutPage());
boolean targetIsWelcomePage
= StringUtils.contains(requestPath, getWelcomePage());
boolean controlRequired //only when request target is NOT welcome or timeout
= !(targetIsWelcomePage || targetIsTimeoutPage) ;
return controlRequired;
}


By including a reference to the welcome page, as well as the separate timeout page, the method readily identifies both pages and determines that no control is neccessary. hth, and a big thanks to TechieExchange for posting the solution.

Anonymous said...

The code works great, but didn't redirect in ajax requests. I am using Richfaces, is there any way around that redirect works on ajax too?

I tried redirect using faces config context, but no success.

Stefan said...

Hi

how can I combine this with an login application and restricted pages?

Stefan

makkes said...

Hi TechieExchange,

thanks for that clean solution but I'm having a problem with the implementation:

I've included some debug messages to track down the problem and I see that sessionDestroyed() is being called properly. But when I then reload the page (so sending the same old session ID stored in the cookie) the session gets created again automatically with that same ID. So isRequestedSessionIdValid() always returns true.

Do you know where that behaviour comes from?

TIA,
Max

raviz said...

Hey,

We have implemented similar way of session timeout in our application using filter in web.xml.

But we are facing one problem with this way of timeout.

Once Session expires proper logout doesn’t not happen while clicking on tabs created in jspx using tr:panelTabbed tags


This is very specifically for the tabs and everything works fine while clicking on link or buttons etc

In server logs we get Session Timed Out message, however at UI nothing happens, same teb is displayed and never logs out.

Additionally we get the below message in logs:-

[3/4/09 16:19:01:125 IST] 00000035 StateManagerI E org.apache.myfaces.trinidadinternal.application.StateManagerImpl restoreView Could not find saved view state for token -1909e0a

Any help or pointers would be appreciated.

Thanks in Advance,
Ravi

Anonymous said...

Exactly what I needed. Thank you so much !!!

I only made one change in isSessionControlRequiredForThisResource():
return requestPath.endsWith(".faces") && !StringUtils.contains(requestPath, getTimeoutPage());

Mahe said...

How do we popup a message saying that your session is abt to expire (showing the time) if user selects yes, we will continue the session else we will expire the session.

Please let me know your suggestion on the above case.

Thanks,
Mahesh Dasari

StBusch said...

Hi,
I used this solution to manage my timeouts and it works nearly perfect for me. Thank You!
The remaining problem is: What happens when I get a timeout at the timeoutpage? I redirect to the loginpage. But when I get a timeout here, the filter doesnt catch it and and I get a ViewExpiredException.
I only use the SessionTimeoutFilter, not the SessionListener.
Any idea how to solve this?

thanks in advance,
StBusch.

Anonymous said...

Great Thanks

Alex C said...

Hi. Great article. How did you add the "View Plain" and "Print" buttons to the top of your code blocks? Thanks.

Anonymous said...

Very usefull!!!
Thanks

Anonymous said...

Hi, i try the code it works but if i run the timeoutpage and wait for 2 min and it show me the timeoutpage again how to prevent it?

Anonymous said...

Hi, When i timeout on the timeoutpage(login page), and i try to submit it will prompt me the timeputpage again. Anyone can help me this?