JSF 2.0 Extension Development: Accessing FacesContext in a Filter
The Problem:
So you need a way to instantiate the 2.0 FacesContext in a Filter, but when you use the same method that you have in the past, you get NullPointerExceptions all over the place when attempting to access any values through El. The ScopedAttributeElResolver bombs when attempting to set values or access methods in backing beans.) It’s not too hard to get this working again. In fact, this is how our own URL-rewrite filter for JSF handles things behind the scenes.
The Answer:
It turns out that it’s not much different from how we may have solved this in the past, but our new JSF2.0 ScopedAttributeElResolver expects a ViewRoot object to be set so that we can gain access to the new ViewScope attributes. However… so the first thing we need to do is set a blank UIViewRoot in the FacesContext so that we will not crash when ScopedAttributeElResolver tries to do this:
Map<string,object> viewMap = facesContext.getViewRoot().getViewMap(false); </string,object> |
See our little FacesContextBuilder class here:
import javax.faces.FactoryFinder; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.context.FacesContextFactory; import javax.faces.lifecycle.Lifecycle; import javax.faces.lifecycle.LifecycleFactory; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class FacesContextBuilder { public FacesContext getFacesContext(final ServletRequest request, final ServletResponse response) { FacesContext facesContext = FacesContext.getCurrentInstance(); if (facesContext != null) { return facesContext; } FacesContextFactory contextFactory = (FacesContextFactory) FactoryFinder .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder .getFactory(FactoryFinder.LIFECYCLE_FACTORY); Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE); ServletContext servletContext = ((HttpServletRequest) request).getSession().getServletContext(); facesContext = contextFactory.getFacesContext(servletContext, request, response, lifecycle); InnerFacesContext.setFacesContextAsCurrentInstance(facesContext); if (null == facesContext.getViewRoot()) { facesContext.setViewRoot(new UIViewRoot()); } return facesContext; } private abstract static class InnerFacesContext extends FacesContext { protected static void setFacesContextAsCurrentInstance(final FacesContext facesContext) { FacesContext.setCurrentInstance(facesContext); } } } |
Just one more thing to worry about:
Once you hand off your request to the FacesServlet, if you try to do any work in a PhaseListener before the RESTORE_VIEW phase (in the beforePhase() method) you will again experience the null ViewRoot issue, but this time, you cannot set a new UIViewRoot object, so you must wait until the afterPhase() method is called to do your work. ViewRoot will have been initialized by the JSF application at that point.
Happy Extension Writing!
Posted in JSF
[…] you are manipulating any FacesContext when doing any kind of Sevlet Forwards – such as from a filter – you MUST release() any FacesContext you’ve created. The consequences of forgetting this are […]
Tried this, faces context returned null viewId, using either the getViewRoot().getViewId(), and Faces api threw NPE. Perhaps because I’m using JSF 2?
Basically I’m trying to store off the viewId so I can bounce the user back to it after login (which I think is the general use case for this). Anyone have any ideas?
There will be no view-root until after the RESTORE_VIEW phase of the JSF lifecycle, so you unfortunately, that’s not going to work :/
I solved this problem… read my message, it’s the last one.
There are no problems for me, because it’s only executed when session expire, and filter detects it, then execute this code and finally redirect to login page.
The login page create a new session.
I wanted to save the current uri to flash scope in a authentification filter which redirects to a login site.
Instead of passing the current uri to the login site as a request query param I wanted to use flash scope to pass it.
But following error occurs trying to save to flash using the retrieved FacesContext:
Ah JSF, I love the detailed exception messages – Instead of using the Flash, I would probably use a SessionScoped bean. The Flash is only available in the JSF lifecycle.
[…] http://ocpsoft.org/java/jsf-java/jsf-20-extension-development-accessing-facescontext-in-a-filter/ […]
Hola / Hello.
Yo lo ocupe porque necesitaba validar peticiones ajax en el filter y no lograba direccionar, ya que el RenderKit y RenderKitId estaban vacios, por ello agregue el siguiente codigo, el cual tuve que buscar dentro de la implementacion de jsf 2.2
I used for validating ajax requests in the filter, however I could not redirect, cause RenderKit and RenderKitId were null, so I add the following code that I could find at jsf 2.2 implementation.
This code must write inside if statement where you test if ViewRoot is null
[…] http://ocpsoft.org/java/jsf-java/jsf-20-extension-development-accessing-facescontext-in-a-filter/ […]