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.application.viewstate.token.ServiceSideStateTokenProcessor;
45  import org.apache.myfaces.application.viewstate.token.StateTokenProcessor;
46  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
47  import org.apache.myfaces.shared.config.MyfacesConfig;
48  import org.apache.myfaces.shared.renderkit.RendererUtils;
49  import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
50  import org.apache.myfaces.shared.util.WebConfigParamUtils;
51  import org.apache.myfaces.spi.ViewScopeProvider;
52  import org.apache.myfaces.spi.ViewScopeProviderFactory;
53  import org.apache.myfaces.view.ViewScopeProxyMap;
54  
55  class ServerSideStateCacheImpl extends StateCache<Object, Object>
56  {
57      private static final Logger log = Logger.getLogger(ServerSideStateCacheImpl.class.getName());
58      
59      public static final String SERIALIZED_VIEW_SESSION_ATTR= 
60          ServerSideStateCacheImpl.class.getName() + ".SERIALIZED_VIEW";
61      
62      public static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR = 
63          ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";
64      
65      public static final String RESTORED_SERIALIZED_VIEW_ID_REQUEST_ATTR = 
66          ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW_ID";
67      public static final String RESTORED_SERIALIZED_VIEW_KEY_REQUEST_ATTR = 
68          ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW_KEY";
69  
70      public static final String RESTORED_VIEW_KEY_REQUEST_ATTR = 
71          ServerSideStateCacheImpl.class.getName() + ".RESTORED_VIEW_KEY";
72      
73      public static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = MyfacesConfig.INIT_PARAM_NUMBER_OF_VIEWS_IN_SESSION;
74  
75      public static final String NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM
76              = MyfacesConfig.INIT_PARAM_NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION;
77      
78      public static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 
79              MyfacesConfig.INIT_PARAM_NUMBER_OF_VIEWS_IN_SESSION_DEFAULT;
80  
81      /**
82       * Indicate if the state should be serialized before save it on the session. 
83       * <p>
84       * Only applicable if state saving method is "server" (= default).
85       * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
86       * If <code>false</code> the state will not be serialized to a byte stream.
87       * </p>
88       * @deprecated 
89       */
90      @Deprecated
91      @JSFWebConfigParam(defaultValue="false",since="1.1", expectedValues="true,false", 
92          group="state", tags="performance", deprecated=true)
93      public static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";
94  
95      /**
96       * Indicates that the serialized state will be compressed before it is written to the session. By default true.
97       * 
98       * Only applicable if state saving method is "server" (= default) and if
99       * <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
100      * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
101      * If <code>false</code> the state will not be compressed.
102      */
103     @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
104     public static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";
105 
106     /**
107      * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
108      */
109     public static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;
110 
111     /**
112      * Default value for <code>javax.faces.SERIALIZE_SERVER_STATE and 
113      * org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
114      */
115     public static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = false;
116     
117     /**
118      * Allow use flash scope to keep track of the views used in session and the previous ones,
119      * so server side state saving can delete old views even if POST-REDIRECT-GET pattern is used.
120      * 
121      * <p>
122      * Only applicable if state saving method is "server" (= default).
123      * The default value is false.</p>
124      */
125     @JSFWebConfigParam(since="2.0.6", defaultValue="false", expectedValues="true, false", group="state")
126     public static final String USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION
127             = "org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION";
128 
129     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM = "secureRandom";
130     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM = "random";
131     
132     /**
133      * Adds a random key to the generated view state session token.
134      */
135     @JSFWebConfigParam(since="2.1.9, 2.0.15", expectedValues="secureRandom, random", 
136             defaultValue="random", group="state")
137     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM
138             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN";
139     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT = 
140             RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM;
141 
142     /**
143      * Set the default length of the random key added to the view state session token.
144      * By default is 8. 
145      */
146     @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="8", group="state")
147     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM 
148             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH";
149     public static final int RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT = 8;
150 
151     /**
152      * Sets the random class to initialize the secure random id generator. 
153      * By default it uses java.security.SecureRandom
154      */
155     @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
156     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM
157             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS";
158     
159     /**
160      * Sets the random provider to initialize the secure random id generator.
161      */
162     @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
163     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM
164             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER";
165     
166     /**
167      * Sets the random algorithm to initialize the secure random id generator. 
168      * By default is SHA1PRNG
169      */
170     @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="SHA1PRNG", group="state")
171     public static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM 
172             = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM";
173     
174     
175     public static final int UNCOMPRESSED_FLAG = 0;
176     public static final int COMPRESSED_FLAG = 1;
177 
178     private Boolean _useFlashScopePurgeViewsInSession = null;
179     
180     private Integer _numberOfSequentialViewsInSession = null;
181     private boolean _numberOfSequentialViewsInSessionSet = false;
182 
183     private SessionViewStorageFactory sessionViewStorageFactory;
184     private CsrfSessionTokenFactory csrfSessionTokenFactory;
185     private StateTokenProcessor stateTokenProcessor;
186 
187     public ServerSideStateCacheImpl()
188     {
189         FacesContext facesContext = FacesContext.getCurrentInstance();
190         String randomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
191                 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM, 
192                 RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT);
193         if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM.equals(randomMode))
194         {
195             sessionViewStorageFactory = new RandomSessionViewStorageFactory(
196                     new SecureRandomKeyFactory(facesContext));
197         }
198         else if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM.equals(randomMode))
199         {
200             sessionViewStorageFactory = new RandomSessionViewStorageFactory(
201                     new RandomKeyFactory(facesContext));
202         }
203         else
204         {
205             if (randomMode != null && !randomMode.isEmpty())
206             {
207                 log.warning(RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM + " \""
208                         + randomMode + "\" is not supported (anymore)."
209                         + " Fallback to \"random\"");
210             }
211             sessionViewStorageFactory = new RandomSessionViewStorageFactory(
212                     new RandomKeyFactory(facesContext));
213         }
214         
215         String csrfRandomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
216                 RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM, 
217                 RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM_DEFAULT);
218         if (RANDOM_KEY_IN_CSRF_SESSION_TOKEN_SECURE_RANDOM.equals(csrfRandomMode))
219         {
220             csrfSessionTokenFactory = new SecureRandomCsrfSessionTokenFactory(facesContext);
221         }
222         else
223         {
224             csrfSessionTokenFactory = new RandomCsrfSessionTokenFactory(facesContext);
225         }
226         
227         stateTokenProcessor = new ServiceSideStateTokenProcessor();
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     protected 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     protected 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 | IOException | ClassNotFoundException e) 
595             {
596                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
597                 return null;
598             }
599         }
600         else if (state instanceof Object[])
601         {
602             if (log.isLoggable(Level.FINEST))
603             {
604                 log.finest("Exiting deserializeView - state not serialized.");
605             }
606 
607             return state;
608         }
609         else if(state == null)
610         {
611             log.severe("Exiting deserializeView - this method should not be called with a null-state.");
612             return null;
613         }
614         else
615         {
616             log.severe("Exiting deserializeView - this method should not be called with a state of type : "
617                        + state.getClass());
618             return null;
619         }
620     }
621     
622     //------------------------------------- METHOD FROM StateCache ------------------------------------------------
623 
624     @Override
625     public Object saveSerializedView(FacesContext facesContext, Object serializedView)
626     {
627         if (log.isLoggable(Level.FINEST))
628         {
629             log.finest("Processing saveSerializedView - server-side state saving - save state");
630         }
631         //save state in server session
632         saveSerializedViewInServletSession(facesContext, serializedView);
633         
634         if (log.isLoggable(Level.FINEST))
635         {
636             log.finest("Exiting saveSerializedView - server-side state saving - saved state");
637         }
638         
639         return encodeSerializedState(facesContext, serializedView);
640     }
641 
642     @Override
643     public Object restoreSerializedView(FacesContext facesContext, String viewId, Object viewState)
644     {
645         if (log.isLoggable(Level.FINEST))
646         {
647             log.finest("Restoring view from session");
648         }
649 
650         Object serverStateId = getServerStateId(facesContext, viewState);
651 
652         return (serverStateId == null)
653                 ? null
654                 : getSerializedViewFromServletSession(facesContext, viewId, serverStateId);
655     }
656 
657     @Override
658     public Object encodeSerializedState(FacesContext facesContext, Object serializedView)
659     {
660         return getKeyFactory(facesContext).encode(getNextViewSequence(facesContext));
661     }
662     
663     @Override
664     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
665     {
666         return false;
667     }
668 
669     //------------------------------------- Custom methods -----------------------------------------------------
670     
671     private boolean isUseFlashScopePurgeViewsInSession(ExternalContext externalContext)
672     {
673         if (_useFlashScopePurgeViewsInSession == null)
674         {
675             _useFlashScopePurgeViewsInSession = WebConfigParamUtils.getBooleanInitParameter(
676                     externalContext, USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION, false);
677         }
678         return _useFlashScopePurgeViewsInSession;
679     }
680     
681     private Integer getNumberOfSequentialViewsInSession(ExternalContext externalContext)
682     {
683         if (!_numberOfSequentialViewsInSessionSet)
684         {
685             _numberOfSequentialViewsInSession = MyfacesConfig.getCurrentInstance(externalContext)
686                     .getNumberOfSequentialViewsInSession();
687             _numberOfSequentialViewsInSessionSet = true;
688         }
689         return _numberOfSequentialViewsInSession;
690     }
691     
692     protected KeyFactory getKeyFactory(FacesContext facesContext)
693     {
694         //return keyFactory;
695         return sessionViewStorageFactory.getKeyFactory();
696     }
697     
698     protected SessionViewStorageFactory getSessionViewStorageFactory()
699     {
700         return sessionViewStorageFactory;
701     }
702 
703     @Override
704     public String createCryptographicallyStrongTokenFromSession(FacesContext context)
705     {
706         return csrfSessionTokenFactory.createCryptographicallyStrongTokenFromSession(context);
707     }
708     
709     @Override
710     public StateTokenProcessor getStateTokenProcessor(FacesContext context)
711     {
712         return stateTokenProcessor;
713     }
714 }