using ExternalContext.dispatch in JSF error handler causes corrupt page rendering
jsf-2
Solution
You should not use `ExternalContext#dispatch()` to render a JSF view. It should only be used to forward to a non-JSF resource. You should instead set the desired JSF view by `FacesContext#setViewRoot()` and let JSF render it.
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, viewId);
context.setViewRoot(viewRoot);
context.renderResponse();
Or, if you're currently already sitting in the render response, then it's too late to let JSF re-render it. You'd need to build and render the new view manually. In that case, replace the last line by:
ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(context, viewId);
vdl.buildView(context, viewRoot);
context.getApplication().publishEvent(context, PreRenderViewEvent.class, viewRoot);
vdl.renderView(context, viewRoot);
context.responseComplete();
Note that this is in turn too late if the response is already committed. You may want to check that beforehand as well.
You may find the source code of the OmniFaces `FullAjaxExceptionHandler` helpful to get some insight.
Problem
Simplified, my error handler looks like this: ``` @Override public void handle() throws FacesException { Iterator<ExceptionQueuedEvent> unhandledExceptionQueuedEvents = getUnhandledExceptionQueuedEvents().iterator(); FacesContext context = FacesContext.getCurrentInstance(); if (unhandledExceptionQueuedEvents.hasNext()) { Throwable exception = unhandledExceptionQueuedEvents.next().getContext().getException(); unhandledExceptionQueuedEvents.remove(); context.getExternalContext().dispatch("/error.jsf"); } while (unhandledExceptionQueuedEvents.hasNext()) { unhandledExceptionQueuedEvents.next(); unhandledExceptionQueuedEvents.remove(); } } ``` simplified template.xhtml ``` <html> <f:view> <h:head> <!-- some stuff here --> </h:head> <h:body> <div id="menu"><!-- menu content --></div> <div id="content"> <ui:insert name="body"></ui:insert> </div> </h:body> </f:view> </html> ``` error.xhtml ``` <html> <ui:composition template="/template.xhtml"> <ui:define name="body"> ERROR </ui:define> </ui:composition> </html> ``` But when rendering error.jsf, templating with ui:composition goes very wrong. JSF renders template.xhtml up until around the , where it starts rendering the template all over again. The result is a page where menus are rendered twice, and all resources are included once again in the middle of the page. The start of it looks something like this: ``` <div id="menu></div> <div<?xml version="1.0" encoding="UTF-8" ?=""> <!-- Page included once more --> ``` This is followed by an infinite loop on the server, causing millions of log rows containing the following bit over and over again ``` at com.xdin.competence.web.error.ErrorHandler.handleException(ErrorHandler.java:123) [:] at com.xdin.competence.web.error.ErrorHandler.handle(ErrorHandler.java:108) [:] at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:115) [:2.0.3-] at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:107) [:2.0.3-] at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114) [:2.0.3-] at javax.faces.webapp.FacesServlet.service(FacesServlet.java:308) [:2.0.3-] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:324) [:6.0.0.Final] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final] at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:734) [:6.0.0.Final] at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:541) [:6.0.0.Final] at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:479) [:6.0.0.Final] at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:407) [:6.0.0.Final] at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:542) [:2.0.3-] at javax.faces.context.ExternalContextWrapper.dispatch(ExternalContextWrapper.java:89) [:2.0.3-] at org.jboss.seam.faces.environment.org$jboss$weld$bean-jboss$classloader:id="vfs:$$$C:$JBoss$jboss-6$0$0$Final$server$career$deploy$career-portal$war"-ManagedBean-class_org$jboss$seam$faces$environment$SeamExternalContext_$$_WeldClientProxy.dispatch(org$jboss$weld$bean-jboss$classloader:id="vfs:$$$C:$JBoss$jboss-6$0$0$Final$server$career$deploy$career-portal$war"-ManagedBean-class_org$jboss$seam$faces$environment$SeamExternalContext_$$_WeldClientProxy.java) [:3.0.1.Final] ``` Any idea of what the cause could be?