In JSF 2.0 or later it is better to create a custom ResourceHandler wrapper and use FacesServlet instead. In that way, JSF will create FacesContext instance.

There have been several posts asking how to get a Faces Context from outside of JSF such as in a servlet. There are many reasons for doing this such as:

  1. Getting to a managed bean in session scope
  2. Getting or setting the current view id
  3. Generating additional content to be displayed in a faces component
  4. Performing authentication and session management
  5. Various other reasons you can come up with...

Below is one way to do this.

<disclaimer>I can't take credit for the approach as the question popped up last year on Sun's Creator forum and has since been modified/adapted/shared so many times I can no longer remember what is adopted code and what is original. However, it works and works well – at least in our Tomcat 5.5x apps</disclaimer>

We used several servlets and filters to drive external content into our JSF apps. In particular we use this approach to generate Jasper Reports, JFreeChart images and generate PDFs on the fly using iText. The same code is also used in a filter to manage sessions.

AbstractFacesServlet.java public abstract class AbstractFacesServlet extends HttpServlet { public AbstractFacesServlet() { super(); } public void init(ServletConfig config) throws ServletException { super.init(config); } protected abstract void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; /** Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected void log(FacesContext facesContext, String message) { facesContext.getExternalContext().log(message); } /** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) { FacesContext facesContext = FacesContext.getCurrentInstance(); if (facesContext == null) { FacesContextFactory contextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE); facesContext = contextFactory.getFacesContext(request.getSession().getServletContext(), request, response, lifecycle); // Set using our inner class InnerFacesContext.setFacesContextAsCurrentInstance(facesContext); // set a new viewRoot, otherwise context.getViewRoot returns null UIViewRoot view = facesContext.getApplication().getViewHandler().createView(facesContext, ""); facesContext.setViewRoot(view); } return facesContext; } public void removeFacesContext() { InnerFacesContext.setFacesContextAsCurrentInstance(null); } protected Application getApplication(FacesContext facesContext) { return facesContext.getApplication(); } protected Object getManagedBean(String beanName, FacesContext facesContext) { return getApplication(facesContext).getVariableResolver().resolveVariable(facesContext, beanName); } // You need an inner class to be able to call FacesContext.setCurrentInstance // since it's a protected method private abstract static class InnerFacesContext extends FacesContext { protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) { FacesContext.setCurrentInstance(facesContext); } } }

This is simple to use and extend and, as I stated earlier, has been handed around several lists. We provide convienience methods for access to managed beans and to the Application. Concrete classes extend the base class and provide getters for session-managed components etc.

Note: You should always relase the FacesContext after you're done with it (see http://ocpsoft.com/java/jsf-java/please-tell-your-developers-to-call-facescontextrelease/). I've added the removeFacesContext() method to the above code, and you should call that after calling .release() on the context.

In closing, I'm sure there may be better ways for doing this but more importantly, there are probably good reasons for not doing this at all and changing to Phase Listeners and staying within the JSF framework which I think is an important topic for continued discussion.

  • No labels