Package org.apache.myfaces.renderkit

Source Code of org.apache.myfaces.renderkit.ServerSideStateCacheImpl$CounterSessionViewStorageFactory

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.renderkit;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.faces.application.ProjectStage;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.myfaces.application.StateCache;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
import org.apache.myfaces.shared.renderkit.RendererUtils;
import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
import org.apache.myfaces.shared.util.WebConfigParamUtils;

class ServerSideStateCacheImpl extends StateCache<Object, Object>
{
    private static final Logger log = Logger.getLogger(ServerSideStateCacheImpl.class.getName());
   
    private static final String SERIALIZED_VIEW_SESSION_ATTR=
        ServerSideStateCacheImpl.class.getName() + ".SERIALIZED_VIEW";
   
    private static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR =
        ServerSideStateCacheImpl.class.getName() + ".RESTORED_SERIALIZED_VIEW";

    private static final String RESTORED_VIEW_KEY_REQUEST_ATTR =
        ServerSideStateCacheImpl.class.getName() + ".RESTORED_VIEW_KEY";
   
    /**
     * Defines the amount (default = 20) of the latest views are stored in session.
     *
     * <p>Only applicable if state saving method is "server" (= default).
     * </p>
     *
     */
    @JSFWebConfigParam(defaultValue="20",since="1.1", classType="java.lang.Integer", group="state", tags="performance")
    private static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";

    /**
     * Indicates the amount of views (default is not active) that should be stored in session between sequential
     * POST or POST-REDIRECT-GET if org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION is true.
     *
     * <p>Only applicable if state saving method is "server" (= default). For example, if this param has value = 2 and
     * in your custom webapp there is a form that is clicked 3 times, only 2 views
     * will be stored and the third one (the one stored the first time) will be
     * removed from session, even if the view can
     * store more sessions org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION.
     * This feature becomes useful for multi-window applications.
     * where without this feature a window can swallow all view slots so
     * the other ones will throw ViewExpiredException.</p>
     */
    @JSFWebConfigParam(since="2.0.6", classType="java.lang.Integer", group="state", tags="performance")
    private static final String NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM
            = "org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION";
   
    /**
     * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
     */
    private static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;

    /**
     * Indicate if the state should be serialized before save it on the session.
     * <p>
     * Only applicable if state saving method is "server" (= default).
     * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
     * If <code>false</code> the state will not be serialized to a byte stream.
     * </p>
     */
    @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
    private static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";

    /**
     * Indicates that the serialized state will be compressed before it is written to the session. By default true.
     *
     * Only applicable if state saving method is "server" (= default) and if
     * <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
     * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
     * If <code>false</code> the state will not be compressed.
     */
    @JSFWebConfigParam(defaultValue="true",since="1.1", expectedValues="true,false", group="state", tags="performance")
    private static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";

    /**
     * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
     */
    private static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;

    /**
     * Default value for <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
     */
    private static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;

    /**
     * Define the way of handle old view references(views removed from session), making possible to
     * store it in a cache, so the state manager first try to get the view from the session. If is it
     * not found and soft or weak ReferenceMap is used, it try to get from it.
     * <p>
     * Only applicable if state saving method is "server" (= default).
     * </p>
     * <p>
     * The gc is responsible for remove the views, according to the rules used for soft, weak or phantom
     * references. If a key in soft and weak mode is garbage collected, its values are purged.
     * </p>
     * <p>
     * By default no cache is used, so views removed from session became phantom references.
     * </p>
     * <ul>
     * <li> off, no: default, no cache is used</li>
     * <li> hard-soft: use an ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT)</li>
     * <li> soft: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true) </li>
     * <li> soft-weak: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true) </li>
     * <li> weak: use an ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true) </li>
     * </ul>
     *
     */
    @JSFWebConfigParam(defaultValue="off", expectedValues="off, no, hard-soft, soft, soft-weak, weak",
                       since="1.2.5", group="state", tags="performance")
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE = "org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE";
   
    /**
     * This option uses an hard-soft ReferenceMap, but it could cause a
     * memory leak, because the keys are not removed by any method
     * (MYFACES-1660). So use with caution.
     */
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT = "hard-soft";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT = "soft";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK = "soft-weak";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK = "weak";
   
    private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF = "off";

    /**
     * Allow use flash scope to keep track of the views used in session and the previous ones,
     * so server side state saving can delete old views even if POST-REDIRECT-GET pattern is used.
     *
     * <p>
     * Only applicable if state saving method is "server" (= default).
     * The default value is false.</p>
     */
    @JSFWebConfigParam(since="2.0.6", defaultValue="false", expectedValues="true, false", group="state")
    private static final String USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION
            = "org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION";

    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE = "none";
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM = "secureRandom";
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM = "random";
   
