You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bs...@apache.org on 2010/10/28 02:39:07 UTC

svn commit: r1028148 - in /myfaces/trinidad/branches/1.2.12.3-branch: trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ trinidad-impl/src/main/java/org/apache/myfaces/trinidad...

Author: bsullivan
Date: Thu Oct 28 00:39:07 2010
New Revision: 1028148

URL: http://svn.apache.org/viewvc?rev=1028148&view=rev
Log:
[Trinidad-1858] Fix fail-over bugs where session state is mutated without dirtying the Session objects

Backport to 1.2.12.3

Modified:
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/CollectionUtils.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/PageFlowScopeMap.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/webapp/TrinidadFilterImpl.java
    myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java Thu Oct 28 00:39:07 2010
@@ -57,6 +57,7 @@ public final class StateUtils
     boolean checkComponentTreeStateSerialization = false;
     boolean checkSessionSerialization = false;
     boolean checkApplicationSerialization = false;
+    boolean checkMangedBeanMutation = false;
 
     String checkSerializationProperty;
 
@@ -90,6 +91,7 @@ public final class StateUtils
           checkComponentTreeStateSerialization = true;
           checkSessionSerialization = true;
           checkApplicationSerialization = true;
+          checkMangedBeanMutation = true;
         }
         else
         {
@@ -98,6 +100,7 @@ public final class StateUtils
           checkComponentTreeStateSerialization = serializationFlags.contains("TREE");       
           checkSessionSerialization = serializationFlags.contains("SESSION");
           checkApplicationSerialization = serializationFlags.contains("APPLICATION");
+          checkMangedBeanMutation = serializationFlags.contains("BEANS");
         }
       }
     }
@@ -107,6 +110,7 @@ public final class StateUtils
     _CHECK_COMPONENT_TREE_STATE_SERIALIZATION = checkComponentTreeStateSerialization;
     _CHECK_SESSION_SERIALIZATION = checkSessionSerialization;
     _CHECK_APPLICATION_SERIALIZATION = checkApplicationSerialization;
+    _CHECK_MANAGED_BEAN_MUTATATION = checkMangedBeanMutation;
   }
 
   private static final boolean _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
@@ -114,6 +118,7 @@ public final class StateUtils
   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;
 
   /**
    * Returns <code>true</code> if properties should be checked for
@@ -200,6 +205,7 @@ public final class StateUtils
    * @see #checkComponentStateSerialization
    * @see #checkComponentTreeStateSerialization
    * @see #checkApplicationSerialization
+   * @see #checkManagedBeanMutation
    */
   public static boolean checkSessionSerialization(ExternalContext extContext)
   {
@@ -222,6 +228,7 @@ public final class StateUtils
    * @see #checkComponentStateSerialization
    * @see #checkComponentTreeStateSerialization
    * @see #checkSessionSerialization
+   * @see #checkManagedBeanMutation
    */
   public static boolean checkApplicationSerialization(ExternalContext extContext)
   {
@@ -229,6 +236,22 @@ public final class StateUtils
   }
 
   /**
+   * Returns <code>true</code> 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
+   * <code>checkSessionSerialization</code> returns <code>true</code>, the contents of the
+   * Session should be checked.  If <code>checkApplicationSerialization</code> returns
+   * <code>true</code>, 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;
+  }
+  
+    
+  /**
    * Persists a property key.
    */
   static public Object saveKey(PropertyKey key)
@@ -591,6 +614,3 @@ public final class StateUtils
 
 }
 
-
-
-

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/CollectionUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/CollectionUtils.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/CollectionUtils.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/CollectionUtils.java Thu Oct 28 00:39:07 2010
@@ -24,11 +24,8 @@ import java.io.ObjectOutputStream;
 import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.lang.reflect.Array;
-
 import java.util.AbstractQueue;
-import java.util.AbstractSet;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.ConcurrentModificationException;
@@ -43,11 +40,9 @@ import java.util.NoSuchElementException;
 import java.util.Queue;
 import java.util.RandomAccess;
 import java.util.Set;
