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 2011/03/10 20:00:03 UTC

svn commit: r1080318 - in /myfaces/trinidad/branches/trinidad-1.2.x: trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/ trinidad-impl/src/main/xrts/org/apache/myface...

Author: bsullivan
Date: Thu Mar 10 19:00:03 2011
New Revision: 1080318

URL: http://svn.apache.org/viewvc?rev=1080318&view=rev
Log:
Adds "REQUEST" parameter to enabled turning on request checking of session attributes at the end of each request.  Also includes more resilient code in case attempts at Serialization throw non-Serialization exceptions and better drilling into the causes of Serialization failures.

Modified:
    myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java
    myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java
    myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts

Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java?rev=1080318&r1=1080317&r2=1080318&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-api/src/main/java/org/apache/myfaces/trinidad/bean/util/StateUtils.java Thu Mar 10 19:00:03 2011
@@ -57,6 +57,7 @@ public final class StateUtils
     boolean checkSessionSerialization = false;
     boolean checkApplicationSerialization = false;
     boolean checkMangedBeanMutation = false;
+    boolean checkAtEndRequest = false;
 
     String checkSerializationProperty;
 
@@ -85,12 +86,13 @@ public final class StateUtils
       {
         if (serializationFlags.contains("ALL"))
         {
-          checkPropertyStateSerialization = true;
-          checkComponentStateSerialization = true;
+          checkPropertyStateSerialization      = true;
+          checkComponentStateSerialization     = true;
           checkComponentTreeStateSerialization = true;
-          checkSessionSerialization = true;
-          checkApplicationSerialization = true;
-          checkMangedBeanMutation = true;
+          checkSessionSerialization            = true;
+          checkApplicationSerialization        = true;
+          checkMangedBeanMutation              = true;
+          checkAtEndRequest                    = true;
         }
         else
         {
@@ -100,6 +102,7 @@ public final class StateUtils
           checkSessionSerialization = serializationFlags.contains("SESSION");
           checkApplicationSerialization = serializationFlags.contains("APPLICATION");
           checkMangedBeanMutation = serializationFlags.contains("BEANS");
+          checkAtEndRequest = serializationFlags.contains("REQUEST");
         }
       }
     }
@@ -110,6 +113,7 @@ public final class StateUtils
     _CHECK_SESSION_SERIALIZATION = checkSessionSerialization;
     _CHECK_APPLICATION_SERIALIZATION = checkApplicationSerialization;
     _CHECK_MANAGED_BEAN_MUTATATION = checkMangedBeanMutation;
+    _CHECK_AT_END_REQUEST = checkAtEndRequest;
   }
 
   private static final boolean _CHECK_COMPONENT_TREE_STATE_SERIALIZATION;
@@ -118,6 +122,7 @@ public final class StateUtils
   private static final boolean _CHECK_SESSION_SERIALIZATION;
   private static final boolean _CHECK_APPLICATION_SERIALIZATION;
   private static final boolean _CHECK_MANAGED_BEAN_MUTATATION;
