blob: 3b0d2653a05eda4361fd621c044505ab52d24e55 [file] [log] [blame]
/*
* 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.trinidadinternal.application;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.application.StateManagerWrapper;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import javax.faces.view.StateManagementStrategy;
import javax.faces.view.ViewDeclarationLanguage;
import javax.servlet.http.HttpSession;
import org.apache.myfaces.trinidad.bean.util.StateUtils;
import org.apache.myfaces.trinidad.component.UIXComponentBase;
import org.apache.myfaces.trinidad.component.core.CoreDocument;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.context.Window;
import org.apache.myfaces.trinidad.context.WindowManager;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
import org.apache.myfaces.trinidad.util.ExternalContextUtils;
import org.apache.myfaces.trinidad.util.ref.PseudoReference;
import org.apache.myfaces.trinidad.util.ref.PseudoReferenceFactory;
import org.apache.myfaces.trinidad.util.ref.SoftPseudoReferenceFactory;
import org.apache.myfaces.trinidad.util.ref.StrongPseudoReferenceFactory;
import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
import org.apache.myfaces.trinidadinternal.context.TrinidadPhaseListener;
import org.apache.myfaces.trinidadinternal.util.JsfUtils;
import org.apache.myfaces.trinidadinternal.util.ObjectInputStreamResolveClass;
import org.apache.myfaces.trinidadinternal.util.SubKeyMap;
import org.apache.myfaces.trinidadinternal.util.TokenCache;
import org.apache.myfaces.trinidadinternal.util.TokenCacheDebugUtils;
/**
* StateManager that handles a hybrid client/server strategy: the state
* is stored on the server, and only a small token
* is stored on the client.
* <p>
* @version $Name: $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/application/StateManagerImpl.java#2 $) $Date: 18-nov-2005.16:12:04 $
*/
public class StateManagerImpl extends StateManagerWrapper
{
// TODO this should be removed, see comments in restoreView where this constant is used
// on why this is needed
public static final String RESPONSE_STATE_MANAGER_STATE_KEY =
"org.apache.myfaces.trinidadinternal.application.StateManagerImp.RESPONSE_STATE_MANAGER_STATE";
static public final String CACHE_VIEW_ROOT_INIT_PARAM =
"org.apache.myfaces.trinidad.CACHE_VIEW_ROOT";
/**
* Servlet context initialization parameter used by
* StateManagerImpl to decide what sort of state should be saved
* on the client. Valid values are "token" and "all"; the
* default is "token".
*/
static public final String CLIENT_STATE_METHOD_PARAM_NAME =
"org.apache.myfaces.trinidad.CLIENT_STATE_METHOD";
/**
* Servlet context initialization parameter used by
* StateManagerImpl to decide how many tokens can be stored
* per user. The default is 15.
*/
static public final String CLIENT_STATE_MAX_TOKENS_PARAM_NAME =
"org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS";
/**
* Servlet context initialization parameter used by
* StateManagerImpl to decide whether to zip state.
* Valid values are true and false
*/
static public final String COMPRESS_VIEW_STATE_PARAM_NAME =
"org.apache.myfaces.trinidad.COMPRESS_VIEW_STATE";
/**
* Value indicating that only a simple token will be stored
* on the client.
*/
static public final String CLIENT_STATE_METHOD_TOKEN = "token";
/**
* Value indicating that the entire component state will be stored
* on the client.
*/
static public final String CLIENT_STATE_METHOD_ALL = "all";
public StateManagerImpl(
StateManager delegate)
{
_delegate = delegate;
}
@Override
public StateManager getWrapped()
{
return _delegate;
}
@Override
public String getViewState(FacesContext context)
{
Object state = saveView(context);
if (state != null)
{
return context.getRenderKit().getResponseStateManager().getViewState(context,state);
}
else
{
return null;
}
}
private boolean _useViewRootCache(FacesContext context, RequestContext trinContext)
{
return _getOrCreateViewRootStateRefFactory(context, trinContext) != null;
}
@SuppressWarnings("deprecation")
@Override
public Object saveView(FacesContext context)
{
assert(context != null);
// see if a view has been saved on the request
Object viewState = _getCachedViewState(context);
if (viewState != null)
{
// TODO gcrawfor
// when is this not null, meaning when is saveView being called multiple times
// per request?
return viewState;
}
// if the root is transient don't state save
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot.isTransient())
{
return null;
}
String viewId = viewRoot.getViewId();
StateManagementStrategy sms = _getStateManagementStrategy(context, viewId);
Map<Object, Object> contextAttributes = context.getAttributes();
try
{
contextAttributes.put(StateManager.IS_SAVING_STATE, Boolean.TRUE);
if (sms != null)
{
// Force view root to use full state saving
// This is necessary because we recreate the view root on postback when view root caching
// is enabled and assume that that we can apply the full state
if (_useViewRootCache(context, RequestContext.getCurrentInstance()))
{
viewRoot.clearInitialState();
}
viewState = sms.saveView(context);
}
else
{
// if there's no stateManagementStrategy handle saving the state ourselves
_removeTransientComponents(viewRoot);
Object structure = !_needStructure(context) ? null : new Structure(viewRoot);
Object state = viewRoot.processSaveState(context);
viewState = new Object[]{structure, state};
}
}
finally
{
contextAttributes.remove(StateManager.IS_SAVING_STATE);
}
if (_saveAsToken(context, false))
{
viewState = _saveStateToCache(context, viewState, viewRoot);
}
_saveCachedViewState(context, viewState);
return viewState;
}
/**
* Save a component tree as an Object.
*/
static public Object saveComponentTree(
FacesContext context,
UIComponent component)
{
// Don't remove transient components...
Object structure = new Structure(component);
Object state = component.processSaveState(context);
return new PageState(context,
_getViewRootStateRefFactory(context, RequestContext.getCurrentInstance()),
new Object[]{structure, state},
null);
}
/**
* Take an object created by saveComponentTree()
* and instantiate it as a UIComponent.
*/
static public UIComponent restoreComponentTree(
FacesContext context,
Object savedState) throws ClassNotFoundException,
InstantiationException,
IllegalAccessException
{
if (savedState == null)
throw new NullPointerException();
if (!(savedState instanceof PageState))
throw new IllegalArgumentException(_LOG.getMessage(
"INVALID_SAVED_STATE_OBJECT"));
PageState viewState = (PageState) savedState;
Object[] stateArray = (Object[])viewState.getViewState(context);
Object structure = stateArray[0];
Object state = stateArray[1];
UIComponent component =
((Structure) structure).createComponent();
if (state != null)
component.processRestoreState(context, state);
return component;
}
/**
* Save a view root. Doesn't return a SerializedView because
* SerializedView is a non-static inner class, and this needs
* to be a static method.
*/
static public Object saveViewRoot(
FacesContext context,
UIViewRoot root)
{
_removeTransientComponents(root);
Object structure = new Structure(root);
Object state = root.processSaveState(context);
return new PageState(context,
_getViewRootStateRefFactory(context, RequestContext.getCurrentInstance()),
new Object[]{structure, state},
root);
}
static public UIViewRoot restoreViewRoot(
FacesContext context,
Object saved) throws ClassNotFoundException, InstantiationException,
IllegalAccessException
{
if (saved == null)
throw new NullPointerException();
PageState viewState = (PageState) saved;
UIViewRoot root = viewState.popRoot(context);
if (root != null)
{
return root; // bug 4712492
}
Object[] stateArray = (Object[])viewState.getViewState(context);
Object structure = stateArray[0];
Object state = stateArray[1];
root = (UIViewRoot)
((Structure) structure).createComponent();
if (state != null)
root.processRestoreState(context, state);
return root;
}
private Object _saveStateToCache(FacesContext context, Object viewState, UIViewRoot root)
{
ExternalContext extContext = context.getExternalContext();
RequestContext trinContext = RequestContext.getCurrentInstance();
TokenCache cache = _getViewCache(trinContext, extContext);
assert(cache != null);
// get per window view cache key with "." separator suffix to separate the SubKeyMap keys
String subkey = _getViewCacheKey(extContext, trinContext, _SUBKEY_SEPARATOR);
Map<String, Object> sessionMap = extContext.getSessionMap();
SubKeyMap<PageState> stateMap = new SubKeyMap<PageState>(sessionMap, subkey);
// Sadly, we can't save just a SerializedView, because we should
// save a serialized object, and SerializedView is a *non*-static
// inner class of StateManager
PageState pageState = new PageState(
context,
_getOrCreateViewRootStateRefFactory(context, trinContext),
viewState,
// Save the view root into the page state as a transient
// if this feature has not been disabled
root);
String requestToken = _getRequestTokenForResponse(context);
String token;
// If we have a cached token that we want to reuse,
// and that token hasn't disappeared from the cache already
// (unlikely, but not impossible), use the stateMap directly
// without asking the cache for a new token
if ((requestToken != null) && cache.isAvailable(requestToken))
{
// NOTE: under *really* high pressure, the cache might
// have been emptied between the isAvailable() call and
// this put(). This seems sufficiently implausible to
// be worth punting on
stateMap.put(requestToken, pageState);
token = requestToken;
// NOTE 2: we have not pinned this reused state to any old state
// This is OK for current uses of pinning and state reuse,
// as pinning stays constant within a window, and we're not
// erasing pinning at all.
}
else
{
// See if we should pin this new state to any old state
String pinnedToken = (String)extContext.getRequestMap().get(_PINNED_STATE_TOKEN_KEY);
token = cache.addNewEntry(pageState,
stateMap,
pinnedToken);
}
assert(token != null);
// And store the token for this request
extContext.getRequestMap().put(_REQUEST_STATE_TOKEN_KEY, token);
// clear out the view root cache, passing in the session key for the new active state
String newActivePageStateKey = stateMap.getBaseKey(token);
_clearViewRootCache(extContext, newActivePageStateKey);
// Create a "tokenView" which abuses state to store
// our token only
return new Object[]{token, null};
}
private void _clearViewRootCache(ExternalContext extContext, String newActivePageStateKey)
{
Map<String, Object> sessionMap = extContext.getSessionMap();
// clear out all of the previous PageStates' UIViewRoots and add this page
// state as an active page state. This is necessary to avoid UIViewRoots
// laying around if the user navigates off of a page using a GET
synchronized(extContext.getSession(true))
{
// Get the session key under which we will store the session key of the PageState object holding
// the cached UIViewRoot. We can either store one cached UIViewRoot per window or one
// UIViewRoot for the entire session.
//
// We only store the token rather than
// the view state itself here in order to keep fail-over Serialization from Serializing this
// state twice, once where it appears here and the second time in the token map itself
// See Trinidad-1779
String keyToActivePageStateKey = (_CACHE_VIEW_ROOT_PER_WINDOW)
? _getActivePageTokenKey(extContext, RequestContext.getCurrentInstance())
: _ACTIVE_PAGE_TOKEN_SESSION_KEY;
String oldActivePageStateKey = (String)sessionMap.get(keyToActivePageStateKey);
// we only need to clear out the UIViewRoot state if we're actually changing pages and thus tokens.
if (!newActivePageStateKey.equals(oldActivePageStateKey))
{
if (oldActivePageStateKey != null)
{
PageState activePageState = (PageState)sessionMap.get(oldActivePageStateKey);
if (activePageState != null)
{
activePageState.clearViewRootState();
}
}
sessionMap.put(keyToActivePageStateKey, newActivePageStateKey);
}
}
}
// controls whether we are caching UIViewRoots per window. If SoftReferences aren't being used
// to hang onto the UIViewRoots, we definitely want this false. If SoftReferences are used, setting
// this true and letting the VM handle releasing the memory can lead to better lightly loaded performance
private static final boolean _CACHE_VIEW_ROOT_PER_WINDOW = false;
public static String getActivePageToken(RequestContext trinContext, ExternalContext external)
{
String activePageStateKey = _getActivePageTokenKey(external, trinContext);
if (activePageStateKey != null)
{
String tokenPrefix = _getViewCacheKey(external, trinContext, _SUBKEY_SEPARATOR);
String tokenSuffix = (String)external.getSessionMap().get(activePageStateKey);
return tokenPrefix + tokenSuffix;
}
else
{
return null;
}
}
/**
* Requests that an old state token be "pinned" to the state of
* the current request. This means that the view state corresponding
* to the token will not be released before the state for this request
* is released.
*/
@SuppressWarnings("unchecked")
static public void pinStateToRequest(FacesContext context, String stateToken)
{
context.getExternalContext().getRequestMap().put(
_PINNED_STATE_TOKEN_KEY, stateToken);
}
/**
* @return the state token for the current request
*/
static public String getStateToken(FacesContext context)
{
return (String) context.getExternalContext().getRequestMap().get(
_REQUEST_STATE_TOKEN_KEY);
}
/**
* Mark the the incoming request token should be used for the response
*/
@SuppressWarnings("unchecked")
static public void reuseRequestTokenForResponse(ExternalContext ec)
{
ec.getRequestMap().put(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY, Boolean.TRUE);
}
/**
* Clears the flag indicating that the old request token should be used for the response.
*/
@SuppressWarnings("unchecked")
static public void clearReuseRequestTokenForResponse(ExternalContext ec)
{
ec.getRequestMap().remove(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY);
}
/**
* If we've been asked to reuse the request token for the response,
* store it off.
*/
@SuppressWarnings("unchecked")
static private void _updateRequestTokenForResponse(
FacesContext context, String token)
{
Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
// Go from TRUE -> the saved token
if (Boolean.TRUE.equals(
requestMap.get(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY)))
{
requestMap.put(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY, token);
}
}
/**
* Get any cached token for the response.
*/
@SuppressWarnings("unchecked")
static private String _getRequestTokenForResponse(
FacesContext context)
{
Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
Object token = requestMap.get(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY);
// We wanted to, but didn't have anything saved
if (Boolean.TRUE.equals(token))
return null;
return (String) token;
}
/**
* Returns whether a token is a currently valid View State Token
* @param external The ExternalContext
* @param token The state token to check for validity
* @return
*/
public static boolean isValidViewStateToken(ExternalContext external, String token)
{
if ((token != null) && _calculateTokenStateSaving(external, true))
{
return (_getPageState(external, token) != null);
}
else
{
return false;
}
}
/**
* Give a valid state token not associated with a window, change it to be associated with
* the specified window Id.
* @param external The ExternalContext
* @param windowId window id to store the state token under
* @param token The state token to remap from windowless to windowed
*/
public static void remapViewState(ExternalContext external, String windowId, String token)
{
// remove the view state stored with no associated window from the session
String oldSubkey = _getViewCacheKey(external, null, _SUBKEY_SEPARATOR);
Map<String, Object> sessionMap = external.getSessionMap();
Map<String, PageState> oldStateMap = new SubKeyMap<PageState>(sessionMap, oldSubkey);
PageState viewState = oldStateMap.remove(token);
if (viewState == null)
throw new IllegalArgumentException();
// store it under the windowId
String windowSubkey = _getPerWindowCacheKey(windowId, _VIEW_CACHE_KEY, _SUBKEY_SEPARATOR);
Map<String, PageState> newStateMap = new SubKeyMap<PageState>(sessionMap, windowSubkey);
newStateMap.put(token, viewState);
}
/**
* Removes all view states per token cache identified by a <code>windowId</code>. When token state saving is used,
* a token cache is created for each unique window. The windowId is used to locate the token cache. Each PageState
* is located in session scope by a composite key that contains both the window id and state token. When a system window
* closed event is generated, this operation should be called to purge stale view state.
*
* @param extContext faces external context
* @param windowId window id to store the state token under
*/
public static void removeViewStatesForWindow(ExternalContext extContext, String windowId)
{
// build a key used to locate the token cache for the target windowId
// Can't call _getTokenCacheKey() because it doesn't
// take a window id, so directly calling _getPerWindowCacheKey
String cacheKey = _getPerWindowCacheKey(windowId, _VIEW_CACHE_KEY, null);
// use the helper factory method to locate a token cache for the window. the 3rd actual parameter is false
// indicating that a new cache should not be created if it doesn't exist.
TokenCache cache = TokenCache.getTokenCacheFromSession(extContext, cacheKey, false, -1);
if (cache != null)
{
// create a sub key that is used by the SubKeyMap to locate PageState per token
// Can't call _getViewCacheKey() because it doesn't
// take a window id, so directly calling _getPerWindowCacheKey
String subkey = _getPerWindowCacheKey(windowId, _VIEW_CACHE_KEY, _SUBKEY_SEPARATOR);
Map<String, Object> sessionMap = extContext.getSessionMap();
// create a map that establishes a moniker (key) between the windowId and token cache used to locate the
// associated page state.
Map<String, PageState> stateMap = new SubKeyMap<PageState>(sessionMap, subkey);
// removes all page states known by the token cache from session
cache.clear(stateMap);
// removes the token cache
sessionMap.remove(cacheKey);
}
}
/**
* Returns the PageState for a state token
* @param external
* @param token
* @return
*/
private static PageState _getPageState(ExternalContext external, String token)
{
// get view cache key with "." separator suffix to separate the SubKeyMap keys
String subkey = _getViewCacheKey(external,
RequestContext.getCurrentInstance(),
_SUBKEY_SEPARATOR);
Map<String, PageState> stateMap = new SubKeyMap<PageState>(
external.getSessionMap(),
subkey);
return stateMap.get(token);
}
@SuppressWarnings({"unchecked", "deprecation"})
@Override
public UIViewRoot restoreView(
FacesContext context,
String viewId,
String renderKitId)
{
final ExternalContext extContext = context.getExternalContext();
// If we're being asked to execute a "return" event from, say, a dialog, always
// restore the "launch view", which was set over in the TrinidadFilter.
Map<String, Object> requestMap = extContext.getRequestMap();
UIViewRoot launchView = (UIViewRoot)
requestMap.remove(RequestContextImpl.LAUNCH_VIEW);
if (launchView != null)
{
TrinidadPhaseListener.markPostback(context);
return launchView;
}
final Object structure;
final Object state;
ResponseStateManager rsm = _getResponseStateManager(context, renderKitId);
if (_saveAsToken(context, true))
{
// we saved the token in the structure portion of the state, so retrieve the
// structure portion of the state to get the token.
String token = (String)rsm.getTreeStructureToRestore(context, viewId);
if (token == null)
{
_LOG.finest("No token in the request for view \"{0}\"; probably a first view.", viewId);
return null;
}
_LOG.finer("Restoring saved view state for token {0}", token);
// get the PageState for the token
PageState viewState = _getPageState(extContext, token);
if (viewState != null)
_updateRequestTokenForResponse(context, token);
RequestContext trinContext = RequestContext.getCurrentInstance();
// Make sure that if the view state is present, the cache still
// has the token, and vice versa
// NOTE: it's very important that we call through to the
// token cache here, not just inside the assert. If we don't,
// then we don't actually access the token, so it doesn't
// get bumped up to the front in the LRU Cache!
boolean isAvailable = _getViewCache(trinContext, extContext).isAvailable((String) token);
assert ((viewState != null) == isAvailable);
if (viewState == null)
{
_LOG.severe("CANNOT_FIND_SAVED_VIEW_STATE", token);
if(TokenCacheDebugUtils.debugTokenCache())
{
TokenCacheDebugUtils.startLog("Restore View");
String sessionId = "";
boolean isNewSession = false;
Object session = extContext.getSession(false);
if (session == null)
{
isNewSession = true;
}
else if (session instanceof HttpSession)
{
isNewSession = ((HttpSession)session).isNew();
sessionId = ((HttpSession)session).getId();
}
if (isNewSession)
{
TokenCacheDebugUtils.addToLog("The session is new. Session id = " + sessionId);
}
else
{
TokenCacheDebugUtils.addToLog("The session is NOT new. Session id = " + sessionId);
}
// get the state map
String subkey = _getViewCacheKey(extContext,
RequestContext.getCurrentInstance(),
_SUBKEY_SEPARATOR);
Map<String, PageState> stateMap = new SubKeyMap<PageState>(
extContext.getSessionMap(),
subkey);
// log what's currently in the state map
TokenCacheDebugUtils.logCacheInfo(stateMap, null, "token '" + token + "' not found");
_LOG.severe(TokenCacheDebugUtils.getLogString());
}
return null;
}
_LOG.fine("Successfully found view state for token {0}", token);
UIViewRoot root = viewState.popRoot(context); // bug 4712492
if (root != null)
{
_LOG.finer("UIViewRoot for token {0} already exists. Bypassing restoreState", token);
return root;
}
StateManagementStrategy sms = _getStateManagementStrategy(context, viewId);
if (sms!= null)
{
// TODO This is a hack because stateManagementStrategy doesn't take
// a state object as a param, instead it always asks the responseStateManager
// for the state, so push the state onto the request where the CoreResponseStateManager
// can return it. We will file a bug agains JSF 2.0 asking that the
// stateManagementStrategy deprecate the current restoreView method in favor of
// a restoreView method that takes state
try
{
requestMap.put(RESPONSE_STATE_MANAGER_STATE_KEY, viewState.getViewState(context));
root = sms.restoreView(context, viewId, renderKitId);
}
finally
{
requestMap.remove(RESPONSE_STATE_MANAGER_STATE_KEY);
}
return root;
}
else
{
Object[] stateArray = (Object[])viewState.getViewState(context);
structure = stateArray[0];
state = stateArray[1];
}
}
else
{
StateManagementStrategy sms = _getStateManagementStrategy(context, viewId);
if (sms!= null)
{
return sms.restoreView(context, viewId, renderKitId);
}
Object[] stateArray = (Object[])rsm.getState(context, viewId);
structure = stateArray[0];
state = stateArray[1];
}
if (structure == null)
{
UIViewRoot root = context.getViewRoot();
if (root == null && _needStructure(context))
{
_LOG.severe("NO_STRUCTURE_ROOT_AVAILABLE");
return null;
}
if (state != null)
root.processRestoreState(context, state);
return root;
}
else
{
if (!(structure instanceof Structure))
{
_LOG.severe("NO_STRUCTURE_AVAILABLE");
return null;
}
// OK, we've structure and state; let's see what we can do!
try
{
UIViewRoot root = (UIViewRoot)
((Structure) structure).createComponent();
if (state != null)
root.processRestoreState(context, state);
_LOG.finer("Restored state for view \"{0}\"", viewId);
return root;
}
catch (ClassNotFoundException cnfe)
{
_LOG.severe(cnfe);
}
catch (InstantiationException ie)
{
_LOG.severe(ie);
}
catch (IllegalAccessException iae)
{
_LOG.severe(iae);
}
}
return null;
}
/**
* The given parameter (<code>perViewStateSaving</code>) indicates
* if we need to enable client- OR server-side state-saving
* for the current VIEW.
*
* <p>
* <b>This is an internal method, that is ONLY called by the
* Trinidad Document</b>
* </p>
*
* @param perViewStateSaving <code>default</code>, <code>server</code> or <code>client</code> for stateSaving
*/
public void setPerViewStateSaving(String perViewStateSaving)
{
// tweak the given value into one of the three possible enums
// TODO: catch wrong/invalid values (aka baby sitting)
Map<Object, Object> attrs = FacesContext.getCurrentInstance().getAttributes();
attrs.put(_PER_PAGE_STATE_SAVING, StateSaving.valueOf(perViewStateSaving.toUpperCase()));
}
@Override
public boolean isSavingStateInClient(FacesContext context)
{
return _delegate.isSavingStateInClient(context);
}
//
// Protected APIs: we don't want
//
@Override
protected Object getTreeStructureToSave(FacesContext context)
{
throw new UnsupportedOperationException();
}
@Override
protected Object getComponentStateToSave(FacesContext context)
{
throw new UnsupportedOperationException();
}
@Override
protected UIViewRoot restoreTreeStructure
(FacesContext context, String viewId, String renderKitId)
{
throw new UnsupportedOperationException();
}
@Override
protected void restoreComponentState
(FacesContext context, UIViewRoot viewRoot, String renderKitId)
{
throw new UnsupportedOperationException();
}
/**
* Returns the key that the TokenCache
* @param extContext
* @return
*/
private static String _getTokenCacheKey(RequestContext trinContext, ExternalContext extContext)
{
return _getViewCacheKey(extContext, trinContext, null);
}
/**
* Returns the TokenCache for the current window
* @param trinContext
* @param extContext
* @return
*/
private TokenCache _getViewCache(RequestContext trinContext, ExternalContext extContext)
{
String cacheKey = _getTokenCacheKey(trinContext, extContext);
return TokenCache.getTokenCacheFromSession(extContext,cacheKey, true,_getCacheSize(extContext));
}
/**
* Returns a key suitable for finding the per-window active page state key
* @param extContext
* @param trinContext
* @return
*/
static private String _getActivePageTokenKey(
ExternalContext extContext,
RequestContext trinContext)
{
return _getPerWindowCacheKey(_getCurrWindowId(extContext, trinContext),
_ACTIVE_PAGE_TOKEN_SESSION_KEY,
null);
}
/**
* Returns a key suitable for finding the per-window cache key
* @param extContext
* @param trinContext
* @param suffix
* @return
*/
static private String _getViewCacheKey(
ExternalContext extContext,
RequestContext trinContext,
Character suffix)
{
return _getPerWindowCacheKey(_getCurrWindowId(extContext, trinContext),
_VIEW_CACHE_KEY,
suffix);
}
/**
* Returns the current windowId, if any
* @param external
* @param trinContext
* @return
*/
static private String _getCurrWindowId(ExternalContext external, RequestContext trinContext)
{
if (trinContext != null)
{
WindowManager wm = trinContext.getWindowManager();
if (wm != null)
{
Window currWindow = wm.getCurrentWindow(external);
if (currWindow != null)
{
return currWindow.getId();
}
}
}
return null;
}
/**
* Returns a key of the form <prefix>.<windowid><suffix> if a window and a suffix are available
* <prefix>.<window> if just a window is available
* <prefix> if neither a window or a suffix is available
* @param windowId
* @param prefix
* @param suffix
* @return
*/
private static String _getPerWindowCacheKey(
String windowId,
String prefix,
Character suffix)
{
// if we have a current window or a suffix, we need a StringBuilder to calculate the cache key
if ((windowId != null) || (suffix != null))
{
// compute the extra size neeeded to store the windowId and its separator
int windowPartSize;
if (windowId != null)
{
// add 1 for separator
windowPartSize = windowId.length() + 1;
}
else
{
windowPartSize = 0;
}
int builderSize = prefix.length() + windowPartSize;
// add extra space for the suffix Character
if (suffix != null)
builderSize += 1;
// add the constant part to the StringBuilder
StringBuilder keyBuilder = new StringBuilder(builderSize);
keyBuilder.append(prefix);
// add the windowId and its separator
if (windowId != null)
{
keyBuilder.append('.');
keyBuilder.append(windowId);
}
// add the suffix if any
if (suffix != null)
keyBuilder.append(suffix);
return keyBuilder.toString();
}
else
{
return prefix;
}
}
/**
* Returns <code>true</code> if we should use token state saving rather than client state
* saving
* @param external
* @return
* @see #_saveAsToken
*/
private static boolean _calculateTokenStateSaving(ExternalContext external, boolean checkRequestToken)
{
Map initParameters = external.getInitParameterMap();
Object stateSavingMethod = initParameters.get(StateManager.STATE_SAVING_METHOD_PARAM_NAME);
// on "SERVER" state-saving we return TRUE, since we want send down a token string.
if ((stateSavingMethod == null) ||
StateManager.STATE_SAVING_METHOD_SERVER.equalsIgnoreCase((String) stateSavingMethod))
{
return true;
}
// if the user set state-saving to "CLIENT" *and* the client-state-method to "ALL"
// we return FALSE, since we want to save the ENTIRE state on the client...
Object clientMethod = initParameters.get(CLIENT_STATE_METHOD_PARAM_NAME);
if ((clientMethod != null) &&
CLIENT_STATE_METHOD_ALL.equalsIgnoreCase((String) clientMethod))
{
return false;
}
if (checkRequestToken)
{
// if the user has used the <document> 'stateSaving' attribute to specify
// client, we force the state mananger (see above) to render the entire
// state on the client. The indicator is stashed on the FacesContext and
// is therefore NOT visible during "restoreView" phase. So if we reach this point
// here AND we are using "full" client-side-state-saving, this has been tweaked
// on the previous page rendering phase...
// In this case we return FALSE to indicate to restore the entire (serialized)
// state from the client!
//
// @see setPerViewStateSaving()
String viewStateValue =
external.getRequestParameterMap().get(ResponseStateManager.VIEW_STATE_PARAM);
if (viewStateValue != null && !viewStateValue.startsWith("!"))
{
return false;
}
}
// In certain situations this method is called from a filter and there's no facesContext, so
// make sure to check for a null context
FacesContext context = FacesContext.getCurrentInstance();
if (context != null)
{
// is vanilla JSF used? No Trinidad render-kit-id give? If so, we need to return FALSE,
// since we want to save the ENTIRE state on the client...
UIViewRoot viewRoot = context.getViewRoot();
if (viewRoot != null && RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(viewRoot.getRenderKitId()))
{
return false;
}
}
// Last missing option: state-saving is "CLIENT" and the client-state-method uses
// its default (token), so we return TRUE to send down a token string.
return true;
}
/**
* Tests whether to send a small string token, or the entire
* serialized component state to the client-side.
* @return true, if the small string token is to be sent to the client-side.
* @see #_calculateTokenStateSaving
*/
private boolean _saveAsToken(FacesContext context, boolean checkRequestToken)
{
// the setPerViewStateSaving() method stores the PER_PAGE_STATE_SAVING value on the
// FacesContext attribute map, during rendering
Map<Object, Object> attrMap = FacesContext.getCurrentInstance().getAttributes();
StateSaving stateSaving = (StateSaving) attrMap.get(_PER_PAGE_STATE_SAVING);
// if the <document> 'stateSaving' attribute said "client" we need to return FALSE
// in order to do "full" client-side-state saving.
Boolean forceStateSavingPerView = null;
if (StateSaving.CLIENT.equals(stateSaving))
{
forceStateSavingPerView = Boolean.FALSE;
}
// for "server" we return TRUE here, as we want client-side
// state-saving to be turned OFF, regardless of the configuration
// settings.
else if (StateSaving.SERVER.equals(stateSaving))
{
forceStateSavingPerView = Boolean.TRUE;
}
// for the stateSaving "defaul" we just let go and do what it
// normally would do...
if (forceStateSavingPerView != null)
{
return forceStateSavingPerView.booleanValue();
}
return _calculateTokenStateSaving(context.getExternalContext(), checkRequestToken);
}
private int _getCacheSize(ExternalContext extContext)
{
Object maxTokens =
extContext.getInitParameterMap().get(CLIENT_STATE_MAX_TOKENS_PARAM_NAME);
if (maxTokens != null)
{
try
{
return Math.max(1, Integer.parseInt((String) maxTokens));
}
catch (NumberFormatException nfe)
{
_LOG.warning("Ignoring servlet init parameter:"+CLIENT_STATE_MAX_TOKENS_PARAM_NAME+
"\n unable to parse:"+maxTokens, nfe);
_LOG.warning(nfe);
}
}
return _DEFAULT_CACHE_SIZE;
}
private PseudoReferenceFactory<ViewRootState> _getOrCreateViewRootStateRefFactory(
FacesContext context, RequestContext trinContext)
{
if (_viewRootStateRefFactoryHolder == null)
_viewRootStateRefFactoryHolder = _getViewRootStateRefFactoryHolder(context, trinContext);
return _viewRootStateRefFactoryHolder.get();
}
/**
* @return the factory to use for creating references to the UIViewRootState. If
* <code>null</code>, no UIViewRoot caching should be performed.
*/
private static PseudoReferenceFactory<ViewRootState> _getViewRootStateRefFactory(
FacesContext context, RequestContext trinContext)
{
return _getViewRootStateRefFactoryHolder(context, trinContext).get();
}
/**
* @return the holder for the factory to use for creating references to the UIViewRootState. If
* <code>null</code>, no UIViewRoot caching should be performed.
*/
private static AtomicReference<PseudoReferenceFactory<ViewRootState>>
_getViewRootStateRefFactoryHolder(FacesContext context, RequestContext trinContext)
{
ConcurrentMap<String, Object> sharedAppMap = trinContext.getApplicationScopedConcurrentMap();
AtomicReference<PseudoReferenceFactory<ViewRootState>> factoryHolder =
(AtomicReference<PseudoReferenceFactory<ViewRootState>>)sharedAppMap.get(CACHE_VIEW_ROOT_INIT_PARAM);
if (factoryHolder != null)
{
return factoryHolder;
}
else
{
ExternalContext extContext = context.getExternalContext();
String viewRootCaching = extContext.getInitParameter(CACHE_VIEW_ROOT_INIT_PARAM);
String caseInsensitiveViewRootCaching;
if ((viewRootCaching != null) && (viewRootCaching.length() > 0))
{
caseInsensitiveViewRootCaching = viewRootCaching.toLowerCase();
}
else
{
// ViewRootCaching conflicts with jsf >= 2,2
if (JsfUtils.IS_JSF_2_2)
{
caseInsensitiveViewRootCaching = "false";
}
else
{
caseInsensitiveViewRootCaching = "true"; // the default
}
}
PseudoReferenceFactory<ViewRootState> factory;
if ("false".equals(caseInsensitiveViewRootCaching))
{
factory = null;
}
else if ("strong".equals(caseInsensitiveViewRootCaching))
factory = new StrongPseudoReferenceFactory<ViewRootState>();
else if ("soft".equals(caseInsensitiveViewRootCaching))
factory = new SoftPseudoReferenceFactory<ViewRootState>();
else if ("true".equals(caseInsensitiveViewRootCaching))
{
factory = _instantiateDefaultPseudoReferenceFactory();
}
else
{
factory = _instantiatePseudoReferenceFactoryFromClass(viewRootCaching);
if (factory == null)
{
// we had an error, so use the default
factory = _instantiateDefaultPseudoReferenceFactory();
}
}
// use a placeholder for null, since ConcurrentHashMap can't store null;
factoryHolder = new AtomicReference<PseudoReferenceFactory<ViewRootState>>(factory);
sharedAppMap.put(CACHE_VIEW_ROOT_INIT_PARAM, factoryHolder);
return factoryHolder;
}
}
/**
* Attempt to instantiate a PseudoReferenceFactory<ViewRootState> from the
* CACHE_VIEW_ROOT_INIT_PARAM service, using a StrongPseudoReferenceFactory if no service
* is specified or the service is not of the correct type.
* <code>null</code> if any errors occur.
* @return
*/
private static PseudoReferenceFactory<ViewRootState> _instantiateDefaultPseudoReferenceFactory()
{
PseudoReferenceFactory<ViewRootState> factory = new StrongPseudoReferenceFactory<ViewRootState>();
Object service = ClassLoaderUtils.getService(CACHE_VIEW_ROOT_INIT_PARAM);
if (service != null)
{
try
{
factory = (PseudoReferenceFactory<ViewRootState>)service;
}
catch (ClassCastException e)
{
_LOG.warning("Default service registered for " + CACHE_VIEW_ROOT_INIT_PARAM +
" is not of the correct type.", e);
}
}
return factory;
}
/**
* Attempt to instantiate a PseudoReferenceFactory<ViewRootState> from a class name, returning
* <code>null</code> if any errors occur.
* @param className
* @return
*/
private static PseudoReferenceFactory<ViewRootState> _instantiatePseudoReferenceFactoryFromClass(
String className)
{
try
{
Class<? extends PseudoReferenceFactory<ViewRootState>> factoryClass =
(Class<? extends PseudoReferenceFactory<ViewRootState>>)ClassLoaderUtils.loadClass(className);
return factoryClass.newInstance();
}
catch (ClassNotFoundException e)
{
_LOG.warning("Could not find PseudoReferenceFactory class " + className + " for " +
" Servlet Initialization Parameter " + CACHE_VIEW_ROOT_INIT_PARAM);
}
catch (InstantiationException e)
{
_logInstantiationError(className, e);
}
catch (IllegalAccessException e)
{
_logInstantiationError(className, e);
}
catch (ClassCastException e)
{
_logInstantiationError(className, e);
}
return null;
}
private static void _logInstantiationError(String className, Throwable e)
{
_LOG.warning("Could not instantiate a PseudoReferenceFactory for Servlet Initialization" +
" Parameter " + CACHE_VIEW_ROOT_INIT_PARAM + " with value " +
className, e);
}
private boolean _needStructure(FacesContext context)
{
// TODO - partial state saving is handled by facelets in JSF 2.0,
// remove this method and anything that depends on it?
return true;
}
static private ResponseStateManager _getResponseStateManager(
FacesContext context,
String renderKitId)
{
RenderKitFactory factory = (RenderKitFactory)
FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit kit = factory.getRenderKit(context, renderKitId);
return kit.getResponseStateManager();
}
@SuppressWarnings("unchecked")
static private void _removeTransientComponents(
UIComponent root)
{
List<UIComponent> components = new ArrayList<UIComponent>();
_gatherTransientComponents(root, components);
Iterator<UIComponent> iter = components.iterator();
while (iter.hasNext())
{
UIComponent kid = iter.next();
UIComponent parent = kid.getParent();
// First, see if its a child
if (parent.getChildCount() > 0)
{
List<UIComponent> children = parent.getChildren();
if (children.remove(kid))
{
continue;
}
}
// Nope, guess it's a facet
// 2006-08-02: -= Simon Lessard
// Not 1.5 structure and inefficient loop
// values() is more efficient as you don't have
// to do a second lookup for the value.
Map<String, UIComponent> facets = parent.getFacets();
for(Iterator<UIComponent> facetIter = facets.values().iterator();
facetIter.hasNext();)
{
if(facetIter.next() == kid)
{
facetIter.remove();
// FIXME: -= Simon Lessard
// Is that continue need to labeled to go all the way up to
// the first while? Currently it won't cause any problem, but
// it's a performance loss.
continue;
}
}
// Couldn't find the component at all in its parent: that's
// not good.
assert false;
}
}
@SuppressWarnings("unchecked")
static private void _gatherTransientComponents(
UIComponent component, List<UIComponent> componentsToRemove)
{
Iterator<UIComponent> kids = component.getFacetsAndChildren();
while (kids.hasNext())
{
UIComponent kid = kids.next();
// UIXComponentBase doesn't mind transient components
// in its saved state, so don't bother with this.
if (!(component instanceof UIXComponentBase) &&
kid.isTransient())
{
componentsToRemove.add(kid);
}
else
{
_gatherTransientComponents(kid, componentsToRemove);
}
}
}
private Object _getCachedViewState(
FacesContext context)
{
return context.getExternalContext().
getRequestMap().get(_CACHED_VIEW_STATE);
}
private void _saveCachedViewState(
FacesContext context, Object state)
{
context.getExternalContext().getRequestMap().put(_CACHED_VIEW_STATE,
state);
}
private StateManagementStrategy _getStateManagementStrategy(FacesContext context, String viewId)
{
ViewDeclarationLanguage vdl = context.getApplication().getViewHandler().
getViewDeclarationLanguage(context, viewId);
if (vdl != null)
{
return vdl.getStateManagementStrategy(context, viewId);
}
else
{
return null;
}
}
private static final class ViewRootState
{
public ViewRootState(FacesContext context, UIViewRoot viewRoot)
{
if (viewRoot == null)
throw new NullPointerException();
_viewRoot = viewRoot;
_viewRootState = viewRoot.saveState(context);
}
public UIViewRoot getViewRoot()
{
return _viewRoot;
}
public Object getViewRootState()
{
return _viewRootState;
}
private final UIViewRoot _viewRoot;
private final Object _viewRootState;
}
private static final class PageState implements Serializable
{
private static final long serialVersionUID = 1L;
private Object _viewState;
// use transient since UIViewRoots are not Serializable. We use a PseudReference so that
// we can support either soft or strong references
private transient PseudoReference<ViewRootState> _cachedState;
public PageState(
FacesContext context, PseudoReferenceFactory<ViewRootState> viewRootStateRefFactory,
Object viewState, UIViewRoot root)
{
if (!(viewState instanceof Serializable))
throw new IllegalArgumentException("Viewstate " + viewState + " is not a Serializable");
boolean zipState = _zipState(context);
if (zipState || StateUtils.checkComponentTreeStateSerialization(context))
{
if (zipState)
{
// zip the page state. This will also catch any serialization problems.
_viewState = _zipToBytes(viewState);
}
else
{
_viewState = viewState;
// if component tree serialization checking is on (in order to validate
// fail over support, attempt to Serialize all of the component state
// immediately
try
{
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(viewState);
}
catch (IOException e)
{
throw new RuntimeException(_LOG.getMessage("COMPONENT_TREE_SERIALIZATION_FAILED"), e);
}
}
}
else
{
_viewState = viewState;
}
// we need this state, as we are going to recreate the UIViewRoot later. see
// the popRoot() method:
_cachedState = ((root != null) && (viewRootStateRefFactory != null))
? viewRootStateRefFactory.create(new ViewRootState(context, root), null)
: null;
}
public Object getViewState(FacesContext context)
{
if (_zipState(context))
{
return _unzipBytes((byte[])_viewState);
}
return _viewState;
}
public void clearViewRootState()
{
synchronized(this)
{
_cachedState = null;
}
}
@SuppressWarnings("unchecked")
public UIViewRoot popRoot(FacesContext fc)
{
UIViewRoot root = null;
Object viewRootState = null;
// we need to synchronize because we are mutating _root
// which is shared between simultaneous requests from the same user:
synchronized(this)
{
if (_cachedState != null)
{
ViewRootState rootState = _cachedState.get();
if (rootState != null)
{
root = rootState.getViewRoot();
viewRootState = rootState.getViewRootState();
}
// we must clear the cached viewRoot. This is because UIComponent trees
// are mutable and if the back button
// is used to come back to an old PageState, then it would be
// really bad if we reused that component tree:
_cachedState = null;
}
}
if (root != null)
{
// If an error happens during updateModel, JSF 1.1 does not
// clear FacesEvents (or FacesMessages, IIRC), so any pending
// events will still be present, on the subsequent request.
// so to clear the events, we create a new UIViewRoot.
// must get the UIViewRoot from the application so that
// we pick up any custom ViewRoot defined in faces-config.xml:
UIViewRoot newRoot = (UIViewRoot)
fc.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE);
//This code handles automatic namespacing in a JSR-301 environment
if(ExternalContextUtils.isPortlet(fc.getExternalContext()))
{
//IMPORTANT: To avoid introducing a runtime dependency on the bridge,
//this method should only be executed when we have a portlet
//request.
try
{
newRoot = (UIViewRoot) root.getClass().newInstance();
}
catch (InstantiationException e)
{
_LOG.finest("Unable to instantiate new root of type class \"{0}\".", root.getClass());
}
catch (IllegalAccessException e)
{
_LOG.finest("IllegalAccessException on new root of type class \"{0}\".", root.getClass());
}
}
// must call restoreState so that we setup attributes, listeners,
// uniqueIds, etc ...
newRoot.restoreState(fc, viewRootState);
// we need to use a temp list because as a side effect of
// adding a child to a UIComponent, that child is removed from
// the parent UIComponent. So the following will break:
// newRoot.getChildren().addAll(root.getChildren());
// because "root"'s child List is being mutated as the List
// is traversed.
List<UIComponent> temp = new ArrayList<UIComponent>(root.getChildCount());
temp.addAll(root.getChildren());
newRoot.getChildren().addAll(temp);
// copy facets
if (root.getFacetCount() > 0)
{
Map<String,UIComponent> facets = new HashMap<String, UIComponent>(root.getFacetCount());
facets.putAll(root.getFacets());
newRoot.getFacets().putAll(facets);
}
return newRoot;
}
return null;
}
private boolean _zipState(FacesContext fc)
{
// default is false
Object zipStateObject =
fc.getExternalContext().getInitParameter(COMPRESS_VIEW_STATE_PARAM_NAME);
if (zipStateObject == null)
return false;
return zipStateObject.toString().equalsIgnoreCase("true");
}
private Object _unzipBytes(byte[] zippedBytes)
{
Inflater decompressor = new Inflater();
try
{
decompressor.setInput(zippedBytes);
ByteArrayOutputStream bos = new ByteArrayOutputStream(zippedBytes.length);
byte[] buf = new byte[zippedBytes.length*5];
while (!decompressor.finished())
{
try
{
int count = decompressor.inflate(buf);
bos.write(buf, 0, count);
}
catch (DataFormatException e)
{
throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), e);
}
}
ByteArrayInputStream baos = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStreamResolveClass(baos);
Object unzippedState = ois.readObject();
ois.close();
return unzippedState;
}
catch(ClassNotFoundException cnfe)
{
throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), cnfe);
}
catch(IOException ioe)
{
throw new RuntimeException(_LOG.getMessage("UNZIP_STATE_FAILED"), ioe);
}
finally
{
decompressor.end();
}
}
/**
* Returned the zipped version of the viewState
*/
private byte[] _zipToBytes(Object viewState)
{
Deflater compresser = new Deflater(Deflater.BEST_SPEED);
try
{
//Serialize state
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(viewState);
oos.flush();
oos.close();
byte[] ret = baos.toByteArray();
compresser.setInput(ret);
compresser.finish();
baos.reset();
byte[] buf = new byte[ret.length/5];
while (!compresser.finished())
{
int count = compresser.deflate(buf);
baos.write(buf, 0, count);
}
return baos.toByteArray();
}
catch (IOException e)
{
throw new RuntimeException(_LOG.getMessage("ZIP_STATE_FAILED"), e);
}
finally
{
compresser.end();
}
}
}
/**
* Static ENUM to capture the values of the <document>'s
* 'stateSaving' attribute
*/
static private enum StateSaving
{
DEFAULT(CoreDocument.STATE_SAVING_DEFAULT),
CLIENT(CoreDocument.STATE_SAVING_CLIENT),
SERVER(CoreDocument.STATE_SAVING_SERVER);
StateSaving(String stateSaving)
{
_stateSaving = stateSaving;
}
private String _stateSaving;
}
// TODO - we used to delegate to the RI when the stateManagement method was server,
// but we no longer do that, do we really need _delegate any more?
private final StateManager _delegate;
private AtomicReference<PseudoReferenceFactory<ViewRootState>> _viewRootStateRefFactoryHolder;
private static final Character _SUBKEY_SEPARATOR = new Character('.');
private static final int _DEFAULT_CACHE_SIZE = 15;
// base key used to identify the view cache. The window name, if any, is appended to this
private static final String _VIEW_CACHE_KEY =
"org.apache.myfaces.trinidadinternal.application.VIEW_CACHE";
// key to stash the per_page_state_saving during rendering
private static final String _PER_PAGE_STATE_SAVING =
"org.apache.myfaces.trinidadimpl.PER_PAGE_STATE_SAVING";
private static final String _CACHED_VIEW_STATE=
"org.apache.myfaces.trinidadinternal.application.CachedViewState";
private static final String _REQUEST_STATE_TOKEN_KEY =
"org.apache.myfaces.trinidadinternal.application.REQUEST_STATE_TOKEN";
private static final String _PINNED_STATE_TOKEN_KEY =
"org.apache.myfaces.trinidadinternal.application.PINNED_STATE_TOKEN";
private static final String _REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY =
"org.apache.myfaces.trinidadinternal.application.REUSE_REQUEST_TOKEN_FOR_RESPONSE";
// key for saving the toekn to the PageState for the last accessed view in this Session
private static final String _ACTIVE_PAGE_TOKEN_SESSION_KEY =
"org.apache.myfaces.trinidadinternal.application.StateManagerImp.ACTIVE_PAGE_STATE";
private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(StateManagerImpl.class);
}