-
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.myfaces.trinidad.component.CompositeIterator;
-import org.apache.myfaces.trinidad.context.Version;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
 /**
@@ -1725,7 +1720,7 @@ public final class CollectionUtils
                                                      e);
         }
       }
-      else
+      else if (value != null)
       {
         if (_requireSerializable)
         {

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java Thu Oct 28 00:39:07 2010
@@ -326,11 +326,13 @@ public class StateManagerImpl extends St
       if (applicationViewCache == null)
       {
         assert(!dontSave);
-        TokenCache cache = _getViewCache(context);
+        
+        RequestContext trinContext = RequestContext.getCurrentInstance();
+
+        TokenCache cache = _getViewCache(trinContext, extContext);
         assert(cache != null);
 
         Map<String, Object> sessionMap  = extContext.getSessionMap();
-        RequestContext      trinContext = RequestContext.getCurrentInstance();
 
         // get view cache key with "." separator suffix to separate the SubKeyMap keys
         String subkey = _getViewCacheKey(extContext, trinContext, _SUBKEY_SEPARATOR);
@@ -599,10 +601,10 @@ public class StateManagerImpl extends St
       }
       else
       {
+        RequestContext trinContext = RequestContext.getCurrentInstance();
+
         // get view cache key with "." separator suffix to separate the SubKeyMap keys
-        String subkey = _getViewCacheKey(extContext,
-                                         RequestContext.getCurrentInstance(),
-                                         _SUBKEY_SEPARATOR);
+        String subkey = _getViewCacheKey(extContext, trinContext, _SUBKEY_SEPARATOR);
 
         Map<String, PageState> stateMap = new SubKeyMap<PageState>(
                          extContext.getSessionMap(),
@@ -619,8 +621,7 @@ public class StateManagerImpl extends St
         // 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(context).isAvailable((String) token);
+        boolean isAvailable = _getViewCache(trinContext, extContext).isAvailable((String) token);
         assert ((viewState != null) == isAvailable);
       }
 
@@ -763,21 +764,19 @@ public class StateManagerImpl extends St
     throw new UnsupportedOperationException();
   }
 
-
-  private TokenCache _getViewCache(FacesContext context)
+  /**
+   * Returns the TokenCache for the current window
+   * @param trinContext
+   * @param extContext
+   * @return
+   */
+  private TokenCache _getViewCache(RequestContext trinContext, ExternalContext extContext)
   {
-    ExternalContext extContext = context.getExternalContext();
-
-    return TokenCache.getTokenCacheFromSession(context,
-                                               _getViewCacheKey(extContext,
-                                                                RequestContext.getCurrentInstance(),
-                                                                null),
-                                               true,
-                                               _getCacheSize(extContext));
+    String cacheKey = _getViewCacheKey(extContext, trinContext, null);
+    
+    return TokenCache.getTokenCacheFromSession(extContext,cacheKey, true,_getCacheSize(extContext));
   }
 
-
-
   /**
    * Returns a key suitable for finding the per-window active page state key
    * @param extContext

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java Thu Oct 28 00:39:07 2010
@@ -18,18 +18,57 @@
  */
 package org.apache.myfaces.trinidadinternal.config;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+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 org.apache.myfaces.trinidad.context.ExternalContextDecorator;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+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.context.ExternalContextDecorator;
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 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;
 
 /**
- * Configurator that uses a wrapped ExternalContext to return a Session Map that validates that
- * only Serializable Objects are placed in the Map.
+ * 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
@@ -37,89 +76,1171 @@ public final class CheckSerializationCon
 
   /**
    * Override to return our ExternalContext wrapped if session serialization checking is enabled
-   * @param externalContext
+   * @param extContext
    * @return
    */
   @Override
