org.apache.myfaces.trinidad.bean.util.StateUtils Maven / Gradle / Ivy
Show all versions of trinidad-api Show documentation
/*
* 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.trinidad.bean.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import javax.faces.component.StateHolder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;
import org.apache.myfaces.trinidad.bean.PropertyMap;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
/**
* Utilities for handling state persistance.
*/
public final class StateUtils
{
static
{
// initialize checks for serialization verification
boolean checkPropertyStateSerialization = false;
boolean checkComponentStateSerialization = false;
boolean checkComponentTreeStateSerialization = false;
boolean checkSessionSerialization = false;
boolean checkApplicationSerialization = false;
boolean checkMangedBeanMutation = false;
boolean checkAtEndRequest = false;
String checkSerializationProperty;
// determine if we need to aggressively check the serialization of components
// we wrap the check in a try/catch in case of weird ecurity managers
try
{
checkSerializationProperty =
System.getProperty("org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION");
}
catch (Throwable t)
{
checkSerializationProperty = null;
}
if (checkSerializationProperty != null)
{
checkSerializationProperty = checkSerializationProperty.toUpperCase();
// comma-separated list with allowed whitespace
String[] paramArray = checkSerializationProperty.split(",");
Set serializationFlags = new HashSet(Arrays.asList(paramArray));
if (!serializationFlags.contains("NONE"))
{
if (serializationFlags.contains("ALL"))
{
checkPropertyStateSerialization = true;
checkComponentStateSerialization = true;
checkComponentTreeStateSerialization = true;
checkSessionSerialization = true;
checkApplicationSerialization = true;
checkMangedBeanMutation = true;
checkAtEndRequest = true;
}
else
{
checkPropertyStateSerialization = serializationFlags.contains("PROPERTY");
checkComponentStateSerialization = serializationFlags.contains("COMPONENT");
checkComponentTreeStateSerialization = serializationFlags.contains("TREE");
checkSessionSerialization = serializationFlags.contains("SESSION");
checkApplicationSerialization = serializationFlags.contains("APPLICATION");
checkMangedBeanMutation = serializationFlags.contains("BEANS");
checkAtEndRequest = serializationFlags.contains("REQUEST");
}
}
}
_CHECK_PROPERTY_STATE_SERIALIZATION = checkPropertyStateSerialization;
_CHECK_COMPONENT_STATE_SERIALIZATION = checkComponentStateSerialization;
_CHECK_COMPONENT_TREE_STATE_SERIALIZATION = checkComponentTreeStateSerialization;
_CHECK_SESSION_SERIALIZATION = checkSessionSerialization;
_CHECK_APPLICATION_SERIALIZATION = checkApplicationSerialization;
_CHECK_MANAGED_BEAN_MUTATATION = checkMangedBeanMutation;
_CHECK_AT_END_REQUEST = checkAtEndRequest;
}
private static final boolean _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
private static final boolean _CHECK_COMPONENT_STATE_SERIALIZATION;
private static final boolean _CHECK_PROPERTY_STATE_SERIALIZATION;
private static final boolean _CHECK_SESSION_SERIALIZATION;
private static final boolean _CHECK_APPLICATION_SERIALIZATION;
private static final boolean _CHECK_MANAGED_BEAN_MUTATATION;
private static final boolean _CHECK_AT_END_REQUEST;
/**
* Returns true
if properties should be checked for
* serializability when when generating the view's state object.
*
* By default property state serialization checking is off. It can be
* enabled by setting the system property
* org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION
* to all
or, more rately, adding property
to the
* comma-separated list of serialization checks to perform.
*
* As property serialization checking is expensive, it is usually
* only enabled after component tree serialization checking has detected
* a problem
* @return
* @see #checkComponentStateSerialization
* @see #checkComponentTreeStateSerialization
*/
private static boolean _checkPropertyStateSerialization()
{
return _CHECK_PROPERTY_STATE_SERIALIZATION;
}
/**
* Returns true
if components should be checked for
* serializability when when generating the view's state object.
*
* By default component state serialization checking is off. It can be
* enabled by setting the system property
* org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION
* to all
or, more rarely, adding component
to the
* comma-separated list of serialization checks to perform.
*
* As property serialization checking is expensive, it is usually
* only enabled after component tree serialization checking has detected
* a problem. In addition, since component serialization checking only
* detects the problem component, it is usually combined with
* property state serialization checking either by specifying all
.
* @return
* @see #checkComponentTreeStateSerialization
*/
public static boolean checkComponentStateSerialization(FacesContext context)
{
return _CHECK_COMPONENT_STATE_SERIALIZATION;
}
/**
* Returns true
if the component tree should be checked for
* serializability when when generating the view's state object.
*
* By default component tree state serialization checking is off. It can be
* enabled by setting the system property
* org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION
* to all
or, more commonly, adding tree
to the
* comma-separated list of serialization checks to perform.
*
* Because unserializable objects defeat fail-over, it is important to
* check for serializability when testing applications. While component
* tree state serializability checking isn't cheap, it is much faster to
* initially only enable checking of the component tree and then switch
* to all
testing to determine the problem component and
* property when the component tree testing determines a problem.
* @return
* @see #checkComponentStateSerialization
*/
public static boolean checkComponentTreeStateSerialization(FacesContext context)
{
return _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
}
/**
* Returns true
if Object written to the ExternalContext's Session Map should be
* checked for Serializability when put
is called.
*
* Configuring this property allows this aspect of high-availability to be tested without
* configuring the server to run in high-availability mode.
*
* By default session serialization checking is off. It can be
* enabled by setting the system property
* org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION
* to all
or, more commonly, adding session
to the
* comma-separated list of serialization checks to perform.
* @return
* @see #checkComponentStateSerialization
* @see #checkComponentTreeStateSerialization
* @see #checkApplicationSerialization
* @see #checkManagedBeanMutation
*/
public static boolean checkSessionSerialization(ExternalContext extContext)
{
return _CHECK_SESSION_SERIALIZATION;
}
/**
* Returns true
if Object written to the ExternalContext's Application Map should be
* checked for Serializability when put
is called.
*
* Configuring this property allows this aspect of high-availability to be tested without
* configuring the server to run in high-availability mode.
*
* By default application serialization checking is off. It can be
* enabled by setting the system property
* org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION
* to all
or, more commonly, adding application
to the
* comma-separated list of serialization checks to perform.
* @return
* @see #checkComponentStateSerialization
* @see #checkComponentTreeStateSerialization
* @see #checkSessionSerialization
* @see #checkManagedBeanMutation
*/
public static boolean checkApplicationSerialization(ExternalContext extContext)
{
return _CHECK_APPLICATION_SERIALIZATION;
}
/**
* Returns true
if the attributes of the session and application Maps should be
* checked for cases where the attribute was mutated but not dirtied for failover. If
* checkSessionSerialization
returns true
, the contents of the
* Session should be checked. If checkApplicationSerialization
returns
* true
, the Serializable content of the Application should be checked.
* @return true if the contents of scopes should be checked for mutation without dirtying.
* @see #checkApplicationSerialization
* @see #checkSessionSerialization
*/
public static boolean checkManagedBeanMutation(ExternalContext extContext)
{
return _CHECK_MANAGED_BEAN_MUTATATION;
}
/**
* Returns true
if all attributes in the session Map should be
* checked for serializability at the end of each request. This check should
* only be performed if checkSessionSerialization
also returns true
.
* @see #checkSessionSerialization
*/
public static boolean checkScopesAtEndOfRequest(ExternalContext extContext)
{
return _CHECK_AT_END_REQUEST;
}
/**
* Persists a property key.
*/
static public Object saveKey(PropertyKey key)
{
int index = key.getIndex();
if (index < 0)
return key.getName();
if (index < 128)
return Byte.valueOf((byte) index);
return Integer.valueOf(index);
}
/**
* Restores a persisted PropertyKey.
*/
static public PropertyKey restoreKey(FacesBean.Type type, Object value)
{
PropertyKey key;
if (value instanceof Number)
{
key = type.findKey(((Number) value).intValue());
if (key == null)
throw new IllegalStateException(_LOG.getMessage(
"INVALID_INDEX"));
}
else
{
key = type.findKey((String) value);
if (key == null)
key = PropertyKey.createPropertyKey((String) value);
}
return key;
}
/**
* Generic (unoptimized) version of PropertyMap state saving.
*/
static public Object saveState(
PropertyMap map,
FacesContext context,
boolean useStateHolder)
{
int size = map.size();
if (size == 0)
return null;
Object[] values = new Object[2 * size];
int i = 0;
for(Map.Entry entry : map.entrySet())
{
PropertyKey key = entry.getKey();
if (key.isTransient())
{
// TRINIDAD-1956: due to the view root caching functionality, the transient properties
// may be retained too long. By removing the value here we can ensure that the next
// request will not have the transient values.
entry.setValue(null);
continue;
}
Object value = entry.getValue();
values[i] = saveKey(key);
if (_LOG.isFinest())
{
_LOG.finest("SAVE {" + key + "=" + value + "}");
}
Object saveValue;
if (useStateHolder)
saveValue = saveStateHolder(context, value);
else
saveValue = key.saveValue(context, value);
// aggressively check the serializability of the value
if (_checkPropertyStateSerialization())
{
try
{
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(saveValue);
}
catch (IOException e)
{
throw new RuntimeException(_LOG.getMessage("UNSERIALIZABLE_PROPERTY_VALUE",
new Object[]{saveValue, key, map}),
e);
}
}
values[i + 1] = saveValue;
i+=2;
}
return values;
}
/**
* Generic (unoptimized) version of PropertyMap state restoring.
*/
static public void restoreState(
PropertyMap map,
FacesContext context,
FacesBean.Type type,
Object state,
boolean useStateHolder)
{
if (state == null)
return;
Object[] values = (Object[]) state;
int size = values.length / 2;
for (int i = 0; i < size; i++)
{
Object savedKey = values[i * 2];
if (savedKey == null)
continue;
Object savedValue = values[i * 2 + 1];
PropertyKey key = restoreKey(type, savedKey);
Object value;
if (useStateHolder)
value = restoreStateHolder(context, savedValue);
else
value = key.restoreValue(context, savedValue);
if (_LOG.isFinest())
{
_LOG.finest("RESTORE {" + key + "=" + value + "}");
}
map.put(key, value);
}
}
/**
* Saves an object that may implement StateHolder.
*/
static public Object saveStateHolder(
FacesContext context,
Object value)
{
if (value == null)
return null;
Saver saver = null;
if (value instanceof StateHolder)
{
if (((StateHolder) value).isTransient())
return null;
saver = new SHSaver();
}
else if (value instanceof Serializable)
{
return value;
}
else
{
saver = new Saver();
}
if (saver != null)
saver.saveState(context, value);
return saver;
}
/**
* Restores an object that was saved using saveStateHolder()
*/
static public Object restoreStateHolder(
FacesContext context,
Object savedValue)
{
if (!(savedValue instanceof Saver))
return savedValue;
return ((Saver) savedValue).restoreState(context);
}
/**
* Saves a List whose elements may implement StateHolder.
*/
@SuppressWarnings("unchecked")
static public Object saveList(
FacesContext context,
Object value)
{
if (value == null)
return null;
List