+  private static final boolean _CHECK_AT_END_REQUEST;
 
   /**
    * Returns <code>true</code> if properties should be checked for
@@ -249,6 +254,16 @@ public final class StateUtils
     return _CHECK_MANAGED_BEAN_MUTATATION;
   }
 
+  /**
+   * Returns <code>true</code> if all attributes in the session Map should be
+   * checked for serializability at the end of each request.  This check should
+   * only be performed if <code>checkSessionSerialization</code> also returns <code>true</code>.
+   * @see #checkSessionSerialization
+   */
+  public static boolean checkScopesAtEndOfRequest(ExternalContext extContext)
+  {
+    return _CHECK_AT_END_REQUEST;
+  }
 
   /**
    * Persists a property key.

Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java?rev=1080318&r1=1080317&r2=1080318&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/CheckSerializationConfigurator.java Thu Mar 10 19:00:03 2011
@@ -29,9 +29,12 @@ import java.io.Serializable;
 import java.net.MalformedURLException;
 import java.net.URL;
 
+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;
@@ -203,30 +206,45 @@ public final class CheckSerializationCon
     Map<String, Object> checkedMap,
     Object              mapWriteLock)
   {
-    Object list = checkedMap.get(_CHECKED_MAPS_KEY);
+    Object holder = checkedMap.get(_CHECKED_MAPS_KEY);
     
-    if (list == null)
+    TransientHolder<List<MutatedBeanChecker>> listHolder;
+    List<MutatedBeanChecker> mutatedBeans = null;
+    
+    if (holder != null)                                         
+    {
+      listHolder = (TransientHolder<List<MutatedBeanChecker>>)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 previou lock holder
-        list = checkedMap.get(_CHECKED_MAPS_KEY);
+        // check again in case we value was written by the previous lock holder
+        holder = checkedMap.get(_CHECKED_MAPS_KEY);
+
+        if (holder != null)                                         
+        {
+          listHolder = (TransientHolder<List<MutatedBeanChecker>>)holder;
+          
+          mutatedBeans = listHolder.getValue();
+        }
         
-        if (list == null)
+        if (mutatedBeans == null)
         {
           // mutations to the list itself need to be thread-safe
-          List<MutatedBeanChecker> beanList = new CopyOnWriteArrayList<MutatedBeanChecker>();
+          mutatedBeans = 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;
+          checkedMap.put(_CHECKED_MAPS_KEY, TransientHolder.newTransientHolder(mutatedBeans));
         }
       }
     }
     
-    return ((TransientHolder<List<MutatedBeanChecker>>)list).getValue();
+    return mutatedBeans;
   }
 
   /**
@@ -243,6 +261,88 @@ public final class CheckSerializationCon
       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<Object> failureStack = new ArrayList<Object>();
+    
+    failureStack.add(keyMessage);
+    failureStack.add(value);
+    
+    _extractFailure(failureStack, value);
+    
+    String joinMessage = _LOG.getMessage("EXTRACT_SERIALIZATION_DETAIL");
+    
+    StringBuilder message = new StringBuilder();
+    
+    Iterator<Object> 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<Object> failureStack,
+    Object       failedObject)
+  {
+    if (failedObject instanceof Map)
+    {
+      _MAP_EXTRACTOR.extractFailure(failureStack, (Map<Object, Object>)failedObject);
+    }
+    else if (failedObject instanceof List)
+    {
+      _LIST_EXTRACTOR.extractFailure(failureStack, (List<Object>)failedObject);
+    }
+    else if (failedObject instanceof Collection)
+    {
+      _COLLECTION_EXTRACTOR.extractFailure(failureStack, (List<Object>)failedObject);
+    }
+    else if (failedObject instanceof Object[])
+    {
+      _ARRAY_EXTRACTOR.extractFailure(failureStack, (Object[])failedObject);
+    }
+  }
 
   /**
    * Wraps the FilterConfig so that we can wrap the ServletContext that it returns so that
@@ -604,7 +704,7 @@ public final class CheckSerializationCon
       
       Class valueClass = value.getClass();
       
-      // check against the lsit of classes to ignore for performance reasons
+      // check against the list of classes to ignore for performance reasons
       if (_INGNORE_CLASS_NAMES.contains(valueClass.getName()))
         return _EMPTY_BYTE_ARRAY;
       
@@ -615,7 +715,7 @@ public final class CheckSerializationCon
           String message = _LOG.getMessage("ATTRIBUTE_NOT_SERIALIZABLE",
                                            new Object[]{_mapName, key, valueClass});
       
-          // throw new IllegalStateException(message);
+          _LOG.severe(message);
         }
         
         return _EMPTY_BYTE_ARRAY;
@@ -625,20 +725,18 @@ public final class CheckSerializationCon
       // verify that the contents of the value are in fact Serializable
       try
       {
-        ByteArrayOutputStream outputByteStream = new ByteArrayOutputStream();
-        
-        new ObjectOutputStream(outputByteStream).writeObject(value);
-        
-        return outputByteStream.toByteArray();
+        return _serialize(value);
       }
-      catch (IOException e)
+      catch (Throwable e)
       {
         if (requireSerialization)
         {
-          String message = _LOG.getMessage("ATTRIBUTE_SERIALIZATION_FAILED",
-                                           new Object[]{_mapName, key, value});
+          String baseMessage = _LOG.getMessage("ATTRIBUTE_SERIALIZATION_FAILED_KEY_VALUE",
+                                           new Object[]{_mapName, key});
+          
+          String message = _extractFailureString(baseMessage, value);
           
-          throw new IllegalArgumentException(message, e);            
+          _LOG.severe(message, e);            
         }
 
         return _EMPTY_BYTE_ARRAY;
@@ -886,11 +984,15 @@ public final class CheckSerializationCon
         // 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);
+                                                                           checkManagedBeanMutation,
+                                                                           checkSessionAtEnd);
+          
           requestMap.put(_SERIALIZATION_CHECKER_KEY, serializationChecker);
           
           return serializationChecker;
@@ -913,7 +1015,8 @@ public final class CheckSerializationCon
       ExternalContext extContext,
       boolean checkSession,
       boolean checkApplication,
-      boolean checkManagedBeanMutation)
+      boolean checkManagedBeanMutation,
+      boolean checkSessionAtEnd)
     {
       Map<String, Object> sessionMap = extContext.getSessionMap();
       Map<String, Object> applicationMap = extContext.getApplicationMap();
@@ -958,8 +1061,9 @@ public final class CheckSerializationCon
         }        
       }
             
-      _sessionMap     = sessionMap;
-      _applicationMap = applicationMap;
+      _sessionMap        = sessionMap;
+      _applicationMap    = applicationMap;
+      _checkSessionAttrs = checkSessionAtEnd;
     }
 
     /**
@@ -1043,6 +1147,39 @@ public final class CheckSerializationCon
     
       if (_applicationBeanChecker != null)
         _applicationBeanChecker.checkForMutations();
+      
+      // check that all of the attributes in the Session are Serializable
+      if (_checkSessionAttrs)
+      {
+        for (Map.Entry<String, Object> 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);              
+            }
+          }
+        }
+      }
     }
    
     /**
@@ -1232,14 +1369,161 @@ public final class CheckSerializationCon
 
       private final HttpSession _wrappedSession;
     }
-       
+ 
+    private static final TrinidadLogger _LOG = 
+                                   TrinidadLogger.createTrinidadLogger(SerializationChecker.class);
+      
     private final MutatedBeanChecker _sessionBeanChecker;
     private final MutatedBeanChecker _applicationBeanChecker;
 
     private final Map<String, Object> _sessionMap;
     private final Map<String, Object> _applicationMap;
+    private final boolean             _checkSessionAttrs;
+  }
+
+  /**
+   * Class for extracting more detailed failure information for a particular object type
+   * that has failed Serialization
+   * 
+   * @param <T>
+   */
+  private static abstract class SerializationFailureExtractor<T>
+  {
+    /**
+     * Called to extract failure information about the failedObject onto the
+     * failureStack
+     */
+    public abstract void extractFailure(List<Object> failureStack, T faileddObject);
   }
