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.viewstate;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.io.OutputStream;
28  import java.security.AccessController;
29  import java.security.PrivilegedActionException;
30  import java.security.PrivilegedExceptionAction;
31  import java.util.Map;
32  import java.util.logging.Level;
33  import java.util.logging.Logger;
34  import java.util.zip.GZIPInputStream;
35  import java.util.zip.GZIPOutputStream;
36  
37  import javax.faces.context.ExternalContext;
38  import javax.faces.context.FacesContext;
39  import org.apache.myfaces.application.StateCache;
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
41  import org.apache.myfaces.shared.renderkit.RendererUtils;
42  import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
43  import org.apache.myfaces.shared.util.WebConfigParamUtils;
44  
45  class ServerSideStateCacheImpl extends StateCache<Object, Object>
46  {
47      private static final Logger log = Logger.getLogger(ServerSideStateCacheImpl.class.getName());
48      
49      public static final String SERIALIZED_VIEW_SESSION_ATTR= 
50          ServerSideStateCacheImpl.class.getName() + ".SERIALIZED_VIEW";
51      
52      public static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR = 
53          ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";
54  
55      public static final String RESTORED_VIEW_KEY_REQUEST_ATTR = 
56          ServerSideStateCacheImpl.class.getName() + ".RESTORED_VIEW_KEY";
57      
58      /**
59       * Defines the amount (default = 20) of the latest views are stored in session.
60       * 
61       * <p>Only applicable if state saving method is "server" (= default).
62       * </p>
63       * 
64       */
65      @JSFWebConfigParam(defaultValue="20",since="1.1", classType="java.lang.Integer", group="state", tags="performance")
66      public static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";
67  
68      /**
69       * Indicates the amount of views (default is not active) that should be stored in session between sequential
70       * POST or POST-REDIRECT-GET if org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION is true.
71       * 
72       * <p>Only applicable if state saving method is "server" (= default). For example, if this param has value = 2 and 
73       * in your custom webapp there is a form that is clicked 3 times, only 2 views
74       * will be stored and the third one (the one stored the first time) will be
75       * removed from session, even if the view can
76       * store more sessions org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION.
77       * This feature becomes useful for multi-window applications.
78       * where without this feature a window can swallow all view slots so
79       * the other ones will throw ViewExpiredException.</p>
80       */
81      @JSFWebConfigParam(since="2.0.6", classType="java.lang.Integer", group="state", tags="performance")
82      public static final String NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM
83              = "org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION";
84      
85      /**
86       * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
87       */
88      public static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;
89  
90      /**
91       * Indicate if the state should be serialized before save it on the session. 
92       * <p>
93       * Only applicable if state saving method is "server" (= default).
94       * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
95       * If <code>false</code> the state will not be serialized to a byte stream.
96       * </p>
97       */
98      @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
99      public static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";
100 
101     /**
102      * Indicates that the serialized state will be compressed before it is written to the session. By default true.
103      * 
104      * Only applicable if state saving method is "server" (= default) and if
105      * <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
106      * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
107      * If <code>false</code> the state will not be compressed.
108      */
109     @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
110     public static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";
111 
112     /**
113      * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
114      */
115     public static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;
116 
117     /**
118      * Default value for <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
119      */
120     public static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;
121 
122     /**
123      * Define the way of handle old view references(views removed from session), making possible to
124      * store it in a cache, so the state manager first try to get the view from the session. If is it
125      * not found and soft or weak ReferenceMap is used, it try to get from it.
126      * <p>
127      * Only applicable if state saving method is "server" (= default).
128      * </p>
129      * <p>
130      * The gc is responsible for remove the views, according to the rules used for soft, weak or phantom
131      * references. If a key in soft and weak mode is garbage collected, its values are purged.
132      * </p>
133      * <p>
134      * By default no cache is used, so views removed from session became phantom references.
135      * </p>
136      * <ul> 
137      * <li> off, no: default, no cache is used</li> 
138      * <li> hard-soft: use an ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT)</li>
139      * <li> soft: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true) </li>
140      * <li> soft-weak: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true) </li>
141      * <li> weak: use an ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true) </li>
142      * </ul>
143      * 
144      */
145     @JSFWebConfigParam(defaultValue="off", expectedValues="off, no, hard-soft, soft, soft-weak, weak",
146                        since="1.2.5", group="state", tags="performance")
147     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE = "org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE";
148     
149     /**
150      * This option uses an hard-soft ReferenceMap, but it could cause a 
151      * memory leak, because the keys are not removed by any method
152      * (MYFACES-1660). So use with caution.
153      */
154     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT = "hard-soft";
155     
156     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT = "soft";
157     
158     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK = "soft-weak";
159     
160     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK = "weak";
161     
162     public static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF = "off";
163 
164     /**
165      * Allow use flash scope to keep track of the views used in session and the previous ones,
166      * so server side state saving can delete old views even if POST-REDIRECT-GET pattern is used.
167      * 
168      * <p>
169      * Only applicable if state saving method is "server" (= default).
170      * The default value is false.</p>
171      */
172     @JSFWebConfigParam(since="2.0.6", defaultValue="false", expectedValues="true, false", group="state")
173     public static final String USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION
174             = "org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION";
175 
176     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE = "none";
177     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM = "secureRandom";
178     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM = "random";
179     
180     /**
181      * Adds a random key to the generated view state session token.
182      */
183     @JSFWebConfigParam(since="2.1.9, 2.0.15", expectedValues="secureRandom, random, none", 
184             defaultValue="none", group="state")
185     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM
186             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN";
187     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT = 
188             RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE;
189 
190     /**
191      * Set the default length of the random key added to the view state session token.
192      * By default is 8. 
193      */
194     @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="8", group="state")
195     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM 
196             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH";
197     public static final int RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT = 8;
198 
199     /**
200      * Sets the random class to initialize the secure random id generator. 
201      * By default it uses java.security.SecureRandom
202      */
203     @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
204     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM
205             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS";
206     
207     /**
208      * Sets the random provider to initialize the secure random id generator.
209      */
210     @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
211     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM
212             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER";
213     
214     /**
215      * Sets the random algorithm to initialize the secure random id generator. 
216      * By default is SHA1PRNG
217      */
218     @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="SHA1PRNG", group="state")
219     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM 
220             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM";
221     
222     
223     public static final int UNCOMPRESSED_FLAG = 0;
224     public static final int COMPRESSED_FLAG = 1;
225 
226     private Boolean _useFlashScopePurgeViewsInSession = null;
227     
228     private Integer _numberOfSequentialViewsInSession = null;
229     private boolean _numberOfSequentialViewsInSessionSet = false;
230 
231     private SessionViewStorageFactory sessionViewStorageFactory;
232 
233     public ServerSideStateCacheImpl()
234     {
235         FacesContext facesContext = FacesContext.getCurrentInstance();
236         String randomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
237                 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM, 
238                 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT);
239         if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM.equals(randomMode))
240         {
241             sessionViewStorageFactory = new RandomSessionViewStorageFactory(
242                     new SecureRandomKeyFactory(facesContext));
243         }
244         else if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM.equals(randomMode))
245         {
246             sessionViewStorageFactory = new RandomSessionViewStorageFactory(
247                     new RandomKeyFactory(facesContext));
248         }
249         else
250         {
251             sessionViewStorageFactory = new CounterSessionViewStorageFactory(new CounterKeyFactory());
252         }
253     }
254     
255     //------------------------------------- METHODS COPIED FROM JspStateManagerImpl--------------------------------
256 
257     protected Object getServerStateId(FacesContext facesContext, Object state)
258     {
259       if (state != null)
260       {
261           return getKeyFactory(facesContext).decode(state);
262       }
263       return null;
264     }
265 
266     protected void saveSerializedViewInServletSession(FacesContext context,
267                                                       Object serializedView)
268     {
269         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
270         SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
271                 .get(SERIALIZED_VIEW_SESSION_ATTR);
272         if (viewCollection == null)
273         {
274             viewCollection = getSessionViewStorageFactory().createSerializedViewCollection(context);
275             sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
276         }
277 
278         Map<Object,Object> attributeMap = context.getAttributes();
279         
280         SerializedViewKey key = null;
281         if (getNumberOfSequentialViewsInSession(context.getExternalContext()) != null &&
282             getNumberOfSequentialViewsInSession(context.getExternalContext()) > 0)
283         {
284             key = (SerializedViewKey) attributeMap.get(RESTORED_VIEW_KEY_REQUEST_ATTR);
285             
286             if (key == null )
287             {
288                 if (isUseFlashScopePurgeViewsInSession(context.getExternalContext()) && 
289                     Boolean.TRUE.equals(context.getExternalContext().getRequestMap()
290                             .get("oam.Flash.REDIRECT.PREVIOUSREQUEST")))
291                 {
292                     key = (SerializedViewKey)
293                             context.getExternalContext().getFlash().get(RESTORED_VIEW_KEY_REQUEST_ATTR);
294                 }
295             }
296         }
297         
298         SerializedViewKey nextKey = getSessionViewStorageFactory().createSerializedViewKey(
299                 context, context.getViewRoot().getViewId(), getNextViewSequence(context));
300         viewCollection.add(context, serializeView(context, serializedView), nextKey, key);
301 
302         // replace the value to notify the container about the change
303         sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
304     }
305 
306     protected Object getSerializedViewFromServletSession(FacesContext context, String viewId, Object sequence)
307     {
308         ExternalContext externalContext = context.getExternalContext();
309         Map<Object, Object> attributeMap = context.getAttributes();
310         Object serializedView = null;
311         if (attributeMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR))
312         {
313             serializedView = attributeMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
314         }
315         else
316         {
317             SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
318                     .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
319             if (viewCollection != null)
320             {
321                 if (sequence != null)
322                 {
323                     Object state = viewCollection.get(
324                             getSessionViewStorageFactory().createSerializedViewKey(
325                             context, viewId, sequence));
326                     if (state != null)
327                     {
328                         serializedView = deserializeView(state);
329                     }
330                 }
331             }
332             attributeMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
333             
334             if (getNumberOfSequentialViewsInSession(externalContext) != null &&
335                 getNumberOfSequentialViewsInSession(externalContext) > 0)
336             {
337                 SerializedViewKey key = getSessionViewStorageFactory().
338                         createSerializedViewKey(context, viewId, sequence);
339                 attributeMap.put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
340                 
341                 if (isUseFlashScopePurgeViewsInSession(externalContext))
342                 {
343                     externalContext.getFlash().put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
344                     externalContext.getFlash().keep(RESTORED_VIEW_KEY_REQUEST_ATTR);
345                 }
346             }
347 
348             nextViewSequence(context);
349         }
350         return serializedView;
351     }
352 
353     public Object getNextViewSequence(FacesContext context)
354     {
355         Object sequence = context.getAttributes().get(RendererUtils.SEQUENCE_PARAM);
356         if (sequence == null)
357         {
358             sequence = nextViewSequence(context);
359             context.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
360         }
361         return sequence;
362     }
363 
364     public Object nextViewSequence(FacesContext facescontext)
365     {
366         Object sequence = getKeyFactory(facescontext).generateKey(facescontext);
367         facescontext.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
368         return sequence;
369     }
370 
371     protected Object serializeView(FacesContext context, Object serializedView)
372     {
373         if (log.isLoggable(Level.FINEST))
374         {
375             log.finest("Entering serializeView");
376         }
377 
378         if(isSerializeStateInSession(context))
379         {
380             if (log.isLoggable(Level.FINEST))
381             {
382                 log.finest("Processing serializeView - serialize state in session");
383             }
384 
385             ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
386             try
387             {
388                 OutputStream os = baos;
389                 if(isCompressStateInSession(context))
390                 {
391                     if (log.isLoggable(Level.FINEST))
392                     {
393                         log.finest("Processing serializeView - serialize compressed");
394                     }
395 
396                     os.write(COMPRESSED_FLAG);
397                     os = new GZIPOutputStream(os, 1024);
398                 }
399                 else
400                 {
401                     if (log.isLoggable(Level.FINEST))
402                     {
403                         log.finest("Processing serializeView - serialize uncompressed");
404                     }
405 
406                     os.write(UNCOMPRESSED_FLAG);
407                 }
408 
409                 //Object[] stateArray = (Object[]) serializedView;
410 
411                 ObjectOutputStream out = new ObjectOutputStream(os);
412                 
413                 out.writeObject(serializedView);
414                 //out.writeObject(stateArray[0]);
415                 //out.writeObject(stateArray[1]);
416                 out.close();
417                 baos.close();
418 
419                 if (log.isLoggable(Level.FINEST))
420                 {
421                     log.finest("Exiting serializeView - serialized. Bytes : " + baos.size());
422                 }
423                 return baos.toByteArray();
424             }
425             catch (IOException e)
426             {
427                 log.log(Level.SEVERE, "Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
428                 return null;
429             }
430         }
431 
432 
433         if (log.isLoggable(Level.FINEST))
434         {
435             log.finest("Exiting serializeView - do not serialize state in session.");
436         }
437 
438         return serializedView;
439 
440     }
441 
442     /**
443      * Reads the value of the <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
444      * @see #SERIALIZE_STATE_IN_SESSION_PARAM
445      * @param context <code>FacesContext</code> for the request we are processing.
446      * @return boolean true, if the server state should be serialized in the session
447      */
448     protected boolean isSerializeStateInSession(FacesContext context)
449     {
450         String value = context.getExternalContext().getInitParameter(
451                 SERIALIZE_STATE_IN_SESSION_PARAM);
452         boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
453         if (value != null)
454         {
455            serialize = Boolean.valueOf(value);
456         }
457         return serialize;
458     }
459 
460     /**
461      * Reads the value of the <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
462      * @see #COMPRESS_SERVER_STATE_PARAM
463      * @param context <code>FacesContext</code> for the request we are processing.
464      * @return boolean true, if the server state steam should be compressed
465      */
466     protected boolean isCompressStateInSession(FacesContext context)
467     {
468         String value = context.getExternalContext().getInitParameter(
469                 COMPRESS_SERVER_STATE_PARAM);
470         boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
471         if (value != null)
472         {
473            compress = Boolean.valueOf(value);
474         }
475         return compress;
476     }
477 
478     protected Object deserializeView(Object state)
479     {
480         if (log.isLoggable(Level.FINEST))
481         {
482             log.finest("Entering deserializeView");
483         }
484 
485         if(state instanceof byte[])
486         {
487             if (log.isLoggable(Level.FINEST))
488             {
489                 log.finest("Processing deserializeView - deserializing serialized state. Bytes : "
490                            + ((byte[]) state).length);
491             }
492 
493             try
494             {
495                 ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
496                 InputStream is = bais;
497                 if(is.read() == COMPRESSED_FLAG)
498                 {
499                     is = new GZIPInputStream(is);
500                 }
501                 ObjectInputStream ois = null;
502                 try
503                 {
504                     final ObjectInputStream in = new MyFacesObjectInputStream(is);
505                     ois = in;
506                     Object object = null;
507                     if (System.getSecurityManager() != null) 
508                     {
509                         object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() 
510                         {
511                             public Object run() throws PrivilegedActionException, IOException, ClassNotFoundException
512                             {
513                                 //return new Object[] {in.readObject(), in.readObject()};
514                                 return in.readObject();
515                             }
516                         });
517                     }
518                     else
519                     {
520                         //object = new Object[] {in.readObject(), in.readObject()};
521                         object = in.readObject();
522                     }
523                     return object;
524                 }
525                 finally
526                 {
527                     if (ois != null)
528                     {
529                         ois.close();
530                         ois = null;
531                     }
532                 }
533             }
534             catch (PrivilegedActionException e) 
535             {
536                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
537                 return null;
538             }
539             catch (IOException e)
540             {
541                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
542                 return null;
543             }
544             catch (ClassNotFoundException e)
545             {
546                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
547                 return null;
548             }
549         }
550         else if (state instanceof Object[])
551         {
552             if (log.isLoggable(Level.FINEST))
553             {
554                 log.finest("Exiting deserializeView - state not serialized.");
555             }
556 
557             return state;
558         }
559         else if(state == null)
560         {
561             log.severe("Exiting deserializeView - this method should not be called with a null-state.");
562             return null;
563         }
564         else
565         {
566             log.severe("Exiting deserializeView - this method should not be called with a state of type : "
567                        + state.getClass());
568             return null;
569         }
570     }
571     
572     //------------------------------------- METHOD FROM StateCache ------------------------------------------------
573 
574     @Override
575     public Object saveSerializedView(FacesContext facesContext, Object serializedView)
576     {
577         if (log.isLoggable(Level.FINEST))
578         {
579             log.finest("Processing saveSerializedView - server-side state saving - save state");
580         }
581         //save state in server session
582         saveSerializedViewInServletSession(facesContext, serializedView);
583         
584         if (log.isLoggable(Level.FINEST))
585         {
586             log.finest("Exiting saveSerializedView - server-side state saving - saved state");
587         }
588         
589         return encodeSerializedState(facesContext, serializedView);
590     }
591 
592     @Override
593     public Object restoreSerializedView(FacesContext facesContext, String viewId, Object viewState)
594     {
595         if (log.isLoggable(Level.FINEST))
596         {
597             log.finest("Restoring view from session");
598         }
599 
600         Object serverStateId = getServerStateId(facesContext, viewState);
601 
602         return (serverStateId == null)
603                 ? null
604                 : getSerializedViewFromServletSession(facesContext, viewId, serverStateId);
605     }
606 
607     public Object encodeSerializedState(FacesContext facesContext, Object serializedView)
608     {
609         return getKeyFactory(facesContext).encode(getNextViewSequence(facesContext));
610     }
611     
612     @Override
613     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
614     {
615         return false;
616     }
617 
618     //------------------------------------- Custom methods -----------------------------------------------------
619     
620     private boolean isUseFlashScopePurgeViewsInSession(ExternalContext externalContext)
621     {
622         if (_useFlashScopePurgeViewsInSession == null)
623         {
624             _useFlashScopePurgeViewsInSession = WebConfigParamUtils.getBooleanInitParameter(
625                     externalContext, USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION, false);
626         }
627         return _useFlashScopePurgeViewsInSession;
628     }
629     
630     private Integer getNumberOfSequentialViewsInSession(ExternalContext externalContext)
631     {
632         if (!_numberOfSequentialViewsInSessionSet)
633         {
634             _numberOfSequentialViewsInSession = WebConfigParamUtils.getIntegerInitParameter(
635                     externalContext, 
636                     NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
637             _numberOfSequentialViewsInSessionSet = true;
638         }
639         return _numberOfSequentialViewsInSession;
640     }
641     
642     protected KeyFactory getKeyFactory(FacesContext facesContext)
643     {
644         //return keyFactory;
645         return sessionViewStorageFactory.getKeyFactory();
646     }
647     
648     protected SessionViewStorageFactory getSessionViewStorageFactory()
649     {
650         return sessionViewStorageFactory;
651     }
652 }