Coverage Report - org.apache.myfaces.view.facelets.ViewPoolProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
ViewPoolProcessor
0%
0/343
0%
0/300
7.68
ViewPoolProcessor$ClearPartialTreeContext
0%
0/7
N/A
7.68
 
 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.view.facelets;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.logging.Level;
 26  
 import java.util.logging.Logger;
 27  
 import javax.faces.application.Application;
 28  
 import javax.faces.application.NavigationHandler;
 29  
 import javax.faces.application.ProjectStage;
 30  
 import javax.faces.application.ResourceDependency;
 31  
 import javax.faces.application.ViewHandler;
 32  
 import javax.faces.component.UIComponent;
 33  
 import javax.faces.component.UIViewRoot;
 34  
 import javax.faces.context.FacesContext;
 35  
 import javax.faces.event.PhaseId;
 36  
 import javax.faces.view.StateManagementStrategy;
 37  
 import javax.faces.view.ViewDeclarationLanguage;
 38  
 import org.apache.myfaces.application.StateManagerImpl;
 39  
 import org.apache.myfaces.component.ComponentResourceContainer;
 40  
 import org.apache.myfaces.context.RequestViewContext;
 41  
 import org.apache.myfaces.context.RequestViewMetadata;
 42  
 import org.apache.myfaces.lifecycle.DefaultRestoreViewSupport;
 43  
 import org.apache.myfaces.lifecycle.RestoreViewSupport;
 44  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 45  
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
 46  
 import org.apache.myfaces.view.facelets.impl.FaceletCompositionContextImpl;
 47  
 import org.apache.myfaces.view.facelets.pool.ViewPool;
 48  
 import org.apache.myfaces.view.facelets.pool.ViewPoolFactory;
 49  
 import org.apache.myfaces.view.facelets.pool.ViewEntry;
 50  
 import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata;
 51  
 import org.apache.myfaces.view.facelets.pool.impl.ViewPoolFactoryImpl;
 52  
 import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
 53  
 import org.apache.myfaces.view.facelets.tag.jsf.FaceletState;
 54  
 
 55  
 /**
 56  
  * This class is reponsible for all processing tasks related to the view pool.
 57  
  * 
 58  
  * For enable the pool only for a subset of your views, you can
 59  
  * add an entry inside faces-config.xml file like this:
 60  
  * <pre>
 61  
  * {@code 
 62  
  * <faces-config-extension>
 63  
  *   <view-pool-mapping>
 64  
  *      <url-pattern>/*</url-pattern>
 65  
  *      <parameter>
 66  
  *          <name>org.apache.myfaces.VIEW_POOL_MAX_POOL_SIZE</name>
 67  
  *          <value>5</value>
 68  
  *      </parameter>
 69  
  *  </view-pool-mapping>
 70  
  * </faces-config-extension>
 71  
  * }
 72  
  * </pre>
 73  
  * 
 74  
  * @author Leonardo Uribe
 75  
  */
 76  
 public class ViewPoolProcessor
 77  
 {
 78  
     /**
 79  
      * Used to hold the view pool processor instance on the application map. 
 80  
      * A ViewPoolProcessor is only accessible if the view pool has been
 81  
      * enabled for the whole application (using a web config parameter) or 
 82  
      * partially (an &lt;view-pool-mapping&gt; entry inside &lt;faces-config-extension&gt;).
 83  
      */
 84  
     private final static String INSTANCE = "oam.ViewPoolProcessor";
 85  
     
 86  
     /**
 87  
      * UIViewRoot attribute to enable/disable the view for use pooling.
 88  
      */
 89  
     public final static String ENABLE_VIEW_POOL = "oamEnableViewPool";
 90  
     
 91  
     /**
 92  
      * Flag that indicates to the StateManagementStrategy that no state needs 
 93  
      * to be stored and we are using saveState() to dispose the view and store it
 94  
      * into the pool directly.
 95  
      */
 96  
     public final static String FORCE_HARD_RESET = "oam.ViewPool.forceHardReset";
 97  
     
 98  
     /**
 99  
      * Flag to indicate that dispose this view on navigation is valid.
 100  
      */
 101  
     public final static String DISPOSE_VIEW_NAVIGATION = "oam.ViewPool.disposeViewOnNavigation";
 102  
     
 103  
     /**
 104  
      * Attribute of UIViewRoot that indicates if a soft (1) or hard(2) 
 105  
      * (reset and check) reset is required in the call to saveState().
 106  
      */
 107  
     public final static String RESET_SAVE_STATE_MODE_KEY ="oam.view.resetSaveStateMode";
 108  
     
 109  
     /**
 110  
      * Indicates no reset should be done on this state saving
 111  
      */
 112  
     public static final int RESET_MODE_OFF = 0;
 113  
     
 114  
     /**
 115  
      * Indicates a soft reset should be done when saveState(...) is performed,
 116  
      * which means all transient state should be cleared but the delta state 
 117  
      * should not be destroyed in the process.
 118  
      */
 119  
     public static final int RESET_MODE_SOFT = 1;
 120  
     
 121  
     /**
 122  
      * Indicates a hard reset should be done when saveState(...) is performed,
 123  
      * which means all transient and delta state should be cleared, destroying
 124  
      * all existing state in the process. If something cannot be reseted, the 
 125  
      * state should return non null, so the algorithm can remove the component
 126  
      * from the tree and mark the tree as partial (requires refresh before
 127  
      * reuse).
 128  
      */
 129  
     public static final int RESET_MODE_HARD = 2;
 130  
     
 131  
     /**
 132  
      * Param used to indicate a "deferred navigation" needs to be done. To allow the view pool to
 133  
      * dispose the view properly (and reuse it later), it is necessary to ensure the view is not
 134  
      * being used at the moment. If the navigation call occur inside an action listener, the current
 135  
      * view is being used and cannot be disposed (because it conflicts with hard/soft reset). This
 136  
      * extension allows to call handleNavigation() before end invoke application phase, but after
 137  
      * traverse the component tree. 
 138  
      */
 139  
     public static final String INVOKE_DEFERRED_NAVIGATION = "oam.invoke.navigation";
 140  
 
 141  
     private ViewPoolFactory viewPoolFactory;
 142  
     private RestoreViewSupport restoreViewSupport;
 143  
     
 144  
     public ViewPoolProcessor(FacesContext context)
 145  0
     {
 146  0
         viewPoolFactory = new ViewPoolFactoryImpl(context);
 147  0
         restoreViewSupport = new DefaultRestoreViewSupport(context);
 148  0
     }
 149  
     
 150  
     public static ViewPoolProcessor getInstance(FacesContext context)
 151  
     {
 152  0
         return (ViewPoolProcessor) context.getExternalContext().
 153  
                 getApplicationMap().get(INSTANCE);
 154  
     }
 155  
     
 156  
     /**
 157  
      * This method should be called at startup to decide if a view processor should be
 158  
      * provided or not to the runtime.
 159  
      * 
 160  
      * @param context 
 161  
      */
 162  
     public static void initialize(FacesContext context)
 163  
     {
 164  0
         if (context.isProjectStage(ProjectStage.Production))
 165  
         {
 166  0
             boolean initialize = true;
 167  0
             String elMode = WebConfigParamUtils.getStringInitParameter(
 168  
                         context.getExternalContext(),
 169  
                         FaceletCompositionContextImpl.INIT_PARAM_CACHE_EL_EXPRESSIONS, 
 170  
                             ELExpressionCacheMode.noCache.name());
 171  0
             if (!elMode.equals(ELExpressionCacheMode.alwaysRecompile.name()))
 172  
             {
 173  0
                 Logger.getLogger(ViewPoolProcessor.class.getName()).log(
 174  
                     Level.INFO, FaceletCompositionContextImpl.INIT_PARAM_CACHE_EL_EXPRESSIONS +
 175  
                     " web config parameter is set to \"" + ( (elMode == null) ? "none" : elMode) +
 176  
                     "\". To enable view pooling this param"+
 177  
                     " must be set to \"alwaysRecompile\". View Pooling disabled.");
 178  0
                 initialize = false;
 179  
             }
 180  
             
 181  0
             long refreshPeriod = WebConfigParamUtils.getLongInitParameter(context.getExternalContext(),
 182  
                     FaceletViewDeclarationLanguage.PARAMS_REFRESH_PERIOD,
 183  
                     FaceletViewDeclarationLanguage.DEFAULT_REFRESH_PERIOD_PRODUCTION);
 184  
 
 185  0
             if (refreshPeriod != -1)
 186  
             {
 187  0
                 Logger.getLogger(ViewPoolProcessor.class.getName()).log(
 188  
                     Level.INFO, ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME +
 189  
                     " web config parameter is set to \"" + Long.toString(refreshPeriod) +
 190  
                     "\". To enable view pooling this param"+
 191  
                     " must be set to \"-1\". View Pooling disabled.");
 192  0
                 initialize = false;
 193  
             }
 194  
             
 195  0
             if (MyfacesConfig.getCurrentInstance(context.getExternalContext()).isStrictJsf2FaceletsCompatibility())
 196  
             {
 197  0
                 Logger.getLogger(ViewPoolProcessor.class.getName()).log(
 198  
                     Level.INFO, MyfacesConfig.INIT_PARAM_STRICT_JSF_2_FACELETS_COMPATIBILITY +
 199  
                     " web config parameter is set to \"" + "true" +
 200  
                     "\". To enable view pooling this param "+
 201  
                     " must be set to \"false\". View Pooling disabled.");
 202  0
                 initialize = false;
 203  
             }
 204  
             
 205  0
             if (initialize)
 206  
             {
 207  0
                 ViewPoolProcessor processor = new ViewPoolProcessor(context);
 208  0
                 context.getExternalContext().
 209  
                     getApplicationMap().put(INSTANCE, processor);
 210  
             }
 211  
         }
 212  0
     }
 213  
 
 214  
     public ViewPool getViewPool(FacesContext context, UIViewRoot root)
 215  
     {
 216  0
         if (root.isTransient())
 217  
         {
 218  
             // Stateless views cannot be pooled, because we are reusing
 219  
             // state saving algorithm for that.
 220  0
             return null;
 221  
         }
 222  0
         Boolean enableViewPool = (Boolean) root.getAttributes().get(ViewPoolProcessor.ENABLE_VIEW_POOL);
 223  0
         if (enableViewPool != null && !Boolean.TRUE.equals(enableViewPool))
 224  
         {
 225  
             // view pool not enabled for this view.
 226  0
             return null;
 227  
         }
 228  
         else
 229  
         {
 230  0
             return viewPoolFactory.getViewPool(context, root);
 231  
         }
 232  
     }
 233  
     
 234  
     public boolean isViewPoolEnabledForThisView(FacesContext context, UIViewRoot root)
 235  
     {
 236  0
         if (root.isTransient())
 237  
         {
 238  
             // Stateless views cannot be pooled, because we are reusing
 239  
             // state saving algorithm for that.
 240  0
             return false;
 241  
         }
 242  0
         Boolean enableViewPool = (Boolean) root.getAttributes().get(ViewPoolProcessor.ENABLE_VIEW_POOL);
 243  0
         if (enableViewPool != null)
 244  
         {
 245  0
             if (Boolean.TRUE.equals(enableViewPool))
 246  
             {
 247  0
                 return true;
 248  
             }
 249  
             else
 250  
             {
 251  0
                 return false;
 252  
             }
 253  
         }
 254  
         else
 255  
         {
 256  0
             ViewPool viewPool = getViewPool(context, root);
 257  0
             if (viewPool != null)
 258  
             {
 259  0
                 return true;
 260  
             }
 261  
         }
 262  0
         return false;
 263  
     }
 264  
 
 265  
     public boolean isViewPoolStrategyAllowedForThisView(FacesContext context, UIViewRoot root)
 266  
     {
 267  
         // Check if the viewId is not null.
 268  0
         if (root.getViewId() == null)
 269  
         {
 270  0
             return false;
 271  
         }
 272  0
         if (root.isTransient())
 273  
         {
 274  
             // Stateless views cannot be pooled, because we are reusing
 275  
             // state saving algorithm for that.
 276  0
             return false;
 277  
         }
 278  
         
 279  
         // Check if the view is enabled or not for use pooling
 280  0
         if (!isViewPoolEnabledForThisView(context, root))
 281  
         {
 282  0
             return false;
 283  
         }
 284  
 
 285  
         // Check if the vdl is using PSS on this view (has a vdl and that vdl is facelets)
 286  0
         ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
 287  
             getViewDeclarationLanguage(context, root.getViewId());
 288  0
         if (vdl == null)
 289  
         {
 290  0
             return false;
 291  
         }
 292  0
         else if (!ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(vdl.getId()))
 293  
         {
 294  0
             return false;
 295  
         }
 296  
         
 297  0
         if (vdl.getStateManagementStrategy(context, root.getViewId()) == null)
 298  
         {
 299  0
             return false;
 300  
         }
 301  0
         return true;
 302  
     }
 303  
 
 304  
     public void setViewPoolDisabledOnThisView(FacesContext context, UIViewRoot root, boolean value)
 305  
     {
 306  0
         root.getAttributes().put(ViewPoolProcessor.ENABLE_VIEW_POOL, !value);
 307  0
     }
 308  
     
 309  
     /**
 310  
      * Takes the newView and restore the state taken as base the provided ViewEntry,
 311  
      * and then move all child components from oldView to newView, to finally obtain
 312  
      * a clean component tree.
 313  
      * 
 314  
      * @param context
 315  
      * @param newView
 316  
      * @param entry 
 317  
      */
 318  
     public void cloneAndRestoreView(FacesContext context, UIViewRoot newView, 
 319  
             ViewEntry entry, ViewStructureMetadata metadata)
 320  
     {
 321  0
         UIViewRoot oldView = entry.getViewRoot();
 322  
         // retrieveViewRootInitialState(context, oldView)
 323  0
         Object viewState = metadata.getViewRootState();
 324  
         Object newViewState;
 325  0
         UIComponent metadataFacet = newView.getFacet(UIViewRoot.METADATA_FACET_NAME);
 326  0
         if (viewState == null)
 327  
         {
 328  
             // (Optional, it should be always metadata)
 329  0
             oldView.clearInitialState();
 330  0
             viewState = oldView.saveState(context);
 331  
         }
 332  0
         Map<String, Object> viewScopeMap = newView.getViewMap(false);
 333  0
         if (viewScopeMap != null && !viewScopeMap.isEmpty())
 334  
         {
 335  0
             newViewState = newView.saveState(context);
 336  
         }
 337  
         else
 338  
         {
 339  0
             newViewState = null;
 340  
         }
 341  
         
 342  0
         boolean oldProcessingEvents = context.isProcessingEvents();
 343  0
         context.setProcessingEvents(false);
 344  
         try
 345  
         {
 346  0
             if (oldView.getFacetCount() > 0)
 347  
             {
 348  0
                 List<String> facetKeys = new ArrayList<String>();
 349  0
                 facetKeys.addAll(oldView.getFacets().keySet());
 350  0
                 for (String facetKey : facetKeys)
 351  
                 {
 352  
                     //context.setProcessingEvents(false);
 353  0
                     if (metadataFacet != null && UIViewRoot.METADATA_FACET_NAME.equals(facetKey) &&
 354  
                         !PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId()))
 355  
                     {
 356  
                         // Metadata facet is special, it is created when ViewHandler.createView(...) is
 357  
                         // called, so it shouldn't be taken from the oldView, otherwise the state
 358  
                         // will be lost. Instead reuse it and discard the one in oldView.
 359  
                         // But on restore view phase, use the old one, because the new one does not
 360  
                         // have initial state marked.
 361  0
                         newView.getFacets().put(facetKey, metadataFacet);
 362  
                     }
 363  
                     else
 364  
                     {
 365  0
                         UIComponent facet = oldView.getFacets().remove(facetKey);
 366  
                         //context.setProcessingEvents(true);
 367  0
                         newView.getFacets().put(facetKey, facet);
 368  
                     }
 369  0
                 }
 370  
             }
 371  0
             if (oldView.getChildCount() > 0)
 372  
             {
 373  0
                 for (Iterator<UIComponent> it = oldView.getChildren().iterator(); it.hasNext();)
 374  
                 {
 375  
                     //context.setProcessingEvents(false);
 376  0
                     UIComponent c = it.next();
 377  0
                     it.remove();
 378  
                     //context.setProcessingEvents(true);
 379  0
                     newView.getChildren().add(c);
 380  0
                 }
 381  
             }
 382  
             
 383  
             // Restore the newView as saved just before markInitialState() call
 384  0
             newView.restoreState(context, viewState);
 385  0
             newView.markInitialState();
 386  
             
 387  0
             if (!PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId()))
 388  
             {
 389  
                 // Restore bindings like in restore view phase, because in this case,
 390  
                 // bindings needs to be set (Application.createComponent is not called!).
 391  0
                 restoreViewSupport.processComponentBinding(context, newView);
 392  
                 
 393  
                 // Restore view scope map if necessary
 394  0
                 if (viewScopeMap != null && !viewScopeMap.isEmpty())
 395  
                 {
 396  0
                     Map<String, Object> newViewScopeMap = newView.getViewMap(false);
 397  0
                     if (newViewScopeMap == null)
 398  
                     {
 399  0
                         newView.restoreViewScopeState(context, newViewState);
 400  
                     }
 401  
                     else
 402  
                     {
 403  
                         // Should theoretically not happen, because when a pooled static view 
 404  
                         // is saved, the view scope map is skipped, otherwise it could be a
 405  
                         // leak. Anyway, we let this code here that overrides the values from
 406  
                         // the original map.
 407  0
                         for (Map.Entry<String, Object> entry2 : viewScopeMap.entrySet())
 408  
                         {
 409  0
                             newViewScopeMap.put(entry2.getKey(), entry2.getValue());
 410  0
                         }
 411  
                     }
 412  
                 }
 413  
             }
 414  
             
 415  
             // Update request view metadata to ensure resource list is restored as when the
 416  
             // view was built on the first time. This ensures correct calculation of added 
 417  
             // resources by dynamic behavior. 
 418  0
             RequestViewContext rcv = RequestViewContext.getCurrentInstance(context, newView, false);
 419  0
             if (rcv != null)
 420  
             {
 421  0
                 rcv.setRequestViewMetadata(metadata.getRequestViewMetadata().cloneInstance());
 422  
             }
 423  
             else
 424  
             {
 425  0
                 RequestViewContext.setCurrentInstance(context, newView,
 426  
                         RequestViewContext.newInstance(metadata.getRequestViewMetadata().cloneInstance()));
 427  
             }
 428  
         }
 429  
         finally
 430  
         {
 431  0
             context.setProcessingEvents(oldProcessingEvents);
 432  0
         }
 433  0
     }
 434  
     
 435  
     public void storeViewStructureMetadata(FacesContext context, UIViewRoot root)
 436  
     {
 437  0
         ViewPool viewPool = getViewPool(context, root);
 438  0
         if (viewPool != null)
 439  
         {
 440  0
             FaceletState faceletState = (FaceletState) root.getAttributes().get(
 441  
                     ComponentSupport.FACELET_STATE_INSTANCE);
 442  0
             boolean isDynamic = faceletState != null ? faceletState.isDynamic() : false;
 443  0
             if (!isDynamic)
 444  
             {
 445  0
                 viewPool.storeStaticViewStructureMetadata(context, root, faceletState);            
 446  
             }
 447  
             else
 448  
             {
 449  0
                 viewPool.storeDynamicViewStructureMetadata(context, root, faceletState);
 450  
             }
 451  
         }
 452  0
     }
 453  
     
 454  
     public ViewStructureMetadata retrieveViewStructureMetadata(FacesContext context,
 455  
             UIViewRoot root)
 456  
     {
 457  0
         ViewPool viewPool = getViewPool(context, root);
 458  0
         if (viewPool != null)
 459  
         {
 460  0
             FaceletState faceletState = (FaceletState) root.getAttributes().get(
 461  
                     ComponentSupport.FACELET_STATE_INSTANCE);
 462  0
             boolean isDynamic = faceletState != null ? faceletState.isDynamic() : false;
 463  0
             if (!isDynamic)
 464  
             {
 465  0
                 return viewPool.retrieveStaticViewStructureMetadata(context, root);
 466  
             }
 467  
             else
 468  
             {
 469  0
                 return viewPool.retrieveDynamicViewStructureMetadata(context, root, faceletState);
 470  
             }
 471  
         }
 472  0
         return null;
 473  
     }
 474  
     
 475  
     public void pushResetableView(FacesContext context, UIViewRoot view, FaceletState faceletViewState)
 476  
     {
 477  0
         ViewPool viewPool = getViewPool(context, view);
 478  0
         if (viewPool != null)
 479  
         {
 480  0
             boolean isDynamic = faceletViewState != null ? faceletViewState.isDynamic() : false;
 481  0
             if (!isDynamic)
 482  
             {
 483  0
                 clearTransientAndNonFaceletComponentsForStaticView(context, view);
 484  0
                 viewPool.pushStaticStructureView(context, view);
 485  
             }
 486  
             else
 487  
             {
 488  0
                 ViewStructureMetadata viewStructureMetadata = viewPool.retrieveDynamicViewStructureMetadata(
 489  
                     context, view, faceletViewState);
 490  0
                 if (viewStructureMetadata != null)
 491  
                 {
 492  0
                     clearTransientAndNonFaceletComponentsForDynamicView(context, view, viewStructureMetadata);
 493  0
                     viewPool.pushDynamicStructureView(context, view, faceletViewState);
 494  
                 }
 495  
             }
 496  
         }
 497  0
     }
 498  
     
 499  
     public void pushPartialView(FacesContext context, UIViewRoot view, FaceletState faceletViewState, int count)
 500  
     {
 501  0
         ViewPool viewPool = getViewPool(context, view);
 502  
         
 503  0
         if (viewPool != null && viewPool.isWorthToRecycleThisView(context, view))
 504  
         {
 505  0
             ViewStructureMetadata viewStructureMetadata = null;
 506  0
             if (faceletViewState == null)
 507  
             {
 508  0
                 viewStructureMetadata = viewPool.retrieveStaticViewStructureMetadata(context, view);
 509  
             }
 510  
             else
 511  
             {
 512  0
                 viewStructureMetadata = viewPool.retrieveDynamicViewStructureMetadata(
 513  
                     context, view, faceletViewState);
 514  
             }
 515  0
             if (viewStructureMetadata != null)
 516  
             {
 517  0
                 ClearPartialTreeContext ptc = new ClearPartialTreeContext();
 518  
                 // add partial structure view to the map.
 519  0
                 clearTransientAndRemoveNonResetableComponents(context, ptc, view, viewStructureMetadata);
 520  0
                 int reusableCount = ptc.getCount();
 521  0
                 float factor = ((float)reusableCount) / ((float)count);
 522  0
                 if (factor > 0.3f)
 523  
                 {
 524  0
                     viewPool.pushPartialStructureView(context, view);
 525  
                 }
 526  
             }
 527  
         }        
 528  0
     }
 529  
     
 530  
     protected void clearTransientAndNonFaceletComponentsForStaticView(final FacesContext context, 
 531  
             final UIViewRoot root)
 532  
     {
 533  
         // In a static view, clear components that are both transient and non bound to any facelet tag handler
 534  
         // is quite simple. Since the structure of the view is static, there is no need to check component resources.
 535  0
         clearTransientAndNonFaceletComponents(context, root);
 536  0
     }
 537  
     
 538  
     public void clearTransientAndNonFaceletComponentsForDynamicView(final FacesContext context, 
 539  
             final UIViewRoot root, final ViewStructureMetadata viewStructureMetadata)
 540  
     {
 541  
         //Scan children
 542  0
         int childCount = root.getChildCount();
 543  0
         if (childCount > 0)
 544  
         {
 545  0
             for (int i = 0; i < childCount; i++)
 546  
             {
 547  0
                 UIComponent child = root.getChildren().get(i);
 548  0
                 if (child != null && child.isTransient() &&
 549  
                     child.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
 550  
                 {
 551  0
                     root.getChildren().remove(i);
 552  0
                     i--;
 553  0
                     childCount--;
 554  
                 }
 555  
                 else
 556  
                 {
 557  0
                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
 558  
                     {
 559  0
                         clearTransientAndNonFaceletComponents(context, child);
 560  
                     }
 561  
                 }
 562  
             }
 563  
         }
 564  
 
 565  0
         clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(
 566  
             context, root, viewStructureMetadata);
 567  0
     }
 568  
     
 569  
     private void clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(final FacesContext context, 
 570  
             final UIViewRoot root, ViewStructureMetadata viewStructureMetadata)
 571  
     {
 572  
         
 573  
         //Scan facets
 574  0
         if (root.getFacetCount() > 0)
 575  
         {
 576  0
             Map<String, UIComponent> facets = root.getFacets();
 577  0
             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
 578  
             {
 579  0
                 UIComponent fc = itr.next();
 580  0
                 if (fc != null && !(fc instanceof ComponentResourceContainer))
 581  
                 {
 582  0
                     if ( fc.isTransient() &&
 583  
                         fc.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
 584  
                     {
 585  0
                         itr.remove();
 586  
                     }
 587  
                     else
 588  
                     {
 589  0
                         if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
 590  
                         {
 591  0
                             clearTransientAndNonFaceletComponents(context, fc);
 592  
                         }
 593  
                     }
 594  
                 }
 595  0
                 else if (fc != null)
 596  
                 {
 597  
                     // In a facet which is a ComponentResourceContainer instance,
 598  
                     // we need to check these two cases:
 599  
                     // 1. Resources relocated by facelets
 600  
                     // 2. Resources created by effect of a @ResourceDependency annotation
 601  0
                     if (fc.getId() != null && fc.getId().startsWith("javax_faces_location_"))
 602  
                     {
 603  0
                         String target = fc.getId().substring("javax_faces_location_".length());
 604  0
                         Map<String, List<ResourceDependency>> addedResources = 
 605  
                             viewStructureMetadata.getRequestViewMetadata().
 606  
                                 getResourceDependencyAnnotations(context);
 607  0
                         List<ResourceDependency> resourceDependencyList = (addedResources != null) ?
 608  
                             addedResources.get(target) : null;
 609  
 
 610  0
                         clearComponentResourceContainer(context, fc, resourceDependencyList);
 611  
                     }
 612  
                 }
 613  0
             }
 614  
         }
 615  0
     }
 616  
     
 617  
     private void clearComponentResourceContainer(final FacesContext context, UIComponent component,
 618  
         List<ResourceDependency> resourceDependencyList)
 619  
     {
 620  
         //Scan children
 621  0
         int childCount = component.getChildCount();
 622  0
         if (childCount > 0)
 623  
         {
 624  0
             for (int i = 0; i < childCount; i++)
 625  
             {
 626  0
                 UIComponent child = component.getChildren().get(i);
 627  0
                 String id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
 628  0
                 if (child != null && child.isTransient() &&
 629  
                     id == null)
 630  
                 {
 631  
                     //Remove both transient not facelets bound components
 632  0
                     component.getChildren().remove(i);
 633  0
                     i--;
 634  0
                     childCount--;
 635  
                 }
 636  0
                 else if (id != null)
 637  
                 {
 638  
                     // If it has an id set, it is a facelet component resource.
 639  
                     // The refresh algorithm take care of the cleanup.
 640  
                 }
 641  
                 /*
 642  
                 else if (!child.isTransient() && id != null && !faceletResources.contains(id))
 643  
                 {
 644  
                     // check if the resource has a facelet tag in this "dynamic state", if not
 645  
                     // remove it. Really leave a component resource does not harm, but 
 646  
                     // the objective is make this view as close as when if it is built as new.
 647  
                     component.getChildren().remove(i);
 648  
                     i--;
 649  
                     childCount--;
 650  
                 }*/
 651  
                 else
 652  
                 {
 653  
                     // Check if the component instance was created using a @ResourceDependency annotation
 654  0
                     Object[] rdk = (Object[]) child.getAttributes().get(
 655  
                                 RequestViewMetadata.RESOURCE_DEPENDENCY_KEY);
 656  0
                     if (rdk != null)
 657  
                     {
 658  0
                         boolean found = false;
 659  0
                         String library = (String) rdk[0];
 660  0
                         String name = (String) rdk[1];
 661  0
                         if (resourceDependencyList != null)
 662  
                         {
 663  0
                             for (ResourceDependency resource : resourceDependencyList)
 664  
                             {
 665  0
                                 if (library == null && resource.library() == null)
 666  
                                 {
 667  0
                                     if (name != null && name.equals(resource.name()))
 668  
                                     {
 669  0
                                         found = true;
 670  0
                                         break;
 671  
                                     }
 672  
                                 }
 673  
                                 else
 674  
                                 {
 675  0
                                     if (library != null && library.equals(resource.library()) &&
 676  
                                         name != null && name.equals(resource.name()) )
 677  
                                     {
 678  0
                                         found = true;
 679  0
                                         break;
 680  
                                     }
 681  
                                 }
 682  0
                             }
 683  
                         }
 684  0
                         if (!found)
 685  
                         {
 686  
                             //Remove it, because for this dynamic state it it does not exists.
 687  0
                             component.getChildren().remove(i);
 688  0
                             i--;
 689  0
                             childCount--;
 690  
                         }
 691  
                         // If found just leave it.
 692  0
                     }
 693  
                     else
 694  
                     {
 695  0
                         if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
 696  
                         {
 697  0
                             clearTransientAndNonFaceletComponents(context, child);
 698  
                         }
 699  
                     }
 700  
                 }
 701  
             }
 702  
         }
 703  0
     }
 704  
 
 705  
     /**
 706  
      * Clear all transient components not created by facelets algorithm. In this way,
 707  
      * we ensure the component tree does not have any changes done after markInitialState.
 708  
      * 
 709  
      * @param context
 710  
      * @param component 
 711  
      */
 712  
     private void clearTransientAndNonFaceletComponents(final FacesContext context, final UIComponent component)
 713  
     {
 714  
         //Scan children
 715  0
         int childCount = component.getChildCount();
 716  0
         if (childCount > 0)
 717  
         {
 718  0
             for (int i = 0; i < childCount; i++)
 719  
             {
 720  0
                 UIComponent child = component.getChildren().get(i);
 721  0
                 if (child != null && child.isTransient() &&
 722  
                     child.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
 723  
                 {
 724  0
                     component.getChildren().remove(i);
 725  0
                     i--;
 726  0
                     childCount--;
 727  
                 }
 728  
                 else
 729  
                 {
 730  0
                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
 731  
                     {
 732  0
                         clearTransientAndNonFaceletComponents(context, child);
 733  
                     }
 734  
                 }
 735  
             }
 736  
         }
 737  
 
 738  
         //Scan facets
 739  0
         if (component.getFacetCount() > 0)
 740  
         {
 741  0
             Map<String, UIComponent> facets = component.getFacets();
 742  0
             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
 743  
             {
 744  0
                 UIComponent fc = itr.next();
 745  0
                 if (fc != null && fc.isTransient() &&
 746  
                     fc.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
 747  
                 {
 748  0
                     itr.remove();
 749  
                 }
 750  
                 else
 751  
                 {
 752  0
                     if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
 753  
                     {
 754  0
                         clearTransientAndNonFaceletComponents(context, fc);
 755  
                     }
 756  
                 }
 757  0
             }
 758  
         }
 759  0
     }
 760  
 
 761  
     private void clearTransientAndRemoveNonResetableComponents(final FacesContext context, 
 762  
         final ClearPartialTreeContext ptc, final UIViewRoot root, 
 763  
         ViewStructureMetadata viewStructureMetadata)
 764  
     {
 765  
         //Scan children
 766  0
         int childCount = root.getChildCount();
 767  
 
 768  
         try
 769  
         {
 770  0
             root.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 771  
                         ViewPoolProcessor.RESET_MODE_HARD);
 772  0
             if (childCount > 0)
 773  
             {
 774  0
                 for (int i = 0; i < childCount; i++)
 775  
                 {
 776  0
                     UIComponent child = root.getChildren().get(i);
 777  0
                     boolean containsFaceletId = child.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
 778  0
                     if (child != null && child.isTransient() && !containsFaceletId)
 779  
                     {
 780  
                         //Transient and not bound to facelets tag, remove it!.
 781  0
                         root.getChildren().remove(i);
 782  0
                         i--;
 783  0
                         childCount--;
 784  
                     }
 785  
                     else
 786  
                     {
 787  0
                         if (child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 788  
                         {
 789  
                             //Dynamically added or moved, remove it!
 790  0
                             root.getChildren().remove(i);
 791  0
                             i--;
 792  0
                             childCount--;
 793  
 
 794  
                         }
 795  0
                         else if (containsFaceletId ||
 796  
                             child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 797  
                         {
 798  
                             // Bound to a facelet tag or created by facelets, we have two options:
 799  
                             // 1. If is not transient, check its state and try to clear it, if fails remove it
 800  
                             // 2. If is transient, assume stateless, continue.
 801  0
                             if (!child.isTransient())
 802  
                             {
 803  
                                 // Remember that hard reset is already enabled.
 804  0
                                 Object state = child.saveState(context);
 805  0
                                 if (state == null)
 806  
                                 {
 807  0
                                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
 808  
                                     {
 809  0
                                         clearTransientAndRemoveNonResetableComponents(context, ptc, child);
 810  
                                     }
 811  0
                                     ptc.incrementCount();
 812  
                                 }
 813  
                                 else
 814  
                                 {
 815  0
                                     root.getChildren().remove(i);
 816  0
                                     i--;
 817  0
                                     childCount--;
 818  
                                 }
 819  0
                             }
 820  
                             else
 821  
                             {
 822  0
                                 ptc.incrementCount();
 823  
                             }
 824  
                         }
 825  
                         else
 826  
                         {
 827  
                             // Non facelets component, remove it!.
 828  0
                             root.getChildren().remove(i);
 829  0
                             i--;
 830  0
                             childCount--;
 831  
                         }
 832  
                     }
 833  
                 }
 834  
             }
 835  
 
 836  0
             clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(
 837  
                 context, root, viewStructureMetadata);
 838  
         }
 839  
         finally
 840  
         {
 841  0
             root.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 842  
                         ViewPoolProcessor.RESET_MODE_OFF);
 843  0
         }
 844  0
     }
 845  
     
 846  
     private void clearTransientAndRemoveNonResetableComponents(final FacesContext context,
 847  
         final ClearPartialTreeContext ptc, final UIComponent component)
 848  
     {
 849  
         //Scan children
 850  0
         int childCount = component.getChildCount();
 851  0
         if (childCount > 0)
 852  
         {
 853  0
             for (int i = 0; i < childCount; i++)
 854  
             {
 855  0
                 UIComponent child = component.getChildren().get(i);
 856  0
                 boolean containsFaceletId = child.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
 857  0
                 if (child != null && child.isTransient() && !containsFaceletId)
 858  
                 {
 859  
                     //Transient and not bound to facelets tag, remove it!.
 860  0
                     component.getChildren().remove(i);
 861  0
                     i--;
 862  0
                     childCount--;
 863  
                 }
 864  
                 else
 865  
                 {
 866  0
                     if (child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 867  
                     {
 868  
                         //Dynamically added or moved, remove it!
 869  0
                         component.getChildren().remove(i);
 870  0
                         i--;
 871  0
                         childCount--;
 872  
 
 873  
                     }
 874  0
                     else if (containsFaceletId ||
 875  
                         child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 876  
                     {
 877  
                         // Bound to a facelet tag or created by facelets, we have two options:
 878  
                         // 1. If is not transient, check its state and try to clear it, if fails remove it
 879  
                         // 2. If is transient, assume stateless, continue.
 880  0
                         if (!child.isTransient())
 881  
                         {
 882  
                             // Remember that hard reset is already enabled.
 883  0
                             Object state = child.saveState(context);
 884  0
                             if (state == null)
 885  
                             {
 886  0
                                 if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
 887  
                                 {
 888  0
                                     clearTransientAndRemoveNonResetableComponents(context, ptc, child);
 889  
                                 }
 890  0
                                 ptc.incrementCount();
 891  
                             }
 892  
                             else
 893  
                             {
 894  0
                                 component.getChildren().remove(i);
 895  0
                                 i--;
 896  0
                                 childCount--;
 897  
                             }
 898  0
                         }
 899  
                         else
 900  
                         {
 901  0
                             ptc.incrementCount();
 902  
                         }
 903  
                     }
 904  
                     else
 905  
                     {
 906  
                         // Non facelets component, remove it!.
 907  0
                         component.getChildren().remove(i);
 908  0
                         i--;
 909  0
                         childCount--;
 910  
                     }
 911  
                 }
 912  
             }
 913  
         }
 914  
 
 915  
         //Scan facets
 916  0
         if (component.getFacetCount() > 0)
 917  
         {
 918  0
             Map<String, UIComponent> facets = component.getFacets();
 919  0
             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
 920  
             {
 921  0
                 UIComponent fc = itr.next();
 922  0
                 boolean containsFaceletId = fc.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
 923  0
                 if (fc != null && fc.isTransient() && !containsFaceletId)
 924  
                 {
 925  
                     //Transient and not bound to facelets tag, remove it!.
 926  0
                     itr.remove();
 927  
                 }
 928  
                 else
 929  
                 {
 930  0
                     if (fc.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 931  
                     {
 932  
                         //Dynamically added or moved, remove it!
 933  0
                         itr.remove();
 934  
 
 935  
                     }
 936  0
                     else if (containsFaceletId ||
 937  
                         fc.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
 938  
                     {
 939  
                         // Bound to a facelet tag or created by facelets, we have two options:
 940  
                         // 1. If is not transient, check its state and try to clear it, if fails remove it
 941  
                         // 2. If is transient, assume stateless, continue.
 942  0
                         if (!fc.isTransient())
 943  
                         {
 944  
                             // Remember that hard reset is already enabled.
 945  0
                             Object state = fc.saveState(context);
 946  0
                             if (state == null)
 947  
                             {
 948  0
                                 if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
 949  
                                 {
 950  0
                                     clearTransientAndRemoveNonResetableComponents(context, ptc, fc);
 951  
                                 }
 952  0
                                 ptc.incrementCount();
 953  
                             }
 954  
                             else
 955  
                             {
 956  0
                                 itr.remove();
 957  
                             }
 958  0
                         }
 959  
                         else
 960  
                         {
 961  0
                             ptc.incrementCount();
 962  
                         }
 963  
                     }
 964  
                     else
 965  
                     {
 966  
                         // Non facelets component, remove it!.
 967  0
                         itr.remove();
 968  
                     }
 969  
                 }
 970  0
             }
 971  
         }
 972  0
     }
 973  
     
 974  
     public void processDeferredNavigation(FacesContext facesContext)
 975  
     {
 976  0
             Object[] command = (Object[]) facesContext.getAttributes().get(
 977  
                 ViewPoolProcessor.INVOKE_DEFERRED_NAVIGATION);
 978  0
         if (command != null)
 979  
         {
 980  
             try
 981  
             {
 982  0
                 facesContext.getAttributes().put(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION, Boolean.TRUE);
 983  0
                 NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();
 984  0
                 if (command.length == 3)
 985  
                 {
 986  0
                     navigationHandler.handleNavigation(facesContext, (String) command[0], (String) command[1], 
 987  
                         (String) command[2]);
 988  
                 }
 989  
                 else
 990  
                 {
 991  0
                     navigationHandler.handleNavigation(facesContext, (String) command[0], (String) command[1]);
 992  
                 }
 993  
                 //Render Response if needed
 994  0
                 facesContext.renderResponse();
 995  0
                 facesContext.getAttributes().remove(ViewPoolProcessor.INVOKE_DEFERRED_NAVIGATION);
 996  
             }
 997  
             finally
 998  
             {
 999  0
                 facesContext.getAttributes().remove(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION);
 1000  0
             }
 1001  
         }
 1002  0
     }
 1003  
     
 1004  0
     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
 1005  
         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
 1006  
 
 1007  
     public void disposeView(FacesContext facesContext, UIViewRoot root)
 1008  
     {
 1009  0
         if (root == null)
 1010  
         {
 1011  0
             return;
 1012  
         }
 1013  
 
 1014  0
         String viewId = root.getViewId();
 1015  0
         if (viewId == null)
 1016  
         {
 1017  0
             return;
 1018  
         }
 1019  0
         Application app = facesContext.getApplication();
 1020  0
         if (app == null)
 1021  
         {
 1022  0
             return;
 1023  
         }
 1024  0
         ViewHandler viewHandler = app.getViewHandler();
 1025  0
         if (viewHandler == null)
 1026  
         {
 1027  0
             return;
 1028  
         }
 1029  
 
 1030  0
         if (Boolean.TRUE.equals(facesContext.getAttributes().get(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION)))
 1031  
         {
 1032  0
             ViewDeclarationLanguage vdl = facesContext.getApplication().
 1033  
                     getViewHandler().getViewDeclarationLanguage(
 1034  
                         facesContext, root.getViewId());
 1035  
 
 1036  0
             if (vdl != null && ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(vdl.getId()))
 1037  
             {
 1038  0
                 StateManagementStrategy sms = vdl.getStateManagementStrategy(facesContext, root.getId());
 1039  0
                 if (sms != null)
 1040  
                 {
 1041  
                     // Force indirectly to store the map in the pool
 1042  0
                     facesContext.getAttributes().put(ViewPoolProcessor.FORCE_HARD_RESET, Boolean.TRUE);
 1043  
 
 1044  
                     try
 1045  
                     {
 1046  0
                         Object state = sms.saveView(facesContext);
 1047  
                     }
 1048  
                     finally
 1049  
                     {
 1050  0
                         facesContext.getAttributes().remove(ViewPoolProcessor.FORCE_HARD_RESET);
 1051  0
                     }
 1052  
 
 1053  
                     // Clear the calculated value from the application map
 1054  0
                     facesContext.getAttributes().remove(SERIALIZED_VIEW_REQUEST_ATTR);
 1055  
                 }
 1056  
             }
 1057  
         }
 1058  0
     }
 1059  
     
 1060  
     private static class ClearPartialTreeContext 
 1061  
     {
 1062  
         private int count;
 1063  
         
 1064  
         public ClearPartialTreeContext()
 1065  0
         {
 1066  0
             count = 0;
 1067  0
         }
 1068  
 
 1069  
         /**
 1070  
          * @return the count
 1071  
          */
 1072  
         public int getCount()
 1073  
         {
 1074  0
             return count;
 1075  
         }
 1076  
 
 1077  
         public int incrementCount()
 1078  
         {
 1079  0
             return count++;
 1080  
         }
 1081  
         /**
 1082  
          * @param count the count to set
 1083  
          */
 1084  
         public void setCount(int count)
 1085  
         {
 1086  0
             this.count = count;
 1087  0
         }
 1088  
     }
 1089  
 }