You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ma...@apache.org on 2008/08/27 09:05:46 UTC

svn commit: r689391 - in /myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal: application/StateManagerImpl.java util/SubKeyMap.java util/TokenCache.java

Author: matzew
Date: Wed Aug 27 00:05:45 2008
New Revision: 689391

URL: http://svn.apache.org/viewvc?rev=689391&view=rev
Log:
TRINIDAD-1193 - Trinidad StateManagerImpl doesn't clean up UIViewRoot instances when navigating from a page using a GET

ported the original fix to JSF 1.1 trunk

Modified:
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java?rev=689391&r1=689390&r2=689391&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java Wed Aug 27 00:05:45 2008
@@ -242,8 +242,8 @@
 
     // See if we're going to use the application view cache for
     // this request
-    Map<String, Object> applicationViewCache = null;
-    Map<String, Object> perSessionApplicationViewCache = null;
+    Map<String, PageState> applicationViewCache = null;
+    Map<String, PageState> perSessionApplicationViewCache = null;
     if (_useApplicationViewCache(context))
     {
       // OK, we are: so find the application cache and
@@ -256,7 +256,7 @@
       {
         // If we've already got a copy of the state stored, then
         // we just need to make sure it's mirrored on the session
-        Object applicationState = applicationViewCache.get(root.getViewId());
+        PageState applicationState = applicationViewCache.get(root.getViewId());
         if (applicationState != null)
         {
           // Note that we've got no work to do...
@@ -284,7 +284,7 @@
         assert(cache != null);
 
         // Store bits of the session as subkeys off of the session
-        Map<String, Object> stateMap = new SubKeyMap(
+        Map<String, PageState> stateMap = new SubKeyMap<PageState>(
                          context.getExternalContext().getSessionMap(),
                          _VIEW_CACHE_KEY + ".");
         // Sadly, we can't save just a SerializedView, because we should
@@ -298,6 +298,17 @@
             // if this feature has not been disabled
             _useViewRootCache(context) ? root : null);
 
+        // clear out all of the previous PageStates' UIViewRoots and add this page
+        // state as an active page state.  This is necessary to avoid UIViewRoots
+        // laying around if the user navigates off of a page using a GET
+        synchronized(this)
+        {
+          if (_activePageState != null)
+            _activePageState.clearViewRootState();
+          
+          _activePageState = pageState;
+        }
+
         String requestToken = _getRequestTokenForResponse(context);
         // If we have a cached token that we want to reuse,
         // and that token hasn't disappeared from the cache already
@@ -330,7 +341,7 @@
       else
       {
         // use null viewRoot since this state is shared across users:
-        Object applicationState = new PageState(context, structure, state, null);
+        PageState applicationState = new PageState(context, structure, state, null);
         // If we need to, stash the state off in our cache
         if (!dontSave)
         {
@@ -473,8 +484,8 @@
       // Load from the application cache
       if (_APPLICATION_CACHE_TOKEN.equals(token))
       {
-        Map<String, Object> cache = _getApplicationViewCache(context);
-        Map<String, Object> perSessionCache =
+        Map<String, PageState> cache = _getApplicationViewCache(context);
+        Map<String, PageState> perSessionCache =
           _getPerSessionApplicationViewCache(context);
 
         // Synchronize on the application-level cache.
@@ -482,7 +493,7 @@
         synchronized (cache)
         {
           // Look first in the per-session cache
-          viewState = (PageState) perSessionCache.get(viewId);
+          viewState = cache.get(viewId);
           if (viewState == null)
           {
             // Nope, it's not there.  Look in the application cache
@@ -502,10 +513,10 @@
       }
       else
       {
-        Map<String, Object> stateMap = new SubKeyMap(
+        Map<String, PageState> stateMap = new SubKeyMap<PageState>(
                          context.getExternalContext().getSessionMap(),
                          _VIEW_CACHE_KEY + ".");
-        viewState = (PageState) stateMap.get(token);
+        viewState = stateMap.get(token);
 
         if (viewState != null)
           _updateRequestTokenForResponse(context, (String) token);
@@ -693,15 +704,15 @@
   // @todo a static size is bad
   //
   @SuppressWarnings("unchecked")
-  static private Map<String, Object> _getApplicationViewCache(FacesContext context)
+  static private Map<String, PageState> _getApplicationViewCache(FacesContext context)
   {
     synchronized (_APPLICATION_VIEW_CACHE_LOCK)
     {
       Map<String, Object> appMap = context.getExternalContext().getApplicationMap();
-      Map<String, Object> cache = (Map<String, Object>) appMap.get(_APPLICATION_VIEW_CACHE_KEY);
+      Map<String, PageState> cache = (Map<String, PageState>)appMap.get(_APPLICATION_VIEW_CACHE_KEY);
       if (cache == null)
       {
-        cache = new HashMap<String, Object>(128);
+        cache = new HashMap<String, PageState>(128);
         appMap.put(_APPLICATION_VIEW_CACHE_KEY, cache);
       }
 
@@ -710,19 +721,19 @@
   }
 
   @SuppressWarnings("unchecked")
-  static private Map<String, Object> _getPerSessionApplicationViewCache(FacesContext context)
+  static private Map<String, PageState> _getPerSessionApplicationViewCache(FacesContext context)
   {
     ExternalContext external = context.getExternalContext();
     Object session = external.getSession(true);
     assert(session != null);
 
-    Map<String, Object> cache;
+    Map<String, PageState> cache;
     // Synchronize on the session object to ensure that
     // we don't ever create two different caches
     synchronized (session)
     {
       Map<String, Object> sessionMap = external.getSessionMap();
-      cache = (Map<String, Object>) sessionMap.get(_APPLICATION_VIEW_CACHE_KEY);
+      cache = (Map<String, PageState>) sessionMap.get(_APPLICATION_VIEW_CACHE_KEY);
       if (cache == null)
       {
         cache = _createPerSessionApplicationViewCache();
@@ -737,9 +748,9 @@
   // For the per-session mirror of the application view cache,
   // use an LRU LinkedHashMap to store the latest 16 pages.
   //
-  static private Map<String, Object> _createPerSessionApplicationViewCache()
+  static private Map<String, PageState> _createPerSessionApplicationViewCache()
   {
-    return new LRUCache<String, Object>(_MAX_PER_SESSION_APPLICATION_SIZE);
+    return new LRUCache<String, PageState>(_MAX_PER_SESSION_APPLICATION_SIZE);
   }
 
   static private final int _MAX_PER_SESSION_APPLICATION_SIZE = 16;
@@ -902,25 +913,50 @@
                                                      state);
   }
 
+  private static final class ViewRootState
+  {
+    public ViewRootState(FacesContext context, UIViewRoot viewRoot)
+    {
+      if (viewRoot == null)
+        throw new NullPointerException();
+      
+      _viewRoot = viewRoot;
+      _viewRootState = viewRoot.saveState(context);
+    }
+
+    public UIViewRoot getViewRoot()
+    {
+      return _viewRoot;
+    }
+
+    public Object getViewRootState()
+    {
+      return _viewRootState;
+    }
+
+    private final UIViewRoot _viewRoot;
+    private final Object _viewRootState;
+  }
+
   private static final class PageState implements Serializable
   {
     private static final long serialVersionUID = 1L;
 
     private final Object _structure, _state;
+
     // use transient since UIViewRoots are not Serializable.
-    private transient UIViewRoot _root;
-    // If the UIViewRoot is lost, then this state is useless, so mark it
-    // transient as well:
-    private transient Object _viewRootState;
+    private transient ViewRootState _cachedState;
 
     public PageState(FacesContext fc, Object structure, Object state, UIViewRoot root)
     {
       _structure = structure;
       _state = state;
-      _root = root;
       // we need this state, as we are going to recreate the UIViewRoot later. see
       // the popRoot() method:
-      _viewRootState = (root != null) ? root.saveState(fc) : null;
+      _cachedState = (root != null)
+                       ? new ViewRootState(fc, root)
+                       : null;
+
     }
 
     public Object getStructure()
@@ -933,6 +969,14 @@
       return _state;
     }
 
+    public void clearViewRootState()
+    {
+      synchronized(this)
+      {
+        _cachedState = null;
+      }
+    }
+
     @SuppressWarnings("unchecked")
     public UIViewRoot popRoot(FacesContext fc)
     {
@@ -942,16 +986,15 @@
       // which is shared between simultaneous requests from the same user:
       synchronized(this)
       {
-        if (_root != null)
+        if (_cachedState != null)
         {
-          root = _root;
-          viewRootState = _viewRootState;
+          root = _cachedState.getViewRoot();
+          viewRootState = _cachedState.getViewRootState();
           // we must clear the cached viewRoot. This is because UIComponent trees
           // are mutable and if the back button
           // is used to come back to an old PageState, then it would be
           // really bad if we reused that component tree:
-          _root = null;
-          _viewRootState = null;
+          _cachedState = null;
         }
       }
       
@@ -1038,7 +1081,7 @@
   private       Boolean      _useViewRootCache;
   private       Boolean      _useApplicationViewCache;
   private       Boolean      _structureGeneratedByTemplate;
-
+  private       PageState    _activePageState;
 
   private static final int _DEFAULT_CACHE_SIZE = 15;
 

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java?rev=689391&r1=689390&r2=689391&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/SubKeyMap.java Wed Aug 27 00:05:45 2008
@@ -41,7 +41,7 @@
  * "flaw" is actually relied on by PageFlowScopeMap (since it provides
  * a handy way to clear out all descendents), so don't "fix" it!
  */
-final public class SubKeyMap extends AbstractMap<String, Object>
+final public class SubKeyMap<V> extends AbstractMap<String, V>
 {
   public SubKeyMap(Map<String, Object> base, String prefix)
   {
@@ -70,25 +70,25 @@
   }
 
   @Override
-  public Object get(Object key)
+  public V get(Object key)
   {
     key = _getBaseKey(key);
-    return _base.get(key);
+    return (V)_base.get(key);
   }
 
   @Override
-  public Object put(String key, Object value)
+  public V put(String key, V value)
   {
     key = _getBaseKey(key);
-    return _base.put(key, value);
+    return (V)_base.put(key, value);
   }
 
   @Override
-  public Object remove(Object key)
+  public V remove(Object key)
   {
     key = _getBaseKey(key);
     _LOG.finest("Removing {0}", key);
-    return _base.remove(key);
+    return (V)_base.remove(key);
   }
 
   @Override
@@ -101,10 +101,10 @@
   }
 
   @Override
-  public Set<Map.Entry<String, Object>> entrySet()
+  public Set<Map.Entry<String, V>> entrySet()
   {
     if (_entrySet == null)
-      _entrySet = new Entries();
+      _entrySet = new Entries<V>();
     return _entrySet;
   }
 
@@ -131,21 +131,21 @@
   //
   // Set implementation for SubkeyMap.entrySet()
   //
-  private class Entries extends AbstractSet<Map.Entry<String, Object>>
+  private class Entries<V> extends AbstractSet<Map.Entry<String, V>>
   {
     public Entries()
     {
     }
 
     @Override
-    public Iterator<Map.Entry<String, Object>> iterator()
+    public Iterator<Map.Entry<String, V>> iterator()
     {
       // Sadly, if you just try to use a filtering approach
       // on the iterator, you'll get concurrent modification
       // exceptions.  Consequently, gather the keys in a list
       // and iterator over that.
       List<String> keyList = _gatherKeys();
-      return new EntryIterator(keyList.iterator());
+      return new EntryIterator<V>(keyList.iterator());
     }
 
     @Override
@@ -193,7 +193,7 @@
     }
   }
 
-  private class EntryIterator implements Iterator<Map.Entry<String, Object>>
+  private class EntryIterator<V> implements Iterator<Map.Entry<String, V>>
   {
     public EntryIterator(Iterator<String> iterator)
     {
@@ -205,11 +205,11 @@
       return _iterator.hasNext();
     }
 
-    public Map.Entry<String, Object> next()
+    public Map.Entry<String, V> next()
     {
       String baseKey = _iterator.next();
       _currentKey = baseKey;
-      return new Entry(baseKey);
+      return new Entry<V>(baseKey);
     }
 
     public void remove()
@@ -226,7 +226,7 @@
     private String    _currentKey;
   }
 
-  private class Entry implements Map.Entry<String, Object>
+  private class Entry<V> implements Map.Entry<String, V>
   {
     public Entry(String baseKey)
     {
@@ -240,14 +240,14 @@
       return _key;
     }
 
-    public Object getValue()
+    public V getValue()
     {
-      return _base.get(_baseKey);
+      return (V)_base.get(_baseKey);
     }
 
-    public Object setValue(Object value)
+    public V setValue(V value)
     {
-      return _base.put(_baseKey, value);
+      return (V)_base.put(_baseKey, value);
     }
 
     @SuppressWarnings("unchecked")
@@ -256,7 +256,7 @@
     {
       if (!(o instanceof Map.Entry))
         return false;
-      Map.Entry<String, Object> e = (Map.Entry<String, Object>)o;
+      Map.Entry<String, V> e = (Map.Entry<String, V>)o;
       return _equals(getKey(), e.getKey()) &&
       _equals(getValue(), e.getValue());
     }
@@ -283,7 +283,7 @@
 
   private final Map<String, Object> _base;
   private final String _prefix;
-  private Set<Map.Entry<String, Object>> _entrySet;
+  private Set<Map.Entry<String, V>> _entrySet;
 
   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(SubKeyMap.class);
 }

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java?rev=689391&r1=689390&r2=689391&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java Wed Aug 27 00:05:45 2008
@@ -139,9 +139,9 @@
    * @param targetStore the map used for storing the value
    * @return the token used to store the value
    */
-  public String addNewEntry(
-      Object value, 
-      Map<String, Object> targetStore)
+  public <V> String addNewEntry(
+      V value, 
+      Map<String, V> targetStore)
   {
     return addNewEntry(value, targetStore, null);
   }
@@ -156,9 +156,9 @@
    *    will not be freed until this current token is also freed
    * @return the token used to store the value
    */
-  public String addNewEntry(
-      Object value, 
-      Map<String, Object> targetStore,
+  public <V> String addNewEntry(
+      V value, 
+      Map<String, V> targetStore,
       String pinnedToken)
   {
     String remove = null;
@@ -218,11 +218,11 @@
    * Remove a token if is ready:  there are no pinned references to it.
    * Note that it will be absent from the LRUCache.
    */
-  synchronized private Object _removeTokenIfReady(
-      Map<String, Object> targetStore, 
+  synchronized private <V> V _removeTokenIfReady(
+      Map<String, V> targetStore, 
       String              token)
   {
-    Object removedValue;
+    V removedValue;
     
     // See if it's pinned to something still in memory
     if (!_pinned.containsValue(token))
@@ -252,9 +252,9 @@
    * Removes a value from the cache.
    * @return previous value associated with the token, if any
    */
-  public Object removeOldEntry(
+  public <V> V removeOldEntry(
       String token, 
-      Map<String, Object> targetStore)
+      Map<String, V> targetStore)
   {
     synchronized (this)
     {
@@ -269,7 +269,7 @@
   /**
    * Clear a cache, without resetting the token.
    */
-  public void clear(Map<String, Object> targetStore)
+  public <V> void clear(Map<String, V> targetStore)
   {
     synchronized (this)
     {