I\'m working on a JSF web application in which I need to bring up a \"Session Expired\" page if the view expires, but a general technical error page for all others. The app
This is because the ViewExpiredException is been wrapped in a ServletException as per the JSF specification. Here's an extract of chapter 10.2.6.2 of the JSF 1.2 specification:
10.2.6.2 FacesServlet
Call the
execute()
method of the savedLifecycle
instance, passing theFacesContext
instance for this request as a parameter. If theexecute()
method throws aFacesException
, re-throw it as aServletException
with theFacesException
as the root cause.
How the error pages are allocated is specified in Servlet API specification. Here's an extract of chapter 9.9.2 of Servlet API specification 2.5:
SRV.9.9.2 Error Pages
If no
error-page
declaration containing anexception-type
fits using the class-hierarchy match, and the exception thrown is aServletException
or subclass thereof, the container extracts the wrapped exception, as defined by theServletException.getRootCause
method. A second pass is made over the error page declarations, again attempting the match against the error page declarations, but using the wrapped exception instead.
In class hierarchy, ServletException
already matches Throwable
, so its root cause won't be extracted for the second pass.
To prove this specified behaviour, replace javax.faces.application.ViewExpiredException
by javax.servlet.ServletException
as <exception-type>
and retry. You'll see the expected error page being displayed.
To solve this, simply remove the error page on java.lang.Throwable
or java.lang.Exception
. If no one exception specific error page matches, then it will fall back to the one for error code of 500
anyway. So, all you need is this:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/jsps/utility/sessionExpired.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/jsps/utility/technicalError.jsp</location>
</error-page>
Update: as per the (deleted) comment of the OP: to reliably test this you cannot do a throw new ViewExpiredException()
in a bean constructor or method or so. It would in turn get wrapped in some EL exception. You can eventually add a debug line printing rootCause
in the Filter
to see it yourself.
If you're using Eclipse/Tomcat, a quick way to test ViewExpiredException
is the following:
As mentioned by BylusC, deployment descriptor must not contain any error-page handler that will catch ViewExpiredException (wrapped inside ServletException) instead of the correct one - error-page for ViewExpiredException.
Do not forget to verify that server's deployment descriptor (e.g. TomEE/conf/web.xml) does not contain java.lang.Throwable or java.lang.Exception error-page definitions. Because these two web.xml are basically merged.
Yes - application's web.xml has precedence over server's web.xml, but if application's web.xml contains
and server's web.xml for
then application context will contain merge of those two:
and ViewExpiredExcpetion error handling will not work correctly.