Friday, January 16, 2015

JSF and PrimeFaces: Time Counter For Session Timeout Handling

      In generally, Developers can define their web applications session timeout at the web.xml as following.

web.xml
 <session-config>
  <session-timeout>10</session-timeout>
 </session-config>

      For detection session expired in web application, developers can use HttpSessionListener to detect session's expiration immediately whenever the container invoke the HttpSessionListener#sessionDestroyed event.
The below example is applyed for detecting data in session before timeout.

/**
 *
 * @author Wittakarn
 */
public class SimpleSessionListener implements 
        HttpSessionListener, Serializable {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session created : " + se.getSession().getId());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        UserSession user = (UserSession) session.getAttribute("userSession");

        if(user != null)
            System.out.println("login user :" + user.getUsername());
        
        /*Do some bussiness logic such as save audit logs to db, 
        save logout history, and so on.*/
        
        System.out.println("session destroyed :" + 
                session.getId() + " Logging out user...");
    }
}

      At last, definition session listener in web.xml.
<listener>
  <listener-class>
    com.wittakarn.listener.SimpleSessionListener
  </listener-class>
</listener>


      However, detection session expired by using HttpSessionListener cannot resolve some user's requirements. In the case of requirement, that require to popup dialog for notifing before session out. In this proposed, I would like to present Time Counter For Session Timeout Handling by applying JSF and primefaces 5.1.

index.xhtml

index.xhtml











<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <f:facet name="first">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <meta name="viewport" 
                  content="user-scalable=no, 
                  width=device-width, 
                  initial-scale=1.0, 
                  maximum-scale=1.0"/>
            <meta name="apple-mobile-web-app-capable" content="yes" />
        </f:facet>

        <title>Login</title>
    </h:head>

    <h:body>
        <h:form>
            <p:outputLabel value="user"/>
            <p:inputText value="#{loginBean.user.username}"
                         required="true"/>
            <br/>
            <p:outputLabel value="password"/>
            <p:password value="#{loginBean.user.password}"
                        required="true"/>
            <br/>
            <p:commandButton value="Login" 
                             process="@form" 
                             update="@form"
                             action="#{loginBean.submit}"/>
        </h:form>
    </h:body>
</html>

display.xhtml

display.xhtml












<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <f:facet name="first">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <meta name="viewport" 
                  content="user-scalable=no, 
                  width=device-width, 
                  initial-scale=1.0, 
                  maximum-scale=1.0"/>
            <meta name="apple-mobile-web-app-capable" content="yes" />
        </f:facet>

        <title>Display</title>
    </h:head>

    <h:body>
        <h:form>
            <p:outputLabel value="user "/>
            <p:outputLabel value="#{loginBean.user.username}"/>
            <p:outputLabel value=" login success..."/>

            <p:idleMonitor timeout="5000"
                           onidle="PF('alertExpire').show();">
            </p:idleMonitor>
        </h:form>

        <p:dialog header="Basic Dialog" 
                  widgetVar="alertExpire" 
                  onShow="startIdleMonitor()">
            <h:outputText value="Your browser session is expiring in " />
            <h:outputText id="dialog-countdown" style="font-weight: bold;"/>
            <h:outputText value=" second." />
            <br/>
            <h:outputText value="Do you want to continue your session?" />
            <h:form>
                <p:commandButton value="Yes" 
                                 process="@this" 
                                 update="@all"
                                 onclick="stopCount()"/>
                <p:commandButton value="No" 
                                 process="@this" 
                                 update="@all"
                                 action="#{loginBean.logout}"/>
                <p:remoteCommand name="logout" action="#{loginBean.logout}" />
            </h:form>
            <script type="text/javascript">
                var TIME = 10; // in seconds
                var countTimer = TIME;
                var processTimer;
                var timer_is_on = 0;

                var countDownDiv = "dialog-countdown";
                var txtCountDown = null;
                if (!txtCountDown)
                    txtCountDown = document.getElementById(countDownDiv);

                function startIdleMonitor() {
                    countTimer = TIME;
                    txtCountDown.innerHTML = countTimer;
                    doTimer();
                }
                function timedCount() {
                    txtCountDown.innerHTML = countTimer;
                    if (countTimer == 0) {
                        stopCount();
                        logout();
                        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;
                }
            </script>
        </p:dialog>
    </h:body>
</html>

LoginBean.java
/**
 *
 * @author Wittakarn
 */
@ViewScoped
@ManagedBean(name = "loginBean")
public class LoginBean {
    @ManagedProperty(value = "#{userSession}")
    private UserSession user;

    public UserSession getUser() {
        return user;
    }

    public void setUser(UserSession user) {
        this.user = user;
    }
    
    public String submit(){
        return "display.xhtml";
    }
    
    public String logout(){
        FacesContext.getCurrentInstance()
                .getExternalContext()
                .invalidateSession();
        return "/index.xhtml?faces-redirect=true";
    }
}

UserSession.java
/**
 *
 * @author Wittakarn
 */
@SessionScoped
@ManagedBean(name = "userSession")
public class UserSession implements Serializable{
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}