-  public ExternalContext getExternalContext(ExternalContext externalContext)
+  public ExternalContext getExternalContext(ExternalContext extContext)
   {
-    boolean checkSession = StateUtils.checkSessionSerialization(externalContext);
-    boolean checkApplication = StateUtils.checkApplicationSerialization(externalContext);
-      
-    if (checkSession || checkApplication)
+    // 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 new SessionSerializationChecker(externalContext, checkSession, checkApplication);
+      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 externalContext;
+      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<MutatedBeanChecker> _getMutatedBeanList(
+    Map<String, Object> checkedMap,
+    Object              mapWriteLock)
+  {
+    Object list = checkedMap.get(_CHECKED_MAPS_KEY);
+    
+    if (list == null)
+    {
+      // make sure that the list is only created once per map
+      synchronized(mapWriteLock)
+      {
+        // check again in case we value was written by the previou lock holder
+        list = checkedMap.get(_CHECKED_MAPS_KEY);
+        
+        if (list == null)
+        {
+          // mutations to the list itself need to be thread-safe
+          List<MutatedBeanChecker> beanList = new CopyOnWriteArrayList<MutatedBeanChecker>();
+          
+          // use a TransientHolder since we don't care if this gets failed over
+          checkedMap.put(_CHECKED_MAPS_KEY, TransientHolder.newTransientHolder(beanList));
+          
+          return beanList;
+        }
+      }
+    }
+    
+    return ((TransientHolder<List<MutatedBeanChecker>>)list).getValue();
+  }
+
+  /**
+   * Remove the modified key from all of the in-flight bean checking requests
+   * @param beanCheckers
+   * @param key
+   */
+  private static void _notifyBeanCheckersOfChange(
+    List<MutatedBeanChecker> beanCheckers,
+    Object                   key)
+  {
+    for (MutatedBeanChecker beanChecker : beanCheckers)
+    {
+      beanChecker._unmutatedKeyValues.remove(key);
+    }
+  }
+
+  /**
+   * 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 = new ContextWrapper(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;
+  }
+
+
   /**
-   * ExternalContextDecorator returning our wrapped SessionMap
+   * 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 SessionSerializationChecker extends ExternalContextDecorator
+  private static class SerializationCheckingWrapper extends ExternalContextDecorator
   {
-    public SessionSerializationChecker(
+    /**
+     * Retrieves the current SerializationCheckingWrapper for this request
+     * @param extContext
+     * @param create If <code>true</code>, create the SerializationCheckingWrapper for this
+     *               request if it doesn't already exist.
+     * @return 
+     */
+    public static SerializationCheckingWrapper getSerializationWrapper(
       ExternalContext extContext,
-      boolean checkSession,
-      boolean checkApplication)
+      boolean         create)
+    {
+      // get the SerializationCheckingWrapper for this request
+      Map<String, Object> 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 getExternalContext()
+    {
+      return _extContext;
+    }
+
+    /**
+     * Override to delegate to the SerializationChecker
+     * @return
+     */
+    @Override
+    public Map<String, Object> getSessionMap()
+    {
+      return _checker.getSessionMap();
+    }
+
+    /**
+     * Override to delegate to the SerializationChecker
+     * @return
+     */
+    @Override
+    public Map<String, Object> 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. <code>checkForMutations</code> 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<String, Object>
+  {
+    /**
+     * 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 <code>true</code> if all of the attributes are required to be
+     *                             Serializable (Sesssion Map attributes are.  Application Map
+     *                             aren't)
+     */
+    public MutatedBeanChecker(
+      Map<String, Object> 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<String, Object>(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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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 ObjectInputStream(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<MutatedBeanChecker> _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 <code>true</code> 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 lsit 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});
+      
+          // throw new IllegalStateException(message);
+        }
+        
+        return _EMPTY_BYTE_ARRAY;
+      }
       
-      Map<String, Object> sessionMap = extContext.getSessionMap();
       
-      if (checkSession)
+      // verify that the contents of the value are in fact Serializable
+      try
       {
-        // skank using type erasure to finess the fact that Collections.checkedMap() expects and
-        // will return a Map<String, Serializable> when in fact, we should be returning a
-        // Map<String, Onject>.  Using checkedMap also has the disadvantage that a ClassCastException
-        // is thrown when we would really prefer to throw a more-explanatory message
-        //Map erasedMap = sessionMap;
-        //_sessionMap = (Map)Collections.checkedMap(erasedMap, String.class, Serializable.class);
-        _sessionMap =  CollectionUtils.getCheckedSerializationMap(sessionMap);
+        ByteArrayOutputStream outputByteStream = new ByteArrayOutputStream();
+        
+        new ObjectOutputStream(outputByteStream).writeObject(value);
+        
+        return outputByteStream.toByteArray();
+      }
+      catch (IOException e)
+      {
+        if (requireSerialization)
+        {
+          String message = _LOG.getMessage("ATTRIBUTE_SERIALIZATION_FAILED",
+                                           new Object[]{_mapName, key, value});
+          
+          throw new IllegalArgumentException(message, e);            
+        }
+
+        return _EMPTY_BYTE_ARRAY;
+      }      
+    }
+
+    private static final byte[] _EMPTY_BYTE_ARRAY = new byte[0];
+    
+    private static final Set<String> _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<String>();
+      _INGNORE_CLASS_NAMES.addAll(Arrays.asList(classNames));        
+    }
+    
+    private static final TrinidadLogger _LOG = 
+                                   TrinidadLogger.createTrinidadLogger(MutatedBeanChecker.class);
+
+    private final Map<String, Object> _unmutatedKeyValues;
+    private final Map<String, Object> _checkedMap;
+    private final String _mapName;
+    private final Object _mapLock;
+  }
+  
+  /**
+   * Wraps the ServletContext so that we can catch modifications to the attributes
+   */
+  private static final class ContextWrapper implements ServletContext
+  {
+    ContextWrapper(
+      ServletContext      servletContext,
+      Map<String, Object> applicationMap)
+    {
+      _delegate = servletContext;
+      
+      // if we already have an Application Map, use it, otherwise create a wrapper around
+      // the ServletContext
+      if (applicationMap != null)
+      {
+        _applicationMap = applicationMap;
       }
       else
       {
-        _sessionMap = sessionMap;
+        _applicationMap = new ServletApplicationMap(servletContext);
       }
- 
+    }
+
+    public String getContextPath()
+    {
+      return _delegate.getContextPath();
+    }
+
+    public ServletContext getContext(String string)
+    {
+      return _delegate.getContext(string);
+    }
+
+    public int getMajorVersion()
+    {
+      return _delegate.getMajorVersion();
+    }
+
+    public int getMinorVersion()
+    {
+      return _delegate.getMinorVersion();
+    }
+
+    public String getMimeType(String string)
+    {
+      return _delegate.getMimeType(string);
+    }
+
+    public Set getResourcePaths(String string)
+    {
+      return _delegate.getResourcePaths(string);
+    }
+
+    public URL getResource(String string) throws MalformedURLException
+    {
+      return _delegate.getResource(string);
+    }
+
+    public InputStream getResourceAsStream(String string)
+    {
+      return _delegate.getResourceAsStream(string);
+    }
+
+    public RequestDispatcher getRequestDispatcher(String string)
+    {
+      return _delegate.getRequestDispatcher(string);
+    }
+
+    public RequestDispatcher getNamedDispatcher(String string)
+    {
+      return _delegate.getNamedDispatcher(string);
+    }
+
+    public Servlet getServlet(String string) throws ServletException
+    {
+      return _delegate.getServlet(string);
+    }
+
+    public Enumeration getServlets()
+    {
+      return _delegate.getServlets();
+    }
+
+    public Enumeration getServletNames()
+    {
+      return _delegate.getServletNames();
+    }
+
+    public void log(String string)
+    {
+      _delegate.log(string);
+    }
+
+    public void log(Exception exception, String string)
+    {
+      _delegate.log(exception, string);
+    }
+
+    public void log(String string, Throwable throwable)
+    {
+      _delegate.log(string, throwable);
+    }
+
+    public String getRealPath(String string)
+    {
+      return _delegate.getRealPath(string);
+    }
+
+    public String getServerInfo()
+    {
+      return _delegate.getServerInfo();
+    }
+
+    public String getInitParameter(String string)
+    {
+      return _delegate.getInitParameter(string);
+    }
+
+    public Enumeration getInitParameterNames()
+    {
+      return _delegate.getInitParameterNames();
+    }
+
+    public Object getAttribute(String key)
+    {
+      return _delegate.getAttribute(key);
+    }
+
+    public Enumeration getAttributeNames()
+    {
+      return _delegate.getAttributeNames();
+    }
+
+    /**
+     * Override to remove the attribute from the list of attributes to fail over
+     * @param key
+     * @param value
+     */
+    public void setAttribute(String key, Object value)
+    {
+      _delegate.setAttribute(key, value);
+      
+      _notifyBeanCheckersOfChange(_getMutatedBeanList(), key);      
+    }
+
+    /**
+     * Override to remove the attribute from the list of attributes to fail over
+     * @param key
+     */
+    public void removeAttribute(String key)
+    {
+      _delegate.removeAttribute(key);
+
+      _notifyBeanCheckersOfChange(_getMutatedBeanList(), key);
+    }
+
+    public String getServletContextName()
+    {
+      return _delegate.getServletContextName();
+    }
+
+    private List<MutatedBeanChecker> _getMutatedBeanList()
+    {
+      return CheckSerializationConfigurator._getMutatedBeanList(_applicationMap, _delegate);
+    }
+
+    private final ServletContext      _delegate;
+    private final Map<String, Object> _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 <code>true</code> 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<String, Object> 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)
+        {
+          SerializationChecker serializationChecker = new SerializationChecker(
+                                                                           extContext,
+                                                                           checkSession,
+                                                                           checkApplication,
+                                                                           checkManagedBeanMutation);
+          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)
+    {
+      Map<String, Object> sessionMap = extContext.getSessionMap();
       Map<String, Object> applicationMap = extContext.getApplicationMap();
       
-      if (checkApplication)
+      if (checkManagedBeanMutation)
       {
-        // skank using type erasure to finess the fact that Collections.checkedMap() expects and
-        // will return a Map<String, Serializable> when in fact, we should be returning a
-        // Map<String, Onject>.  Using checkedMap also has the disadvantage that a ClassCastException
-        // is thrown when we would really prefer to throw a more-explanatory message
-        //Map erasedMap = applicationMap;
-        //_applicationMap = (Map)Collections.checkedMap(erasedMap, String.class, Serializable.class);
-        _applicationMap =  CollectionUtils.getCheckedSerializationMap(applicationMap);
+        // 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
       {
-        _applicationMap = applicationMap;
+        _sessionBeanChecker     = null;
+        _applicationBeanChecker = null;
+        
+        if (checkSession)
+        {
+          sessionMap = CollectionUtils.getCheckedSerializationMap(sessionMap, true);
+        }
+
+        if (checkApplication)
+        {
+          applicationMap =  CollectionUtils.getCheckedSerializationMap(applicationMap, false);
+        }        
       }
-   }
+            
+      _sessionMap     = sessionMap;
+      _applicationMap = applicationMap;
+    }
 
