January 3rd, 2009 by Lincoln Baxter III

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.

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);
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

9 Comments

  1. […] 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 […]

  2. Joe says:

    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?

    1. 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 :/

    2. Andres says:

      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.

  3. djmj says:

    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:

    java.lang.NullPointerException
    	at com.sun.faces.context.flash.ELFlash.loggingGetPhaseMapForWriting(ELFlash.java:751)
    	at com.sun.faces.context.flash.ELFlash.getPhaseMapForWriting(ELFlash.java:785)
    	at com.sun.faces.context.flash.ELFlash.put(ELFlash.java:392)
    	at com.sun.faces.context.flash.ELFlash.put(ELFlash.java:112)
            ...
  4. 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.

  5. Andres says:

    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.

    ViewHandler viewHandler = Util.getViewHandler(facesContext);
                UIViewRoot viewRoot = viewHandler.createView(facesContext, ((HttpServletRequest) request).getRequestURI());
                facesContext.setViewRoot(viewRoot);

    This code must write inside if statement where you test if ViewRoot is null

Reply to Lincoln Baxter III




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.