+  
+  /**
+   * Extract information about failures in Maps
+   */
+  private static class MapExtractor extends SerializationFailureExtractor<Map<Object, Object>>
+  {
+    public void extractFailure(List<Object> failureStack, Map<Object, Object> failedMap)
+    {
+      for (Map.Entry<Object, Object> 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<List<Object>>
+  {
+    public void extractFailure(List<Object> failureStack, List<Object> 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<Object[]>
+  {
+    public void extractFailure(List<Object> 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<Collection<Object>>
+  {
+    public void extractFailure(List<Object> failureStack, Collection<Object> 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 = 

Modified: myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts?rev=1080318&r1=1080317&r2=1080318&view=diff
==============================================================================
--- myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts (original)
+++ myfaces/trinidad/branches/trinidad-1.2.x/trinidad-impl/src/main/xrts/org/apache/myfaces/trinidadinternal/resource/LoggerBundle.xrts Thu Mar 10 19:00:03 2011
@@ -1081,8 +1081,11 @@ The skin {0} specified on the requestMap
 
 <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_SERIALIZATION_FAILED_KEY_VALUE -->
+<resource key="ATTRIBUTE_SERIALIZATION_FAILED_KEY_VALUE">Error serializing {0} attribute:{1} </resource>
+
+<!-- EXTRACT_SERIALIZATION_DETAIL -->
+<resource key="EXTRACT_SERIALIZATION_DETAIL"> failed value=</resource>
 
 <!-- ATTRIBUTE_NOT_SERIALIABLE -->
 <resource key="ATTRIBUTE_NOT_SERIALIABLE">Failover error: {0} attribute:{1} of type {2} is not Serializable</resource>
@@ -1090,4 +1093,13 @@ The skin {0} specified on the requestMap
 <!-- 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>
 
+<!-- SERIALIZATION_TESTING_FAILURE -->
+<resource key="SERIALIZATION_TESTING_FAILURE">Session attribute serialization testing failed for {0}. This check can be disabled by removing 'REQUEST' from the 'org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION' system property</resource>
+
+<!-- SERIALIZATION_LOGGING_FAILURE -->
+<resource key="SERIALIZATION_LOGGING_FAILURE">Exception gathering session attribute serialization testing failure</resource>
+
+<!-- SESSION_SERIALIZATION_ATTRIBUTE -->
+<resource key="SESSION_SERIALIZATION_ATTRIBUTE">Session attribute:{0}</resource>
+
 </resources>