• cdi
  • components
  • contexts
  • converters
  • eventlisteners
  • exceptionhandlers
  • facesviews
  • filters
  • functions
  • managedbeans
  • renderkits
  • resourcehandlers
  • scripts
  • taghandlers
  • utils
  • validators
-
  • FacesMessageExceptionHandler
  • FullAjaxExceptionHandler

Description

By default, when an exception occurs during a JSF ajax request, the enduser would not get any form of feedback if the action was successfully performed or not. In Mojarra, only when the project stage is set to Development, the enduser would see a bare JavaScript alert with only the exception type and message. It would make sense if exceptions during ajax requests are handled the same way as exceptions during synchronous requests, which is utilizing the standard Servlet API <error-page> mechanisms in web.xml.

This FullAjaxExceptionHandler enables you to show the full error page in its entirety to the enduser in case of exceptions during ajax requests. This exception handler will parse the web.xml and web-fragment.xml files to find the error page locations of the HTTP error code 500 and all declared specific exception types.

Installation

This handler must be registered by a factory as follows in faces-config.xml in order to get it to run:


<factory>
    <exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
		

Demo

The buttons in the below demo will each purposefully throw an exception. You'll see that an error page template is presented regardless of if it's an ajax request or not. Also, if you wait 30 minutes or manually delete the JSESSIONID cookie or press the below "Invalidate session" button and then click any of the buttons on the demo, then you'll get a view expired exception which will also end up in a specific error page.

This exception handler is also configured on this showcase web application. The following error pages are been configured on this showcase web application:


<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>
<error-page>
    <exception-type>java.sql.SQLException</exception-type>
    <location>/WEB-INF/errorpages/database.xhtml</location>
</error-page>
<error-page>
    <exception-type>java.lang.RuntimeException</exception-type>
    <location>/WEB-INF/errorpages/bug.xhtml</location>
</error-page>
		

Please note that the error page must be a valid Facelets page! JSP is not supported.

Normal requests

Note that the FullAjaxExceptionHandler does not deal with normal (non-ajax) requests at all. To properly handle JSF and EL exceptions on normal requests as well, you need an additional FacesExceptionFilter. This will extract the root cause from a wrapped FacesException and ELException before delegating the ServletException further to the container (the container will namely use the first root cause of ServletException to match an error page by exception in web.xml).

Error in error page itself

When the rendering of the error page itself failed due to a bug in the error page itself, then the FullAjaxExceptionHandler will reset the response and display a hardcoded error message in "plain text". You can see it by pressing the "cause epic fail on ajax request" button in the below demo.

Customizing FullAjaxExceptionHandler

The FullAjaxExceptionHandler offers three protected methods which can be overridden on by extending.

Don't forget to create a custom ExceptionHandlerFactory for it as well, so that it could be registered in faces-config.xml. This does not necessarily need to extend from FullAjaxExceptionHandlerFactory.

Demo
Source code
<h:form>
    <h:commandButton value="throw runtime exception on ajax request" action="#{exceptionBean.throwRuntimeException}">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton>
</h:form>
<h:form>
    <h:commandButton value="throw runtime exception on normal request" action="#{exceptionBean.throwRuntimeException}" />
</h:form>
<h:form>
    <h:commandButton value="throw SQL exception on ajax request" action="#{exceptionBean.throwSQLException}">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton>
</h:form>
<h:form>
    <h:commandButton value="throw SQL exception on normal request" action="#{exceptionBean.throwSQLException}" />
</h:form>
<h:form>
    <h:commandButton value="cause epic fail on ajax request" action="#{exceptionBean.throwEpicFailException}">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton>
</h:form>