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