JSF Error Pages That Actually WorkBy Roger Keays, 27 October 2012, 12:27 PM |

Here is an annoying problem using JSF error pages for JSF requests. Everything looks just fine, HttpServletResponse.sendError() sends the error page, but JSF continues processing and starts throwing exceptions after the response is complete. This happens even if you call FacesContext.responseComplete(), and also when the error page is sent at different stages of the JSF lifecycle.
It seems like invoking the FacesServlet for sendError() breaks the state of the original FacesContext.
When sending an error during view build I get this exception:
JSF Error Pages That Actually Workjava.lang.NullPointerException at com.sun.faces.facelets.util.Resource.getResourceUrl(Resource.java:105) at com.sun.faces.facelets.impl.DefaultResourceResolver.resolveUrl(DefaultResourceResolver.java:77) at com.sun.faces.facelets.impl.DefaultFaceletFactory.resolveURL(DefaultFaceletFactory.java:229) at com.sun.faces.facelets.impl.DefaultFacelet.getRelativePath(DefaultFacelet.java:273) at com.sun.faces.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:341) at com.sun.faces.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:199) at com.sun.faces.facelets.tag.ui.DecorateHandler.apply(DecorateHandler.java:145) at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93) at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:86) at com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:149) at com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:838) at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:100) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
and if renderView() has already started, it gets even more obscure:
java.lang.NullPointerException at org.richfaces.skin.SkinFactoryImpl.clearSkinCaches(SkinFactoryImpl.java:94) at org.richfaces.skin.SkinFactoryPreRenderViewListener.processEvent(SkinFactoryPreRenderViewListener.java:35) at javax.faces.event.SystemEvent.processListener(SystemEvent.java:106) at com.sun.faces.application.ApplicationImpl.processListeners(ApplicationImpl.java:2169) at com.sun.faces.application.ApplicationImpl.invokeListenersFor(ApplicationImpl.java:2145) at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:303) at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:247) at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:108) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)P
JSF continues to RENDER phase in an all messed up drunken way.
I couldn't believe something so basic should be so complicated.
Well it turns out there is a fairly simple solution. Calling reponse.setStatus() instead of response.sendError() does not interrupt the JSF lifecycle. This works nicely, except the original view is still rendered in spite of the error.
So all we have to do is manually render a new view (the error page) as soon as the error occurs. This doesn't break JSF state and lets the lifecycle finish without all those random exceptions.
Here's what I'm talking about.
/**
* The standard request.sendError() breaks JSF state if it is called
* too late in the lifecycle. This method does the same thing but
* copes better with interrupting the current request.
*/
public void sendError(FacesContext faces, int code, String message) {
try {
faces.getExternalContext().setResponseStatus(code);
faces.getExternalContext().getRequestMap().put
("javax.servlet.error.message", message);
ViewHandler views = faces.getApplication().getViewHandler();
String template = "/error/" + code + ".xhtml";
UIViewRoot view = views.createView(faces, template);
faces.setViewRoot(view);
views.getViewDeclarationLanguage(faces, template).
buildView(faces, view);
views.renderView(faces, view);
faces.responseComplete();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
This method works any time before the view has started rendering. Normally it should be triggered during the view build by an event or managed bean @PostConstruct method. In fact it also works during the render phase but you get a mixed up response (see the comments below).
Hope you find that useful.
NB: if you use this method yourself, don't forget to update the code with the correct path of your error templates.
![]() |
Roger is an active member of the JSF 2 Expert Group and is happy to be a contributor to the Java Community. He has been writing software since the age of 8 and his other interests include languages, science, travel and surfing. You can follow Roger on Twitter and Google+. |
| « How To Get The HTTP Status Code In Selenium WebDriver | Back to Blog | Why Is Maven So Slow? [Solved] » |