-    protected ExternalContext getExternalContext()
+    /**
+     * 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)
     {
-      return _extContext;
+      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<String, Object> getSessionMap()
     {
       return _sessionMap;
     }
 
+    /**
+     * Returns the potentially wrapped Application Map
+     * @return
+     */
     public Map<String, Object> 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();
+    }
    
-    private final ExternalContext _extContext;
-    private final Map _sessionMap;
-    private final Map _applicationMap;
+    /**
+     * 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<String, Object> sessionMap,
+        Map<String, Object> 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<String, Object> sessionMap,
+          Map<String, Object> applicationMap)
+        {
+          _delegate        = session;
+          _sessionChecker  = sessionChecker;
+          _sessionMap      = sessionMap;
+          // determine whether we need to return a wrapped ServletContext as well
+          if (applicationMap != null)
+          {
+            _wrappedContext = new ContextWrapper(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<String, Object> _sessionMap;
+        private final ServletContext      _wrappedContext;
+      }
+
+      private final HttpSession _wrappedSession;
+    }
+       
+    private final MutatedBeanChecker _sessionBeanChecker;
+    private final MutatedBeanChecker _applicationBeanChecker;
+
+    private final Map<String, Object> _sessionMap;
+    private final Map<String, Object> _applicationMap;
   }
+
+  private static final String _CHECKED_MAPS_KEY = MutatedBeanChecker.class.getName() +"#MAPS";
+
+  private static final String _SERIALIZATION_CHECKER_KEY = 
+                                       CheckSerializationConfigurator.class.getName() + "#CHECKER";
 }

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/GlobalConfiguratorImpl.java Thu Oct 28 00:39:07 2010
@@ -19,18 +19,16 @@
 
 package org.apache.myfaces.trinidadinternal.config;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.faces.context.ExternalContext;
 
 import javax.servlet.ServletRequest;
-
 import javax.servlet.ServletRequestWrapper;
-
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.myfaces.trinidad.config.Configurator;
@@ -40,6 +38,10 @@ import org.apache.myfaces.trinidad.conte
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 import org.apache.myfaces.trinidad.skin.SkinFactory;
 import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
+import org.apache.myfaces.trinidad.util.ComponentReference;
+import org.apache.myfaces.trinidad.util.ExternalContextUtils;
+import org.apache.myfaces.trinidad.util.RequestStateMap;
+import org.apache.myfaces.trinidad.util.RequestType;
 import org.apache.myfaces.trinidadinternal.context.RequestContextFactoryImpl;
 import org.apache.myfaces.trinidadinternal.context.external.ServletCookieMap;
 import org.apache.myfaces.trinidadinternal.context.external.ServletRequestHeaderMap;
@@ -49,9 +51,6 @@ import org.apache.myfaces.trinidadintern
 import org.apache.myfaces.trinidadinternal.context.external.ServletRequestParameterValuesMap;
 import org.apache.myfaces.trinidadinternal.skin.SkinFactoryImpl;
 import org.apache.myfaces.trinidadinternal.skin.SkinUtils;
-import org.apache.myfaces.trinidad.util.ExternalContextUtils;
-import org.apache.myfaces.trinidad.util.RequestStateMap;
-import org.apache.myfaces.trinidad.util.RequestType;
 
 /**
  * This is the implementation of the Trinidad's Global configurator. It provides the entry point for
@@ -409,8 +408,7 @@ public final class GlobalConfiguratorImp
       }
       finally
       {
-
-        //Do cleanup of anything which may have use the thread local manager durring
+        //Do cleanup of anything which may have use the thread local manager during
         //init.
         _releaseManagedThreadLocals();
       }
@@ -480,12 +478,19 @@ public final class GlobalConfiguratorImp
     RequestContext context = RequestContext.getCurrentInstance();
     if (context != null)
     {
+      // ensure that any deferred ComponentReferences are initialized
+      _finishComponentReferenceInitialization(ec);
+
       context.release();
       _releaseManagedThreadLocals();
+
       assert RequestContext.getCurrentInstance() == null;
     }
   }
 
+  /**
+   * Ensure that any ThreadLocals initialized during this request are cleared
+   */
   private void _releaseManagedThreadLocals()
   {
     ThreadLocalResetter resetter = _threadResetter.get();
@@ -496,6 +501,29 @@ public final class GlobalConfiguratorImp
     }
   }
 