    /**
     * Adds a random key to the generated view state session token.
     */
    @JSFWebConfigParam(since="2.1.9, 2.0.15", expectedValues="secureRandom, random, none",
            defaultValue="none", group="state")
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM
            = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN";
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT =
            RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_NONE;

    /**
     * Set the default length of the random key added to the view state session token.
     * By default is 8.
     */
    @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="8", group="state")
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM
            = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH";
    private static final int RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT = 8;

    /**
     * Sets the random class to initialize the secure random id generator.
     * By default it uses java.security.SecureRandom
     */
    @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM
            = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS";
   
    /**
     * Sets the random provider to initialize the secure random id generator.
     */
    @JSFWebConfigParam(since="2.1.9, 2.0.15", group="state")
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM
            = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER";
   
    /**
     * Sets the random algorithm to initialize the secure random id generator.
     * By default is SHA1PRNG
     */
    @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="SHA1PRNG", group="state")
    private static final String RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM
            = "org.apache.myfaces.RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM";
   
   
    private static final int UNCOMPRESSED_FLAG = 0;
    private static final int COMPRESSED_FLAG = 1;
   
    private static final Object[] EMPTY_STATES = new Object[]{null, null};

    //private static final int JSF_SEQUENCE_INDEX = 0;
   
    private Boolean _useFlashScopePurgeViewsInSession = null;
   
    private Integer _numberOfSequentialViewsInSession = null;
    private boolean _numberOfSequentialViewsInSessionSet = false;

    //private final KeyFactory keyFactory;
    private SessionViewStorageFactory sessionViewStorageFactory;

    public ServerSideStateCacheImpl()
    {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        String randomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
                RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM,
                RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_PARAM_DEFAULT);
        if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM.equals(randomMode))
        {
            //keyFactory = new SecureRandomKeyFactory(facesContext);
            sessionViewStorageFactory = new RandomSessionViewStorageFactory(
                    new SecureRandomKeyFactory(facesContext));
        }
        else if (RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_RANDOM.equals(randomMode))
        {
            //keyFactory = new RandomKeyFactory(facesContext);
            sessionViewStorageFactory = new RandomSessionViewStorageFactory(
                    new RandomKeyFactory(facesContext));
        }
        else
        {
            //keyFactory = new CounterKeyFactory();
            sessionViewStorageFactory = new CounterSessionViewStorageFactory(new CounterKeyFactory());
        }
    }
   
    //------------------------------------- METHODS COPIED FROM JspStateManagerImpl--------------------------------

    protected Object getServerStateId(FacesContext facesContext, Object state)
    {
      if (state != null)
      {
          return getKeyFactory(facesContext).decode(state);
      }
      return null;
    }

    protected void saveSerializedViewInServletSession(FacesContext context,
                                                      Object serializedView)
    {
        Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
        SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
                .get(SERIALIZED_VIEW_SESSION_ATTR);
        if (viewCollection == null)
        {
            viewCollection = getSessionViewStorageFactory().createSerializedViewCollection(context);
            sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
        }

        Map<Object,Object> attributeMap = context.getAttributes();
       
        SerializedViewKey key = null;
        if (getNumberOfSequentialViewsInSession(context.getExternalContext()) != null &&
            getNumberOfSequentialViewsInSession(context.getExternalContext()) > 0)
        {
            key = (SerializedViewKey) attributeMap.get(RESTORED_VIEW_KEY_REQUEST_ATTR);
           
            if (key == null )
            {
                if (isUseFlashScopePurgeViewsInSession(context.getExternalContext()) &&
                    Boolean.TRUE.equals(context.getExternalContext().getRequestMap()
                            .get("oam.Flash.REDIRECT.PREVIOUSREQUEST")))
                {
                    key = (SerializedViewKey)
                            context.getExternalContext().getFlash().get(RESTORED_VIEW_KEY_REQUEST_ATTR);
                }
            }
        }
       
        SerializedViewKey nextKey = getSessionViewStorageFactory().createSerializedViewKey(
                context, context.getViewRoot().getViewId(), getNextViewSequence(context));
        viewCollection.add(context, serializeView(context, serializedView), nextKey, key);

        // replace the value to notify the container about the change
        sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
    }

    protected Object getSerializedViewFromServletSession(FacesContext context, String viewId, Object sequence)
    {
        ExternalContext externalContext = context.getExternalContext();
        Map<Object, Object> attributeMap = context.getAttributes();
        Object serializedView = null;
        if (attributeMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR))
        {
            serializedView = attributeMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
        }
        else
        {
            SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
                    .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
            if (viewCollection != null)
            {
                if (sequence != null)
                {
                    Object state = viewCollection.get(
                            getSessionViewStorageFactory().createSerializedViewKey(
                            context, viewId, sequence));
                    if (state != null)
                    {
                        serializedView = deserializeView(state);
                    }
                }
            }
            attributeMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
           
            if (getNumberOfSequentialViewsInSession(externalContext) != null &&
                getNumberOfSequentialViewsInSession(externalContext) > 0)
            {
                SerializedViewKey key = getSessionViewStorageFactory().
                        createSerializedViewKey(context, viewId, sequence);
                attributeMap.put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
               
                if (isUseFlashScopePurgeViewsInSession(externalContext))
                {
                    externalContext.getFlash().put(RESTORED_VIEW_KEY_REQUEST_ATTR, key);
                    externalContext.getFlash().keep(RESTORED_VIEW_KEY_REQUEST_ATTR);
                }
            }

            nextViewSequence(context);
        }
        return serializedView;
    }

    public Object getNextViewSequence(FacesContext context)
    {
        Object sequence = context.getAttributes().get(RendererUtils.SEQUENCE_PARAM);
        if (sequence == null)
        {
            sequence = nextViewSequence(context);
            context.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
        }
        return sequence;
    }

    public Object nextViewSequence(FacesContext facescontext)
    {
        Object sequence = getKeyFactory(facescontext).generateKey(facescontext);
        facescontext.getAttributes().put(RendererUtils.SEQUENCE_PARAM, sequence);
        return sequence;
    }

    protected Object serializeView(FacesContext context, Object serializedView)
    {
        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Entering serializeView");
        }

        if(isSerializeStateInSession(context))
        {
            if (log.isLoggable(Level.FINEST))
            {
                log.finest("Processing serializeView - serialize state in session");
            }

            ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
            try
            {
                OutputStream os = baos;
                if(isCompressStateInSession(context))
                {
                    if (log.isLoggable(Level.FINEST))
                    {
                        log.finest("Processing serializeView - serialize compressed");
                    }

                    os.write(COMPRESSED_FLAG);
                    os = new GZIPOutputStream(os, 1024);
                }
                else
                {
                    if (log.isLoggable(Level.FINEST))
                    {
                        log.finest("Processing serializeView - serialize uncompressed");
                    }

                    os.write(UNCOMPRESSED_FLAG);
                }

                //Object[] stateArray = (Object[]) serializedView;

                ObjectOutputStream out = new ObjectOutputStream(os);
               
                out.writeObject(serializedView);
                //out.writeObject(stateArray[0]);
                //out.writeObject(stateArray[1]);
                out.close();
                baos.close();

                if (log.isLoggable(Level.FINEST))
                {
                    log.finest("Exiting serializeView - serialized. Bytes : " + baos.size());
                }
                return baos.toByteArray();
            }
            catch (IOException e)
            {
                log.log(Level.SEVERE, "Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
                return null;
            }
        }


        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Exiting serializeView - do not serialize state in session.");
        }

        return serializedView;

    }

    /**
     * Reads the value of the <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
     * @see #SERIALIZE_STATE_IN_SESSION_PARAM
     * @param context <code>FacesContext</code> for the request we are processing.
     * @return boolean true, if the server state should be serialized in the session
     */
    protected boolean isSerializeStateInSession(FacesContext context)
    {
        String value = context.getExternalContext().getInitParameter(
                SERIALIZE_STATE_IN_SESSION_PARAM);
        boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
        if (value != null)
        {
           serialize = Boolean.valueOf(value);
        }
        return serialize;
    }

    /**
     * Reads the value of the <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
     * @see #COMPRESS_SERVER_STATE_PARAM
     * @param context <code>FacesContext</code> for the request we are processing.
     * @return boolean true, if the server state steam should be compressed
     */
    protected boolean isCompressStateInSession(FacesContext context)
    {
        String value = context.getExternalContext().getInitParameter(
                COMPRESS_SERVER_STATE_PARAM);
        boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
        if (value != null)
        {
           compress = Boolean.valueOf(value);
        }
        return compress;
    }

    protected Object deserializeView(Object state)
    {
        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Entering deserializeView");
        }

        if(state instanceof byte[])
        {
            if (log.isLoggable(Level.FINEST))
            {
                log.finest("Processing deserializeView - deserializing serialized state. Bytes : "
                           + ((byte[]) state).length);
            }

            try
            {
                ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
                InputStream is = bais;
                if(is.read() == COMPRESSED_FLAG)
                {
                    is = new GZIPInputStream(is);
                }
                ObjectInputStream ois = null;
                try
                {
                    final ObjectInputStream in = new MyFacesObjectInputStream(is);
                    ois = in;
                    Object object = null;
                    if (System.getSecurityManager() != null)
                    {
                        object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
                        {
                            public Object run() throws PrivilegedActionException, IOException, ClassNotFoundException
                            {
                                //return new Object[] {in.readObject(), in.readObject()};
                                return in.readObject();
                            }
                        });
                    }
                    else
                    {
                        //object = new Object[] {in.readObject(), in.readObject()};
                        object = in.readObject();
                    }
                    return object;
                }
                finally
                {
                    if (ois != null)
                    {
                        ois.close();
                        ois = null;
                    }
                }
            }
            catch (PrivilegedActionException e)
            {
                log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
                return null;
            }
            catch (IOException e)
            {
                log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
                return null;
            }
            catch (ClassNotFoundException e)
            {
                log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
                return null;
            }
        }
        else if (state instanceof Object[])
        {
            if (log.isLoggable(Level.FINEST))
            {
                log.finest("Exiting deserializeView - state not serialized.");
            }

            return state;
        }
        else if(state == null)
        {
            log.severe("Exiting deserializeView - this method should not be called with a null-state.");
            return null;
        }
        else
        {
            log.severe("Exiting deserializeView - this method should not be called with a state of type : "
                       + state.getClass());
            return null;
        }
    }
   
    protected static class SerializedViewCollection implements Serializable
    {
        private static final long serialVersionUID = -3734849062185115847L;

        private final List<SerializedViewKey> _keys
                = new ArrayList<SerializedViewKey>(DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
        private final Map<SerializedViewKey, Object> _serializedViews = new HashMap<SerializedViewKey, Object>();
       
        private final Map<SerializedViewKey, SerializedViewKey> _precedence =
            new HashMap<SerializedViewKey, SerializedViewKey>();

        // old views will be hold as soft references which will be removed by
        // the garbage collector if free memory is low
        private transient Map<Object, Object> _oldSerializedViews = null;

        public synchronized void add(FacesContext context, Object state, SerializedViewKey key,
                                     SerializedViewKey previousRestoredKey)
        {
            if (state == null)
            {
                state = EMPTY_STATES;
            }
            else if (state instanceof Object[] &&
                ((Object[])state).length == 2 &&
                ((Object[])state)[0] == null &&
                ((Object[])state)[1] == null)
            {
                // The generated state can be considered zero, set it as null
                // into the map.
                state = null;
            }
           
            Integer maxCount = getNumberOfSequentialViewsInSession(context);
            if (maxCount != null)
            {
                if (previousRestoredKey != null)
                {
                    if (!_serializedViews.isEmpty())
                    {
                        _precedence.put((SerializedViewKey) key, previousRestoredKey);
                    }
                    else
                    {
                        // Note when the session is invalidated, _serializedViews map is empty,
                        // but we could have a not null previousRestoredKey (the last one before
                        // invalidate the session), so we need to check that condition before
                        // set the precence. In that way, we ensure the precedence map will always
                        // have valid keys.
                        previousRestoredKey = null;
                    }
                }
            }
            _serializedViews.put(key, state);

            while (_keys.remove(key))
            {
                // do nothing
            }
            _keys.add(key);

            if (previousRestoredKey != null && maxCount != null && maxCount > 0)
            {
                int count = 0;
                SerializedViewKey previousKey = (SerializedViewKey) key;
                do
                {
                  previousKey = _precedence.get(previousKey);
                  count++;
                } while (previousKey != null && count < maxCount);
               
                if (previousKey != null)
                {
                    SerializedViewKey keyToRemove = (SerializedViewKey) previousKey;
                    // In theory it should be only one key but just to be sure
                    // do it in a loop, but in this case if cache old views is on,
                    // put on that map.
                    do
                    {
                        while (_keys.remove(keyToRemove))
                        {
                            // do nothing
                        }

                        if (_serializedViews.containsKey(keyToRemove) &&
                                !CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.equals(getCacheOldViewsInSessionMode(context)))
                        {
                            getOldSerializedViewsMap().put(keyToRemove, _serializedViews.remove(keyToRemove));
                        }
                        else
                        {
                            _serializedViews.remove(keyToRemove);
                        }
                   
                        keyToRemove = _precedence.remove(keyToRemove);
                    while(keyToRemove != null);
                }
            }

            int views = getNumberOfViewsInSession(context);
            while (_keys.size() > views)
            {
                key = _keys.remove(0);
               
                if (maxCount != null && maxCount > 0)
                {
                    SerializedViewKey keyToRemove = (SerializedViewKey) key;
                    // Note in this case the key to delete is the oldest one,
                    // so it could be at least one precedence, but to be safe
                    // do it with a loop.
                    do
                    {
                        keyToRemove = _precedence.remove(keyToRemove);
                    } while (keyToRemove != null);
                }

                if (_serializedViews.containsKey(key) &&
                    !CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.equals(getCacheOldViewsInSessionMode(context)))
                {
                   
                    getOldSerializedViewsMap().put(key, _serializedViews.remove(key));
                }
                else
                {
                    _serializedViews.remove(key);
                }
            }
        }

        protected Integer getNumberOfSequentialViewsInSession(FacesContext context)
        {
            return WebConfigParamUtils.getIntegerInitParameter(context.getExternalContext(),
                    NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
        }
       
        /**
         * Reads the amount (default = 20) of views to be stored in session.
         * @see #NUMBER_OF_VIEWS_IN_SESSION_PARAM
         * @param context FacesContext for the current request, we are processing
         * @return Number vf views stored in the session
         */
        protected int getNumberOfViewsInSession(FacesContext context)
        {
            String value = context.getExternalContext().getInitParameter(
                    NUMBER_OF_VIEWS_IN_SESSION_PARAM);
            int views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
            if (value != null)
            {
                try
                {
                    views = Integer.parseInt(value);
                    if (views <= 0)
                    {
                        log.severe("Configured value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
                                  + " is not valid, must be an value > 0, using default value ("
                                  + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
                        views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
                    }
                }
                catch (Throwable e)
                {
                    log.log(Level.SEVERE, "Error determining the value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
                              + ", expected an integer value > 0, using default value ("
                              + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
                }
            }
            return views;
        }

        /**
         * @return old serialized views map
         */
        @SuppressWarnings("unchecked")
        protected Map<Object, Object> getOldSerializedViewsMap()
        {
            FacesContext context = FacesContext.getCurrentInstance();
            if (_oldSerializedViews == null && context != null)
            {
                String cacheMode = getCacheOldViewsInSessionMode(context);
                if (CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true);
                }
                else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT.equals(cacheMode))
                {
                    _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT);
                }
            }
           
            return _oldSerializedViews;
        }
       
        /**
         * Reads the value of the <code>org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE</code> context parameter.
         *
         * @since 1.2.5
         * @param context
         * @return constant indicating caching mode
         * @see #CACHE_OLD_VIEWS_IN_SESSION_MODE
         */
        protected String getCacheOldViewsInSessionMode(FacesContext context)
        {
            String value = context.getExternalContext().getInitParameter(
                    CACHE_OLD_VIEWS_IN_SESSION_MODE);
            if (value == null)
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK;
            }           
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK;
            }
            else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT))
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT;
            }
            else
            {
                return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
            }
        }
       
        public Object get(SerializedViewKey key)
        {
            Object value = _serializedViews.get(key);
            if (value == null)
            {
                if (_serializedViews.containsKey(key))
                {
                    return EMPTY_STATES;
                }
                Map<Object,Object> oldSerializedViewMap = getOldSerializedViewsMap();
                if (oldSerializedViewMap != null)
                {
                    value = oldSerializedViewMap.get(key);
                    if (value == null && oldSerializedViewMap.containsKey(key) )
                    {
                        return EMPTY_STATES;
                    }
                }
            }
            else if (value instanceof Object[] &&
                ((Object[])value).length == 2 &&
                ((Object[])value)[0] == null &&
                ((Object[])value)[1] == null)
            {
                // Remember inside the state map null is stored as an empty array.
                return null;
            }
            return value;
        }
    }

    /**
     * Base implementation where all keys used to identify the state of a view should
     * extend.
     */
    protected abstract static class SerializedViewKey implements Serializable
    {
    }

    /**
     * Implementation of SerializedViewKey, where the hashCode of the viewId is used
     * and the sequenceId is a numeric value.
     */
    private static class IntIntSerializedViewKey extends SerializedViewKey
        implements Serializable
    {
        private static final long serialVersionUID = -1170697124386063642L;
       
        private final int _viewId;
        private final int _sequenceId;
       
        public IntIntSerializedViewKey(int viewId, int sequence)
        {
            _sequenceId = sequence;
            _viewId = viewId;
        }

        @Override
        public boolean equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (getClass() != obj.getClass())
            {
                return false;
            }
            final IntIntSerializedViewKey other = (IntIntSerializedViewKey) obj;
            if (this._viewId != other._viewId)
            {
                return false;
            }
            if (this._sequenceId != other._sequenceId)
            {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode()
        {
            int hash = 7;
            hash = 83 * hash + this._viewId;
            hash = 83 * hash + this._sequenceId;
            return hash;
        }
    }

    /**
     * Implementation of SerializedViewKey, where the hashCode of the viewId is used
     * and the sequenceId is a string value.
     */
    private static class IntByteArraySerializedViewKey extends SerializedViewKey
        implements Serializable
    {
        private final int _viewId;
        private final byte[] _sequenceId;
       
        public IntByteArraySerializedViewKey(int viewId, byte[] sequence)
        {
            _sequenceId = sequence;
            _viewId = viewId;
        }

        @Override
        public boolean equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (getClass() != obj.getClass())
            {
                return false;
            }
            final IntByteArraySerializedViewKey other = (IntByteArraySerializedViewKey) obj;
            if (this._viewId != other._viewId)
            {
                return false;
            }
            if (!Arrays.equals(this._sequenceId, other._sequenceId))
            {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode()
        {
            int hash = 5;
            hash = 37 * hash + this._viewId;
            hash = 37 * hash + Arrays.hashCode(this._sequenceId);
            return hash;
        }
    }
   
   
    /**
     * Implementation of SerializedViewKey, where the viewId and the sequenceId can be
     * anything.
     */
    private static class ReferenceSerializedViewKey<I,K> extends SerializedViewKey
        implements Serializable
    {
        private static final long serialVersionUID = -1170697124386063642L;

        private final I _viewId;
        private final K _sequenceId;

        public ReferenceSerializedViewKey()
        {
            _sequenceId = null;
            _viewId = null;
        }
        public ReferenceSerializedViewKey(I viewId, K sequence)
        {
            _sequenceId = sequence;
            _viewId = viewId;
        }

        @Override
        public boolean equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (getClass() != obj.getClass())
            {
                return false;
            }
            final ReferenceSerializedViewKey<I, K> other = (ReferenceSerializedViewKey<I, K>) obj;
            if (this._viewId != other._viewId && (this._viewId == null || !this._viewId.equals(other._viewId)))
            {
                return false;
            }
            if (this._sequenceId != other._sequenceId &&
                (this._sequenceId == null || !this._sequenceId.equals(other._sequenceId)))
            {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode()
        {
            int hash = 7;
            hash = 83 * hash + (this._viewId != null ? this._viewId.hashCode() : 0);
            hash = 83 * hash + (this._sequenceId != null ? this._sequenceId.hashCode() : 0);
            return hash;
        }
    }
   
    //------------------------------------- METHOD FROM StateCache ------------------------------------------------

    @Override
    public Object saveSerializedView(FacesContext facesContext, Object serializedView)
    {
        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Processing saveSerializedView - server-side state saving - save state");
        }
        //save state in server session
        saveSerializedViewInServletSession(facesContext, serializedView);
       
        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Exiting saveSerializedView - server-side state saving - saved state");
        }
       
        return encodeSerializedState(facesContext, serializedView);
    }

    @Override
    public Object restoreSerializedView(FacesContext facesContext, String viewId, Object viewState)
    {
        if (log.isLoggable(Level.FINEST))
        {
            log.finest("Restoring view from session");
        }

        Object serverStateId = getServerStateId(facesContext, viewState);

        return (serverStateId == null)
                ? null
                : getSerializedViewFromServletSession(facesContext, viewId, serverStateId);
    }

    public Object encodeSerializedState(FacesContext facesContext, Object serializedView)
    {
        return getKeyFactory(facesContext).encode(getNextViewSequence(facesContext));
    }
   
    @Override
    public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
    {
        return false;
    }

    //------------------------------------- Custom methods -----------------------------------------------------
   
    private boolean isUseFlashScopePurgeViewsInSession(ExternalContext externalContext)
    {
        if (_useFlashScopePurgeViewsInSession == null)
        {
            _useFlashScopePurgeViewsInSession = WebConfigParamUtils.getBooleanInitParameter(
                    externalContext, USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION, false);
        }
        return _useFlashScopePurgeViewsInSession;
    }
   
    private Integer getNumberOfSequentialViewsInSession(ExternalContext externalContext)
    {
        if (!_numberOfSequentialViewsInSessionSet)
        {
            _numberOfSequentialViewsInSession = WebConfigParamUtils.getIntegerInitParameter(
                    externalContext,
                    NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_PARAM);
            _numberOfSequentialViewsInSessionSet = true;
        }
        return _numberOfSequentialViewsInSession;
    }
   
    protected KeyFactory getKeyFactory(FacesContext facesContext)
    {
        //return keyFactory;
        return sessionViewStorageFactory.getKeyFactory();
    }
   
    protected SessionViewStorageFactory getSessionViewStorageFactory()
    {
        return sessionViewStorageFactory;
    }
           

    protected abstract static class KeyFactory<K, V>
    {
       
        /**
         * Generates a unique key per session
         *
         * @param facesContext
         * @return
         */
        public abstract K generateKey(FacesContext facesContext);
       
        /**
         * Encode a Key into a value that will be used as view state session token
         *
         * @param key
         * @return
         */
        public abstract V encode(K key);

        /**
         * Decode a view state session token into a key
         *
         * @param value
         * @return
         */
        public abstract K decode(V value);

    }

    private static class CounterKeyFactory extends KeyFactory<Integer, String>
    {
        /**
         * Take the counter from session scope and increment
         *
         * @param facesContext
         * @return
         */
        @Override
        public Integer generateKey(FacesContext facesContext)
        {
            ExternalContext externalContext = facesContext.getExternalContext();
            Object sessionObj = externalContext.getSession(true);
            Integer sequence = null;
            synchronized(sessionObj) // synchronized to increase sequence if multiple requests
                                    // are handled at the same time for the session
            {
                Map<String, Object> map = externalContext.getSessionMap();
                sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
                if(sequence == null || sequence.intValue() == Integer.MAX_VALUE)
                {
                    sequence = Integer.valueOf(1);
                }
                else
                {
                    sequence = Integer.valueOf(sequence.intValue() + 1);
                }
                map.put(RendererUtils.SEQUENCE_PARAM, sequence);
            }
            return sequence;
        }
       
        public String encode(Integer sequence)
        {
            return Integer.toString(sequence, Character.MAX_RADIX);
        }
               
        public Integer decode(String serverStateId)
        {
             return Integer.valueOf((String) serverStateId, Character.MAX_RADIX);
        }
    }
   
    /**
     * This factory generate a key composed by a counter and a random number. The
     * counter ensures uniqueness, and the random number prevents guess the next
     * session token.
     */
    private static class SecureRandomKeyFactory extends KeyFactory<byte[], String>
    {
        private final SessionIdGenerator sessionIdGenerator;
        private final int length;

        public SecureRandomKeyFactory(FacesContext facesContext)
        {
            length = WebConfigParamUtils.getIntegerInitParameter(facesContext.getExternalContext(),
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM,
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT);
            sessionIdGenerator = new SessionIdGenerator();
            sessionIdGenerator.setSessionIdLength(length);
            String secureRandomClass = WebConfigParamUtils.getStringInitParameter(
                    facesContext.getExternalContext(),
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_CLASS_PARAM);
            if (secureRandomClass != null)
            {
                sessionIdGenerator.setSecureRandomClass(secureRandomClass);
            }
            String secureRandomProvider = WebConfigParamUtils.getStringInitParameter(
                    facesContext.getExternalContext(),
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_PROVIDER_PARAM);
            if (secureRandomProvider != null)
            {
                sessionIdGenerator.setSecureRandomProvider(secureRandomProvider);
            }
            String secureRandomAlgorithm = WebConfigParamUtils.getStringInitParameter(
                    facesContext.getExternalContext(),
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_SECURE_RANDOM_ALGORITM_PARAM);
            if (secureRandomAlgorithm != null)
            {
                sessionIdGenerator.setSecureRandomAlgorithm(secureRandomAlgorithm);
            }
        }
       
        public Integer generateCounterKey(FacesContext facesContext)
        {
            ExternalContext externalContext = facesContext.getExternalContext();
            Object sessionObj = externalContext.getSession(true);
            Integer sequence = null;
            synchronized(sessionObj) // synchronized to increase sequence if multiple requests
                                    // are handled at the same time for the session
            {
                Map<String, Object> map = externalContext.getSessionMap();
                sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
                if(sequence == null || sequence.intValue() == Integer.MAX_VALUE)
                {
                    sequence = Integer.valueOf(1);
                }
                else
                {
                    sequence = Integer.valueOf(sequence.intValue() + 1);
                }
                map.put(RendererUtils.SEQUENCE_PARAM, sequence);
            }
            return sequence;
        }

        @Override
        public byte[] generateKey(FacesContext facesContext)
        {
            byte[] array = new byte[length];
            byte[] key = new byte[length+4];
           
            sessionIdGenerator.getRandomBytes(array);
            for (int i = 0; i < array.length; i++)
            {
                key[i] = array[i];
            }
            int value = generateCounterKey(facesContext);
            key[array.length] (byte) (value >>> 24);
            key[array.length+1] (byte) (value >>> 16);
            key[array.length+2] (byte) (value >>> 8);
            key[array.length+3] (byte) (value);
           
            return key;
        }

        @Override
        public String encode(byte[] key)
        {
            return new String(Hex.encodeHex(key));
        }
       
        @Override
        public byte[] decode(String value)
        {
            try
            {
                return Hex.decodeHex(value.toCharArray());
            }
            catch (DecoderException ex)
            {
                // Cannot decode, ignore silently, later it will be handled as
                // ViewExpiredException
            }
            return null;
        }
    }
   
    private static class RandomKeyFactory extends KeyFactory<byte[], String>
    {
        private final Random random;
        private final int length;
       
        public RandomKeyFactory(FacesContext facesContext)
        {
            length = WebConfigParamUtils.getIntegerInitParameter(facesContext.getExternalContext(),
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM,
                    RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN_LENGTH_PARAM_DEFAULT);
            random = new Random(((int)System.nanoTime())+this.hashCode());
        }
       
        public Integer generateCounterKey(FacesContext facesContext)
        {
            ExternalContext externalContext = facesContext.getExternalContext();
            Object sessionObj = externalContext.getSession(true);
            Integer sequence = null;
            synchronized(sessionObj) // synchronized to increase sequence if multiple requests
                                    // are handled at the same time for the session
            {
                Map<String, Object> map = externalContext.getSessionMap();
                sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
                if(sequence == null || sequence.intValue() == Integer.MAX_VALUE)
                {
                    sequence = Integer.valueOf(1);
                }
                else
                {
                    sequence = Integer.valueOf(sequence.intValue() + 1);
                }
                map.put(RendererUtils.SEQUENCE_PARAM, sequence);
            }
            return sequence;
        }

        @Override
        public byte[] generateKey(FacesContext facesContext)
        {
            byte[] array = new byte[length];
            byte[] key = new byte[length+4];
           
            //sessionIdGenerator.getRandomBytes(array);
            random.nextBytes(array);
            for (int i = 0; i < array.length; i++)
            {
                key[i] = array[i];
            }
            int value = generateCounterKey(facesContext);
            key[array.length] (byte) (value >>> 24);
            key[array.length+1] (byte) (value >>> 16);
            key[array.length+2] (byte) (value >>> 8);
            key[array.length+3] (byte) (value);

            return key;
        }

        @Override
        public String encode(byte[] key)
        {
            return new String(Hex.encodeHex(key));
        }
       
        @Override
        public byte[] decode(String value)
        {
            try
            {
                return Hex.decodeHex(value.toCharArray());
            }
            catch (DecoderException ex)
            {
                // Cannot decode, ignore silently, later it will be handled as
                // ViewExpiredException
            }
            return null;
        }
    }
   
    /**
     *
     * @param <T>
     * @param <K>
     * @param <V>
     */
    protected abstract static class SessionViewStorageFactory <T extends KeyFactory<K,V>, K, V >
    {
        private KeyFactory<K, V> keyFactory;
       
        public SessionViewStorageFactory(KeyFactory<K, V> keyFactory)
        {
            this.keyFactory = keyFactory;
        }
       
        public KeyFactory<K, V> getKeyFactory()
        {
            return keyFactory;
        }
       
        public abstract SerializedViewCollection createSerializedViewCollection(
                FacesContext context);
       
        public abstract SerializedViewKey createSerializedViewKey(
                FacesContext facesContext, String viewId, K key);
       
    }
   
    private static class CounterSessionViewStorageFactory
        extends SessionViewStorageFactory <KeyFactory <Integer,String>, Integer, String>
    {
        public CounterSessionViewStorageFactory(KeyFactory<Integer, String> keyFactory)
        {
            super(keyFactory);
        }

        @Override
        public SerializedViewCollection createSerializedViewCollection(
                FacesContext context)
        {
            return new SerializedViewCollection();
        }

        @Override
        public SerializedViewKey createSerializedViewKey(
                FacesContext context, String viewId, Integer key)
        {
            if (context.isProjectStage(ProjectStage.Production))
            {
                return new IntIntSerializedViewKey(viewId.hashCode(), key);
            }
            else
            {
                return new ReferenceSerializedViewKey(viewId, key);
            }
        }
    }
   
    private static class RandomSessionViewStorageFactory
        extends SessionViewStorageFactory <KeyFactory <byte[],String>, byte[], String>
    {
        public RandomSessionViewStorageFactory(KeyFactory<byte[], String> keyFactory)
        {
            super(keyFactory);
        }

        @Override
        public SerializedViewCollection createSerializedViewCollection(
                FacesContext context)
        {
            return new SerializedViewCollection();
        }

        @Override
        public SerializedViewKey createSerializedViewKey(
                FacesContext context, String viewId, byte[] key)
        {
            if (context.isProjectStage(ProjectStage.Production))
            {
                return new IntByteArraySerializedViewKey(viewId.hashCode(), key);
            }
            else
            {
                return new ReferenceSerializedViewKey(viewId, key);
            }
        }
    }
}
TOP

Related Classes of org.apache.myfaces.renderkit.ServerSideStateCacheImpl$CounterSessionViewStorageFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.