Coverage Report - org.apache.myfaces.application.jsp.JspViewHandlerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JspViewHandlerImpl
0%
0/151
0%
0/74
3.773
JspViewHandlerImpl$StateMarkerAwareWriter
0%
0/46
0%
0/22
3.773
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.application.jsp;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.io.StringWriter;
 23  
 import java.io.Writer;
 24  
 import java.util.Iterator;
 25  
 import java.util.Locale;
 26  
 import java.util.logging.Level;
 27  
 import java.util.logging.Logger;
 28  
 
 29  
 import javax.faces.FacesException;
 30  
 import javax.faces.FactoryFinder;
 31  
 import javax.faces.application.Application;
 32  
 import javax.faces.application.StateManager;
 33  
 import javax.faces.application.ViewHandler;
 34  
 import javax.faces.component.UIViewRoot;
 35  
 import javax.faces.context.ExternalContext;
 36  
 import javax.faces.context.FacesContext;
 37  
 import javax.faces.context.ResponseWriter;
 38  
 import javax.faces.render.RenderKit;
 39  
 import javax.faces.render.RenderKitFactory;
 40  
 import javax.faces.render.ResponseStateManager;
 41  
 import javax.servlet.ServletRequest;
 42  
 import javax.servlet.ServletResponse;
 43  
 import javax.servlet.http.HttpServletRequest;
 44  
 import javax.servlet.http.HttpServletResponse;
 45  
 import javax.servlet.http.HttpSession;
 46  
 import javax.servlet.jsp.jstl.core.Config;
 47  
 
 48  
 import org.apache.myfaces.shared.application.DefaultViewHandlerSupport;
 49  
 import org.apache.myfaces.shared.application.InvalidViewIdException;
 50  
 import org.apache.myfaces.shared.application.ViewHandlerSupport;
 51  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 52  
 import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
 53  
 
 54  
 /**
 55  
  * Implementation of the ViewHandler interface that knows how to use JSP pages
 56  
  * as the view templating mechanism.
 57  
  * <p>
 58  
  * This implementation works tightly together with the various JSP TagHandler classes
 59  
  * to implement the behaviour mandated by the ViewHandler specification. 
 60  
  * <p>
 61  
  * Rendering of a view is done in two parts: first a jsp-generated servlet is invoked
 62  
  * to create or refresh a jsf component tree, then the component tree is walked to generate
 63  
  * the output to send to the user.
 64  
  * <p>
 65  
  * The invoked servlet is the one generated from the jsp file which corresponds to the
 66  
  * viewId of the view being rendered. As is normal for jsp, this servlet alternates between
 67  
  * writing literal text to the response output stream and invoking "tag handler" classes
 68  
  * representing the jsp tags that were present in the page. This servlet is not aware of
 69  
  * JSF at all.
 70  
  * <p>
 71  
  * On the first visit to a view, when each JSF taghandler is invoked, the corresponding
 72  
  * JSF component will not yet exist so it is created and added to the current view tree.
 73  
  * Each JSF taghandler also marks itself as having "buffered body content", which means that
 74  
  * after the start-tag is executed a temporary output stream is installed for the response.
 75  
  * Any output generated by the jsp-servlet therefore gets written into a memory buffer
 76  
  * rather than sent via the network socket to the sender of the request. When the end
 77  
  * of the JSF tag is encountered, the JSF tag checks whether any such body text did exist,
 78  
  * and if so it creates a transient f:verbatim component and inserts it into the component
 79  
  * tree. The final result is that after this "first pass" a component tree exists which has
 80  
  * all the JSF components in it, plus a bunch of auto-generated f:verbatim components that
 81  
  * hold all plain text, or output generated by non-jsf jsp tags. Absolutely NO output has
 82  
  * yet been sent to the real response stream.
 83  
  * <p>
 84  
  * On later visits to the same view, the component tree already exists (has been restored).
 85  
  * However the "verbatim" components holding static text are not present as they were
 86  
  * marked "transient" (not keeping them reduces the amount of memory required to "save state").
 87  
  * Note that these components are not needed for any phase prior to RENDER because they
 88  
  * are not "input" components. When the jsp-generated servlet is executed, JSF taghandlers
 89  
  * that are invoked will simply verify that a corresponding component already exists in the
 90  
  * view-tree rather than creating a new one. However the "body buffering" occurs again, so
 91  
  * that the appropriate transient verbatim components are once again created and inserted into
 92  
  * the tree.
 93  
  * <p>
 94  
  * Regardless of whether the view is new or restored, the rendered output can now be generated
 95  
  * simply by walking the component tree and invoking the encodeBegin/encodeChildren/encodeEnd
 96  
  * methods on each component. The static components simply output their contained text.
 97  
  * <p>
 98  
  * Notes for JSF1.1 users: the new two-phase approach that uses "output buffering" to capture
 99  
  * non-JSF output is rather like wrapping all non-jsf components in an invisible f:verbatim tag.
 100  
  * Although that doesn't sound like a big change, it allows processing to occur in two passes
 101  
  * rather than one. And that means that before any component is rendered the entire component
 102  
  * tree already exists. This solves a number of JSF1.1 problems, including output-ordering
 103  
  * problems between text and jsf components, and errors when using the "for" attribute of a
 104  
  * label to reference a component later in the page. It does introduce a performance penalty;
 105  
  * non-JSF-generated output now gets buffered rather than being streamed directly to the
 106  
  * user.
 107  
  * <p>
 108  
  * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
 109  
  * @author Bruno Aranda
 110  
  * @version $Revision: 1535613 $ $Date: 2013-10-24 21:44:35 -0500 (Thu, 24 Oct 2013) $
 111  
  */
 112  
 public class JspViewHandlerImpl extends ViewHandler
 113  
 {
 114  
     //private static final Log log = LogFactory.getLog(JspViewHandlerImpl.class);
 115  0
     private static final Logger log = Logger.getLogger(JspViewHandlerImpl.class.getName());
 116  
     public static final String FORM_STATE_MARKER = "<!--@@JSF_FORM_STATE_MARKER@@-->";
 117  0
     public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
 118  
 
 119  0
     private static final String AFTER_VIEW_TAG_CONTENT_PARAM = JspViewHandlerImpl.class + ".AFTER_VIEW_TAG_CONTENT";
 120  
 
 121  
     private ViewHandlerSupport _viewHandlerSupport;
 122  
 
 123  
     public JspViewHandlerImpl()
 124  0
     {
 125  0
         if (log.isLoggable(Level.FINEST))
 126  
         {
 127  0
             log.finest("New ViewHandler instance created");
 128  
         }
 129  0
     }
 130  
 
 131  
     /**
 132  
      * @param viewHandlerSupport
 133  
      *            the viewHandlerSupport to set
 134  
      */
 135  
     public void setViewHandlerSupport(ViewHandlerSupport viewHandlerSupport)
 136  
     {
 137  0
         _viewHandlerSupport = viewHandlerSupport;
 138  0
     }
 139  
 
 140  
     /**
 141  
      * @return the viewHandlerSupport
 142  
      */
 143  
     protected ViewHandlerSupport getViewHandlerSupport()
 144  
     {
 145  0
         if (_viewHandlerSupport == null)
 146  
         {
 147  0
             _viewHandlerSupport = new DefaultViewHandlerSupport();
 148  
         }
 149  0
         return _viewHandlerSupport;
 150  
     }
 151  
 
 152  
     /**
 153  
      * Get the locales specified as acceptable by the original request, compare them to the
 154  
      * locales supported by this Application and return the best match.
 155  
      */
 156  
     @Override
 157  
     public Locale calculateLocale(FacesContext facesContext)
 158  
     {
 159  0
         Application application = facesContext.getApplication();
 160  0
         for (Iterator<Locale> requestLocales = facesContext.getExternalContext().getRequestLocales(); requestLocales
 161  0
                 .hasNext();)
 162  
         {
 163  0
             Locale requestLocale = requestLocales.next();
 164  0
             for (Iterator<Locale> supportedLocales = application.getSupportedLocales(); supportedLocales.hasNext();)
 165  
             {
 166  0
                 Locale supportedLocale = supportedLocales.next();
 167  
                 // higher priority to a language match over an exact match
 168  
                 // that occures further down (see Jstl Reference 1.0 8.3.1)
 169  0
                 if (requestLocale.getLanguage().equals(supportedLocale.getLanguage())
 170  
                         && (supportedLocale.getCountry() == null || supportedLocale.getCountry().length() == 0))
 171  
                 {
 172  0
                     return supportedLocale;
 173  
                 }
 174  0
                 else if (supportedLocale.equals(requestLocale))
 175  
                 {
 176  0
                     return supportedLocale;
 177  
                 }
 178  0
             }
 179  0
         }
 180  
 
 181  0
         Locale defaultLocale = application.getDefaultLocale();
 182  0
         return defaultLocale != null ? defaultLocale : Locale.getDefault();
 183  
     }
 184  
 
 185  
     @Override
 186  
     public String calculateRenderKitId(FacesContext facesContext)
 187  
     {
 188  0
         Object renderKitId = facesContext.getExternalContext().getRequestMap().get(
 189  
                 ResponseStateManager.RENDER_KIT_ID_PARAM);
 190  0
         if (renderKitId == null)
 191  
         {
 192  0
             renderKitId = facesContext.getApplication().getDefaultRenderKitId();
 193  
         }
 194  0
         if (renderKitId == null)
 195  
         {
 196  0
             renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
 197  
         }
 198  0
         return renderKitId.toString();
 199  
     }
 200  
 
 201  
     /**
 202  
      * Create a UIViewRoot object and return it; the returned object has no children.
 203  
      * <p>
 204  
      * As required by the spec, the returned object inherits locale and renderkit settings from
 205  
      * the viewRoot currently configured for the facesContext (if any). This means that on navigation
 206  
      * from one view to another these settings are "inherited".
 207  
      */
 208  
     @Override
 209  
     public UIViewRoot createView(FacesContext facesContext, String viewId)
 210  
     {
 211  0
         String calculatedViewId = viewId;
 212  
         try
 213  
         {
 214  0
             calculatedViewId = getViewHandlerSupport().calculateViewId(facesContext, viewId);
 215  
         }
 216  0
         catch (InvalidViewIdException e)
 217  
         {
 218  0
             sendSourceNotFound(facesContext, e.getMessage());
 219  0
         }
 220  
 
 221  0
         Application application = facesContext.getApplication();
 222  0
         ViewHandler applicationViewHandler = application.getViewHandler();
 223  
 
 224  0
         Locale currentLocale = null;
 225  0
         String currentRenderKitId = null;
 226  0
         UIViewRoot uiViewRoot = facesContext.getViewRoot();
 227  0
         if (uiViewRoot != null)
 228  
         {
 229  
             // Remember current locale and renderKitId
 230  0
             currentLocale = uiViewRoot.getLocale();
 231  0
             currentRenderKitId = uiViewRoot.getRenderKitId();
 232  
         }
 233  
 
 234  0
         uiViewRoot = (UIViewRoot) application.createComponent(UIViewRoot.COMPONENT_TYPE);
 235  
 
 236  0
         uiViewRoot.setViewId(calculatedViewId);
 237  
 
 238  0
         if (currentLocale != null)
 239  
         {
 240  
             // set old locale
 241  0
             uiViewRoot.setLocale(currentLocale);
 242  
         }
 243  
         else
 244  
         {
 245  
             // calculate locale
 246  0
             uiViewRoot.setLocale(applicationViewHandler.calculateLocale(facesContext));
 247  
         }
 248  
 
 249  0
         if (currentRenderKitId != null)
 250  
         {
 251  
             // set old renderKit
 252  0
             uiViewRoot.setRenderKitId(currentRenderKitId);
 253  
         }
 254  
         else
 255  
         {
 256  
             // calculate renderKit
 257  0
             uiViewRoot.setRenderKitId(applicationViewHandler.calculateRenderKitId(facesContext));
 258  
         }
 259  
 
 260  0
         if (log.isLoggable(Level.FINEST))
 261  
         {
 262  0
             log.finest("Created view " + viewId);
 263  
         }
 264  0
         return uiViewRoot;
 265  
     }
 266  
 
 267  
     private void sendSourceNotFound(FacesContext context, String message)
 268  
     {
 269  0
         HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
 270  
         try
 271  
         {
 272  0
             context.responseComplete();
 273  0
             response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
 274  
         }
 275  0
         catch (IOException ioe)
 276  
         {
 277  0
             throw new FacesException(ioe);
 278  0
         }
 279  0
     }
 280  
 
 281  
     /**
 282  
      * Return a string containing a webapp-relative URL that the user can invoke
 283  
      * to render the specified view.
 284  
      * <p>
 285  
      * URLs and ViewIds are not quite the same; for example a url of "/foo.jsf"
 286  
      * or "/faces/foo.jsp" may be needed to access the view "/foo.jsp". 
 287  
      * <p>
 288  
      * This method simply delegates to ViewHandlerSupport.calculateActionURL.
 289  
      */
 290  
     @Override
 291  
     public String getActionURL(FacesContext facesContext, String viewId)
 292  
     {
 293  0
         return getViewHandlerSupport().calculateActionURL(facesContext, viewId);
 294  
     }
 295  
 
 296  
     @Override
 297  
     public String getResourceURL(FacesContext facesContext, String path)
 298  
     {
 299  0
         if (path.length() > 0 && path.charAt(0) == '/')
 300  
         {
 301  0
             String contextPath = facesContext.getExternalContext().getRequestContextPath();
 302  0
             if (contextPath == null)
 303  
             {
 304  0
                 return path;
 305  
         }
 306  0
             else if (contextPath.length() == 1 && contextPath.charAt(0) == '/')
 307  
             {
 308  
                 // If the context path is root, it is not necessary to append it, otherwise
 309  
                 // and extra '/' will be set.
 310  0
                 return path;
 311  
             }
 312  
             else
 313  
             {
 314  0
                 return  contextPath + path;
 315  
             }
 316  
         }
 317  
 
 318  0
         return path;
 319  
 
 320  
     }
 321  
 
 322  
     /**
 323  
      * Generate output to the user by combining the data in the jsp-page specified by viewToRender
 324  
      * with the existing JSF component tree (if any).
 325  
      * <p>
 326  
      * As described in the class documentation, this first runs the jsp-generated servlet to
 327  
      * create or enhance the JSF component tree - including verbatim nodes for any non-jsf
 328  
      * data in that page.
 329  
      * <p>
 330  
      * The component tree is then walked to generate the appropriate output for each component.
 331  
      */
 332  
     @Override
 333  
     public void renderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException, FacesException
 334  
     {
 335  0
         if (viewToRender == null)
 336  
         {
 337  0
             log.severe("viewToRender must not be null");
 338  0
             throw new NullPointerException("viewToRender must not be null");
 339  
         }
 340  
 
 341  
         // do not render the view if the rendered attribute for the view is false
 342  0
         if (!viewToRender.isRendered())
 343  
         {
 344  0
             if (log.isLoggable(Level.FINEST))
 345  
             {
 346  0
                 log.finest("View is not rendered");
 347  
             }
 348  0
             return;
 349  
         }
 350  
 
 351  0
         ExternalContext externalContext = facesContext.getExternalContext();
 352  
 
 353  0
         String viewId = facesContext.getViewRoot().getViewId();
 354  
 
 355  0
         if (log.isLoggable(Level.FINEST))
 356  
         {
 357  0
             log.finest("Rendering JSP view: " + viewId);
 358  
         }
 359  
 
 360  0
         ServletResponse response = (ServletResponse) externalContext.getResponse();
 361  0
         ServletRequest request = (ServletRequest) externalContext.getRequest();
 362  
 
 363  0
         Locale locale = viewToRender.getLocale();
 364  0
         response.setLocale(locale);
 365  0
         Config.set(request, Config.FMT_LOCALE, facesContext.getViewRoot().getLocale());
 366  
 
 367  0
         if(!buildView(response, externalContext, viewId))
 368  
         {
 369  
             //building the view was unsuccessful - an exception occurred during rendering
 370  
             //we need to jump out
 371  0
             return;
 372  
         }
 373  
 
 374  
         // handle character encoding as of section 2.5.2.2 of JSF 1.1
 375  0
         if (externalContext.getRequest() instanceof HttpServletRequest)
 376  
         {
 377  0
             HttpServletRequest httpServletRequest = (HttpServletRequest) externalContext.getRequest();
 378  0
             HttpSession session = httpServletRequest.getSession(false);
 379  
 
 380  0
             if (session != null)
 381  
             {
 382  0
                 session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
 383  
             }
 384  
         }
 385  
 
 386  
         // render the view in this method (since JSF 1.2)
 387  0
         RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
 388  0
         RenderKit renderKit = renderFactory.getRenderKit(facesContext, viewToRender.getRenderKitId());
 389  
 
 390  0
         ResponseWriter responseWriter = facesContext.getResponseWriter();
 391  0
         if (responseWriter == null)
 392  
         {
 393  0
             responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
 394  
                     ((HttpServletRequest) externalContext.getRequest()).getCharacterEncoding());
 395  0
             facesContext.setResponseWriter(responseWriter);
 396  
         }
 397  
 
 398  0
         ResponseWriter oldResponseWriter = responseWriter;
 399  0
         StateMarkerAwareWriter stateAwareWriter = null;
 400  
 
 401  0
         StateManager stateManager = facesContext.getApplication().getStateManager();
 402  0
         if (stateManager.isSavingStateInClient(facesContext))
 403  
         {
 404  0
             stateAwareWriter = new StateMarkerAwareWriter();
 405  
 
 406  
             // Create a new response-writer using as an underlying writer the stateAwareWriter
 407  
             // Effectively, all output will be buffered in the stateAwareWriter so that later
 408  
             // this writer can replace the state-markers with the actual state.
 409  0
             responseWriter = hookInStateAwareWriter(
 410  
                     oldResponseWriter, stateAwareWriter, renderKit, externalContext);
 411  0
             facesContext.setResponseWriter(responseWriter);
 412  
         }
 413  
 
 414  0
         actuallyRenderView(facesContext, viewToRender);
 415  
 
 416  0
         facesContext.setResponseWriter(oldResponseWriter);
 417  
 
 418  
         //We're done with the document - now we can write all content
 419  
         //to the response, properly replacing the state-markers on the way out
 420  
         //by using the stateAwareWriter
 421  0
         if (stateManager.isSavingStateInClient(facesContext))
 422  
         {
 423  0
             stateAwareWriter.flushToWriter(response.getWriter());
 424  
         }
 425  
         else
 426  
         {
 427  0
             stateManager.saveView(facesContext);
 428  
         }
 429  
 
 430  
         // Final step - we output any content in the wrappedResponse response from above to the response,
 431  
         // removing the wrappedResponse response from the request, we don't need it anymore
 432  0
         ServletViewResponseWrapper afterViewTagResponse
 433  
                 = (ServletViewResponseWrapper) externalContext.getRequestMap().get(AFTER_VIEW_TAG_CONTENT_PARAM);
 434  0
         externalContext.getRequestMap().remove(AFTER_VIEW_TAG_CONTENT_PARAM);
 435  
 
 436  0
         if (afterViewTagResponse != null)
 437  
         {
 438  0
             afterViewTagResponse.flushToWriter(response.getWriter(),
 439  
                     facesContext.getExternalContext().getResponseCharacterEncoding());
 440  
         }
 441  
 
 442  0
         response.flushBuffer();
 443  0
     }
 444  
 
 445  
     /**
 446  
      * Render the view now - properly setting and resetting the response writer
 447  
      */
 448  
     private void actuallyRenderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException
 449  
     {
 450  
         // Set the new ResponseWriter into the FacesContext, saving the old one aside.
 451  0
         ResponseWriter responseWriter = facesContext.getResponseWriter();
 452  
 
 453  
         //Now we actually render the document
 454  
         // Call startDocument() on the ResponseWriter.
 455  0
         responseWriter.startDocument();
 456  
 
 457  
         // Call encodeAll() on the UIViewRoot
 458  0
         viewToRender.encodeAll(facesContext);
 459  
 
 460  
         // Call endDocument() on the ResponseWriter
 461  0
         responseWriter.endDocument();
 462  
 
 463  0
         responseWriter.flush();
 464  0
     }
 465  
 
 466  
     /**Create a new response-writer using as an underlying writer the stateAwareWriter
 467  
      * Effectively, all output will be buffered in the stateAwareWriter so that later
 468  
      * this writer can replace the state-markers with the actual state.
 469  
      *
 470  
      * If the FacesContext has a non-null ResponseWriter create a new writer using its
 471  
      * cloneWithWriter() method, passing the response's Writer as the argument.
 472  
      * Otherwise, use the current RenderKit to create a new ResponseWriter.
 473  
      *
 474  
      * @param oldResponseWriter
 475  
      * @param stateAwareWriter
 476  
      * @param renderKit
 477  
      * @param externalContext
 478  
      * @return
 479  
      */
 480  
     private ResponseWriter hookInStateAwareWriter(ResponseWriter oldResponseWriter,
 481  
                                                   StateMarkerAwareWriter stateAwareWriter,
 482  
                                                   RenderKit renderKit,
 483  
                                                   ExternalContext externalContext)
 484  
     {
 485  0
         return oldResponseWriter.cloneWithWriter(stateAwareWriter);
 486  
     }
 487  
 
 488  
     /**Build the view-tree before rendering.
 489  
      * This is done by dispatching to the underlying JSP-page, effectively processing it, creating
 490  
      * components out of any text in between JSF components (not rendering the text to the output of course, this
 491  
      * will happen later while rendering), attaching these components
 492  
      * to the component tree, and buffering any content after the view-root.
 493  
      *
 494  
      * @param response The current response - it will be replaced while the view-building happens
 495  
      * (we want the text in the component tree, not on the actual servlet output stream)
 496  
      * @param externalContext The external context where the response will be replaced while building
 497  
      * @param viewId The view-id to dispatch to
 498  
      * @return true if successfull, false if an error occurred during rendering
 499  
      * @throws IOException
 500  
      */
 501  
     private boolean buildView(ServletResponse response, ExternalContext externalContext, String viewId)
 502  
             throws IOException
 503  
     {
 504  0
         ServletViewResponseWrapper wrappedResponse = new ServletViewResponseWrapper((HttpServletResponse) response);
 505  
 
 506  0
         externalContext.setResponse(wrappedResponse);
 507  
         try
 508  
         {
 509  0
             externalContext.dispatch(viewId);
 510  
         }
 511  
         finally
 512  
         {
 513  0
             externalContext.setResponse(response);
 514  0
         }
 515  
 
 516  0
         boolean errorResponse = wrappedResponse.getStatus() < 200 || wrappedResponse.getStatus() > 299;
 517  0
         if (errorResponse)
 518  
         {
 519  0
             wrappedResponse.flushToWrappedResponse();
 520  0
             return false;
 521  
         }
 522  
 
 523  
         // store the wrapped response in the request, so it is thread-safe
 524  0
         externalContext.getRequestMap().put(AFTER_VIEW_TAG_CONTENT_PARAM, wrappedResponse);
 525  
 
 526  0
         return true;
 527  
     }
 528  
 
 529  
     /**
 530  
      * Just invoke StateManager.restoreView.
 531  
      */
 532  
     @Override
 533  
     public UIViewRoot restoreView(FacesContext facesContext, String viewId)
 534  
     {
 535  0
         Application application = facesContext.getApplication();
 536  0
         ViewHandler applicationViewHandler = application.getViewHandler();
 537  0
         String renderKitId = applicationViewHandler.calculateRenderKitId(facesContext);
 538  0
         String calculatedViewId = getViewHandlerSupport().calculateViewId(facesContext, viewId);
 539  0
         UIViewRoot viewRoot = application.getStateManager().restoreView(facesContext, calculatedViewId, renderKitId);
 540  0
         return viewRoot;
 541  
     }
 542  
 
 543  
     /**
 544  
      * Writes a state marker that is replaced later by one or more hidden form
 545  
      * inputs.
 546  
      * <p>
 547  
      * The problem with html is that the only place to encode client-side state is
 548  
      * in a hidden html input field. However when a form is submitted, only the fields
 549  
      * within a particular form are sent; fields in other forms are not sent. Therefore
 550  
      * the view tree state must be written into every form in the page. This method
 551  
      * is therefore invoked at the end of every form.
 552  
      * <p>
 553  
      * Theoretically the state of a component tree will not change after rendering
 554  
      * starts. Therefore it is possible to create a serialized representation of that
 555  
      * state at the start of the rendering phase (or when first needed) and output it
 556  
      * whenever needed as described above. However this is not currently implemented;
 557  
      * instead the entire page being generated is buffered, and a "marker" string is
 558  
      * output instead of the tree state. After the rendering pass is complete the component
 559  
      * final tree state is computed and the buffer is then post-processed to replace the
 560  
      * "marker" strings with the real data. 
 561  
      * <p>
 562  
      * This method also supports "javascript viewstate". TODO: document this.
 563  
      *  
 564  
      * @param facesContext
 565  
      * @throws IOException
 566  
      */
 567  
     @Override
 568  
     public void writeState(FacesContext facesContext) throws IOException
 569  
     {
 570  0
         StateManager stateManager = facesContext.getApplication().getStateManager();
 571  0
         if (stateManager.isSavingStateInClient(facesContext))
 572  
         {
 573  
         // Only write state marker if javascript view state is disabled
 574  0
         ExternalContext extContext = facesContext.getExternalContext();
 575  0
         if (!(JavascriptUtils.isJavascriptAllowed(extContext) &&
 576  
                 MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript()))
 577  
         {
 578  0
             facesContext.getResponseWriter().write(FORM_STATE_MARKER);
 579  
         }
 580  0
         }
 581  
         else
 582  
         {
 583  0
             stateManager.writeState(facesContext, new Object[2]);
 584  
         }
 585  0
     }
 586  
 
 587  
     /**
 588  
      * Writes the response and replaces the state marker tags with the state information for the current context
 589  
      */
 590  
     private static class StateMarkerAwareWriter extends Writer
 591  
     {
 592  
         private StringBuilder buf;
 593  
 
 594  
         public StateMarkerAwareWriter()
 595  0
         {
 596  0
             this.buf = new StringBuilder();
 597  0
         }
 598  
 
 599  
         @Override
 600  
         public void close() throws IOException
 601  
         {
 602  0
         }
 603  
 
 604  
         @Override
 605  
         public void flush() throws IOException
 606  
         {
 607  0
         }
 608  
 
 609  
         @Override
 610  
         public void write(char[] cbuf, int off, int len) throws IOException
 611  
         {
 612  0
             if ((off < 0) || (off > cbuf.length) || (len < 0) ||
 613  
                     ((off + len) > cbuf.length) || ((off + len) < 0))
 614  
             {
 615  0
                 throw new IndexOutOfBoundsException();
 616  
             }
 617  0
             else if (len == 0)
 618  
             {
 619  0
                 return;
 620  
             }
 621  0
             buf.append(cbuf, off, len);
 622  0
         }
 623  
 
 624  
         public StringBuilder getStringBuilder()
 625  
         {
 626  0
             return buf;
 627  
         }
 628  
 
 629  
         public void flushToWriter(Writer writer) throws IOException
 630  
         {
 631  0
             FacesContext facesContext = FacesContext.getCurrentInstance();
 632  0
             StateManager stateManager = facesContext.getApplication().getStateManager();
 633  
 
 634  0
             StringWriter stateWriter = new StringWriter();
 635  0
             ResponseWriter realWriter = facesContext.getResponseWriter();
 636  0
             facesContext.setResponseWriter(realWriter.cloneWithWriter(stateWriter));
 637  
 
 638  0
             Object serializedView = stateManager.saveView(facesContext);
 639  
 
 640  0
             stateManager.writeState(facesContext, serializedView);
 641  0
             facesContext.setResponseWriter(realWriter);
 642  
 
 643  0
             StringBuilder contentBuffer = getStringBuilder();
 644  0
             String state = stateWriter.getBuffer().toString();
 645  
 
 646  0
             ExternalContext extContext = facesContext.getExternalContext();
 647  0
             if (JavascriptUtils.isJavascriptAllowed(extContext) &&
 648  
                     MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript())
 649  
             {
 650  
                 // If javascript viewstate is enabled no state markers were written
 651  0
                 write(contentBuffer, 0, contentBuffer.length(), writer);
 652  0
                 writer.write(state);
 653  
             }
 654  
             else
 655  
             {
 656  
                 // If javascript viewstate is disabled state markers must be replaced
 657  0
                 int lastFormMarkerPos = 0;
 658  0
                 int formMarkerPos = 0;
 659  
                 // Find all state markers and write out actual state instead
 660  0
                 while ((formMarkerPos = contentBuffer.indexOf(FORM_STATE_MARKER, formMarkerPos)) > -1)
 661  
                 {
 662  
                     // Write content before state marker
 663  0
                     write(contentBuffer, lastFormMarkerPos, formMarkerPos, writer);
 664  
                     // Write state and move position in buffer after marker
 665  0
                     writer.write(state);
 666  0
                     formMarkerPos += FORM_STATE_MARKER_LEN;
 667  0
                     lastFormMarkerPos = formMarkerPos;
 668  
                 }
 669  
                 // Write content after last state marker
 670  0
                 if (lastFormMarkerPos < contentBuffer.length())
 671  
                 {
 672  0
                     write(contentBuffer, lastFormMarkerPos, contentBuffer.length(), writer);
 673  
                 }
 674  
             }
 675  
 
 676  0
         }
 677  
 
 678  
         /**
 679  
          * Writes the content of the specified StringBuffer from index
 680  
          * <code>beginIndex</code> to index <code>endIndex - 1</code>.
 681  
          *
 682  
          * @param contentBuffer  the <code>StringBuffer</code> to copy content from
 683  
          * @param beginIndex  the beginning index, inclusive.
 684  
          * @param endIndex  the ending index, exclusive
 685  
          * @param writer  the <code>Writer</code> to write to
 686  
          * @throws IOException  if an error occurs writing to specified <code>Writer</code>
 687  
          */
 688  
         private void write(StringBuilder contentBuffer, int beginIndex, int endIndex, Writer writer) throws IOException
 689  
         {
 690  0
             int index = beginIndex;
 691  0
             int bufferSize = 2048;
 692  0
             char[] bufToWrite = new char[bufferSize];
 693  
 
 694  0
             while (index < endIndex)
 695  
             {
 696  0
                 int maxSize = Math.min(bufferSize, endIndex - index);
 697  
 
 698  0
                 contentBuffer.getChars(index, index + maxSize, bufToWrite, 0);
 699  0
                 writer.write(bufToWrite, 0, maxSize);
 700  
 
 701  0
                 index += bufferSize;
 702  0
             }
 703  0
         }
 704  
     }
 705  
 }