+  /**
+   * Ensure that all DeferredComponentReferences are fully initialized before the
+   * request completes
+   */
+  private void _finishComponentReferenceInitialization(ExternalContext ec)
+  {
+    Map<String, Object> requestMap = ec.getRequestMap();
+    
+    Collection<ComponentReference<?>> initializeList = (Collection<ComponentReference<?>>)
+                                             requestMap.get(_FINISH_INITIALIZATION_LIST_KEY);
+    
+    if ((initializeList != null) && !initializeList.isEmpty())
+    {
+      for (ComponentReference<?> reference : initializeList)
+      {
+        reference.ensureInitialization();
+      }
+      
+      // we've initialized everything, so we're done
+      initializeList.clear();
+    }
+  }
+
   private void _endConfiguratorServiceRequest(final ExternalContext ec)
   {
     // Physical request has now ended
@@ -738,6 +766,10 @@ public final class GlobalConfiguratorImp
     static private String _TEST_PARAM = TestRequest.class.getName() + ".TEST_PARAM";
   }
 
+  // skanky duplication of key from ComponentReference Class
+  private static final String _FINISH_INITIALIZATION_LIST_KEY = ComponentReference.class.getName() +
+                                                                "#FINISH_INITIALIZATION";
+
   // hacky reference to the ThreadLocalResetter used to clean up request-scoped
   // ThreadLocals
   private AtomicReference<ThreadLocalResetter> _threadResetter =

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/PageFlowScopeMap.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/PageFlowScopeMap.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/PageFlowScopeMap.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/PageFlowScopeMap.java Thu Oct 28 00:39:07 2010
@@ -368,7 +368,7 @@ class PageFlowScopeMap implements Map<St
   static private TokenCache _getRootTokenCache(FacesContext context,
                                                int lifetime)
   {
-    return TokenCache.getTokenCacheFromSession(context,
+    return TokenCache.getTokenCacheFromSession(context.getExternalContext(),
                                                _PAGE_FLOW_SCOPE_CACHE,
                                                true,
                                                lifetime);
@@ -426,8 +426,8 @@ class PageFlowScopeMap implements Map<St
   private String     _token;
   private HashMap<String, Object> _map;
 
-  static private final String _PAGE_FLOW_SCOPE_CACHE =
+  private static final String _PAGE_FLOW_SCOPE_CACHE =
     "org.apache.myfaces.trinidadinternal.application.PageFlowScope";
-  static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(PageFlowScopeMap.class);
+  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(PageFlowScopeMap.class);
   private static final long serialVersionUID = 1L;
 }

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java Thu Oct 28 00:39:07 2010
@@ -104,7 +104,7 @@ final public class SubKeyMap<V> extends 
   public Set<Map.Entry<String, V>> entrySet()
   {
     if (_entrySet == null)
-      _entrySet = new Entries<V>();
+      _entrySet = new Entries();
     return _entrySet;
   }
 
@@ -131,7 +131,7 @@ final public class SubKeyMap<V> extends 
   //
   // Set implementation for SubkeyMap.entrySet()
   //
-  private class Entries<V> extends AbstractSet<Map.Entry<String, V>>
+  private class Entries extends AbstractSet<Map.Entry<String, V>>
   {
     public Entries()
     {
@@ -145,7 +145,7 @@ final public class SubKeyMap<V> extends 
       // exceptions.  Consequently, gather the keys in a list
       // and iterator over that.
       List<String> keyList = _gatherKeys();
-      return new EntryIterator<V>(keyList.iterator());
+      return new EntryIterator(keyList.iterator());
     }
 
     @Override
@@ -193,7 +193,7 @@ final public class SubKeyMap<V> extends 
     }
   }
 
