Tips on Dealing with the Session Time Out Popup

Introduction

During performance tuning engagements, the A-Team frequently recommends that the HTTP session timeout value for WebCenter custom portal applications be set to a lower value (for example:10 minutes) than the out-of-the-box value of 45 minutes.  The lower number helps the server stay performant, particularly during significant load.  The longer the session time, the longer the server will have to hold memory until the inactive session(s) expires.  Configuring the session timeout value is simple and can be done through the web.xml file:

<session-config>
  <session-timeout>10</session-timeout>
</session-config>

Once this value is set, a warning popup will appear, with a default value of two seconds, before the session is set to expire.  After the warning has expired, the final page-expire message appears.

SessionTimeOut_1

Having these messages appear is usually justified in many custom portal deployments.  However, in the majority of custom portals, a session is equivalent to being logged in.  So in the instance of a public user, the appearance of these messages may not be an expected behavior.  The good news is that there is an easy configuration to disable these messages and keep them from appearing.

Main Article

The following configuration settings in the web.xml do all of the work:

<context-param>
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
  <param-value>client</param-value>
</context-param>

<context-param>
  <param-name>
     oracle.adf.view.rich.sessionHandling.WARNING_BEFORE_TIMEOUT
  </param-name>
    <param-value>0</param-value>
</context-param>

The STATE_SAVING_METHOD value is set to client, and the value correlates to the value needed for a page’s af:document tag.  This tag, has the stateSaving property set to default, which means it gets the value from the init-context-param.  Notice that the WARNING_BEFORE_TIMEOUT value has been set to 0.  Out-of-the-box, the default value is 120 seconds.

Any value less than the default will disable the popup.

The unfortunate news is that this configuration only keeps the popup message from appearing.  Any subsequent page action, partial-page event, or navigation through an af:commandLink will produce the un-handled, ViewExpiredException (ADF_FACES-60098) in the Faces, RESTORE_VIEW, lifecycle:

SessionTimeOut_2

The issue is also detailed in the log message:

javax.faces.application.ViewExpiredException: viewId:/programs – ADF_FACES-30108:The view state of the page has expired because of inactivity.  Reload the page.
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._restoreView(LifecycleImpl.java:650)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._executePhase(LifecycleImpl.java:301)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:186)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)

For a WebCenter custom portal application the use of goLinks that use the navigation model based prettyURL as the destination is preferred.  goLinks will not have this particular issue, since the framework will handle creating a new state.

One way of handling this would be to extend the portal application to support a custom exception handler.  In this blog post, I would like to introduce an alternative approach.

The solution would use a servlet filter to handle the exception and then enable the application to control the recovery destination.  For example, one scenario is to be able to either return to the page that I was previously on, or navigate to the page that I have chosen through one of the (navigation model) page links.  Since this solution is based on custom code, there could be also support for other use cases.  Here is the code, which supports the scenario that I have mentioned:

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SessionExpiryFilter implements Filter {
    private FilterConfig _filterConfig = null;

    public SessionExpiryFilter() {
        super();
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        _filterConfig = filterConfig;
    }

    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException,
                                                         ServletException {
        String requestedSession =
            ((HttpServletRequest)servletRequest).getRequestedSessionId();
        String currentWebSession =
            ((HttpServletRequest)servletRequest).getSession().getId();
        String requestURI =
            ((HttpServletRequest)servletRequest).getRequestURI();
        boolean sessionOk =
            currentWebSession.equalsIgnoreCase(requestedSession);
        System.out.println("currentWebSession == requestedSession? : " + sessionOk);
        if (!sessionOk && requestedSession != null) {
            ((HttpServletResponse)servletResponse).sendRedirect(requestURI);
            System.out.println("redirecting to : " + requestURI);
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
            System.out.println("session is OK");
        }
    }

    public void destroy() {
        _filterConfig = null;
    }
}

Basically, the code compares the value of the session IDs.  If the IDs do not match, then a redirect executes to the page that is determined by the getRequestURI().  Otherwise, the normal handling of the request is done.  The following is how the servlet filter is registered with the application through the web.xml file:

<filter>
 <filter-name>AppSessionExpiryFilter</filter-name>
   <filter-class>ateam.sample.SessionExpiryFilter</filter-class>
 </filter>
<filter-mapping>
  <filter-name>AppSessionExpiryFilter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

Both the popup and inactive session error messages can be easily managed.  However, since WebCenter (really ADF) is a stateful application framework, once the session has been timed out (invalidated), the state of the application is lost as well.  This means any managed beans, ADF bindings, .etc would reset to the initial state.  In addition, any information that was being persisted in a managed bean would also be lost.

Add Your Comment