View Javadoc

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     {
146         viewPoolFactory = new ViewPoolFactoryImpl(context);
147         restoreViewSupport = new DefaultRestoreViewSupport(context);
148     }
149     
150     public static ViewPoolProcessor getInstance(FacesContext context)
151     {
152         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         if (context.isProjectStage(ProjectStage.Production))
165         {
166             boolean initialize = true;
167             String elMode = WebConfigParamUtils.getStringInitParameter(
168                         context.getExternalContext(),
169                         FaceletCompositionContextImpl.INIT_PARAM_CACHE_EL_EXPRESSIONS, 
170                             ELExpressionCacheMode.noCache.name());
171             if (!elMode.equals(ELExpressionCacheMode.alwaysRecompile.name()))
172             {
173                 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                 initialize = false;
179             }
180             
181             long refreshPeriod = WebConfigParamUtils.getLongInitParameter(context.getExternalContext(),
182                     FaceletViewDeclarationLanguage.PARAMS_REFRESH_PERIOD,
183                     FaceletViewDeclarationLanguage.DEFAULT_REFRESH_PERIOD_PRODUCTION);
184 
185             if (refreshPeriod != -1)
186             {
187                 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                 initialize = false;
193             }
194             
195             if (MyfacesConfig.getCurrentInstance(context.getExternalContext()).isStrictJsf2FaceletsCompatibility())
196             {
197                 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                 initialize = false;
203             }
204             
205             if (initialize)
206             {
207                 ViewPoolProcessor processor = new ViewPoolProcessor(context);
208                 context.getExternalContext().
209                     getApplicationMap().put(INSTANCE, processor);
210             }
211         }
212     }
213 
214     public ViewPool getViewPool(FacesContext context, UIViewRoot root)
215     {
216         if (root.isTransient())
217         {
218             // Stateless views cannot be pooled, because we are reusing
219             // state saving algorithm for that.
220             return null;
221         }
222         Boolean enableViewPool = (Boolean) root.getAttributes().get(ViewPoolProcessor.ENABLE_VIEW_POOL);
223         if (enableViewPool != null && !Boolean.TRUE.equals(enableViewPool))
224         {
225             // view pool not enabled for this view.
226             return null;
227         }
228         else
229         {
230             return viewPoolFactory.getViewPool(context, root);
231         }
232     }
233     
234     public boolean isViewPoolEnabledForThisView(FacesContext context, UIViewRoot root)
235     {
236         if (root.isTransient())
237         {
238             // Stateless views cannot be pooled, because we are reusing
239             // state saving algorithm for that.
240             return false;
241         }
242         Boolean enableViewPool = (Boolean) root.getAttributes().get(ViewPoolProcessor.ENABLE_VIEW_POOL);
243         if (enableViewPool != null)
244         {
245             if (Boolean.TRUE.equals(enableViewPool))
246             {
247                 return true;
248             }
249             else
250             {
251                 return false;
252             }
253         }
254         else
255         {
256             ViewPool viewPool = getViewPool(context, root);
257             if (viewPool != null)
258             {
259                 return true;
260             }
261         }
262         return false;
263     }
264 
265     public boolean isViewPoolStrategyAllowedForThisView(FacesContext context, UIViewRoot root)
266     {
267         // Check if the viewId is not null.
268         if (root.getViewId() == null)
269         {
270             return false;
271         }
272         if (root.isTransient())
273         {
274             // Stateless views cannot be pooled, because we are reusing
275             // state saving algorithm for that.
276             return false;
277         }
278         
279         // Check if the view is enabled or not for use pooling
280         if (!isViewPoolEnabledForThisView(context, root))
281         {
282             return false;
283         }
284 
285         // Check if the vdl is using PSS on this view (has a vdl and that vdl is facelets)
286         ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
287             getViewDeclarationLanguage(context, root.getViewId());
288         if (vdl == null)
289         {
290             return false;
291         }
292         else if (!ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(vdl.getId()))
293         {
294             return false;
295         }
296         
297         if (vdl.getStateManagementStrategy(context, root.getViewId()) == null)
298         {
299             return false;
300         }
301         return true;
302     }
303 
304     public void setViewPoolDisabledOnThisView(FacesContext context, UIViewRoot root, boolean value)
305     {
306         root.getAttributes().put(ViewPoolProcessor.ENABLE_VIEW_POOL, !value);
307     }
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         UIViewRoot oldView = entry.getViewRoot();
322         // retrieveViewRootInitialState(context, oldView)
323         Object viewState = metadata.getViewRootState();
324         Object newViewState;
325         UIComponent metadataFacet = newView.getFacet(UIViewRoot.METADATA_FACET_NAME);
326         if (viewState == null)
327         {
328             // (Optional, it should be always metadata)
329             oldView.clearInitialState();
330             viewState = oldView.saveState(context);
331         }
332         Map<String, Object> viewScopeMap = newView.getViewMap(false);
333         if (viewScopeMap != null && !viewScopeMap.isEmpty())
334         {
335             newViewState = newView.saveState(context);
336         }
337         else
338         {
339             newViewState = null;
340         }
341         
342         boolean oldProcessingEvents = context.isProcessingEvents();
343         context.setProcessingEvents(false);
344         try
345         {
346             if (oldView.getFacetCount() > 0)
347             {
348                 List<String> facetKeys = new ArrayList<String>();
349                 facetKeys.addAll(oldView.getFacets().keySet());
350                 for (String facetKey : facetKeys)
351                 {
352                     //context.setProcessingEvents(false);
353                     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                         newView.getFacets().put(facetKey, metadataFacet);
362                     }
363                     else
364                     {
365                         UIComponent facet = oldView.getFacets().remove(facetKey);
366                         //context.setProcessingEvents(true);
367                         newView.getFacets().put(facetKey, facet);
368                     }
369                 }
370             }
371             if (oldView.getChildCount() > 0)
372             {
373                 for (Iterator<UIComponent> it = oldView.getChildren().iterator(); it.hasNext();)
374                 {
375                     //context.setProcessingEvents(false);
376                     UIComponent c = it.next();
377                     it.remove();
378                     //context.setProcessingEvents(true);
379                     newView.getChildren().add(c);
380                 }
381             }
382             
383             // Restore the newView as saved just before markInitialState() call
384             newView.restoreState(context, viewState);
385             newView.markInitialState();
386             
387             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                 restoreViewSupport.processComponentBinding(context, newView);
392                 
393                 // Restore view scope map if necessary
394                 if (viewScopeMap != null && !viewScopeMap.isEmpty())
395                 {
396                     Map<String, Object> newViewScopeMap = newView.getViewMap(false);
397                     if (newViewScopeMap == null)
398                     {
399                         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                         for (Map.Entry<String, Object> entry2 : viewScopeMap.entrySet())
408                         {
409                             newViewScopeMap.put(entry2.getKey(), entry2.getValue());
410                         }
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             RequestViewContext rcv = RequestViewContext.getCurrentInstance(context, newView, false);
419             if (rcv != null)
420             {
421                 rcv.setRequestViewMetadata(metadata.getRequestViewMetadata().cloneInstance());
422             }
423             else
424             {
425                 RequestViewContext.setCurrentInstance(context, newView,
426                         RequestViewContext.newInstance(metadata.getRequestViewMetadata().cloneInstance()));
427             }
428         }
429         finally
430         {
431             context.setProcessingEvents(oldProcessingEvents);
432         }
433     }
434     
435     public void storeViewStructureMetadata(FacesContext context, UIViewRoot root)
436     {
437         ViewPool viewPool = getViewPool(context, root);
438         if (viewPool != null)
439         {
440             FaceletState faceletState = (FaceletState) root.getAttributes().get(
441                     ComponentSupport.FACELET_STATE_INSTANCE);
442             boolean isDynamic = faceletState != null ? faceletState.isDynamic() : false;
443             if (!isDynamic)
444             {
445                 viewPool.storeStaticViewStructureMetadata(context, root, faceletState);            
446             }
447             else
448             {
449                 viewPool.storeDynamicViewStructureMetadata(context, root, faceletState);
450             }
451         }
452     }
453     
454     public ViewStructureMetadata retrieveViewStructureMetadata(FacesContext context,
455             UIViewRoot root)
456     {
457         ViewPool viewPool = getViewPool(context, root);
458         if (viewPool != null)
459         {
460             FaceletState faceletState = (FaceletState) root.getAttributes().get(
461                     ComponentSupport.FACELET_STATE_INSTANCE);
462             boolean isDynamic = faceletState != null ? faceletState.isDynamic() : false;
463             if (!isDynamic)
464             {
465                 return viewPool.retrieveStaticViewStructureMetadata(context, root);
466             }
467             else
468             {
469                 return viewPool.retrieveDynamicViewStructureMetadata(context, root, faceletState);
470             }
471         }
472         return null;
473     }
474     
475     public void pushResetableView(FacesContext context, UIViewRoot view, FaceletState faceletViewState)
476     {
477         ViewPool viewPool = getViewPool(context, view);
478         if (viewPool != null)
479         {
480             boolean isDynamic = faceletViewState != null ? faceletViewState.isDynamic() : false;
481             if (!isDynamic)
482             {
483                 clearTransientAndNonFaceletComponentsForStaticView(context, view);
484                 viewPool.pushStaticStructureView(context, view);
485             }
486             else
487             {
488                 ViewStructureMetadata viewStructureMetadata = viewPool.retrieveDynamicViewStructureMetadata(
489                     context, view, faceletViewState);
490                 if (viewStructureMetadata != null)
491                 {
492                     clearTransientAndNonFaceletComponentsForDynamicView(context, view, viewStructureMetadata);
493                     viewPool.pushDynamicStructureView(context, view, faceletViewState);
494                 }
495             }
496         }
497     }
498     
499     public void pushPartialView(FacesContext context, UIViewRoot view, FaceletState faceletViewState, int count)
500     {
501         ViewPool viewPool = getViewPool(context, view);
502         
503         if (viewPool != null && viewPool.isWorthToRecycleThisView(context, view))
504         {
505             ViewStructureMetadata viewStructureMetadata = null;
506             if (faceletViewState == null)
507             {
508                 viewStructureMetadata = viewPool.retrieveStaticViewStructureMetadata(context, view);
509             }
510             else
511             {
512                 viewStructureMetadata = viewPool.retrieveDynamicViewStructureMetadata(
513                     context, view, faceletViewState);
514             }
515             if (viewStructureMetadata != null)
516             {
517                 ClearPartialTreeContext ptc = new ClearPartialTreeContext();
518                 // add partial structure view to the map.
519                 clearTransientAndRemoveNonResetableComponents(context, ptc, view, viewStructureMetadata);
520                 int reusableCount = ptc.getCount();
521                 float factor = ((float)reusableCount) / ((float)count);
522                 if (factor > 0.3f)
523                 {
524                     viewPool.pushPartialStructureView(context, view);
525                 }
526             }
527         }        
528     }
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         clearTransientAndNonFaceletComponents(context, root);
536     }
537     
538     public void clearTransientAndNonFaceletComponentsForDynamicView(final FacesContext context, 
539             final UIViewRoot root, final ViewStructureMetadata viewStructureMetadata)
540     {
541         //Scan children
542         int childCount = root.getChildCount();
543         if (childCount > 0)
544         {
545             for (int i = 0; i < childCount; i++)
546             {
547                 UIComponent child = root.getChildren().get(i);
548                 if (child != null && child.isTransient() &&
549                     child.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
550                 {
551                     root.getChildren().remove(i);
552                     i--;
553                     childCount--;
554                 }
555                 else
556                 {
557                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
558                     {
559                         clearTransientAndNonFaceletComponents(context, child);
560                     }
561                 }
562             }
563         }
564 
565         clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(
566             context, root, viewStructureMetadata);
567     }
568     
569     private void clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(final FacesContext context, 
570             final UIViewRoot root, ViewStructureMetadata viewStructureMetadata)
571     {
572         
573         //Scan facets
574         if (root.getFacetCount() > 0)
575         {
576             Map<String, UIComponent> facets = root.getFacets();
577             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
578             {
579                 UIComponent fc = itr.next();
580                 if (fc != null && !(fc instanceof ComponentResourceContainer))
581                 {
582                     if ( fc.isTransient() &&
583                         fc.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
584                     {
585                         itr.remove();
586                     }
587                     else
588                     {
589                         if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
590                         {
591                             clearTransientAndNonFaceletComponents(context, fc);
592                         }
593                     }
594                 }
595                 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                     if (fc.getId() != null && fc.getId().startsWith("javax_faces_location_"))
602                     {
603                         String target = fc.getId().substring("javax_faces_location_".length());
604                         Map<String, List<ResourceDependency>> addedResources = 
605                             viewStructureMetadata.getRequestViewMetadata().
606                                 getResourceDependencyAnnotations(context);
607                         List<ResourceDependency> resourceDependencyList = (addedResources != null) ?
608                             addedResources.get(target) : null;
609 
610                         clearComponentResourceContainer(context, fc, resourceDependencyList);
611                     }
612                 }
613             }
614         }
615     }
616     
617     private void clearComponentResourceContainer(final FacesContext context, UIComponent component,
618         List<ResourceDependency> resourceDependencyList)
619     {
620         //Scan children
621         int childCount = component.getChildCount();
622         if (childCount > 0)
623         {
624             for (int i = 0; i < childCount; i++)
625             {
626                 UIComponent child = component.getChildren().get(i);
627                 String id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
628                 if (child != null && child.isTransient() &&
629                     id == null)
630                 {
631                     //Remove both transient not facelets bound components
632                     component.getChildren().remove(i);
633                     i--;
634                     childCount--;
635                 }
636                 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                     Object[] rdk = (Object[]) child.getAttributes().get(
655                                 RequestViewMetadata.RESOURCE_DEPENDENCY_KEY);
656                     if (rdk != null)
657                     {
658                         boolean found = false;
659                         String library = (String) rdk[0];
660                         String name = (String) rdk[1];
661                         if (resourceDependencyList != null)
662                         {
663                             for (ResourceDependency resource : resourceDependencyList)
664                             {
665                                 if (library == null && resource.library() == null)
666                                 {
667                                     if (name != null && name.equals(resource.name()))
668                                     {
669                                         found = true;
670                                         break;
671                                     }
672                                 }
673                                 else
674                                 {
675                                     if (library != null && library.equals(resource.library()) &&
676                                         name != null && name.equals(resource.name()) )
677                                     {
678                                         found = true;
679                                         break;
680                                     }
681                                 }
682                             }
683                         }
684                         if (!found)
685                         {
686                             //Remove it, because for this dynamic state it it does not exists.
687                             component.getChildren().remove(i);
688                             i--;
689                             childCount--;
690                         }
691                         // If found just leave it.
692                     }
693                     else
694                     {
695                         if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
696                         {
697                             clearTransientAndNonFaceletComponents(context, child);
698                         }
699                     }
700                 }
701             }
702         }
703     }
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         int childCount = component.getChildCount();
716         if (childCount > 0)
717         {
718             for (int i = 0; i < childCount; i++)
719             {
720                 UIComponent child = component.getChildren().get(i);
721                 if (child != null && child.isTransient() &&
722                     child.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
723                 {
724                     component.getChildren().remove(i);
725                     i--;
726                     childCount--;
727                 }
728                 else
729                 {
730                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
731                     {
732                         clearTransientAndNonFaceletComponents(context, child);
733                     }
734                 }
735             }
736         }
737 
738         //Scan facets
739         if (component.getFacetCount() > 0)
740         {
741             Map<String, UIComponent> facets = component.getFacets();
742             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
743             {
744                 UIComponent fc = itr.next();
745                 if (fc != null && fc.isTransient() &&
746                     fc.getAttributes().get(ComponentSupport.MARK_CREATED) == null)
747                 {
748                     itr.remove();
749                 }
750                 else
751                 {
752                     if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
753                     {
754                         clearTransientAndNonFaceletComponents(context, fc);
755                     }
756                 }
757             }
758         }
759     }
760 
761     private void clearTransientAndRemoveNonResetableComponents(final FacesContext context, 
762         final ClearPartialTreeContext ptc, final UIViewRoot root, 
763         ViewStructureMetadata viewStructureMetadata)
764     {
765         //Scan children
766         int childCount = root.getChildCount();
767 
768         try
769         {
770             root.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
771                         ViewPoolProcessor.RESET_MODE_HARD);
772             if (childCount > 0)
773             {
774                 for (int i = 0; i < childCount; i++)
775                 {
776                     UIComponent child = root.getChildren().get(i);
777                     boolean containsFaceletId = child.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
778                     if (child != null && child.isTransient() && !containsFaceletId)
779                     {
780                         //Transient and not bound to facelets tag, remove it!.
781                         root.getChildren().remove(i);
782                         i--;
783                         childCount--;
784                     }
785                     else
786                     {
787                         if (child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
788                         {
789                             //Dynamically added or moved, remove it!
790                             root.getChildren().remove(i);
791                             i--;
792                             childCount--;
793 
794                         }
795                         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                             if (!child.isTransient())
802                             {
803                                 // Remember that hard reset is already enabled.
804                                 Object state = child.saveState(context);
805                                 if (state == null)
806                                 {
807                                     if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
808                                     {
809                                         clearTransientAndRemoveNonResetableComponents(context, ptc, child);
810                                     }
811                                     ptc.incrementCount();
812                                 }
813                                 else
814                                 {
815                                     root.getChildren().remove(i);
816                                     i--;
817                                     childCount--;
818                                 }
819                             }
820                             else
821                             {
822                                 ptc.incrementCount();
823                             }
824                         }
825                         else
826                         {
827                             // Non facelets component, remove it!.
828                             root.getChildren().remove(i);
829                             i--;
830                             childCount--;
831                         }
832                     }
833                 }
834             }
835 
836             clearTransientAndNonFaceletComponentsForDynamicViewUIViewRootFacets(
837                 context, root, viewStructureMetadata);
838         }
839         finally
840         {
841             root.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
842                         ViewPoolProcessor.RESET_MODE_OFF);
843         }
844     }
845     
846     private void clearTransientAndRemoveNonResetableComponents(final FacesContext context,
847         final ClearPartialTreeContext ptc, final UIComponent component)
848     {
849         //Scan children
850         int childCount = component.getChildCount();
851         if (childCount > 0)
852         {
853             for (int i = 0; i < childCount; i++)
854             {
855                 UIComponent child = component.getChildren().get(i);
856                 boolean containsFaceletId = child.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
857                 if (child != null && child.isTransient() && !containsFaceletId)
858                 {
859                     //Transient and not bound to facelets tag, remove it!.
860                     component.getChildren().remove(i);
861                     i--;
862                     childCount--;
863                 }
864                 else
865                 {
866                     if (child.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
867                     {
868                         //Dynamically added or moved, remove it!
869                         component.getChildren().remove(i);
870                         i--;
871                         childCount--;
872 
873                     }
874                     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                         if (!child.isTransient())
881                         {
882                             // Remember that hard reset is already enabled.
883                             Object state = child.saveState(context);
884                             if (state == null)
885                             {
886                                 if (child.getChildCount() > 0 || !child.getFacets().isEmpty())
887                                 {
888                                     clearTransientAndRemoveNonResetableComponents(context, ptc, child);
889                                 }
890                                 ptc.incrementCount();
891                             }
892                             else
893                             {
894                                 component.getChildren().remove(i);
895                                 i--;
896                                 childCount--;
897                             }
898                         }
899                         else
900                         {
901                             ptc.incrementCount();
902                         }
903                     }
904                     else
905                     {
906                         // Non facelets component, remove it!.
907                         component.getChildren().remove(i);
908                         i--;
909                         childCount--;
910                     }
911                 }
912             }
913         }
914 
915         //Scan facets
916         if (component.getFacetCount() > 0)
917         {
918             Map<String, UIComponent> facets = component.getFacets();
919             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
920             {
921                 UIComponent fc = itr.next();
922                 boolean containsFaceletId = fc.getAttributes().containsKey(ComponentSupport.MARK_CREATED);
923                 if (fc != null && fc.isTransient() && !containsFaceletId)
924                 {
925                     //Transient and not bound to facelets tag, remove it!.
926                     itr.remove();
927                 }
928                 else
929                 {
930                     if (fc.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
931                     {
932                         //Dynamically added or moved, remove it!
933                         itr.remove();
934 
935                     }
936                     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                         if (!fc.isTransient())
943                         {
944                             // Remember that hard reset is already enabled.
945                             Object state = fc.saveState(context);
946                             if (state == null)
947                             {
948                                 if (fc.getChildCount() > 0 || !fc.getFacets().isEmpty())
949                                 {
950                                     clearTransientAndRemoveNonResetableComponents(context, ptc, fc);
951                                 }
952                                 ptc.incrementCount();
953                             }
954                             else
955                             {
956                                 itr.remove();
957                             }
958                         }
959                         else
960                         {
961                             ptc.incrementCount();
962                         }
963                     }
964                     else
965                     {
966                         // Non facelets component, remove it!.
967                         itr.remove();
968                     }
969                 }
970             }
971         }
972     }
973     
974     public void processDeferredNavigation(FacesContext facesContext)
975     {
976             Object[] command = (Object[]) facesContext.getAttributes().get(
977                 ViewPoolProcessor.INVOKE_DEFERRED_NAVIGATION);
978         if (command != null)
979         {
980             try
981             {
982                 facesContext.getAttributes().put(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION, Boolean.TRUE);
983                 NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();
984                 if (command.length == 3)
985                 {
986                     navigationHandler.handleNavigation(facesContext, (String) command[0], (String) command[1], 
987                         (String) command[2]);
988                 }
989                 else
990                 {
991                     navigationHandler.handleNavigation(facesContext, (String) command[0], (String) command[1]);
992                 }
993                 //Render Response if needed
994                 facesContext.renderResponse();
995                 facesContext.getAttributes().remove(ViewPoolProcessor.INVOKE_DEFERRED_NAVIGATION);
996             }
997             finally
998             {
999                 facesContext.getAttributes().remove(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION);
1000             }
1001         }
1002     }
1003     
1004     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         if (root == null)
1010         {
1011             return;
1012         }
1013 
1014         String viewId = root.getViewId();
1015         if (viewId == null)
1016         {
1017             return;
1018         }
1019         Application app = facesContext.getApplication();
1020         if (app == null)
1021         {
1022             return;
1023         }
1024         ViewHandler viewHandler = app.getViewHandler();
1025         if (viewHandler == null)
1026         {
1027             return;
1028         }
1029 
1030         if (Boolean.TRUE.equals(facesContext.getAttributes().get(ViewPoolProcessor.DISPOSE_VIEW_NAVIGATION)))
1031         {
1032             ViewDeclarationLanguage vdl = facesContext.getApplication().
1033                     getViewHandler().getViewDeclarationLanguage(
1034                         facesContext, root.getViewId());
1035 
1036             if (vdl != null && ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(vdl.getId()))
1037             {
1038                 StateManagementStrategy sms = vdl.getStateManagementStrategy(facesContext, root.getId());
1039                 if (sms != null)
1040                 {
1041                     // Force indirectly to store the map in the pool
1042                     facesContext.getAttributes().put(ViewPoolProcessor.FORCE_HARD_RESET, Boolean.TRUE);
1043 
1044                     try
1045                     {
1046                         Object state = sms.saveView(facesContext);
1047                     }
1048                     finally
1049                     {
1050                         facesContext.getAttributes().remove(ViewPoolProcessor.FORCE_HARD_RESET);
1051                     }
1052 
1053                     // Clear the calculated value from the application map
1054                     facesContext.getAttributes().remove(SERIALIZED_VIEW_REQUEST_ATTR);
1055                 }
1056             }
1057         }
1058     }
1059     
1060     private static class ClearPartialTreeContext 
1061     {
1062         private int count;
1063         
1064         public ClearPartialTreeContext()
1065         {
1066             count = 0;
1067         }
1068 
1069         /**
1070          * @return the count
1071          */
1072         public int getCount()
1073         {
1074             return count;
1075         }
1076 
1077         public int incrementCount()
1078         {
1079             return count++;
1080         }
1081         /**
1082          * @param count the count to set
1083          */
1084         public void setCount(int count)
1085         {
1086             this.count = count;
1087         }
1088     }
1089 }