Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request
Exceptions which are thrown during ajax requests have by default totally no feedback in the client side. Only when you run Mojarra with project stage set to Development
and use <f:ajax>
, then you will get a bare JavaScript alert with the exception type and message. But other than that, and in PrimeFaces, there's by default no feedback at all. You can however see the exception in the server log and in the ajax response (in the webbrowser's developer toolset's "Network" section).
You need to implement a custom ExceptionHandler
which does basically the following job when there's a ViewExpiredException
in the queue:
String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));context.getPartialViewContext().setRenderAll(true);context.renderResponse();
Alternatively, you could use the JSF utility library OmniFaces. It has a FullAjaxExceptionHandler
for exactly this purpose (source code here, showcase demo here).
See also:
A merge between the answer of @BalusC and this post, I solved my problem!
My ExceptionHandlerWrapper:
public class CustomExceptionHandler extends ExceptionHandlerWrapper { private ExceptionHandler wrapped; CustomExceptionHandler(ExceptionHandler exception) { this.wrapped = exception; } @Override public ExceptionHandler getWrapped() { return wrapped; } @Override public void handle() throws FacesException { final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); while (i.hasNext()) { ExceptionQueuedEvent event = i.next(); ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource(); // get the exception from context Throwable t = context.getException(); final FacesContext fc = FacesContext.getCurrentInstance(); final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap(); final NavigationHandler nav = fc.getApplication().getNavigationHandler(); //here you do what ever you want with exception try { //log error ? //log.log(Level.SEVERE, "Critical Exception!", t); if (t instanceof ViewExpiredException) { requestMap.put("javax.servlet.error.message", "Session expired, try again!"); String errorPageLocation = "/erro.xhtml"; fc.setViewRoot(fc.getApplication().getViewHandler().createView(fc, errorPageLocation)); fc.getPartialViewContext().setRenderAll(true); fc.renderResponse(); } else { //redirect error page requestMap.put("javax.servlet.error.message", t.getMessage()); nav.handleNavigation(fc, null, "/erro.xhtml"); } fc.renderResponse(); // remove the comment below if you want to report the error in a jsf error message //JsfUtil.addErrorMessage(t.getMessage()); } finally { //remove it from queue i.remove(); } } //parent hanle getWrapped().handle(); }}
My ExceptionHandlerFactory:
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory { private ExceptionHandlerFactory parent; // this injection handles jsf public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) { this.parent = parent; } @Override public ExceptionHandler getExceptionHandler() { ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler()); return handler; }}
My faces-config.xml
<?xml version='1.0' encoding='UTF-8'?><faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"> <factory> <exception-handler-factory> your.package.here.CustomExceptionHandlerFactory </exception-handler-factory> </factory></faces-config>
I am using Mojarra 2.1.7 in Production mode with JBoss 7. After the session expires, AJAX calls return an error XML document. You can easily catch this error using the usual onerror handler of f:ajax.
<script type="text/javascript"> function showError(data) { alert("An error happened"); console.log(data); }</script><h:commandLink action="..."> <f:ajax execute="..." render="..." onerror="showError"/></h:commandLink>