title: Post-Get-Redirect and ajax in JSF
body: <p>I was using this code provided from <a href="http://balusc.blogspot.com/2007/03/post-redirect-get-pattern.html" rel="nofollow">BalusC blog</a> to handle my PRG and prevent double submits when refreshing a POST request. But it "kills" ajax functionality, and now ajax keeps refreshing whole page instead of one component. </p>
  
  <pre><code>import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  import javax.faces.FacesException;
  import javax.faces.application.FacesMessage;
  import javax.faces.component.UIComponent;
  import javax.faces.component.UIInput;
  import javax.faces.component.UIViewRoot;
  import javax.faces.context.ExternalContext;
  import javax.faces.context.FacesContext;
  import javax.faces.event.PhaseEvent;
  import javax.faces.event.PhaseId;
  import javax.faces.event.PhaseListener;
  import javax.servlet.http.HttpServletRequest;
  
  /**
   * Implement the POST-Redirect-GET pattern for JSF.
   * &lt;p&gt;
   * This phaselistener is designed to be used for JSF 1.2 with request scoped beans of which its
   * facesmessages and input values should be retained in the new GET request. If you're using session
   * scoped beans only, then you can safely remove the &lt;tt&gt;saveUIInputValues()&lt;/tt&gt; and
   * &lt;tt&gt;restoreUIInputValues()&lt;/tt&gt; methods to save (little) performance. If you're using JSF 1.1,
   * then you can also remove the &lt;tt&gt;saveViewRoot()&lt;/tt&gt; and &lt;tt&gt;restoreViewRoot&lt;/tt&gt; methods,
   * because it is not needed with its view state saving system.
   * 
   * @author BalusC
   * @link http://balusc.blogspot.com/2007/03/post-redirect-get-pattern.html
   */
  public class PostRedirectGetListener implements PhaseListener {
  
      // Init ---------------------------------------------------------------------------------------
  
      private static final String PRG_DONE_ID = "PostRedirectGetListener.postRedirectGetDone";
      private static final String SAVED_VIEW_ROOT_ID = "PostRedirectGetListener.savedViewRoot";
      private static final String ALL_FACES_MESSAGES_ID = "PostRedirectGetListener.allFacesMessages";
      private static final String ALL_UIINPUT_VALUES_ID = "PostRedirectGetListener.allUIInputValues";
  
      // Actions ------------------------------------------------------------------------------------
  
      /**
       * @see javax.faces.event.PhaseListener#getPhaseId()
       */
      public PhaseId getPhaseId() {
  
          // Only listen during the render response phase.
          return PhaseId.RENDER_RESPONSE;
      }
  
      /**
       * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
       */
      public void beforePhase(PhaseEvent event) {
  
          // Prepare.
          FacesContext facesContext = event.getFacesContext();
          ExternalContext externalContext = facesContext.getExternalContext();
          HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
          Map&lt;String, Object&gt; sessionMap = externalContext.getSessionMap();
  
          if ("POST".equals(request.getMethod())) {
  
              // Save viewroot, facesmessages and UIInput values from POST request in session so that
              // they'll be available on the subsequent GET request.
              saveViewRoot(facesContext);
              saveFacesMessages(facesContext);
              saveUIInputValues(facesContext);
  
              // Redirect POST request to GET request.
              redirect(facesContext);
  
              // Set the PRG toggle.
              sessionMap.put(PRG_DONE_ID, true);
  
          } else if (sessionMap.containsKey(PRG_DONE_ID)) {
  
              // Restore any viewroot, facesmessages and UIInput values in the GET request.
              restoreViewRoot(facesContext);
              restoreFacesMessages(facesContext);
              restoreUIInputValues(facesContext);
  
              // Remove the PRG toggle.
              sessionMap.remove(PRG_DONE_ID);
          }
      }
  
      /**
       * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
       */
      public void afterPhase(PhaseEvent event) {
          // Do nothing.
      }
  
      // Helpers ------------------------------------------------------------------------------------
  
      /**
       * Save the current viewroot of the given facescontext in session. This is important in JSF 1.2,
       * because the viewroot would be lost in the new GET request and will only be created during
       * the afterPhase of RENDER_RESPONSE. But as we need to restore the input values in the 
       * beforePhase of RENDER_RESPONSE, we have to save and restore the viewroot first ourselves.
       * @param facesContext The involved facescontext.
       */
      private static void saveViewRoot(FacesContext facesContext) {
          UIViewRoot savedViewRoot = facesContext.getViewRoot();
          facesContext.getExternalContext().getSessionMap()
              .put(SAVED_VIEW_ROOT_ID, savedViewRoot);
      }
  
      /**
       * Save all facesmessages of the given facescontext in session. This is done so because the
       * facesmessages are purely request scoped and would be lost in the new GET request otherwise.
       * @param facesContext The involved facescontext.
       */
      private static void saveFacesMessages(FacesContext facesContext) {
  
          // Prepare the facesmessages holder in the sessionmap. The LinkedHashMap has precedence over
          // HashMap, because in a LinkedHashMap the FacesMessages will be kept in order, which can be
          // very useful for certain error and focus handlings. Anyway, it's just your design choice.
          Map&lt;String, List&lt;FacesMessage&gt;&gt; allFacesMessages =
              new LinkedHashMap&lt;String, List&lt;FacesMessage&gt;&gt;();
          facesContext.getExternalContext().getSessionMap()
              .put(ALL_FACES_MESSAGES_ID, allFacesMessages);
  
          // Get client ID's of all components with facesmessages.
          Iterator&lt;String&gt; clientIdsWithMessages = facesContext.getClientIdsWithMessages();
          while (clientIdsWithMessages.hasNext()) {
              String clientIdWithMessage = clientIdsWithMessages.next();
  
              // Prepare client-specific facesmessages holder in the main facesmessages holder.
              List&lt;FacesMessage&gt; clientFacesMessages = new ArrayList&lt;FacesMessage&gt;();
              allFacesMessages.put(clientIdWithMessage, clientFacesMessages);
  
              // Get all messages from client and add them to the client-specific facesmessage list.
              Iterator&lt;FacesMessage&gt; facesMessages = facesContext.getMessages(clientIdWithMessage);
              while (facesMessages.hasNext()) {
                  clientFacesMessages.add(facesMessages.next());
              }
          }
      }
  
      /**
       * Save all input values of the given facescontext in session. This is done specific for request
       * scoped beans, because its properties would be lost in the new GET request otherwise.
       * @param facesContext The involved facescontext.
       */
      private static void saveUIInputValues(FacesContext facesContext) {
  
          // Prepare the input values holder in sessionmap.
          Map&lt;String, Object&gt; allUIInputValues = new HashMap&lt;String, Object&gt;();
          facesContext.getExternalContext().getSessionMap()
              .put(ALL_UIINPUT_VALUES_ID, allUIInputValues);
  
          // Pass viewroot children to the recursive method which saves all input values.
          saveUIInputValues(facesContext, facesContext.getViewRoot().getChildren(), allUIInputValues);
      }
  
      /**
       * A recursive method to save all input values of the given facescontext in session.
       * @param facesContext The involved facescontext.
       */
      private static void saveUIInputValues(
          FacesContext facesContext, List&lt;UIComponent&gt; components, Map&lt;String, Object&gt; allUIInputValues)
      {
          // Walk through the components and if it is an instance of UIInput, then save the value.
          for (UIComponent component : components) {
              if (component instanceof UIInput) {
                  UIInput input = (UIInput) component;
                  allUIInputValues.put(input.getClientId(facesContext), input.getValue());
              }
  
              // Pass the children of the current component back to this recursive method.
              saveUIInputValues(facesContext, component.getChildren(), allUIInputValues);
          }
      }
  
      /**
       * Invoke a redirect to the same URL as the current action URL.
       * @param facesContext The involved facescontext.
       */
      private static void redirect(FacesContext facesContext) {
  
          // Obtain the action URL of the current view.
          String url = facesContext.getApplication().getViewHandler().getActionURL(
              facesContext, facesContext.getViewRoot().getViewId());
  
          try {
              // Invoke a redirect to the action URL.
              facesContext.getExternalContext().redirect(url);
          } catch (IOException e) {
              // Uhh, something went seriously wrong.
              throw new FacesException("Cannot redirect to " + url + " due to IO exception.", e);
          }
      }
  
      /**
       * Restore any viewroot from session in the given facescontext.
       * @param facesContext The involved FacesContext.
       */
      private static void restoreViewRoot(FacesContext facesContext) {
  
          // Remove the saved viewroot from session.
          UIViewRoot savedViewRoot = (UIViewRoot)
              facesContext.getExternalContext().getSessionMap().remove(SAVED_VIEW_ROOT_ID);
  
          // Restore it in the given facescontext.
          facesContext.setViewRoot(savedViewRoot);
      }
  
      /**
       * Restore any facesmessages from session in the given FacesContext.
       * @param facesContext The involved FacesContext.
       */
      @SuppressWarnings("unchecked")
      private static void restoreFacesMessages(FacesContext facesContext) {
  
          // Remove all facesmessages from session.
          Map&lt;String, List&lt;FacesMessage&gt;&gt; allFacesMessages = (Map&lt;String, List&lt;FacesMessage&gt;&gt;)
              facesContext.getExternalContext().getSessionMap().remove(ALL_FACES_MESSAGES_ID);
  
          // Restore them in the given facescontext.
          for (Entry&lt;String, List&lt;FacesMessage&gt;&gt; entry : allFacesMessages.entrySet()) {
              for (FacesMessage clientFacesMessage : entry.getValue()) {
                  facesContext.addMessage(entry.getKey(), clientFacesMessage);
              }
          }
      }
  
      /**
       * Restore any input values from session in the given FacesContext.
       * @param facesContext The involved FacesContext.
       */
      @SuppressWarnings("unchecked")
      private static void restoreUIInputValues(FacesContext facesContext) {
  
          // Remove all input values from session.
          Map&lt;String, Object&gt; allUIInputValues = (Map&lt;String, Object&gt;)
              facesContext.getExternalContext().getSessionMap().remove(ALL_UIINPUT_VALUES_ID);
  
          // Restore them in the given facescontext.
          for (Entry&lt;String, Object&gt; entry : allUIInputValues.entrySet()) {
              UIInput input = (UIInput) facesContext.getViewRoot().findComponent(entry.getKey());
              input.setValue(entry.getValue());
          }
      }
  </code></pre>