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.application.pss;
20  
21  import org.apache.myfaces.application.MyfacesStateManager;
22  import org.apache.myfaces.application.TreeStructureManager;
23  import org.apache.myfaces.renderkit.MyfacesResponseStateManager;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.commons.collections.map.ReferenceMap;
27  import org.apache.commons.lang.builder.EqualsBuilder;
28  import org.apache.commons.lang.builder.HashCodeBuilder;
29  import org.apache.commons.beanutils.BeanUtils;
30  
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ExternalContext;
33  import javax.faces.application.StateManager;
34  import javax.faces.application.Application;
35  import javax.faces.application.ViewHandler;
36  import javax.faces.component.UIViewRoot;
37  import javax.faces.component.UIComponent;
38  import javax.faces.component.NamingContainer;
39  import javax.faces.component.UIOutput;
40  import javax.faces.render.RenderKit;
41  import javax.faces.render.ResponseStateManager;
42  import javax.faces.render.RenderKitFactory;
43  import javax.faces.FactoryFinder;
44  import javax.servlet.http.HttpServletResponse;
45  import java.io.*;
46  import java.util.*;
47  import java.util.zip.GZIPOutputStream;
48  import java.util.zip.GZIPInputStream;
49  import java.lang.reflect.InvocationTargetException;
50  
51  import org.apache.myfaces.shared_impl.renderkit.ViewSequenceUtils;
52  import org.apache.myfaces.shared_impl.util.MyFacesObjectInputStream;
53  import org.apache.myfaces.context.servlet.ServletExternalContextImpl;
54  import org.apache.myfaces.context.portlet.PortletExternalContextImpl;
55  
56  /**
57   * @author Martin Haimberger
58   */
59  public class PssJspStateManagerImpl extends MyfacesStateManager
60  {
61  
62      private static final Log log = LogFactory.getLog(PssJspStateManagerImpl.class);
63      private static final String SERIALIZED_VIEW_SESSION_ATTR
64              = PssJspStateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
65      private static final String SERIALIZED_VIEW_REQUEST_ATTR
66              = PssJspStateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
67      private static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR
68      = PssJspStateManagerImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";
69  
70      private static final String PARTIAL_STATE_MANAGER_TREES = PssJspStateManagerImpl.class.getName() + ".PARTIAL_STATE_MANAGER_TREES";
71  
72      /**
73       * Only applicable if state saving method is "server" (= default).
74       * Defines the amount (default = 20) of the latest views are stored in session.
75       */
76      private static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";
77  
78      /**
79       * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
80       */
81      private static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;
82  
83      /**
84       * Only applicable if state saving method is "server" (= default).
85       * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
86       * If <code>false</code> the state will not be serialized to a byte stream.
87       */
88      private static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";
89  
90      /**
91       * Only applicable if state saving method is "server" (= default) and if <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
92       * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
93       * If <code>false</code> the state will not be compressed.
94       */
95      private static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";
96  
97      /**
98       * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
99       */
100     private static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;
101 
102     /**
103      * Default value for <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
104      */
105     private static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;
106 
107     private static final int UNCOMPRESSED_FLAG = 0;
108     private static final int COMPRESSED_FLAG = 1;
109 
110     private RenderKitFactory _renderKitFactory = null;
111 
112     private static final String PARTIAL_STATE_SAVING_METHOD_PARAM_NAME = "javax.faces.PARTIAL_STATE_SAVING_METHOD";
113     private static final String PARTIAL_STATE_SAVING_METHOD_ON = "true";
114     private static final String PARTIAL_STATE_SAVING_METHOD_OFF = "false";
115 
116     private static final String PARTIAL_STATE_SAVING_DISPATCH_PARAM_NAME = "javax.faces.PARTIAL_STATE_SAVING_DISPATCH_EVERY_TIME";
117 
118 
119     private Boolean _partialStateSaving = null;
120     private Boolean _partialStateSavingDispatch = null;
121 
122     public PssJspStateManagerImpl()
123     {
124         if (log.isTraceEnabled()) log.trace("New JspStateManagerImpl instance created");
125     }
126 
127     private boolean isPartialStateSavingOn(FacesContext context)
128     {
129         if(context == null) throw new NullPointerException("context");
130         if (_partialStateSaving != null) return _partialStateSaving.booleanValue();
131         String stateSavingMethod = context.getExternalContext().getInitParameter(PARTIAL_STATE_SAVING_METHOD_PARAM_NAME);
132         if (stateSavingMethod == null)
133         {
134             _partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
135             context.getExternalContext().log("No context init parameter '"+PARTIAL_STATE_SAVING_METHOD_PARAM_NAME+"' found; no partial state saving method defined, assuming default partial state saving method off.");
136         }
137         else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_ON))
138         {
139             _partialStateSaving = Boolean.TRUE;
140         }
141         else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_OFF))
142         {
143             _partialStateSaving = Boolean.FALSE;
144         }
145         else
146         {
147             _partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
148             context.getExternalContext().log("Illegal partial state saving method '" + stateSavingMethod + "', default partial state saving will be used (partial state saving off).");
149         }
150         return _partialStateSaving.booleanValue();
151     }
152 
153     private boolean isPartialStateSavingDispatch(FacesContext context)
154     {
155         if(context == null) throw new NullPointerException("context");
156         if (_partialStateSavingDispatch != null) return _partialStateSavingDispatch.booleanValue();
157         String stateSavingDispatch = context.getExternalContext().getInitParameter(PARTIAL_STATE_SAVING_DISPATCH_PARAM_NAME);
158         if (stateSavingDispatch == null)
159         {
160             _partialStateSavingDispatch = Boolean.TRUE; //Specs 10.1.3: default server saving
161             context.getExternalContext().log("No context init parameter '"+PARTIAL_STATE_SAVING_DISPATCH_PARAM_NAME+"' found; no partial state saving dispatch usage behavior, assuming default partial state saving dispatch mode on.");
162         }
163         else if (stateSavingDispatch.equals(PARTIAL_STATE_SAVING_METHOD_ON))
164         {
165             _partialStateSavingDispatch = Boolean.TRUE;
166         }
167         else if (stateSavingDispatch.equals(PARTIAL_STATE_SAVING_METHOD_OFF))
168         {
169             _partialStateSavingDispatch = Boolean.FALSE;
170         }
171         else
172         {
173             _partialStateSavingDispatch = Boolean.TRUE; //Specs 10.1.3: default server saving
174             context.getExternalContext().log("Illegal partial state saving behavior '" + stateSavingDispatch + "', default partial state saving dispatch behavior will be used (dispatch behavior on).");
175         }
176         return _partialStateSaving.booleanValue();
177     }
178 
179 
180     protected Object getComponentStateToSave(FacesContext facesContext)
181     {
182         if (log.isTraceEnabled()) log.trace("Entering getComponentStateToSave");
183 
184         UIViewRoot viewRoot = facesContext.getViewRoot();
185         if (viewRoot.isTransient())
186         {
187             return null;
188         }
189 
190         Object serializedComponentStates = viewRoot.processSaveState(facesContext);
191         //Locale is a state attribute of UIViewRoot and need not be saved explicitly
192         if (log.isTraceEnabled()) log.trace("Exiting getComponentStateToSave");
193         return serializedComponentStates;
194     }
195 
196     /**
197      * Return an object which contains info about the UIComponent type
198      * of each node in the view tree. This allows an identical UIComponent
199      * tree to be recreated later, though all the components will have
200      * just default values for their members.
201      */
202     protected Object getTreeStructureToSave(FacesContext facesContext)
203     {
204         if (log.isTraceEnabled()) log.trace("Entering getTreeStructureToSave");
205         UIViewRoot viewRoot = facesContext.getViewRoot();
206         if (viewRoot.isTransient())
207         {
208             return null;
209         }
210         TreeStructureManager tsm = new TreeStructureManager();
211         Object retVal = tsm.buildTreeStructureToSave(viewRoot);
212         if (log.isTraceEnabled()) log.trace("Exiting getTreeStructureToSave");
213         return retVal;
214     }
215 
216     /**
217      * Return an object which contains info about the UIComponent type
218      * of each node in the view tree. This allows an identical UIComponent
219      * tree to be recreated later, though all the components will have
220      * just default values for their members.
221      */
222     protected Object getTreeToSave(FacesContext facesContext)
223     {
224         if (log.isTraceEnabled()) log.trace("Entering getTreeStructureToSave");
225         UIViewRoot viewRoot = facesContext.getViewRoot();
226         if (viewRoot.isTransient())
227         {
228             return null;
229         }
230         PartialTreeStructureManager tsm = new PartialTreeStructureManager(facesContext);
231         Object retVal = tsm.buildTreeStructureToSave(viewRoot,facesContext);
232         if (log.isTraceEnabled()) log.trace("Exiting getTreeStructureToSave");
233         return retVal;
234     }
235 
236 
237     /**
238         * Return an object which contains info about the UIComponent type
239         * of each node in the view tree. This allows an identical UIComponent
240         * tree to be recreated later, though all the components will have
241         * just default values for their members.
242         */
243        protected Object restoreViewRoot(FacesContext facesContext, Object storedView)
244        {
245            if (log.isTraceEnabled()) log.trace("Entering getTreeStructureToSave");
246            PartialTreeStructureManager tsm = new PartialTreeStructureManager(facesContext);
247            Object retVal = tsm.restoreTreeStructure(facesContext,storedView);
248            if (log.isTraceEnabled()) log.trace("Exiting getTreeStructureToSave");
249            return retVal;
250        }
251 
252 
253     private String getSequenceString(FacesContext facesContext, String renderKitId, String viewId) {
254         RenderKit rk = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
255         ResponseStateManager responseStateManager = rk.getResponseStateManager();
256         String sequenceStr = (String) responseStateManager.getTreeStructureToRestore(facesContext, viewId);
257         return sequenceStr;
258     }
259 
260 
261     /**
262      * Given a tree of UIComponent objects created the default constructor
263      * for each node, retrieve saved state info (from either the client or
264      * the server) and walk the tree restoring the members of each node
265      * from the saved state information.
266      */
267     protected UIViewRoot restoreComponentState(FacesContext facesContext,
268                                                String viewID,
269                                                String renderKitId)
270     {
271         if (log.isTraceEnabled()) log.trace("Entering restoreComponentState");
272 
273         //===========================================
274         // first, locate the saved state information
275         //===========================================
276 
277         UIViewRoot uiViewRoot = null;
278 
279         Object serializedComponentStates = null;
280         if (isSavingStateInClient(facesContext))
281         {
282             RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
283             ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
284             // the State with the View ID
285             serializedComponentStates = responseStateManager.getComponentStateToRestore(facesContext);
286         }
287         else
288         {
289             String sequenceStr = getSequenceString(facesContext, renderKitId, viewID);
290             SerializedView serializedView = getSerializedViewFromServletSession(facesContext,
291                                                                                 viewID,
292                                                                                 sequenceStr);
293 
294             if (serializedView != null)
295             {
296                 serializedComponentStates = serializedView.getStructure();
297             }
298 
299         }
300 
301         // now ask the view root component to restore its state
302 
303         Object template = LoadTreeFromManager(facesContext,viewID);
304         if (serializedComponentStates != null )
305         {
306             // we have got a view
307             String serializedViewID = (String)((Object[])serializedComponentStates)[0];
308             if (!serializedViewID.equals(viewID)) {
309                 // not the right view.
310                 serializedComponentStates = null;
311             }
312         }
313         if (serializedComponentStates != null) {
314 
315 
316             uiViewRoot = mergeComponentData(facesContext,viewID,(TreeStructComponent)((Object [])serializedComponentStates)[1]);
317         }
318         else {
319             uiViewRoot = mergeComponentData(facesContext,viewID,(TreeStructComponent)null);
320         }
321         Object rebuildStateTree = null;
322         if (serializedComponentStates != null)
323         {
324 
325             rebuildStateTree = rebuildStateTree((TreeStructComponent)((Object [])serializedComponentStates)[1]);
326         }
327         else
328         {
329             rebuildStateTree = rebuildStateTree((TreeStructComponent)template);
330         }
331 
332         uiViewRoot.processRestoreState(facesContext,rebuildStateTree);
333 
334         if (uiViewRoot.getRenderKitId() == null)
335         {
336             //Just to be sure...
337             uiViewRoot.setRenderKitId(renderKitId);
338         }
339 
340 
341         if (log.isTraceEnabled()) log.trace("Exiting restoreComponentState");
342         return uiViewRoot;
343     }
344 
345     private Object[] rebuildStateTree(TreeStructComponent componentState) {
346 
347         Map facetMap = null;
348         if (componentState.getFacets() != null)
349         {
350             for (int iter = 0;iter < componentState.getFacets().length;iter++)
351             {
352                 if (facetMap == null) facetMap = new HashMap();
353                 TreeStructComponent componentchild = (TreeStructComponent)((Object[])(componentState.getFacets()[iter]))[1];
354                 Object componentKey = ((Object[])(componentState.getFacets()[iter]))[0];
355                 facetMap.put(componentKey,rebuildStateTree(componentchild));
356             }
357 
358         }
359 
360         List childrenList = null;
361         if (componentState.getChildren() != null)
362         {
363 
364             TreeStructComponent[]  children = componentState.getChildren();
365             for (int it = 0; it < children.length;it++ )
366             {
367 
368             TreeStructComponent child = children[it];
369               if (childrenList == null) {
370                 childrenList = new ArrayList(children.length);
371               }
372               Object childState = rebuildStateTree(child);
373               if (childState != null) {
374                 childrenList.add(childState);
375               }
376             }
377         }
378         return new Object[] {componentState.get_componentState(),
379                              facetMap,
380                              childrenList};
381     }
382 
383 
384     private void mergeComponent(TreeStructComponent currentComponent, TreeStructComponent templateComponent)
385     {
386 
387         TreeStructComponent[] currentchildren;
388         Object[] facets;
389 
390         TreeStructComponent[] templatechildren = null;
391 
392         currentchildren = currentComponent.getChildren();
393         facets = currentComponent.getFacets();
394 
395         if (templateComponent != null)
396         {
397             templatechildren = templateComponent.getChildren();
398             if (currentComponent.getStatus() == TreeStructComponent.STATE_IS_TEMPLATE_STATE)
399             {
400                 // restore State from template
401                 currentComponent.set_componentState(templateComponent.get_componentState());
402                 currentComponent.setStatus(TreeStructComponent.STATE_IS_RESTORED);
403             }
404             if ((currentComponent.getStatus() == TreeStructComponent.STATE_IS_NEW_COMPONENT) ||
405                     (currentComponent.getStatus() == TreeStructComponent.STATE_IS_NEW_STATE))
406             {
407                 currentComponent.setStatus(TreeStructComponent.STATE_IS_RESTORED);
408             }
409         }
410         else
411         {   // new Component ... State is restored
412             currentComponent.setStatus(TreeStructComponent.STATE_IS_RESTORED);
413 
414         }
415 
416         if ((currentchildren != null) )
417         {
418             // create HashMap to find the children easily
419             HashMap templatechildrenMap = new HashMap();
420             if (templatechildren != null)
421             {
422                 for(int componentIndex = 0;componentIndex < templatechildren.length;componentIndex++ )
423                 {
424                     templatechildrenMap.put(templatechildren[componentIndex].getComponentId(),templatechildren[componentIndex]);
425                 }
426             }
427             // loop throw the childs
428             for(int componentIndex = 0;componentIndex < currentchildren.length;componentIndex++ )
429             {
430                 String id = currentchildren[componentIndex].getComponentId();
431                 TreeStructComponent foundtemplateComponent =(TreeStructComponent) templatechildrenMap.get(id);
432                 mergeComponent(currentchildren[componentIndex] ,foundtemplateComponent);
433             }
434 
435         }
436 
437         if (facets != null)
438         {
439             // create HashMap to find the children easily
440             HashMap templatefacetMap = new HashMap();
441             if ((templateComponent != null)  && (templateComponent.getFacets() != null))
442             {
443                 Object [] templatefacets = templateComponent.getFacets();
444                 for(int componentIndex = 0;componentIndex < templatefacets.length;componentIndex++ )
445                 {
446                     templatefacetMap.put(((Object[])(templatefacets[componentIndex]))[0],(TreeStructComponent)((Object[])(templatefacets[componentIndex]))[1]);
447                 }
448             }
449 
450             Map facetMap = new HashMap();
451             for (int iter = 0;iter < facets.length;iter++)
452             {
453                 TreeStructComponent facet = (TreeStructComponent)(((Object[])facets[iter])[1]);
454                 Object key = (((Object[])facets[iter])[0]);
455                 facetMap.put(key,facet);
456             }
457 
458             Iterator iter = facetMap.entrySet().iterator();
459 
460             while(iter.hasNext())
461             {
462                 Map.Entry entry = (Map.Entry)iter.next();
463 
464                 TreeStructComponent foundtemplateComponent =(TreeStructComponent) templatefacetMap.get(entry.getKey());
465                 mergeComponent((TreeStructComponent)entry.getValue() ,foundtemplateComponent);
466             }
467         }
468 
469     }
470 
471 
472 
473     private UIViewRoot mergeComponentData(FacesContext facesContext,String viewRoot, TreeStructComponent state) {
474 
475         TreeStructComponent template = (TreeStructComponent)LoadTreeFromManager(facesContext,viewRoot);
476 
477         UIViewRoot tempRoot = null;
478         if (state == null)
479         {
480             // nothing loaded ... use the templeate
481             tempRoot =  (UIViewRoot)restoreViewRoot(facesContext,template);
482 
483         }
484         else
485         {
486             mergeComponent(state,template);
487             tempRoot =  (UIViewRoot)restoreViewRoot(facesContext,state);
488         }
489         return tempRoot;
490     }
491 
492     /**
493      * See getTreeStructureToSave.
494      */
495     protected UIViewRoot restoreTreeStructure(FacesContext facesContext,
496                                               String viewId,
497                                               String renderKitId)
498     {
499         if (log.isTraceEnabled()) log.trace("Entering restoreTreeStructure");
500         if (!isPartialStateSavingOn(facesContext)) {
501             log.fatal("Partial state saving StateManager is installed, but partial state saving is not enabled. Please enable partial state saving or use another StateManager.");
502         }
503 
504         UIViewRoot uiViewRoot;
505         if (isSavingStateInClient(facesContext))
506         {
507             //reconstruct tree structure from request
508             RenderKit rk = getRenderKitFactory().getRenderKit(facesContext, renderKitId);
509             ResponseStateManager responseStateManager = rk.getResponseStateManager();
510             Object treeStructure = responseStateManager.getTreeStructureToRestore(facesContext, viewId);
511             if (treeStructure == null)
512             {
513                 if (log.isDebugEnabled()) log.debug("Exiting restoreTreeStructure - No tree structure state found in client request");
514                 return null;
515             }
516 
517             TreeStructureManager tsm = new TreeStructureManager();
518             uiViewRoot = tsm.restoreTreeStructure((TreeStructureManager.TreeStructComponent)treeStructure);
519             if (log.isTraceEnabled()) log.trace("Tree structure restored from client request");
520         }
521         else {
522             String sequenceStr = getSequenceString(facesContext, renderKitId, viewId);
523             //reconstruct tree structure from ServletSession
524             SerializedView serializedView = getSerializedViewFromServletSession(facesContext,
525                                                                                 viewId,
526                                                                                 sequenceStr);
527             if (serializedView == null)
528             {
529                 if (log.isDebugEnabled()) log.debug("Exiting restoreTreeStructure - No serialized view found in server session!");
530                 return null;
531             }
532 
533             Object treeStructure = serializedView.getStructure();
534             if (treeStructure == null)
535             {
536                 if (log.isDebugEnabled()) log.debug("Exiting restoreTreeStructure - No tree structure state found in server session, former UIViewRoot must have been transient");
537                 return null;
538             }
539 
540             TreeStructureManager tsm = new TreeStructureManager();
541             uiViewRoot = tsm.restoreTreeStructure((TreeStructureManager.TreeStructComponent)serializedView.getStructure());
542             if (log.isTraceEnabled()) log.trace("Tree structure restored from server session");
543         }
544 
545         if (log.isTraceEnabled()) log.trace("Exiting restoreTreeStructure");
546         return uiViewRoot;
547     }
548 
549     protected void restoreComponentState(FacesContext context, UIViewRoot viewRoot, String renderKitId) {
550     }
551 
552     public UIViewRoot restoreView(FacesContext facescontext, String viewId, String renderKitId)
553     {
554         if (log.isTraceEnabled()) log.trace("Entering restoreView");
555         UIViewRoot uiViewRoot = null;
556 
557         Object template = LoadTreeFromManager(facescontext,viewId);
558 
559         if (template != null)
560         {
561             uiViewRoot = restoreComponentState(facescontext,viewId,renderKitId);
562             uiViewRoot.setViewId(viewId);
563             String restoredViewId = uiViewRoot.getViewId();
564             if (restoredViewId == null || !(restoredViewId.equals(viewId)))
565             {
566                 if (log.isTraceEnabled()) log.trace("Exiting restoreView - restored view is null.");
567                 return null;
568             }
569             if (isPartialStateSavingDispatch(facescontext)) {
570                 dispatchJSP(facescontext,viewId);
571             }
572         }
573         else
574         {
575             dispatchJSP(facescontext, viewId);
576 
577             SaveTreeInManager(facescontext);
578             // reload the current view.
579             uiViewRoot = restoreComponentState(facescontext,viewId,renderKitId);
580 
581 
582         }
583 
584         if (log.isTraceEnabled()) log.trace("Exiting restoreView");
585 
586         return uiViewRoot;
587     }
588 
589     /**
590      * Dispatches the JSP with a wrapped UIViewRoot so no Component is rendered, but
591      * the component tree is created and every tag will be executed too. This is helpfull
592      * if you want to use the f:loadBundle instead of the new s:loadBundle from the Sandbox.
593      * @param facescontext the faces context.
594      * @param viewId the view id which should be dispatched.
595      */
596 
597     private void dispatchJSP(FacesContext facescontext, String viewId) {
598         UIViewRoot uiViewRoot;
599         // the first time the jsp page is called
600         // so create tree
601 
602         Application application = facescontext.getApplication();
603         ViewHandler applicationViewHandler = application.getViewHandler();
604 
605         // create Component Tree
606         ExternalContext externalContext = facescontext.getExternalContext();
607         uiViewRoot = applicationViewHandler.createView(facescontext, viewId);
608         uiViewRoot.setViewId(viewId);
609         UIViewRootWrapper currentWrapper = new UIViewRootWrapper(uiViewRoot);
610 
611         facescontext.setViewRoot(currentWrapper);
612 
613         // save original Response
614         Object orgResponse = facescontext.getExternalContext().getResponse();
615 
616         ViewHandlerResponseWrapperHelperImpl wrapped = new ViewHandlerResponseWrapperHelperImpl((HttpServletResponse)facescontext.getExternalContext().getResponse());
617 
618         // set the wrapped response into the externalContext
619         if ((externalContext instanceof ServletExternalContextImpl)
620             || (externalContext instanceof PortletExternalContextImpl)) {
621             try {
622                 BeanUtils.setProperty(externalContext,"response",wrapped);
623             } catch (IllegalAccessException e) {
624                 log.error(e.toString());
625             } catch (InvocationTargetException e) {
626                 log.error(e.toString());
627             }
628         } else {
629             log.error("External Response could not be set! Deaktivate Partial State Saving!");
630         }
631 
632         try
633         {
634             externalContext.dispatch(viewId);
635         }
636         catch (IOException e)
637         {
638             log.error(e.toString());
639         }
640         // set the original response back into the externalContext
641         if ((externalContext instanceof ServletExternalContextImpl)
642             || (externalContext instanceof PortletExternalContextImpl)) {
643             try {
644                 BeanUtils.setProperty(externalContext,"response",orgResponse);
645             } catch (IllegalAccessException e) {
646                 log.error(e.toString());
647             } catch (InvocationTargetException e) {
648                 log.error(e.toString());
649             }
650 
651         } else {
652             log.error("External Response could not be set! Deaktivate Partial State Saving!");
653         }
654 
655         // After view Content
656         // the first Component in the UIViewRoot is the before view content .... get it's length
657 
658         int beforeViewLength = 0;
659         UIComponent beforeview = (UIComponent)facescontext.getViewRoot().getChildren().get(0);
660         if (beforeview instanceof UIOutput) {
661             // what we expect
662             beforeViewLength = ((String)(((UIOutput)beforeview).getValue())).length();
663         } else {
664             log.error("No before view element found!");
665         }
666 
667         String beforeViewContent = wrapped.toString().substring(beforeViewLength);
668         facescontext.getViewRoot().getChildren().add(facescontext.getViewRoot().getChildCount(),createUIOutputComponentFromString(facescontext,beforeViewContent));
669         wrapped.resetWriter();
670     }
671 
672 
673     protected UIComponent createUIOutputComponentFromString(FacesContext context, String content) {
674      UIOutput verbatim = null;
675 
676      verbatim = createUIOutputComponent(context);
677      verbatim.setValue(content);
678 
679      return verbatim;
680      }
681 
682 
683 
684      protected UIOutput createUIOutputComponent(FacesContext context) {
685          //assert(null != (context));
686          if (context == null) return null;
687          UIOutput verbatim = null;
688          Application application = context.getApplication();
689          verbatim = (UIOutput) application.createComponent("javax.faces.HtmlOutputText");
690          verbatim.setTransient(true);
691          verbatim.getAttributes().put("escape", Boolean.FALSE);
692          verbatim.setId(context.getViewRoot().createUniqueId());
693          return verbatim;
694      }
695 
696 
697 
698     public void SaveTreeInManager(FacesContext facesContext) throws IllegalStateException
699     {
700         if (log.isTraceEnabled()) log.trace("Entering saveSerializedView");
701 
702         checkForDuplicateIds(facesContext, facesContext.getViewRoot(), new HashSet());
703 
704         if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - Checked for duplicate Ids");
705 
706         // save the Tree for Partial State Saving
707 
708         Object Tree = getTreeToSave(facesContext);
709 
710 
711         Map appmap =  facesContext.getExternalContext().getApplicationMap();
712 
713         HashMap mymap = (HashMap)appmap.get(PARTIAL_STATE_MANAGER_TREES);
714 
715         if ( mymap== null)
716         {
717             // create on
718             mymap = new HashMap();
719         }
720 
721         mymap.put(facesContext.getViewRoot().getViewId(),Tree);
722         appmap.put(PARTIAL_STATE_MANAGER_TREES,mymap);
723 
724     }
725 
726     public Object LoadUIViewRootFromManager(FacesContext facesContext, String viewID) throws IllegalStateException
727     {
728         Object tree = LoadTreeFromManager(facesContext,viewID);
729         // get state
730         if (tree != null)
731         {
732             return restoreViewRoot(facesContext,tree);
733         }
734         return null;
735     }
736 
737      public Object LoadTreeFromManager(FacesContext facesContext, String viewID) throws IllegalStateException
738     {
739         Map appmap =  facesContext.getExternalContext().getApplicationMap();
740 
741         HashMap mymap = (HashMap)appmap.get(PARTIAL_STATE_MANAGER_TREES);
742 
743         if ( mymap != null)
744         {
745             Object test = mymap.get(viewID);
746             return test;
747         }
748         return null;
749     }
750 
751     public SerializedView saveSerializedView(FacesContext facesContext) throws IllegalStateException
752     {
753         if (log.isTraceEnabled()) log.trace("Entering saveSerializedView");
754 
755         checkForDuplicateIds(facesContext, facesContext.getViewRoot(), new HashSet());
756 
757         if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - Checked for duplicate Ids");
758 
759         ExternalContext externalContext = facesContext.getExternalContext();
760 
761         TreeStructComponent templateRoot = (TreeStructComponent)LoadTreeFromManager(facesContext,facesContext.getViewRoot().getViewId());
762         // now diff betwee the current and the template ViewRoot
763 
764         TreeStructComponent diff  = null;
765         if (templateRoot != null)
766         {
767             diff = diffAgainsTemplate(facesContext, templateRoot);
768         }
769 
770         // SerializedView already created before within this request?
771         SerializedView serializedView = (SerializedView)externalContext.getRequestMap()
772                                                             .get(SERIALIZED_VIEW_REQUEST_ATTR);
773         if (serializedView == null)
774         {
775             if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - create new serialized view");
776 
777             // first call to saveSerializedView --> create SerializedView
778             // save the View ID also in the serialzed View
779             serializedView = new StateManager.SerializedView(null, new Object[]{facesContext.getViewRoot().getViewId(),diff});
780             externalContext.getRequestMap().put(SERIALIZED_VIEW_REQUEST_ATTR,
781                                                 serializedView);
782 
783             if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - new serialized view created");
784         }
785 
786         if (!isSavingStateInClient(facesContext))
787         {
788             if (log.isTraceEnabled()) log.trace("Processing saveSerializedView - server-side state saving - save state");
789             //save state in server session
790             saveSerializedViewInServletSession(facesContext, serializedView);
791 
792             if (log.isTraceEnabled()) log.trace("Exiting saveSerializedView - server-side state saving - saved state");
793             Integer sequence = ViewSequenceUtils.getViewSequence(facesContext);
794             return new SerializedView(sequence.toString(), null);        }
795 
796         if (log.isTraceEnabled()) log.trace("Exiting saveSerializedView - client-side state saving");
797 
798         return serializedView;
799     }
800 
801     private boolean difState(Object[] currentState,Object[] templateState)
802     {
803 
804         boolean isEqual = true;
805 
806         for(int index = 0;index < currentState.length;index++)
807             {
808                 if ((templateState[index] == null) && (currentState[index] != null )) {
809                     isEqual = false;
810                 }else if (currentState[index] instanceof Object[])
811                 {
812                     if ( !difState((Object[])currentState[index],(Object[])templateState[index]))
813                     {
814                         isEqual = false;
815                     }
816                 }
817                 else
818                 {
819                     if ((currentState[index] != null)&&(!currentState[index].equals(templateState[index])))
820                     {
821                         isEqual= false;
822                     }
823                 }
824             }
825         return isEqual;
826     }
827 
828     private TreeStructComponent diffComponent(TreeStructComponent currentComponent, TreeStructComponent templateComponent)
829     {
830         TreeStructComponent diffComponent = currentComponent.clone(templateComponent);
831 
832         boolean isEqual = false;
833         Object[] currentComponentState;
834         TreeStructComponent[] currentchildren;
835 
836         Object[] templateComponentState = null;
837         TreeStructComponent[] templatechildren = null;
838 
839         currentComponentState = (Object[])currentComponent.get_componentState();
840         currentchildren = currentComponent.getChildren();
841 
842         if (templateComponent != null)
843         {
844             templateComponentState = (Object[])templateComponent.get_componentState();
845             templatechildren = templateComponent.getChildren();
846 
847             if (currentComponentState.length == templateComponentState.length)
848             {
849                 isEqual = difState(currentComponentState,templateComponentState);
850             }
851         }
852         else
853         {
854             isEqual = false;
855         }
856 
857         if (!isEqual)
858         {
859             diffComponent.set_componentState(currentComponent.get_componentState());
860             diffComponent.setStatus(TreeStructComponent.STATE_IS_NEW_STATE);
861         }
862         else
863         {
864             diffComponent.setStatus(TreeStructComponent.STATE_IS_TEMPLATE_STATE);
865             diffComponent.set_componentState(null);
866         }
867 
868         if ((currentchildren != null) )
869         {
870             // create HashMap to find the children easily
871             HashMap templatechildrenMap = new HashMap();
872             if (templatechildren != null)
873             {
874                 for(int componentIndex = 0;componentIndex < templatechildren.length;componentIndex++ )
875                 {
876                     templatechildrenMap.put(templatechildren[componentIndex].getComponentId(),templatechildren[componentIndex]);
877                 }
878             }
879             // loop throw the childs
880             // get count of current child which are not transient
881             ArrayList childs = new ArrayList();
882             //TreeStructComponent[] childs= new TreeStructComponent[currentchildren.length];
883             for(int componentIndex = 0;componentIndex < currentchildren.length;componentIndex++ )
884             {
885                 // only process not transient components
886                 if (!currentchildren[componentIndex].isTransient()) {
887                     String id = currentchildren[componentIndex].getComponentId();
888                     TreeStructComponent foundtemplateComponent =(TreeStructComponent) templatechildrenMap.get(id);
889                     childs.add(diffComponent(currentchildren[componentIndex] ,foundtemplateComponent));
890                 }
891             }
892             childs.toArray(new TreeStructComponent[childs.size()]);
893             diffComponent.setChildren((TreeStructComponent[])childs.toArray(new TreeStructComponent[childs.size()]));
894         }
895 
896         // process facets
897 
898         if (currentComponent.getFacets() != null)
899         {
900 
901             ArrayList facets = new ArrayList();
902             //Object[] facets= new Object[currentComponent.getFacets().length];
903 
904             // create HashMap to find the children easily
905             HashMap templatefacetMap = new HashMap();
906             if ((templateComponent != null)  && (templateComponent.getFacets() != null))
907             {
908                 Object [] templatefacets = templateComponent.getFacets();
909                 for(int componentIndex = 0;componentIndex < templatefacets.length;componentIndex++ )
910                 {
911                     templatefacetMap.put(((Object[])(templatefacets[componentIndex]))[0],(TreeStructComponent)((Object[])(templatefacets[componentIndex]))[1]);
912                 }
913             }
914 
915             Map facetMap = new HashMap();
916             if ((currentComponent != null)  && (currentComponent.getFacets() != null))
917             {
918                 Object [] currentfacets = currentComponent.getFacets();
919                 for(int componentIndex = 0;componentIndex < currentfacets.length;componentIndex++ )
920                 {
921                     facetMap.put(((Object[])(currentfacets[componentIndex]))[0],(TreeStructComponent)((Object[])(currentfacets[componentIndex]))[1]);
922                 }
923             }
924 
925             Iterator iter = facetMap.entrySet().iterator();
926             while(iter.hasNext())
927             {
928                 Map.Entry entry = (Map.Entry)iter.next();
929                 // only process not transient components
930                 if (!((TreeStructComponent)entry.getValue()).isTransient() ) {
931                     TreeStructComponent foundtemplateComponent =(TreeStructComponent) (templatefacetMap.get(entry.getKey()));
932                     Object[] objectEntry = new Object[2];
933                     objectEntry[0] = entry.getKey();
934                     objectEntry[1] = diffComponent((TreeStructComponent)entry.getValue() ,foundtemplateComponent);
935                     facets.add(objectEntry);
936                 }
937             }
938             diffComponent.setFacets(facets.toArray());
939         }
940 
941         return diffComponent;
942     }
943 
944     private TreeStructComponent diffAgainsTemplate(FacesContext facesContext, TreeStructComponent templateRoot)
945     {
946 
947         TreeStructComponent currentTree = (TreeStructComponent)getTreeToSave(facesContext);
948         TreeStructComponent dif = diffComponent(currentTree,templateRoot);
949             return dif;
950 
951     }
952 
953 
954     private static void checkForDuplicateIds(FacesContext context,
955                                              UIComponent component,
956                                              Set ids)
957     {
958         String id = component.getId();
959         if (id != null && !ids.add(id))
960         {
961             throw new IllegalStateException("Client-id : "+id +
962                                             " is duplicated in the faces tree. Component : "+component.getClientId(context)+", path: "+
963                                             getPathToComponent(component));
964         }
965         Iterator it = component.getFacetsAndChildren();
966         boolean namingContainer = component instanceof NamingContainer;
967         while (it.hasNext())
968         {
969             UIComponent kid = (UIComponent) it.next();
970             if (namingContainer)
971             {
972                 checkForDuplicateIds(context, kid, new HashSet());
973             }
974             else
975             {
976                 checkForDuplicateIds(context, kid, ids);
977             }
978         }
979     }
980 
981     private static String getPathToComponent(UIComponent component)
982     {
983         StringBuffer buf = new StringBuffer();
984 
985         if(component == null)
986         {
987             buf.append("{Component-Path : ");
988             buf.append("[null]}");
989             return buf.toString();
990         }
991 
992         getPathToComponent(component,buf);
993 
994         buf.insert(0,"{Component-Path : ");
995         buf.append("}");
996 
997         return buf.toString();
998     }
999 
1000     private static void getPathToComponent(UIComponent component, StringBuffer buf)
1001     {
1002         if(component == null)
1003             return;
1004 
1005         StringBuffer intBuf = new StringBuffer();
1006 
1007         intBuf.append("[Class: ");
1008         intBuf.append(component.getClass().getName());
1009         if(component instanceof UIViewRoot)
1010         {
1011             intBuf.append(",ViewId: ");
1012             intBuf.append(((UIViewRoot) component).getViewId());
1013         }
1014         else
1015         {
1016             intBuf.append(",Id: ");
1017             intBuf.append(component.getId());
1018         }
1019         intBuf.append("]");
1020 
1021         buf.insert(0,intBuf.toString());
1022 
1023         if(component!=null)
1024         {
1025             getPathToComponent(component.getParent(),buf);
1026         }
1027     }
1028 
1029 
1030     public void writeState(FacesContext facesContext,
1031                            SerializedView serializedView) throws IOException {
1032         if (log.isTraceEnabled()) log.trace("Entering writeState");
1033 
1034         if (log.isTraceEnabled())
1035             log.trace("Processing writeState - either client-side (full state) or server-side (partial information; e.g. sequence)");
1036         if (serializedView != null) {
1037             UIViewRoot uiViewRoot = facesContext.getViewRoot();
1038             //save state in response (client-side: full state; server-side: sequence)
1039             RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, uiViewRoot.getRenderKitId());
1040             renderKit.getResponseStateManager().writeState(facesContext, serializedView);
1041 
1042             if (log.isTraceEnabled()) log.trace("Exiting writeState");
1043         }
1044     }
1045 
1046     /**
1047      * MyFaces extension
1048      * @param facesContext
1049      * @param serializedView
1050      * @throws IOException
1051      */
1052     public void writeStateAsUrlParams(FacesContext facesContext,
1053                                       SerializedView serializedView) throws IOException
1054     {
1055         if (log.isTraceEnabled()) log.trace("Entering writeStateAsUrlParams");
1056 
1057         if (isSavingStateInClient(facesContext))
1058         {
1059             if (log.isTraceEnabled()) log.trace("Processing writeStateAsUrlParams - client-side state saving writing state");
1060 
1061             UIViewRoot uiViewRoot = facesContext.getViewRoot();
1062             //save state in response (client)
1063             RenderKit renderKit = getRenderKitFactory().getRenderKit(facesContext, uiViewRoot.getRenderKitId());
1064             ResponseStateManager responseStateManager = renderKit.getResponseStateManager();
1065             if (responseStateManager instanceof MyfacesResponseStateManager)
1066             {
1067                 ((MyfacesResponseStateManager)responseStateManager).writeStateAsUrlParams(facesContext,
1068                                                                                           serializedView);
1069             }
1070             else
1071             {
1072                 log.error("ResponseStateManager of render kit " + uiViewRoot.getRenderKitId() + " is no MyfacesResponseStateManager and does not support saving state in url parameters.");
1073             }
1074         }
1075 
1076         if (log.isTraceEnabled()) log.trace("Exiting writeStateAsUrlParams");
1077     }
1078 
1079     //helpers
1080 
1081     protected RenderKitFactory getRenderKitFactory()
1082     {
1083         if (_renderKitFactory == null)
1084         {
1085             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1086         }
1087         return _renderKitFactory;
1088     }
1089 
1090 
1091     protected void saveSerializedViewInServletSession(FacesContext context,
1092                                                       SerializedView serializedView) {
1093         Map sessionMap = context.getExternalContext().getSessionMap();
1094         SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
1095             .get(SERIALIZED_VIEW_SESSION_ATTR);
1096         if (viewCollection == null) {
1097             viewCollection = new SerializedViewCollection();
1098             sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
1099         }
1100         viewCollection.add(context, serializeView(context, serializedView));
1101         // replace the value to notify the container about the change
1102         sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
1103     }
1104 
1105     protected SerializedView getSerializedViewFromServletSession(FacesContext context, String viewId, String sequenceStr)
1106     {
1107         ExternalContext externalContext = context.getExternalContext();
1108         Map requestMap = externalContext.getRequestMap();
1109         SerializedView serializedView = null;
1110         if (requestMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR))
1111         {
1112             serializedView = (SerializedView) requestMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
1113         }
1114         else
1115         {
1116             SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
1117                 .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
1118             if (viewCollection != null) {
1119                 Integer sequence = null;
1120                 if (sequenceStr == null) {
1121                     // use latest sequence
1122                     sequence = ViewSequenceUtils.getCurrentSequence(context);
1123                 }
1124                 else {
1125                     sequence = new Integer(sequenceStr);
1126                 }
1127                 if (sequence != null) {
1128                     Object state = viewCollection.get(sequence, viewId);
1129                     if (state != null) {
1130                         serializedView = deserializeView(state);
1131                     }
1132                 }
1133             }
1134             requestMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
1135             ViewSequenceUtils.nextViewSequence(context);
1136         }
1137         return serializedView;
1138     }
1139 
1140     protected Object serializeView(FacesContext context, SerializedView serializedView)
1141     {
1142         if (log.isTraceEnabled()) log.trace("Entering serializeView");
1143 
1144         if(isSerializeStateInSession(context))
1145         {
1146             if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize state in session");
1147 
1148             ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
1149             try
1150             {
1151                 OutputStream os = baos;
1152                 if(isCompressStateInSession(context))
1153                 {
1154                     if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize compressed");
1155 
1156                     os.write(COMPRESSED_FLAG);
1157                     os = new GZIPOutputStream(os, 1024);
1158                 }
1159                 else
1160                 {
1161                     if (log.isTraceEnabled()) log.trace("Processing serializeView - serialize uncompressed");
1162 
1163                     os.write(UNCOMPRESSED_FLAG);
1164                 }
1165                 ObjectOutputStream out = new ObjectOutputStream(os);
1166                 out.writeObject(serializedView.getStructure());
1167                 out.writeObject(serializedView.getState());
1168                 out.close();
1169                 baos.close();
1170 
1171                 if (log.isTraceEnabled()) log.trace("Exiting serializeView - serialized. Bytes : "+baos.size());
1172                 return baos.toByteArray();
1173             }
1174             catch (IOException e)
1175             {
1176                 log.error("Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
1177                 return null;
1178             }
1179         }
1180         else
1181         {
1182             if (log.isTraceEnabled()) log.trace("Exiting serializeView - do not serialize state in session.");
1183             return new Object[] {serializedView.getStructure(), serializedView.getState()};
1184         }
1185     }
1186 
1187     /**
1188      * Reads the value of the <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
1189      * @see SERIALIZE_STATE_IN_SESSION_PARAM
1190      * @param context <code>FacesContext</code> for the request we are processing.
1191      * @return boolean true, if the server state should be serialized in the session
1192      */
1193     protected boolean isSerializeStateInSession(FacesContext context)
1194     {
1195         String value = context.getExternalContext().getInitParameter(
1196                 SERIALIZE_STATE_IN_SESSION_PARAM);
1197         boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
1198         if (value != null)
1199         {
1200            serialize = new Boolean(value).booleanValue();
1201         }
1202         return serialize;
1203     }
1204 
1205     /**
1206      * Reads the value of the <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
1207      * @see COMPRESS_SERVER_STATE_PARAM
1208      * @param context <code>FacesContext</code> for the request we are processing.
1209      * @return boolean true, if the server state steam should be compressed
1210      */
1211     protected boolean isCompressStateInSession(FacesContext context)
1212     {
1213         String value = context.getExternalContext().getInitParameter(
1214                 COMPRESS_SERVER_STATE_PARAM);
1215         boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
1216         if (value != null)
1217         {
1218            compress = new Boolean(value).booleanValue();
1219         }
1220         return compress;
1221     }
1222 
1223     protected SerializedView deserializeView(Object state)
1224     {
1225         if (log.isTraceEnabled()) log.trace("Entering deserializeView");
1226 
1227         if(state instanceof byte[])
1228         {
1229             if (log.isTraceEnabled()) log.trace("Processing deserializeView - deserializing serialized state. Bytes : "+((byte[]) state).length);
1230 
1231             try
1232             {
1233                 ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
1234                 InputStream is = bais;
1235                 if(is.read() == COMPRESSED_FLAG)
1236                 {
1237                     is = new GZIPInputStream(is);
1238                 }
1239                 ObjectInputStream in = new MyFacesObjectInputStream(
1240                         is);
1241                 Object a = in.readObject();
1242                 Object b = in.readObject();
1243                 return new SerializedView(a, b);
1244             }
1245             catch (IOException e)
1246             {
1247                 log.error("Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
1248                 return null;
1249             }
1250             catch (ClassNotFoundException e)
1251             {
1252                 log.error("Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
1253                 return null;
1254             }
1255         }
1256         else if (state instanceof Object[])
1257         {
1258             if (log.isTraceEnabled()) log.trace("Exiting deserializeView - state not serialized.");
1259 
1260             Object[] value = (Object[]) state;
1261             return new SerializedView(value[0], value[1]);
1262         }
1263         else if(state == null)
1264         {
1265             log.error("Exiting deserializeView - this method should not be called with a null-state.");
1266             return null;
1267         }
1268         else
1269         {
1270             log.error("Exiting deserializeView - this method should not be called with a state of type : "+state.getClass());
1271             return null;
1272         }
1273     }
1274 
1275     protected static class SerializedViewCollection implements Serializable
1276     {
1277         private static final long serialVersionUID = -3734849062185115847L;
1278 
1279         private final List _keys = new ArrayList(DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
1280         private final Map _serializedViews = new HashMap();
1281 
1282         // old views will be hold as soft references which will be removed by
1283         // the garbage collector if free memory is low
1284         private transient Map _oldSerializedViews = null;
1285 
1286         public synchronized void add(FacesContext context, Object state)
1287         {
1288             Object key = new SerializedViewKey(context);
1289             _serializedViews.put(key, state);
1290 
1291             while (_keys.remove(key));
1292             _keys.add(key);
1293 
1294             int views = getNumberOfViewsInSession(context);
1295             while (_keys.size() > views)
1296             {
1297                 key = _keys.remove(0);
1298                 Object oldView = _serializedViews.remove(key);
1299                 if (oldView != null)
1300                 {
1301                     getOldSerializedViewsMap().put(key, oldView);
1302                 }
1303             }
1304         }
1305 
1306         /**
1307          * Reads the amount (default = 20) of views to be stored in session.
1308          * @see NUMBER_OF_VIEWS_IN_SESSION_PARAM
1309          * @param context FacesContext for the current request, we are processing
1310          * @return Number vf views stored in the session
1311          */
1312         protected int getNumberOfViewsInSession(FacesContext context)
1313         {
1314             String value = context.getExternalContext().getInitParameter(
1315                     NUMBER_OF_VIEWS_IN_SESSION_PARAM);
1316             int views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
1317             if (value != null)
1318             {
1319                 try
1320                 {
1321                     views = Integer.parseInt(value);
1322                     if (views <= 0)
1323                     {
1324                         log.error("Configured value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
1325                                   + " is not valid, must be an value > 0, using default value ("
1326                                   + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
1327                         views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
1328                     }
1329                 }
1330                 catch (Throwable e)
1331                 {
1332                     log.error("Error determining the value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
1333                               + ", expected an integer value > 0, using default value ("
1334                               + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
1335                 }
1336             }
1337             return views;
1338         }
1339 
1340         /**
1341          * @return old serialized views map
1342          */
1343         protected Map getOldSerializedViewsMap()
1344         {
1345             if (_oldSerializedViews == null)
1346             {
1347                 _oldSerializedViews = new ReferenceMap();
1348             }
1349             return _oldSerializedViews;
1350         }
1351 
1352         public Object get(Integer sequence, String viewId)
1353         {
1354             Object key = new SerializedViewKey(viewId, sequence);
1355             Object value = _serializedViews.get(key);
1356             if (value == null)
1357             {
1358                 value = getOldSerializedViewsMap().get(key);
1359             }
1360             return value;
1361         }
1362     }
1363 
1364  protected static class SerializedViewKey implements Serializable {
1365      private static final long serialVersionUID = -1170697124386063642L;
1366 
1367      private final String _viewId;
1368      private final Integer _sequenceId;
1369 
1370      public SerializedViewKey(String viewId, Integer sequence) {
1371          _sequenceId = sequence;
1372          _viewId = viewId;
1373      }
1374 
1375      public SerializedViewKey(FacesContext context) {
1376          _sequenceId = ViewSequenceUtils.getViewSequence(context);
1377          _viewId = context.getViewRoot().getViewId();
1378      }
1379 
1380      public boolean equals(Object obj) {
1381          if (obj == null) {
1382              return false;
1383          }
1384          if (obj == this) {
1385              return true;
1386          }
1387          if (obj instanceof SerializedViewKey) {
1388              SerializedViewKey other = (SerializedViewKey) obj;
1389              return new EqualsBuilder().append(other._viewId, _viewId).append(other._sequenceId,
1390                                                                               _sequenceId).isEquals();
1391          }
1392          return false;
1393      }
1394 
1395      public int hashCode() {
1396          return new HashCodeBuilder().append(_viewId).append(_sequenceId).toHashCode();
1397      }
1398  }
1399 }