Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.myfaces.trinidadinternal.config.CheckSerializationConfigurator Maven / Gradle / Ivy
Go to download
Private implementation of the Apache MyFaces Trinidad project
/*
* 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.config;
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.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.faces.context.ExternalContext;
import javax.faces.context.ExternalContextWrapper;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import org.apache.myfaces.trinidad.bean.util.StateUtils;
import org.apache.myfaces.trinidad.config.Configurator;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
import org.apache.myfaces.trinidad.util.CollectionUtils;
import org.apache.myfaces.trinidad.util.CollectionUtils.MapMutationHooks;
import org.apache.myfaces.trinidad.util.TransientHolder;
import org.apache.myfaces.trinidadinternal.context.external.ServletApplicationMap;
import org.apache.myfaces.trinidadinternal.util.ObjectInputStreamResolveClass;
/**
* Configurator that uses both wrapped ExternalContext (for Portlet cases) and wrapped
* ServletContext and HttpSession (for HttpServlet cases) to validate that
* only Serializable Objects are placed in the Sesssion Map and that mutations to the
* Session and ApplicationMap content dirty the entries.
* @version $Revision$ $Date$
*/
public final class CheckSerializationConfigurator extends Configurator
{
/**
* Override to return our ExternalContext wrapped if session serialization checking is enabled
* @param extContext
* @return
*/
@Override
public ExternalContext getExternalContext(ExternalContext extContext)
{
// retrieve our wrapped ExternalContext, creating it if necessary. We wrap the external
// context to trap calls to retrieve the Session and Application Maps in the portal case.
// For the HttpServlet case, we rely on
ExternalContext checkingContext =
SerializationCheckingWrapper.getSerializationWrapper(extContext, true);
if (checkingContext != null)
{
return checkingContext;
}
else
{
return extContext;
}
}
/**
* Check if any of the non-dirtied checked managed beans have been mutated in this request
* @param extContext
*/
@Override
public void endRequest(ExternalContext extContext)
{
// get the wrapper without creating it if it doesn't already exist
SerializationCheckingWrapper checkingWrapper =
SerializationCheckingWrapper.getSerializationWrapper(extContext, false);
if (checkingWrapper != null)
{
checkingWrapper.checkForMutations();
}
}
/**
* Returns the FilterConfig to use for initializing the filters so that we can wrap it
* if necessary
* @param filterConfig
* @return
*/
public static FilterConfig getFilterConfig(FilterConfig filterConfig)
{
// skankily don't pass the ExternalContext since we don't have one when the filters are
// initialized. This only really works because checkApplicvationSerialization doesn't
// need the ExternalContext
if (StateUtils.checkApplicationSerialization(null))
{
return new FilterConfigWrapper(filterConfig);
}
else
{
return filterConfig;
}
}
/**
* Returns the HttpServletRequest to use for this request, so that we can wrap it if
* necessary.
* @param extContext
* @param request
* @return
*/
public static HttpServletRequest getHttpServletRequest(
ExternalContext extContext,
HttpServletRequest request)
{
SerializationChecker checker = SerializationChecker.getSerializationChecker(extContext, true);
if (checker != null)
{
return checker.getWrappedRequest(request);
}
else
{
return request;
}
}
/**
* Unregisters the checking of the specified session attribute
* @param external ExternalContext
* @param key Name of session attribute to unregister
*/
public static void unregisterSessionAttribute(ExternalContext external, String key)
{
SerializationChecker checker = SerializationChecker.getSerializationChecker(external, false);
if (checker != null)
{
checker.unregisterSessionAttribute(external, key);
}
}
/**
* Unregisters the checking of the specified application attribute
* @param external ExternalContext
* @param key Name of session attribute to unregister
*/
public static void unregisterApplicationAttribute(ExternalContext external, String key)
{
SerializationChecker checker = SerializationChecker.getSerializationChecker(external, false);
if (checker != null)
{
checker.unregisterApplicationAttribute(external, key);
}
}
/**
* Returns the list of in-flight MutatedBeanCheckers for the specified Map and its associated
* lock object
* @param checkedMap Map checked by the MutatedBeanCheckers
* @param mapWriteLock Lock object to synchronize on when mutatating the amp
* @return
*/
private static List _getMutatedBeanList(
Map checkedMap,
Object mapWriteLock)
{
Object holder = checkedMap.get(_CHECKED_MAPS_KEY);
TransientHolder> listHolder;
List mutatedBeans = null;
if (holder != null)
{
listHolder = (TransientHolder>)holder;
mutatedBeans = listHolder.getValue();
}
if (mutatedBeans == null)
{
// make sure that the list is only created once per map
synchronized(mapWriteLock)
{
// check again in case we value was written by the previous lock holder
holder = checkedMap.get(_CHECKED_MAPS_KEY);
if (holder != null)
{
listHolder = (TransientHolder>)holder;
mutatedBeans = listHolder.getValue();
}
if (mutatedBeans == null)
{
// mutations to the list itself need to be thread-safe
mutatedBeans = new CopyOnWriteArrayList();
// use a TransientHolder since we don't care if this gets failed over
checkedMap.put(_CHECKED_MAPS_KEY, TransientHolder.newTransientHolder(mutatedBeans));
}
}
}
return mutatedBeans;
}
/**
* Remove the modified key from all of the in-flight bean checking requests
* @param beanCheckers
* @param key
*/
private static void _notifyBeanCheckersOfChange(
List beanCheckers,
Object key)
{
for (MutatedBeanChecker beanChecker : beanCheckers)
{
beanChecker._unmutatedKeyValues.remove(key);
}
}
/**
* Serialize an object and return the byte[]
* @param objectToSerialize
* @return
* @throws IOException
*/
private static byte[] _serialize(Object objectToSerialize) throws IOException
{
ByteArrayOutputStream outputByteStream = new ByteArrayOutputStream();
new ObjectOutputStream(outputByteStream).writeObject(objectToSerialize);
return outputByteStream.toByteArray();
}
/**
* Extract the detailed messages for a serialization failure
* @param keyMessage
* @param value
* @return
*/
private static String _extractFailureString(String keyMessage, Object value)
{
List failureStack = new ArrayList();
failureStack.add(keyMessage);
failureStack.add(value);
_extractFailure(failureStack, value);
String joinMessage = _LOG.getMessage("EXTRACT_SERIALIZATION_DETAIL");
StringBuilder message = new StringBuilder();
Iterator failures = failureStack.iterator();
do
{
String keyInfo = (String)failures.next();
Object failedValue = failures.next();
message.append(keyInfo);
message.append(joinMessage);
message.append(failedValue);
if (failures.hasNext())
message.append(", ");
else
break;
} while (true);
return message.toString();
}
/**
* Extract the detailed causes of a serialization failure onto the failureStack
* @param failureStack
* @param failedObject
*/
private static void _extractFailure(
List failureStack,
Object failedObject)
{
if (failedObject instanceof Map)
{
_MAP_EXTRACTOR.extractFailure(failureStack, (Map)failedObject);
}
else if (failedObject instanceof List)
{
_LIST_EXTRACTOR.extractFailure(failureStack, (List)failedObject);
}
else if (failedObject instanceof Collection)
{
_COLLECTION_EXTRACTOR.extractFailure(failureStack, (List)failedObject);
}
else if (failedObject instanceof Object[])
{
_ARRAY_EXTRACTOR.extractFailure(failureStack, (Object[])failedObject);
}
}
private static ServletContext _getServletContextProxy(ServletContext orig, Map map)
{
return (ServletContext) Proxy.newProxyInstance(ClassLoaderUtils.getContextClassLoader(),
new Class[] {ServletContext.class},
new ServletContextMonitorInvocationHandler(orig, map));
}
/**
* Wraps the FilterConfig so that we can wrap the ServletContext that it returns so that
* we can trap calls the setting and removing ServletContext attributes. Phew!
*/
private static class FilterConfigWrapper implements FilterConfig
{
FilterConfigWrapper(FilterConfig filterConfig)
{
_delegate = filterConfig;
// create ServletContext wrapper to catch sets and removes from the ServletContext
_wrappedContext = _getServletContextProxy(filterConfig.getServletContext(), null);
}
public String getFilterName()
{
return _delegate.getFilterName();
}
public ServletContext getServletContext()
{
return _wrappedContext;
}
public String getInitParameter(String paramName)
{
return _delegate.getInitParameter(paramName);
}
public Enumeration getInitParameterNames()
{
return _delegate.getInitParameterNames();
}
private final FilterConfig _delegate;
private final ServletContext _wrappedContext;
}
/**
* ExternalContextWrapper that returns wrapped versions of the Session and ApplicationMaps that
* we can track changes to. This is needed for the Portlet case. For the HttpServel case,
* it is redundant with the ServletContext and Session wrapping but shouldn't do any harm.
*/
private static class SerializationCheckingWrapper extends ExternalContextWrapper
{
/**
* Retrieves the current SerializationCheckingWrapper for this request
* @param extContext
* @param create If true
, create the SerializationCheckingWrapper for this
* request if it doesn't already exist.
* @return
*/
public static SerializationCheckingWrapper getSerializationWrapper(
ExternalContext extContext,
boolean create)
{
// get the SerializationCheckingWrapper for this request
Map requestMap = extContext.getRequestMap();
Object wrapper = requestMap.get(_SERIALIZATION_WRAPPER_KEY);
if (wrapper != null)
{
return (SerializationCheckingWrapper)wrapper;
}
else if (create)
{
// create the wrapper for this request and store it on the request so that we don't
// recreate it
SerializationChecker checker = SerializationChecker.getSerializationChecker(extContext,
create);
if (checker != null)
{
SerializationCheckingWrapper checkingWrapper = new SerializationCheckingWrapper(extContext,
checker);
requestMap.put(_SERIALIZATION_WRAPPER_KEY, checkingWrapper);
return checkingWrapper;
}
}
return null;
}
/**
* Create a SerializationCheckingWrapper
* @param extContext ExternalContext to wrap
* @param checker SerializationChecker to call back on mutations
*/
private SerializationCheckingWrapper(
ExternalContext extContext,
SerializationChecker checker)
{
_extContext = extContext;
_checker = checker;
}
/**
* Check for mutations to beans trancked by the SerializationCheckingWrapper
*/
public void checkForMutations()
{
_checker.checkForMutations();
}
@Override
public ExternalContext getWrapped()
{
return _extContext;
}
/**
* Override to delegate to the SerializationChecker
* @return
*/
@Override
public Map getSessionMap()
{
return _checker.getSessionMap();
}
/**
* Override to delegate to the SerializationChecker
* @return
*/
@Override
public Map getApplicationMap()
{
return _checker.getApplicationMap();
}
private static final String _SERIALIZATION_WRAPPER_KEY =
CheckSerializationConfigurator.class.getName() + "#WRAPPER";
private final ExternalContext _extContext;
private final SerializationChecker _checker;
}
/**
* Checks a Map for mutations to the contents of its Serializable attributes that weren't
* dirtied in the current request.
* The mutations are checked by snapshotting the serialized bytes of the current Serializable
* values of the Map at the beginning of the request. We then catch all puts and removes to
* the Map across any request and remove those entries, since they have been correctly dirtied.
* At the end of the request. checkForMutations
is called and we iterate over
* the remaining entries and compared their serialized bytes against the serialized bytes
* of the current values. If the bytes are different, we assume that some part of the
* attribute's object subtree has been changed without appropriately dirtying it and we
* log an errror.
* @see #checkForMutations
*/
private static class MutatedBeanChecker implements MapMutationHooks
{
/**
* Creates a MutatedBeanChecker to check for undirtied mutations to the Serialized state
* of a Map in the current request.
* @param checkedMap Map containing attributes to check for mutations
* @param mapName Name of map used when logging
* @param mapLock Lock to use when mutating the map
* @param requireSerialization true
if all of the attributes are required to be
* Serializable (Sesssion Map attributes are. Application Map
* aren't)
*/
public MutatedBeanChecker(
Map checkedMap,
String mapName,
Object mapLock,
boolean requireSerialization)
{
_checkedMap = checkedMap;
_mapName = mapName;
_mapLock = mapLock;
// snapshot the initial serialized bytes of the mutable values in the Map so that we
// can compare them at the end of the request to see if they have changed
_unmutatedKeyValues = new ConcurrentHashMap(checkedMap.size() * 2);
_unmutatedKeyValues.putAll(checkedMap);
// register this request with the list of checkers for this map.
// I think there is a possible race condition between the keys being added to the map and
// our registering ourselves as listening for puts and removes made by other requests.
// This makes me sad
_getMutatedBeanList().add(this);
// loop through the map's valid keys getting the serialized bytes
for (String key : _unmutatedKeyValues.keySet())
{
Object value = checkedMap.get(key);
// get the serialized bytes for this key. If the key's vale isn't serializable or is
// immutable, this will be an empty array and we won't need to check it
byte[] serializedBytes = _getSerializedValue(key, value, requireSerialization);
if (serializedBytes.length > 0)
{
// save the bytes for comparing at the end of the request
_unmutatedKeyValues.put(key, serializedBytes);
}
else
{
// either not serializable or immutable, so we don't need to worry about checking it
_unmutatedKeyValues.remove(key);
}
}
}
/**
* Unregisters the checking of the specified attribute
* @param key Name of session attribute to unregister
*/
public void unregisterAttribute(String key)
{
CheckSerializationConfigurator._notifyBeanCheckersOfChange(_getMutatedBeanList(), key);
}
/** Implement to catch writes to the Map, since that means that the Map has been dirtied */
@Override
public void writeNotify(Map map, String key, Object value)
{
unregisterAttribute(key);
}
/** Implement to catch remove from the Map, since that means the attribute won't
* be failed over
*/
@Override
public void removeNotify(Map map, Object key)
{
unregisterAttribute((String)key);
}
/** Implement to catch clear of Map, since that means the attributes won't
* be failed over
*/
@Override
public void clearNotify(Map map)
{
// clear all of the keys across all of the beans
for (String key : map.keySet())
{
unregisterAttribute(key);
}
}
/**
* Clear all of the values we were checking. This is called if the Session is invalidated,
* for example.
*/
public void clearCheckedValues()
{
for (MutatedBeanChecker beanChecker : _getMutatedBeanList())
{
beanChecker._unmutatedKeyValues.clear();
}
}
/**
* Check for any undirtied mutations of this map, logging severe messages if there are
*/
public void checkForMutations()
{
// loop through the unmodified items in the Map and verify that the curent
// Serialized values haven't changed
for (Map.Entry checkedEntry : _unmutatedKeyValues.entrySet())
{
String key = checkedEntry.getKey();
Object currValue = _checkedMap.get(key);
byte[] currentBytes = _getSerializedValue(key, currValue, false);
byte[] oldBytes = (byte[])checkedEntry.getValue();
// check if the bytes are different
if (!Arrays.equals(oldBytes, currentBytes))
{
// deserialize the original object so we can dump it out (hopefully it has a
// good toString()) We also do this so we can
Object oldValue = _deserializeObject(oldBytes);
// deserialize the new bytes so that we are comparing two deserialized objects
Object newValue = _deserializeObject(currentBytes);
// This doesn't do anything, but is a handy comparison of debugging when things go awry
oldValue.equals(newValue);
// build up the message
String message = _LOG.getMessage("SERIALIZABLE_ATTRIBUTE_MUTATED",
new Object[]{_mapName, key, oldValue, newValue});
// Log message because user might not notice exception since we are at the end of the
// request
_LOG.severe(message);
}
}
// we no longer need to track changes to the map
_getMutatedBeanList().remove(this);
}
/**
* Given the serialized bytes of an Object, return the Object itself
* @param serializedBytes
* @return
*/
private Object _deserializeObject(byte[] serializedBytes)
{
Object deserializedObject;
try
{
// copy the bytes before passing to the ByteArrayInputStream, since it mutates
// the array
byte[] copyBytes = Arrays.copyOf(serializedBytes, serializedBytes.length);
ByteArrayInputStream baos = new ByteArrayInputStream(copyBytes);
ObjectInputStream ois = new ObjectInputStreamResolveClass(baos);
deserializedObject = ois.readObject();
ois.close();
}
catch (IOException e)
{
throw new IllegalArgumentException(e);
}
catch (ClassNotFoundException e)
{
throw new IllegalArgumentException(e);
}
return deserializedObject;
}
/**
* Returns the List of MutatedBeanCheckers across all in flight requests listening for changes
* to this Map
* @return
*/
private List _getMutatedBeanList()
{
return CheckSerializationConfigurator._getMutatedBeanList(_checkedMap, _mapLock);
}
/**
* Returns the serialized value of the object as a byte[] or an empty array if the object is
* immutable and therefore doesn't need to be checked
* @param key Key in map to get the serialized value of
* @param value Value in map to serialize
* @param requireSerialization true
if this value is required to be serializable
* @return The serialized bytes for the Object if serializable and mutable
* @throws IllegalStateException if the Object is not serializable or serialization fails
*/
private byte[] _getSerializedValue(String key, Object value, boolean requireSerialization)
{
if (value == null)
return _EMPTY_BYTE_ARRAY;
Class valueClass = value.getClass();
// check against the list of classes to ignore for performance reasons
if (_INGNORE_CLASS_NAMES.contains(valueClass.getName()))
return _EMPTY_BYTE_ARRAY;
if (!(value instanceof Serializable))
{
if (requireSerialization)
{
String message = _LOG.getMessage("ATTRIBUTE_NOT_SERIALIZABLE",
new Object[]{_mapName, key, valueClass});
_LOG.severe(message);
}
return _EMPTY_BYTE_ARRAY;
}
// verify that the contents of the value are in fact Serializable
try
{
return _serialize(value);
}
catch (Throwable e)
{
if (requireSerialization)
{
String baseMessage = _LOG.getMessage("ATTRIBUTE_SERIALIZATION_FAILED_KEY_VALUE",
new Object[]{_mapName, key});
String message = _extractFailureString(baseMessage, value);
_LOG.severe(message, e);
}
return _EMPTY_BYTE_ARRAY;
}
}
private static final byte[] _EMPTY_BYTE_ARRAY = new byte[0];
private static final Set _INGNORE_CLASS_NAMES;
static
{
// initialize Set of class names to ignore for Serialization tracking because they
// are immutable or not serialiable
String[] classNames = new String[]
{
"java.lang.Boolean", // immutable
"java.lang.Character", // immutable
"java.lang.Double", // immutable
"java.lang.Float", // immutable
"java.lang.Integer", // immutable
"java.lang.Long", // immutable
"java.lang.Short", // immutable
"java.lang.String", // immutable
"java.math.BigDecimal", // immutable
"java.math.BigInteger", // immutable
"org.apache.myfaces.trinidad.util.TransientHolder" // Not serializable
};
_INGNORE_CLASS_NAMES = new HashSet();
_INGNORE_CLASS_NAMES.addAll(Arrays.asList(classNames));
}
private static final TrinidadLogger _LOG =
TrinidadLogger.createTrinidadLogger(MutatedBeanChecker.class);
private final Map _unmutatedKeyValues;
private final Map _checkedMap;
private final String _mapName;
private final Object _mapLock;
}
/**
* Wraps the ServletContext so that we can catch modifications to the attributes. This
* had to be changed to support a dynmaic proxy so that it could be use with both
* Server 2.1 and Servlet 3.0 Specifications.
*/
private static final class ServletContextMonitorInvocationHandler implements InvocationHandler
{
public ServletContextMonitorInvocationHandler(ServletContext context, Map appMap)
{
_delegate = context;
// if we already have an Application Map, use it, otherwise create a wrapper around
// the ServletContext
if (appMap != null)
{
_applicationMap = appMap;
}
else
{
_applicationMap = new ServletApplicationMap(context);
}
}
@Override
public Object invoke(Object object, Method method, Object[] objects) throws Throwable
{
//Notifys the system that an attribute has changed on set or remove
String name = method.getName();
//This method delegates all methods to the delegate
Object result = method.invoke(_delegate, objects);
if("setAttribute".equals(name) || "removeAttribute".equals(name))
{
_notifyBeanCheckersOfChange(_getMutatedBeanList(), objects[0]);
}
return result;
}
private List _getMutatedBeanList()
{
return CheckSerializationConfigurator._getMutatedBeanList(_applicationMap, _delegate);
}
private final ServletContext _delegate;
private final Map _applicationMap;
}
/**
* Performs any configured serialization checking of the Session or Application Maps including
* whether the contents are Serializable and whether the contents have changed.
*/
private static class SerializationChecker
{
/**
* Get the current SerializaionChecker for this request, potentially creating one
* @param extContext ExternalContext to use to create the SerializationChecker
* @param create If true
a SerializationChecker will be created and registered
* for this request if one does not alreadfy exist.
* @return
*/
public static SerializationChecker getSerializationChecker(
ExternalContext extContext,
boolean create)
{
Map requestMap = extContext.getRequestMap();
Object checker = requestMap.get(_SERIALIZATION_CHECKER_KEY);
if (checker != null)
{
return (SerializationChecker)checker;
}
else if (create)
{
boolean checkSession = StateUtils.checkSessionSerialization(extContext);
boolean checkApplication = StateUtils.checkApplicationSerialization(extContext);
boolean checkManagedBeanMutation = StateUtils.checkManagedBeanMutation(extContext);
// check the possible conditions under which we would need to create a SerializationChecker
if (checkSession || checkApplication || checkManagedBeanMutation)
{
boolean checkSessionAtEnd = StateUtils.checkScopesAtEndOfRequest(extContext);
SerializationChecker serializationChecker = new SerializationChecker(
extContext,
checkSession,
checkApplication,
checkManagedBeanMutation,
checkSessionAtEnd);
requestMap.put(_SERIALIZATION_CHECKER_KEY, serializationChecker);
return serializationChecker;
}
}
return null;
}
/**
* Creates a SerializationChecker for this request
* @param extContext ExternalContext to use to initialize the SerializationChecker
* @param checkSession If true check serializability of session attributes
* @param checkApplication if true, check serializability of application attributes
* @param checkManagedBeanMutation if true, check for mutations to attributes in the session
* if checkSession is true and the application if
* checkApplication is true.
*/
private SerializationChecker(
ExternalContext extContext,
boolean checkSession,
boolean checkApplication,
boolean checkManagedBeanMutation,
boolean checkSessionAtEnd)
{
Map sessionMap = extContext.getSessionMap();
Map applicationMap = extContext.getApplicationMap();
if (checkManagedBeanMutation)
{
// note that the mutated bean checekd implicitly checks for attribute serialization as well.
_sessionBeanChecker = new MutatedBeanChecker(sessionMap,
"Session",
extContext.getSession(true),
true);
sessionMap = CollectionUtils.newMutationHookedMap(sessionMap, _sessionBeanChecker);
// only check the application for mutations if the application checking is enabled
if (checkApplication)
{
_applicationBeanChecker = new MutatedBeanChecker(applicationMap,
"Application",
extContext.getContext(),
false);
applicationMap = CollectionUtils.newMutationHookedMap(applicationMap,
_applicationBeanChecker);
}
else
{
_applicationBeanChecker = null;
}
}
else
{
_sessionBeanChecker = null;
_applicationBeanChecker = null;
if (checkSession)
{
sessionMap = CollectionUtils.getCheckedSerializationMap(sessionMap, true);
}
if (checkApplication)
{
applicationMap = CollectionUtils.getCheckedSerializationMap(applicationMap, false);
}
}
_sessionMap = sessionMap;
_applicationMap = applicationMap;
_checkSessionAttrs = checkSessionAtEnd;
}
/**
* Unregisters the checking of the specified session attribute
* @param external ExternalContext
* @param key Name of session attribute to unregister
*/
public void unregisterSessionAttribute(ExternalContext external, String key)
{
SerializationChecker checker = SerializationChecker.getSerializationChecker(external, false);
if (checker != null)
{
if (_sessionBeanChecker != null)
{
_sessionBeanChecker.unregisterAttribute(key);
}
}
}
/**
* Unregisters the checking of the specified session attribute
* @param external ExternalContext
* @param key Name of session attribute to unregister
*/
public void unregisterApplicationAttribute(ExternalContext external, String key)
{
SerializationChecker checker = SerializationChecker.getSerializationChecker(external, false);
if (checker != null)
{
if (_applicationBeanChecker != null)
{
_applicationBeanChecker.unregisterAttribute(key);
}
}
}
/**
* Return a wrapped HttpServletRequest if necessary to implement the checking features
* @param request
* @return
*/
public HttpServletRequest getWrappedRequest(HttpServletRequest request)
{
if (_sessionBeanChecker != null)
{
return new SessionBeanTracker(request, _sessionBeanChecker, _sessionMap, _applicationMap);
}
else
{
return request;
}
}
/**
* Returns the potentially wrapped Session Map
* @return
*/
public Map getSessionMap()
{
return _sessionMap;
}
/**
* Returns the potentially wrapped Application Map
* @return
*/
public Map getApplicationMap()
{
return _applicationMap;
}
/**
* Check the session and application for mutations if configured to do so
*/
public void checkForMutations()
{
if (_sessionBeanChecker != null)
_sessionBeanChecker.checkForMutations();
if (_applicationBeanChecker != null)
_applicationBeanChecker.checkForMutations();
// check that all of the attributes in the Session are Serializable
if (_checkSessionAttrs)
{
for (Map.Entry attrEntry : _sessionMap.entrySet())
{
String key = attrEntry.getKey();
Object value = attrEntry.getValue();
try
{
_serialize(value);
}
catch (Throwable e)
{
try
{
String sessionAttributeString = _LOG.getMessage("SESSION_SERIALIZATION_ATTRIBUTE", key);
String failureDetails = _extractFailureString(sessionAttributeString, value);
String message = _LOG.getMessage("SERIALIZATION_TESTING_FAILURE", failureDetails);
_LOG.severe(message);
}
catch (Throwable ee)
{
String failureMessage = _LOG.getMessage("SERIALIZATION_LOGGING_FAILURE");
_LOG.severe(failureMessage, ee);
}
}
}
}
}
/**
* Wraps the HttpServletRequest so that we can return a wrapped Session so that we can catch
* changes to the Session attributes and/or return a wrapped ServletContext so that we can
* catch changes to the SevletContext attributes.
*/
private static class SessionBeanTracker extends HttpServletRequestWrapper
{
public SessionBeanTracker(
HttpServletRequest request,
MutatedBeanChecker sessionBeanChecker,
Map sessionMap,
Map applicationMap)
{
super(request);
_wrappedSession = new SessionWrapper(request.getSession(),
sessionBeanChecker,
sessionMap,
applicationMap);
}
@Override
public HttpSession getSession()
{
return _wrappedSession;
}
@Override
public HttpSession getSession(boolean p1)
{
return _wrappedSession;
}
/**
* Wraps the HttpSession sso that we can catch
* changes to the Session attributes and/or return a wrapped ServletContext so that we can
* catch changes to the SevletContext attributes.
*/
private static final class SessionWrapper implements HttpSession
{
SessionWrapper(
HttpSession session,
MutatedBeanChecker sessionChecker,
Map sessionMap,
Map applicationMap)
{
_delegate = session;
_sessionChecker = sessionChecker;
_sessionMap = sessionMap;
// determine whether we need to return a wrapped ServletContext as well
if (applicationMap != null)
{
_wrappedContext = _getServletContextProxy(session.getServletContext(), applicationMap);
}
else
{
_wrappedContext = null;
}
}
public long getCreationTime()
{
return _delegate.getCreationTime();
}
public String getId()
{
return _delegate.getId();
}
public long getLastAccessedTime()
{
return _delegate.getLastAccessedTime();
}
public ServletContext getServletContext()
{
if (_wrappedContext != null)
{
return _wrappedContext;
}
else
{
return _delegate.getServletContext();
}
}
public void setMaxInactiveInterval(int maxInterval)
{
_delegate.setMaxInactiveInterval(maxInterval);
}
public int getMaxInactiveInterval()
{
return _delegate.getMaxInactiveInterval();
}
public HttpSessionContext getSessionContext()
{
return _delegate.getSessionContext();
}
public Object getAttribute(String attrName)
{
return _delegate.getAttribute(attrName);
}
public Object getValue(String attrName)
{
return _delegate.getValue(attrName);
}
public Enumeration getAttributeNames()
{
return _delegate.getAttributeNames();
}
public String[] getValueNames()
{
return _delegate.getValueNames();
}
/**
* Implement to delegate and inform the sessionChecker that the attribute is dirty
* @param key
* @param value
*/
public void setAttribute(String key, Object value)
{
_delegate.setAttribute(key, value);
_sessionChecker.writeNotify(_sessionMap, key, value);
}
/**
* Implement to delegate and inform the sessionChecker that the attribute is dirty
* @param key
* @param value
*/
public void putValue(String key, Object value)
{
_delegate.putValue(key, value);
_sessionChecker.writeNotify(_sessionMap, key, value);
}
/**
* Implement to delegate and inform the sessionChecker that the attribute is dirty
* @param key
*/
public void removeAttribute(String key)
{
_delegate.removeAttribute(key);
_sessionChecker.removeNotify(_sessionMap, key);
}
/**
* Implement to delegate and inform the sessionChecker that the attribute is dirty
* @param key
*/
public void removeValue(String key)
{
_delegate.removeValue(key);
_sessionChecker.removeNotify(_sessionMap, key);
}
/**
* Implement to delegate and inform the sessionChecker that all atrributes are dirty since
* the session has been blown away
*/
public void invalidate()
{
_delegate.invalidate();
_sessionChecker.clearCheckedValues();
}
public boolean isNew()
{
return _delegate.isNew();
}
private final HttpSession _delegate;
private final MutatedBeanChecker _sessionChecker;
private final Map _sessionMap;
private final ServletContext _wrappedContext;
}
private final HttpSession _wrappedSession;
}
private static final TrinidadLogger _LOG =
TrinidadLogger.createTrinidadLogger(SerializationChecker.class);
private final MutatedBeanChecker _sessionBeanChecker;
private final MutatedBeanChecker _applicationBeanChecker;
private final Map _sessionMap;
private final Map _applicationMap;
private final boolean _checkSessionAttrs;
}
/**
* Class for extracting more detailed failure information for a particular object type
* that has failed Serialization
*
* @param
*/
private static abstract class SerializationFailureExtractor
{
/**
* Called to extract failure information about the failedObject onto the
* failureStack
*/
public abstract void extractFailure(List failureStack, T faileddObject);
}
/**
* Extract information about failures in Maps
*/
private static class MapExtractor extends SerializationFailureExtractor>
{
public void extractFailure(List failureStack, Map failedMap)
{
for (Map.Entry entry : failedMap.entrySet())
{
Object key = entry.getKey();
try
{
_serialize(key);
}
catch (Throwable e)
{
failureStack.add("Failed map key:" + key);
failureStack.add(key);
_extractFailure(failureStack, key);
}
Object value = entry.getValue();
try
{
_serialize(value);
}
catch (Throwable e)
{
failureStack.add("Failed map value for key=" + key + " value:" + value);
failureStack.add(value);
_extractFailure(failureStack, value);
}
}
}
}
/**
* Extract information about serialization failures in Lists
*/
private static class ListExtractor extends SerializationFailureExtractor>
{
public void extractFailure(List failureStack, List failedList)
{
int size = failedList.size();
for (int i = 0; i < size; i++)
{
Object value = failedList.get(i);
try
{
_serialize(value);
}
catch (Throwable e)
{
failureStack.add("Failed List value for index=" + i + " value:" + value);
failureStack.add(value);
_extractFailure(failureStack, value);
}
}
}
}
/**
* Extract information about serialization failures in Arrays
*/
private static class ArrayExtractor extends SerializationFailureExtractor
{
public void extractFailure(List failureStack, Object[] failedArray)
{
int size = failedArray.length;
for (int i = 0; i < size; i++)
{
Object value = failedArray[i];
try
{
_serialize(value);
}
catch (Throwable e)
{
failureStack.add("Failed array value for index=" + i + " value:" + value);
failureStack.add(value);
_extractFailure(failureStack, value);
}
}
}
}
/**
* Extract information about serialization failures in Collections
*/
private static class CollectionExtractor extends SerializationFailureExtractor>
{
public void extractFailure(List failureStack, Collection failedCollection)
{
for (Object value : failedCollection)
{
try
{
_serialize(value);
}
catch (Throwable e)
{
failureStack.add("Failed Collection value:" + value);
failureStack.add(value);
_extractFailure(failureStack, value);
}
}
}
}
private static final TrinidadLogger _LOG =
TrinidadLogger.createTrinidadLogger(CheckSerializationConfigurator.class);
private static final MapExtractor _MAP_EXTRACTOR = new MapExtractor();
private static final ListExtractor _LIST_EXTRACTOR = new ListExtractor();
private static final CollectionExtractor _COLLECTION_EXTRACTOR = new CollectionExtractor();
private static final ArrayExtractor _ARRAY_EXTRACTOR = new ArrayExtractor();
private static final String _CHECKED_MAPS_KEY = MutatedBeanChecker.class.getName() +"#MAPS";
private static final String _SERIALIZATION_CHECKER_KEY =
CheckSerializationConfigurator.class.getName() + "#CHECKER";
}