JSF and PrimeFaces: Session Timeout Handling
Implementing session timeout handling in an ajax web application can be hard. You have to redirect to a login page when a single ajax call fails after user session is invalid.
My solution is based on PrimeFaces idle monitor. User is redirected to logout action after timeout to invalidate the session. 2 minutes before a countdown dialog is shown to warn user. After moving the mouse again session is extended.
PrimeFaces idle monitor and dialog is placed in a template you can add to every page which is involved:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"> <ui:composition> <h:form prependId="false"> <p:idleMonitor timeout="#{session.maxInactiveInterval * 1000 - 125000}" onidle="startIdleMonitor()" onactive="timeoutDialog.hide()" /> <p:dialog id="timeoutSession" header="#{msg['session.expire']}" widgetVar="timeoutDialog" showEffect="fade" hideEffect="fade" modal="true" width="400" height="110" closable="false" draggable="false" resizable="false" appendToBody="true" onHide="stopCount()" onShow="doTimer()"> <br /> <p> <span class="ui-icon ui-icon-alert" style="float: left; margin: 8px 8px 0;"/> <p:panel> #{msg['logoff.soon.1']} <span id="dialog-countdown" style="font-weight: bold"></span> #{msg['logoff.soon.2']} </p:panel> </p> <br /> <p style="font-weight: bold;">#{msg['move.cursor']}</p> </p:dialog> <p:remoteCommand name="keepAlive" actionListener="#{auth.keepSessionAlive}" /> </h:form> <script type="text/javascript"> var TIME = 120; // in seconds var countTimer = TIME; var processTimer; var timer_is_on = 0; var redirectPage = "#{request.contextPath}/auth/j_verinice_timeout"; var countDownDiv = "dialog-countdown"; var txtCountDown = null; if (!txtCountDown) txtCountDown = document.getElementById(countDownDiv); function startIdleMonitor() { countTimer = TIME; txtCountDown.innerHTML = countTimer; timeoutDialog.show(); } function timedCount() { txtCountDown.innerHTML = countTimer; if (countTimer == 0) { stopCount(); window.location.href = redirectPage; return; } countTimer = countTimer - 1; processTimer = setTimeout("timedCount()", 1000); } function doTimer() { if (!timer_is_on) { timer_is_on = 1; timedCount(); } } function stopCount() { clearTimeout(processTimer); timer_is_on = 0; keepAlive(); } </script> </ui:composition> </html>
- Line 11: The timeout of the idle monitor is set by system var session.maxInactiveInterval. The value you set in your web.xml or server configuration.
- Line 12/13: Javascript method startIdleMonitor() is called after timeout without any user interaction. This method opens the dialog. timeoutDialog.hide() is called when user is busy again: Dialog is closed
- Line 26/27: Two more Javascript methods are called when dialog is shown or hide: doTimer() starts and stopCount() stops the countdown.
- Line 40: PrimeFaces remote command to keep session alive. By calling an arbitrary method on server the session is extended. Command is called by Javascript method keepAlive() in line 78.
- Line 59-68: Javascript method timedCount() is called every second to execute the countdown. After timeout redirect is done in line 63.
To activate timeout handling in multiple pages include the timeout template in your layout template:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xml:lang="de-DE"> <h:head> ... </h:head> <body> <ui:include src="/template/sessionTimeOut.xhtml" /> <ui:include src="/nav.xhtml"/>> <ui:insert name="content">Default content</ui:insert> <ui:include src="/footer.xhtml"/>> </body> </html>
A specific time out for your web application you can set in your web.xml:
<!--?xml version="1.0" encoding="UTF-8"?--> <web-app> ... <session-config> <!-- Session idle timeout in min. --> <session-timeout>30</session-timeout> </session-config> </web-app>
Hi,
Thanks for great example. What does your keepSessionAlive() backing bean function look like?
Calling keepSessionAlive() stops idle time for the user session on application server. Just call an arbitrary getter in this method. I think it can even be empty.
Thanks for the response! Actually an empty method didnt extend the session in my case.
I had to add:
FacesContext fc = FacesContext.getCurrentnstance();
HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
request.getSession();
to the keepSessionAlive() function
Now it works as expected.
Thanks again!
Take care that keepSessionAlive() is really called before session is expired by increasing the 120000 from line 11:
timeout=“#{session.maxInactiveInterval * 1000 – 125000}“
(I just changed the code sample in the article.)
dear murygin
this solution doesn’t work as idle monitor time out triggers only when that page is idle (i.e. not even mouse hovering on that page) and session will be timed out because of no request from page for specific time. to reproduce follow below steps
assumption:
* sessionMaxInterval is 3 minute (180 seconds)
* idle monitor timeout value is 125000 ms.
1. load the page move the mouse for 30 seconds
2. go to another page
3. idle monitor pop ups at 55th second which will count for 125 seconds
4. so total time here is 125+30+55 = 210 seconds
5. so if user moves mouse at last 2nd second of timer by that time session would already get invalidated.
Dear Kedar,
I am a beginner in JSF. Could you send to me an example of the classes used in this tutorial?
Dear Kedar,
The meaning of „idle“ for PrimeFaces is no action at all (including mouse motion). The meaning „idle“ for the server is no request from the client.
Unfortunately I don’t have a solution for this problem…
Daniel.
Hi
I dont understand the metrics…
#{session.maxInactiveInterval * 1000 – 125000}
session.maxInactiveInterval is minutes (30 in your example)
so the result is negative…..
How does it work ?
Time unit for session.maxInactiveInterval is milliseconds.
HI. Please help me.
I have this error.
Grave: JSF1073: se ha interceptado javax.faces.event.AbortProcessingException durante el procesamiento de INVOKE_APPLICATION 5 : UIComponent-ClientId=j_idt13, Mensaje=/Paginas/Entorno/sessionTimeOut.xhtml @33,48 actionListener=“#{auth.keepSessionAlive}“: Objetivo inalcanzable, identificador ‚auth‘ resuelto a nulo
jul 24, 2013 9:23:59 AM com.sun.faces.context.AjaxExceptionHandlerImpl log
Grave: /Paginas/Entorno/sessionTimeOut.xhtml @33,48 actionListener=“#{auth.keepSessionAlive}“: Objetivo inalcanzable, identificador ‚auth‘ resuelto a nulo
javax.faces.event.AbortProcessingException: /Paginas/Entorno/sessionTimeOut.xhtml @33,48 actionListener=“#{auth.keepSessionAlive}“: Objetivo inalcanzable, identificador ‚auth‘ resuelto a nulo
Hi, here is an update:
………..
…………………………..
…………
var TIME = 120; // in seconds
var maxSesion=“#{session.maxInactiveInterval}“
var countTimer = maxSesion;
var processTimer;
var timer_is_on = 0;
var redirectPage = „#{request.contextPath}/seguridad/login.xhtml“;
var countDownDiv = „dialog-countdown“;
var txtCountDown = null;
if (!txtCountDown)
txtCountDown = document.getElementById(countDownDiv);
function startIdleMonitor() {
if(timer_is_on){
txtCountDown.innerHTML = countTimer;
timeoutDialog.show();
}
}
function timedCount() {
txtCountDown.innerHTML = countTimer;
if (countTimer == 0) {
stopCount();
window.location.href = redirectPage;
return;
}
if(countTimer == TIME)
{
timer_is_on = 1;
}
countTimer = countTimer – 1;
processTimer = setTimeout(„timedCount()“, 1000);
}
function stopCount() {
clearTimeout(processTimer);
countTimer = maxSesion;
timer_is_on = 0;
keepAlive();
}
………………………..
This guarranted that time in page be less that sesion time, and reset when move the mouse
Better yet, use primefaces poll:
http://www.primefaces.org/showcase/ui/poll.jsf
This will let you call a keepAlive method every X seconds.
@rsisto Then your session never expires. For some applications this might be a problem.
You can see a complete implementation at this adress.
http://javacodetips.blogspot.com.tr/2014/03/how-to-implement-session-expired-alert.html
Thank you, great solution!
I have a question:
Consider I have set th Timeout for 30 minutes, thb that case: startIdleMonitor() will start from 28th minute.
Now user moves the Mouse(mot click) at 27th minute and startIdleMonitor() will still start at 28 minute and logs out at 30th minute.
Form user(who move the mouse)’s perspective the timer had reset to 30 minutes again, when he moved the mouse at 27th minute.
Wouldn’t he be surprised when system logs out, 3 minutes after he moved the mouse???
Hi,
Thanks for the solution.
I am getting the following JS error in startIdleMonitor()
ReferenceError: timeoutDialog is not defined
timeoutDialog.show();
Looks like the js is executing before the dialog is loaded? Did you come across this?
Hi,
I was able to fix my issue. From Primefaces 5.0 onwards, the JS call to show the dialog is:
PF(‚timeoutDialog‘).show();
instead of:
timeoutDialog.show();
Thank you!
Thanks for the solution. When I click the browser back button after the timer has stopped, the session is still present and the user can continue with the actions.
Is there a way to call session.invalidate in timedCount(). I tried different ways including clicking a hidden button to call a backend method, but it doesn’t get called.
Thank you!
Also what is the implementation of : clearTimeout(processTimer);
and processTimer = setTimeout(“timedCount()”, 1000);
hi
When do browser print then this session dialog message is alos including. How do I exclude the message.
how to handle Browser Multiple Tab issue?????
If multiple tabs are open and if one Tab is active but other Tabs are idle session is still going to expire. (In my application session is managed by Spring).