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