During performancing tuning engagements, the ATEAM frequently recommends that the HTTP session timeout value for WebCenter custom portal applications be set to 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:
<session-config>
session-timeout>10</session-timeout>
</session-config>
Once this value is set, a warning popup will appear (default) two seconds before the session is set to expire. After the warning has expired, the final page expire message appears.
Having these messages appear are usually justified in many custom portal deployments. However, in other custom portals, a session can equivalate 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 a easy configuration setting to disable these messages from appearing. 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 value needed for a page's af:document tag. This tag, has the stateSaving property set to "default", which means get the value from the context param. Notice in 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 bad news is that the setting only handles the popup. Any subsequent page action, partial-page event, or navigation through a commandLink* will produce the unhandled ViewExpiredException (ADF_FACES-60098) in the Faces, RESTORE_VIEW, lifecycle:
* Note: For a WebCenter custom portal application the use of goLinks that use the navigation model based prettyURL as the destination is preferred. In addition, goLinks would not have this issue.
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 enable the application to control where the application would recover to. 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 chosen through one of the (navigation model) page links. Since this the 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:
package ateam.sample;
import java.io.IOException;
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:
<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>
One important thing to note. 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.