Home

Advertisement

JSF Session Timeouts

If the application handles its own authentication, one can detect session timeouts by initializing a "User" object or token in the session immediately after authenticating and checking to see whether it is null using a filter. (There are some problems with using a filter, namely that you must exclude the login page request URI from the null user check to avoid endless dispatch loops.) In JSF this would mean storing the "User" object/token in a property of a session-scoped managed bean. (If the "User" object is itself a session-scoped managed bean, then JSF will automatically initialize it upon request, so it will never be null. One can still check the properties of a session-scoped user object for null values to detect a session timeout.)

But what if the application does not handle its own authentication? Suppose the application server is configured with a JAAS LoginModule and the web application is configured to use basic authentication? The user principal is now part of the request. It could be stored in the session, but the application is completely unaware of when authentication occurs and does not control the initial page the user sees. (The user can navigate to any URI to trigger authentication and will be served that URI if authenticated.) Worse, authentication is not tied to the session, so the session can timeout several times before the user is actually "logged out" (by closing the browser.)

So if JSF creates session-scoped managed beans on demand, how does one detect whether the bean was just created? One approach would be to assume that if certain properties in the session scoped bean are null then the bean hasn't been properly initialized and must be new. (A better way of looking at this is if a certain property is null then the session bean is in an invalid state, and the user must restart a transaction.) This might work, but it would require manipulation of properties that may not have anything to do with the application, and will most likely result in a kludgy workaround.

And supposing that a session timeout can be detected, where should it be detected? How should it be handled? Ideally the user should be redirected to a "timeout" page and be given the ability to return to a stable state. Can this be accomplished using a filter?

Using filters in JSF is problematic for several reasons. First, before processing the filter chain there is no FacesContext. On the request side, filters are invoked before FacesServlet, so there is no reasonable expectation that a FacesContext or any of its facilities will be available. Detecting timeouts on the response side is useless, because by then the application has barfed with all kinds of NullPointerExceptions due to the timeout. Finally, filters operate on ServletRequest and ServletResponse, so there are all kinds of casts and checks that have to be made before HTTP properties can be used. Filters are brittle for HTTP applications in general and for JSF applications in particular.

A JSF PhaseListener provides more granular control over the JSF lifecycle, and, more importantly, a FacesContext. The session timeout needs to be detected as early as possible, so the detection code should be implemented during the RESTORE_VIEW phase in the beforePhase method:

faces-config.xml
<faces-config> <lifecycle> <phase-listener> SessionTimeoutPhaseListener </phase-listener> </lifecycle> </faces-config>

