Nothing ventured, nothing gained

a blog by Marc Chung

Performing a JSF GET

»

by Marc Chung

From the java and programming part of the brain.

According to several JSF forum posts (here and here), A JSF GET is not something a developer can convienently do.

In fact, it is a recommendation that all form submissions are done via the POST method. Well, over the past 3 months, I have adored JSF too much to allow this one lacking feature to get in the way of practical and simple use cases.
So here’s how I did a JSF GET:

  1. Create the link to an URL which maps to a filter
  2. Use the ServletRequest to fetch the link’s arguments.
  3. Using those arguments, create a backing bean of the appropriate state and put it in the HttpSession.
  4. Create and update the FacesContext object
  5. Forward the user to the correct JSP/JSF (UIView) page

You’ll need to construct a link in your JSP/JSF page. You can do it by hard coding an actual link, but in this example I’ve chose to do construct my link using the JSF HTML taglibs:

<h:outputLink id="x" value="jsf.get">
    <f:verbatim>Some Link</f:verbatim>
    <f:param name="editItem" value="00004"/>
</h:outputLink>

This will emit the following link in your JSP/JSF page.

http://localhost/context/jsf.get?editKey=00004

Now we add a filter to listen to requests for “jsf.get”:

<filter>
    <filter-name>JSFGet Filter</filter-name>
    <filter-class>com.JSFGet</filter-class>
</filter>

<filter-mapping>
    <filter-name>JSFGet Filter</filter-name>
    <url-pattern>*.get</url-pattern>
</filter-mapping>

Now, you need a filter that handles incoming GET requests properly. Included below is a stripped down version. I hope this bit of code will help anyone trying to accommodate GET requests with JSF.

import com.SuperBean;
import java.io.IOException;

public class JSFGet implements Filter {
    private FilterConfig filterConf;
    private ServletContext servletContext;

    public void init(FilterConfig filterConfig) 
        throws ServletException {
          
        this.filterConf = filterConfig;
        this.servletContext = filterConfig.getServletContext();
    }

    public void doFilter(ServletRequest req, 
                         ServletResponse res, 
                         FilterChain fc) 
        throws IOException, ServletException {

        FacesContext facesContext = getFacesContext(req, res);
        req.getRequestDispatcher(facesContext.getViewRoot().getViewId()).
            forward(req, res);
    }

    public void destroy() {
        this.filterConf = null;
        this.servletContext = null;
    }

    /** 
      FacesContext.setFacesContextAsCurrentInstance method is 
      protected. 
    */
    private abstract static class ProtectedFacesContext
        extends FacesContext {

        protected static void setFacesContextAsCurrentInstance(
            FacesContext facesContext) {

            FacesContext.setCurrentInstance(facesContext);
        }
    }

    /** Here's where the Filter/JSF integration takes place. */
    private FacesContext getFacesContext(ServletRequest req, 
                                         ServletResponse res) {

        /** Try to get it first */
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext != null)
            return facesContext;

        // Use the FactoryFinder to grab the Lifecycle object
        FacesContextFactory contextFactory = (FacesContextFactory) 
            FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);

        LifecycleFactory lifecycleFactory = (LifecycleFactory) 
            FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);

        Lifecycle lifecycle = 
            lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

        // Put my Bean into HttpSession
        HttpServletRequest httpReq = (HttpServletRequest) req;
        HttpSession httpSess = httpReq.getSession();
        SuperBean editor = new SuperBean();

        String key = req.getParameter("editKey");
        editor.setEditKey(key);

        // If your loadSomething method requires a FacesContext object, 
        // call it after it has been constructed
        editor.loadSomething();
        httpSess.setAttribute("SuperBean", editor);

        // Here's where the ProtectedFacesContext comes in.
        facesContext = contextFactory.
            getFacesContext(servletContext, req, res, lifecycle);
        ProtectedFacesContext.setFacesContextAsCurrentInstance(facesContext);

        // Create a new ViewRoot.  Default behavior returns null

        UIViewRoot view = facesContext.getApplication().
            getViewHandler().createView(facesContext,"edit.jsf");

        facesContext.setViewRoot(view);

        // Call loadSomething(FacesContext)
        // editor.loadSomething(facesContext);

        return facesContext;
    }
}

Want to know more?

I'm Marc Chung, and you're reading Nothing ventured, Nothing gained, a blog about building beautiful software. I'm the founder of OpenRain Software, a web design and development company located in Arizona, where I make millions of users happy by building breathtaking software with brilliant people.

Presentations, Talks, Etc