-  private class EntryIterator<V> implements Iterator<Map.Entry<String, V>>
+  private class EntryIterator implements Iterator<Map.Entry<String, V>>
   {
     public EntryIterator(Iterator<String> iterator)
     {
@@ -209,7 +209,7 @@ final public class SubKeyMap<V> extends 
     {
       String baseKey = _iterator.next();
       _currentKey = baseKey;
-      return new Entry<V>(baseKey);
+      return new Entry(baseKey);
     }
 
     public void remove()
@@ -226,7 +226,7 @@ final public class SubKeyMap<V> extends 
     private String    _currentKey;
   }
 
-  private class Entry<V> implements Map.Entry<String, V>
+  private class Entry implements Map.Entry<String, V>
   {
     public Entry(String baseKey)
     {

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java Thu Oct 28 00:39:07 2010
@@ -66,33 +66,51 @@ public class TokenCache implements Seria
    */
   @SuppressWarnings("unchecked")
   static public TokenCache getTokenCacheFromSession(
-    FacesContext context,
-    String       cacheName,
-    boolean      createIfNeeded,
-    int          defaultSize)
-  {
-    ExternalContext external = context.getExternalContext();
-    Object session = external.getSession(true);
-    assert(session != null);
-
-    TokenCache cache;
-    // Synchronize on the session object to ensure that
-    // we don't ever create two different caches
-    synchronized (session)
+    ExternalContext extContext,
+    String          cacheName,
+    boolean         createIfNeeded,
+    int             defaultSize)
+  {
+    Map<String, Object> sessionMap = extContext.getSessionMap();
+
+    TokenCache cache = (TokenCache)sessionMap.get(cacheName);
+    
+    if (cache == null)
     {
-      cache = (TokenCache) external.getSessionMap().get(cacheName);
-      if ((cache == null) && createIfNeeded)
+      if (createIfNeeded)
       {
-        // create the TokenCache with the crytographically random seed
-        cache = new TokenCache(defaultSize, _getSeed());
-
-        external.getSessionMap().put(cacheName, cache);
+        Object session = extContext.getSession(true);
+  
+        // Synchronize on the session object to ensure that
+        // we don't ever create two different caches
+        synchronized (session)
+        {
+          cache = (TokenCache)sessionMap.get(cacheName);
+          
+          if (cache == null)
+          {
+            // create the TokenCache with the crytographically random seed
+            cache = new TokenCache(defaultSize, _getSeed(), sessionMap, cacheName);
+    
+            sessionMap.put(cacheName, cache);
+          }
+          else
+          {
+            // make sure the existing cache has its own attached so it can dirty itself
+            cache.reattachOwner(sessionMap);
+          }
+        }
       }
     }
+    else
+    {
+      // make sure the existing cache has its own attached so it can dirty itself
+      cache.reattachOwner(sessionMap);
+    }
 
     return cache;
   }
-  
+    
   /**
    * Returns a cryptographically secure random number to use as the TokenCache seed
    */
@@ -124,11 +142,10 @@ public class TokenCache implements Seria
   /**
    * For serialization only
    */
-  public TokenCache()
+  TokenCache()
   {
-    this(_DEFAULT_SIZE, 0L);
-  }
-
+    this(_DEFAULT_SIZE, 0L, null, null);
+  }   
 
   /**
    * Create a TokenCache that will store the last "size" entries.  This version should
@@ -138,30 +155,37 @@ public class TokenCache implements Seria
    */
   public TokenCache(int size)
   {
-    this(size, 0L);
-  }
-
-  /**
-   * Create a TokenCache that will store the last "size" entries,
-   * and begins its tokens based on the seed (instead of always
-   * starting at "0").
-   * @Deprecated Use version using a long size instead for greater security
-   */
-  public TokenCache(int size, int seed)
-  {
-    this(size, (long)seed);
+    this(size, 0L, null, null);
   }
 
  /**
   * Create a TokenCache that will store the last "size" entries,
   * and begins its tokens based on the seed (instead of always
   * starting at "0").
+  * @patam owner      Optional Cache that stores the token cache
+  * @param keyInOwner Optional Name under which this cache is stored in the owner
   */
- public TokenCache(int size, long seed)
+  private TokenCache(int size, long seed, Map<String, Object> owner, String keyInOwner)
+  {
+    _cache      = new LRU(size);
+    _pinned     = new ConcurrentHashMap<String, String>(size);
+    _count      = new AtomicLong(seed);
+    _owner      = owner;
+    _keyInOwner = keyInOwner;
+  } 
+
+  /**
+   * Reattaches the owner after Serialization since the owner might not be Serializable or it
+   * might not be a good idea to serialize the owner.
+   * @param owner
+   * @throws NullPointerException if owner is null
+   */
+  public void reattachOwner(Map<String, Object> owner)
   {
-    _cache = new LRU(size);
-    _pinned = new ConcurrentHashMap<String, String>(size);
-    _count = new AtomicLong(seed);
+    if (owner == null)
+      throw new NullPointerException("Can't set owner to null");
+      
+    _owner = owner;
   }
 
   /**
@@ -222,6 +246,9 @@ public class TokenCache implements Seria
     
     targetStore.put(token, value);
 
+    // our contents have changed, so mark ourselves as dirty in our owner
+    _dirty();
+    
     return token;
   }
 
@@ -289,14 +316,22 @@ public class TokenCache implements Seria
       String token, 
       Map<String, V> targetStore)
   {
+    V oldValue;
+    
     synchronized (this)
     {
       _LOG.finest("Removing token {0} from cache", token);
       _cache.remove(token);
+      
       // TODO: should removing a value that is "pinned" take?
       // Or should it stay in memory?
-      return _removeTokenIfReady(targetStore, token);
+      oldValue = _removeTokenIfReady(targetStore, token);
     }
+
+    // our contents have changed, so mark ourselves as dirty in our owner
+    _dirty();
+    
+    return oldValue;
   }
 
   /**
@@ -314,6 +349,9 @@ public class TokenCache implements Seria
 
       _cache.clear();
     }
+
+    // our contents have changed, so mark ourselves as dirty in our owner
+    _dirty();
   }
 
   private String _getNextToken()
@@ -324,6 +362,17 @@ public class TokenCache implements Seria
     // convert using base 36 because it is a fast efficient subset of base-64
     return Long.toString(nextToken, 36);
   }
+  
+  /**
+   * Mark the cache as dirty in the owner
+   */
+  private void _dirty()
+  {
+    if (_keyInOwner != null)
+    {
+      _owner.put(_keyInOwner, this);
+    }
+  }
 
   private class LRU extends LRUCache<String, String>
   {
@@ -351,10 +400,15 @@ public class TokenCache implements Seria
   // the current token value
   private final AtomicLong _count;
 
+  private final String _keyInOwner;
+  
   // Hack instance parameter used to communicate between the LRU cache's
   // removing() method, and the addNewEntry() method that may trigger it
   private transient String _removed;
 
+  // owning cache
+  private transient Map<String, Object> _owner;
+
   static private final int _DEFAULT_SIZE = 15;
   static private final long serialVersionUID = 1L;
   static private final TrinidadLogger _LOG =

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/webapp/TrinidadFilterImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/webapp/TrinidadFilterImpl.java?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/webapp/TrinidadFilterImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/webapp/TrinidadFilterImpl.java Thu Oct 28 00:39:07 2010
@@ -39,11 +39,14 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import javax.servlet.http.HttpSession;
+
 import org.apache.myfaces.trinidad.context.RequestContext;
 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.RequestStateMap;
+import org.apache.myfaces.trinidadinternal.config.CheckSerializationConfigurator;
 import org.apache.myfaces.trinidadinternal.config.GlobalConfiguratorImpl;
 import org.apache.myfaces.trinidadinternal.config.dispatch.DispatchResponseConfiguratorImpl;
 import org.apache.myfaces.trinidadinternal.config.dispatch.DispatchServletResponse;
@@ -94,10 +97,14 @@ public class TrinidadFilterImpl implemen
 
   public void init(FilterConfig filterConfig) throws ServletException
   {
+    // potentially wrap the FilterConfig to catch Serialization changes
+    filterConfig = CheckSerializationConfigurator.getFilterConfig(filterConfig);
+    
     _servletContext = filterConfig.getServletContext();
             
     //There is some functionality that still might require servlet-only filter services.
     _filters = ClassLoaderUtils.getServices(TrinidadFilterImpl.class.getName());
+    
     for(Filter f:_filters)
     {
       f.init(filterConfig);
@@ -130,7 +137,20 @@ public class TrinidadFilterImpl implemen
     // properly installed.
     request.setAttribute(_FILTER_EXECUTED_KEY, Boolean.TRUE);
 
-    ExternalContext externalContext = new ServletExternalContext(_servletContext, request, response);    
+    // potentially wrap the request in order to check managed bean HA
+    if (request instanceof HttpServletRequest)
+    {
+      request = CheckSerializationConfigurator.getHttpServletRequest(
+                                    new ServletExternalContext(_servletContext, request, response),
+                                    (HttpServletRequest)request);
+    }
+    
+    // potentially wrap the ServletContext in order to check managed bean HA
+    ExternalContext externalContext = new ServletExternalContext(
+                                        _getPotentiallyWrappedServletContext(request),
+                                        request,
+                                        response);
+    
     GlobalConfiguratorImpl config = GlobalConfiguratorImpl.getInstance();
     config.beginRequest(externalContext);
     
@@ -207,7 +227,11 @@ public class TrinidadFilterImpl implemen
   {
     // -= Scott O'Bryan =-
     // Added for backward compatibility
-    ExternalContext ec = new ServletExternalContext(_servletContext, request, response);
+    // potentially wrap the ServletContext to check ManagerBean HA
+    ExternalContext ec = new ServletExternalContext(_getPotentiallyWrappedServletContext(request),
+                                                    request,
+                                                    response);
+    
     boolean isHttpReq = ExternalContextUtils.isHttpServletRequest(ec);
     
     if(isHttpReq)
@@ -384,6 +408,7 @@ public class TrinidadFilterImpl implemen
     {
       return _launchParam;
     }
+
     private static final long serialVersionUID = 1L;
   }
 
@@ -422,6 +447,24 @@ public class TrinidadFilterImpl implemen
     }
   }
 
+  /**
+   * Returns a potentially wrapped ServletContext for ManagedBean HA
+   */
+  private ServletContext _getPotentiallyWrappedServletContext(ServletRequest request)
+  {
+    if (request instanceof HttpServletRequest)
+    {
+      HttpSession session = ((HttpServletRequest)request).getSession(false);
+      
+      if (session != null)
+      {
+        return session.getServletContext();
+      }
+    }
+    
+    return _servletContext;
+  }
+
   private ServletContext _servletContext;
   private List<Filter> _filters = null;
 

Modified: myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts?rev=1028148&r1=1028147&r2=1028148&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/branches/1.2.12.3-branch/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts Thu Oct 28 00:39:07 2010
@@ -1080,4 +1080,14 @@ The skin {0} specified on the requestMap
 <resource key="INVALID_LOCALE_VARIANT_HAS_SLASH">Invalid variant for Locale identifier {0} - cannot contain slashes to avoid XSS attack. Will use empty string for variant.</resource>
 
 <resource key="COULD_NOT_DELETE_FILE">Could not delete the file {0}</resource>
+
+<!-- ATTRIBUTE_SERIALIZATION_FAILED -->
+<resource key="ATTRIBUTE_SERIALIZATION_FAILED">Error serializing {0} attribute:{1} value:{2}</resource>
+
+<!-- ATTRIBUTE_NOT_SERIALIABLE -->
+<resource key="ATTRIBUTE_NOT_SERIALIABLE">Failover error: {0} attribute:{1} of type {2} is not Serializable</resource>
+
+<!-- SERIALIZABLE_ATTRIBUTE_MUTATED -->
+<resource key="SERIALIZABLE_ATTRIBUTE_MUTATED">Failover error: Serialization of {0} attribute:{1} has changed from {2} to {3} without the attribute being dirtied</resource>
+
 </resources>