SessionTimeoutPhaseListener.java
public class SessionTimeoutPhaseListener { public void beforePhase(PhaseEvent event) { // Session detection code goes here } public void afterPhase(PhaseEvent event) { // Do nothing } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }

Session-scope managed beans are created on demand, but JSF still provides control over whether a session is instantiated. The getSession(boolean) method of ExternalContext will return null if there is no session:

SessionTimeoutPhaseListener.java
public class SessionTimeoutPhaseListener { public void beforePhase(PhaseEvent event) { FacesContext facesCtx = event .getFacesContext(); ExternalContext extCtx = facesCtx .getExternalContext(); HttpSession session = (HttpSession)extCtx .getSession(false); boolean newSession = (session == null) || (session.isNew()); } public void afterPhase(PhaseEvent event) { // Do nothing } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }

But there is one problem with this approach. On the first request to the application, there is no session. The user shouldn't be presented with a timeout message the first time he accesses the application. Note that the only time it really matters whether a user's session has timed out is when a form is posted back to the application and there is no backing bean property to update or action method to invoke. (Generally speaking, the application will barf during the APPLY_REQUEST_VALUES or UPDATE_MODEL_VALUES phases before it even gets to INVOKE_APPLICATION or RENDER_RESPONSE.) So whether the request is a postback can be approximated by determining whether there are any request parameters:

SessionTimeoutPhaseListener.java
public class SessionTimeoutPhaseListener { public void beforePhase(PhaseEvent event) { FacesContext facesCtx = event .getFacesContext(); ExternalContext extCtx = facesCtx .getExternalContext(); HttpSession session = (HttpSession)extCtx .getSession(false); boolean newSession = (session == null) || (session.isNew()); boolean postback = !extCtx.getRequestParameterMap().isEmpty(); boolean timedout = postback && newSession; if(timedout) { // Handle timeout } } public void afterPhase(PhaseEvent event) { // Do nothing } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }

Finally, once a timeout has been detected, something has to happen. The ViewHandler can be used to render a timeout page and end the request:

SessionTimeoutPhaseListener.java
public class SessionTimeoutPhaseListener { public void beforePhase(PhaseEvent event) { FacesContext facesCtx = event .getFacesContext(); ExternalContext extCtx = facesCtx .getExternalContext(); HttpSession session = (HttpSession)extCtx .getSession(false); boolean newSession = (session == null) || (session.isNew()); boolean postback = !extCtx .getRequestParameterMap().isEmpty(); boolean timedout = postback && newSession; if(timedout) { Application app = facesCtx.getApplication(); ViewHandler viewHandler = app.getViewHandler(); UIViewRoot view = viewHandler.createView( facesCtx, "/sessionTimeout.xhtml"); facesCtx.setViewRoot(view); facesCtx.renderResponse(); try { viewHandler.renderView(facesCtx, view); facesCtx.responseComplete(); } catch(Throwable t) { throw new FacesException( "Session timed out", t); } } } public void afterPhase(PhaseEvent event) { // Do nothing } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }

Note that renderView and responseComplete must be invoked to prevent the JSF Lifecycle from continuing on with the RESTORE_VIEW processing, which will cause the originally requested page to be used instead, which obviates any of the work done in the PhaseListener.

Tags:

Comments

(Anonymous)

Did you figure out any way to work around non jsf requests?

Hi, i'm thinking you are probably not going to answer this one question given the age of this post, but I'm trying anyways =P, so the subject pretty much says what my problem is. Anyways thank you very much for this post here it is very usefull to me.

Cheers

.:WolfEng:.

(Anonymous)

You da man.....

This article is right on time for my project and make you the sweetest in the land in my estimation. But enough idolatry, so I just make this class a session scoped managed bean and I'm good to go right? It catches the timeout event and logs out.


Kudos, Cheers, We are not worthy....

Thanks.

Re: You da man.....

I think so ... it's been so long since I wrote this I'm not sure. This bit of code is actually obsolete since JBoss Seam came out ... they have a much better mechanism for catching and handling timeouts in JSF. If you haven't heard about it yet, I'd recommend checking it out at http://www.seamframework.org (and I'd definitely recommend using Facelets.)

(Anonymous)

Re: You da man.....

Right on, but I'm stuck with JDeveloper working for the government and they don't let you change you're toolkit more than once a decade (if you're lucky). No worries, I'll try it out, I've worked with a PhaseListener before and it was pretty reliable except when JDeveloper's ADF Faces context takes over which has a different lifecycle, but yeah, it's close enough for government work.

Thanks.

(Anonymous)

Great article, but not enough

How about put :post url and ajax request in the this article

(Anonymous)

Help

I am very new to JSF, you are passing event, how do i pass this event and from where should i pass it from? what will be the contents of this event?

(Anonymous)

Help

Hey,

I finally understood and configured your listener and added entry in faces.config.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 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.

(Anonymous)

First of all:

Many thx - this helped me a great deal!

However, one small addtion:

When I first used your code (under MyFaces 1.1 with Tomahawk, don't know whether or not that plays a role), it didn't do anything at all. Using a debugger showed that it actually was never called.

This changed when I added " implements PhaseListener" to the class definition. From then on, it worked perfectly.

(Anonymous)

THKS

Thank's a lot for this article, it helped me a lot...

(Anonymous)

help please

I have a problem implementing this code... It works fine but when I run the application the first time it redirects me at /sessionTimeout.jsp
I havent change anything I just use jsps in my application. The version of tomcat is 6.
Please help if somebody knows the answer.
Thks.

January 2007

S M T W T F S
 123456
78910111213
14151617181920
21222324252627
28293031   
Powered by LiveJournal.com

Advertisement

Customize