July 24th, 2009 by
Lincoln Baxter III
Please! Tell your developers to call facesContext.release()
If 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 potentially dire.
You may even want to go so far as to completely UN-set the FacesContext from the current thread. We would do this by calling FacesContextBuilder.removeFacesContext()
Call FacesContext.release() when you are done!
FacesContextBuilder.java
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);
return facesContext;
}
public void removeFacesContext()
{
InnerFacesContext.setFacesContextAsCurrentInstance(null);
}
private abstract static class InnerFacesContext extends FacesContext
{
protected static void setFacesContextAsCurrentInstance(final FacesContext facesContext)
{
FacesContext.setCurrentInstance(facesContext);
}
}
} |
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);
return facesContext;
}
public void removeFacesContext()
{
InnerFacesContext.setFacesContextAsCurrentInstance(null);
}
private abstract static class InnerFacesContext extends FacesContext
{
protected static void setFacesContextAsCurrentInstance(final FacesContext facesContext)
{
FacesContext.setCurrentInstance(facesContext);
}
}
}
FacesContext wraps the HttpServletRequest with its own RequestWrapper, and when attempting to hold on to references to any response through a Servlet Forward (via
RequestDispatcher), bad things will happen.
For example:
When using the
PrettyFaces URL bookmarking/rewriting utility (who’s PrettyFilter relies on Servlet forwards) any FacesContext created before this forward occurs will be left open, and sporadic NullPointerExceptions will occur depending on Request thread timing in the Servlet container.
Caused by: java.lang.NullPointerException
at org.apache.catalina.connector.Request.setAttribute(Request.java:1424)
at org.apache.catalina.connector.RequestFacade.setAttribute(RequestFacade.java:503)
at javax.servlet.ServletRequestWrapper.setAttribute(ServletRequestWrapper.java:284)
at com.ocpsoft.pretty.PrettyContext.setCurrentInstance(PrettyContext.java:93)
at com.ocpsoft.pretty.PrettyContext.getCurrentInstance(PrettyContext.java:84)
at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:58)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
... |
Caused by: java.lang.NullPointerException
at org.apache.catalina.connector.Request.setAttribute(Request.java:1424)
at org.apache.catalina.connector.RequestFacade.setAttribute(RequestFacade.java:503)
at javax.servlet.ServletRequestWrapper.setAttribute(ServletRequestWrapper.java:284)
at com.ocpsoft.pretty.PrettyContext.setCurrentInstance(PrettyContext.java:93)
at com.ocpsoft.pretty.PrettyContext.getCurrentInstance(PrettyContext.java:84)
at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:58)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
...
Or sometimes request attributes will be missing entirely, even if the request is accessible.
In order to safely navigate FacesContext in a ServletFilter, make absolutely sure that you release() and remove the context when you are done with it. Then you get happy applications working together with other frameworks!
Cheers, happy developing! 🙂
Posted in
JSF
Lincoln,
Thanks for the tip. We were using J2EE webapp to house our Flex SWFs, which communicated through the webapp framework to request data from another server via webservices. The SWF would do a RemoteObject call over BlazeDS to our backing bean method, which invoked the webapp framework query. A valid Session and FacesContext were required, but of course none existed. In JSF1.1 we created the context in the doFilter chain, but when we moved to JSF1.2 we got NPEs all over (not under Tomcat – only under Websphere 7).
We were already setting a UIViewRoot (to a dummy string view) in the JSF1.1 version, so I don’t know if that will make a difference for us. Any other ideas?
We had to revert to JSF1.1/Websphere 6.1 to get it working again.
It’s very difficult to say what your problem could be without more information, or seeing it :/ Where are the NPE’s occurring? At what stage of the request?
Awesome article. I had the NPE with the FacesContext which was reference in one of the functionality. This article helped in getting rid of the NPE and get the context which was missing.
I’m having similar problem but with session following is the exception i got. my environment is jboss eap 4.3 on linux with jsf 1.2
java.lang.NullPointerException
at com.sun.faces.context.SessionMap.put(ExternalContextImpl.java:970)
Thank you, that saved my day. Got weird exceptions (when trying to access the SessionMap). But release() and remove solved that.
I was using a servlet (from an ajax call) the server was extended from the abstract class described here:
https://cwiki.apache.org/MYFACES/access-facescontext-from-servlet.html
I also updated that wiki page.
thanks
After hours of research on the internet and effort about my NPE I finally end up here. Thanks a lot for your post.
I didn’t undesrtand what happens in "sporadic NullPointerExceptions will occur depending on Request thread timing in the Servlet container." Why it happens?