When working with JSF 2.0 you will encounter a situation in which the user’s session times out and ajax requests fail. The response on a ajax request will be a viewExpiredException. However, the root cause is the session has expired. They are essentially stuck on the page and are forced to reload.
The solution:
Using a WebFilter the user can gracefully be redirected to a view expired exception page. This solution is accomplished by checking if the users session is valid, and if the context path is within our required conditions, finally if it is a ajax request it overwrites the default JSF response with our own custom xml response that tells the browser to redirect to the view expired exception page.
@WebFilter(filterName = "SessionTimeoutFilter", urlPatterns = "*.jsf") public class SessionTimeoutFilter implements Filter { private final String timeoutPage = "/errors/viewExpired.xhtml"; private final String loginPage = "/login.jsf"; @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; if (isRequireSessionControl(httpServletRequest) && isSessionInvalid(httpServletRequest)) { if (isAJAXRequest(httpServletRequest)) { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><partial-response><redirect url=\"") .append(httpServletRequest.getContextPath() + timeoutPage).append("\"></redirect></partial-response>"); httpServletResponse.setHeader("Cache-Control", "no-cache"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/xml"); PrintWriter pw = response.getWriter(); pw.println(sb.toString()); pw.flush(); return; } httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + timeoutPage); return; } } filterChain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } private boolean isAJAXRequest(HttpServletRequest request) { boolean check = false; String facesRequest = request.getHeader("Faces-Request"); if (facesRequest != null && facesRequest.equals("partial/ajax")) { check = true; } return check; } private boolean isRequireSessionControl(HttpServletRequest httpServletRequest) { String requestPath = httpServletRequest.getRequestURI(); return !requestPath.contains(timeoutPage) && !requestPath.contains(loginPage) && !requestPath.contains("javax.faces.resource"); } private boolean isSessionInvalid(HttpServletRequest httpServletRequest) { return httpServletRequest.getRequestedSessionId() != null && !httpServletRequest.isRequestedSessionIdValid(); } }
Note: This fix is required due to a bug that is set to be resolved in JSF 2.3 release: https://java.net/jira/browse/