You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by aw...@apache.org on 2007/05/17 02:39:22 UTC
svn commit: r538778 - in /myfaces/trinidad/trunk/trinidad:
trinidad-demo/src/main/webapp/WEB-INF/
trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/
trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/
trin...
Author: awiner
Date: Wed May 16 17:39:21 2007
New Revision: 538778
URL: http://svn.apache.org/viewvc?view=rev&rev=538778
Log:
TRINIDAD-21: dialog framework does not pin view state of launching page
- Enhance the TokenCache to support the concept of "pinned" tokens, which won't
be flushed until everything that is pinning them down is gone
- Add a unit test for token cache to verify the above is working
- Add StateManagerImpl APIs to get the state token used for a request,
and to store a token that should be "pinned" by the current request
- Add DialogServiceImpl APIs to stash off a state token for use
in the dialog about to be launched and to pin that state token later,
in each case using the StateManagerImpl APIs
- Call the DialogService CoreRenderKit to invoke the DialogServiceImpl APIs
at the start and end of rendering
- Fix use of generics in LRUCache
- Add enough to the launchDialog demo to verify this is working
Added:
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java (with props)
Modified:
myfaces/trinidad/trunk/trinidad/trinidad-demo/src/main/webapp/WEB-INF/web.xml
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/DialogServiceImpl.java
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreRenderKit.java
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/LRUCache.java
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java
myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java
Modified: myfaces/trinidad/trunk/trinidad/trinidad-demo/src/main/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-demo/src/main/webapp/WEB-INF/web.xml?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-demo/src/main/webapp/WEB-INF/web.xml (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-demo/src/main/webapp/WEB-INF/web.xml Wed May 16 17:39:21 2007
@@ -31,6 +31,14 @@
<!--param-value>server</param-value-->
</context-param>
+ <!-- Parameter to set the maximum number of client view state tokens.
+ Uncomment this to test low-token-count scenarios.
+ <context-param>
+ <param-name>org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS</param-name>
+ <param-value>3</param-value>
+ </context-param>
+ -->
+
<!-- Trinidad by default uses an optimized client-side state saving
mechanism. To disable that, uncomment the following -->
<!--context-param>
@@ -121,16 +129,6 @@
<servlet-name>source</servlet-name>
<servlet-class>org.apache.myfaces.trinidaddemo.webapp.SourceCodeServlet</servlet-class>
</servlet>
-
- <!-- On some version of OC4J, the following is needed when using jsf 1.0 -->
- <!--servlet>
- <servlet-name>jsp</servlet-name>
- <servlet-class>oracle.jsp.runtimev2.JspServlet</servlet-class>
- <init-param>
- <param-name>tags_reuse_default</param-name>
- <param-value>none</param-value>
- </init-param>
- </servlet-->
<!-- Faces Servlet Mappings -->
<servlet-mapping>
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/application/StateManagerImpl.java Wed May 16 17:39:21 2007
@@ -279,8 +279,13 @@
// if this feature has not been disabled
_useViewRootCache(context) ? root : null);
+ // See if we should pin this new state to any old state
+ String pinnedToken = (String)
+ context.getExternalContext().getRequestMap().get(_PINNED_STATE_TOKEN_KEY);
+
token = cache.addNewEntry(pageState,
- stateMap);
+ stateMap,
+ pinnedToken);
}
// If we got the "applicationViewCache", we're using it.
else
@@ -307,6 +312,10 @@
// Create a "tokenView" which abuses SerializedView to store
// our token only
view = new SerializedView(token, null);
+
+ // And store the token for this request
+ context.getExternalContext().getRequestMap().put(_REQUEST_STATE_TOKEN_KEY,
+ token);
}
else
{
@@ -319,6 +328,29 @@
return view;
}
+ /**
+ * Requests that an old state token be "pinned" to the state of
+ * the current request. This means that the view state corresponding
+ * to the token will not be released before the state for this request
+ * is released.
+ */
+ static public void pinStateToRequest(FacesContext context, String stateToken)
+ {
+ context.getExternalContext().getRequestMap().put(
+ _PINNED_STATE_TOKEN_KEY, stateToken);
+
+ }
+
+ /**
+ * @return the state token for the current request
+ */
+ static public String getStateToken(FacesContext context)
+ {
+ return (String) context.getExternalContext().getRequestMap().get(
+ _REQUEST_STATE_TOKEN_KEY);
+ }
+
+
@Override
public void writeState(FacesContext context,
SerializedView state) throws IOException
@@ -917,6 +949,11 @@
private static final String _CACHED_SERIALIZED_VIEW =
"org.apache.myfaces.trinidadinternal.application.CachedSerializedView";
+ private static final String _REQUEST_STATE_TOKEN_KEY =
+ "org.apache.myfaces.trinidadinternal.application.REQUEST_STATE_TOKEN";
+
+ private static final String _PINNED_STATE_TOKEN_KEY =
+ "org.apache.myfaces.trinidadinternal.application.PINNED_STATE_TOKEN";
private static final String _APPLICATION_CACHE_TOKEN = "_a_";
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/DialogServiceImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/DialogServiceImpl.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/DialogServiceImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/DialogServiceImpl.java Wed May 16 17:39:21 2007
@@ -283,8 +283,48 @@
}
}
+ /**
+ * Store the current state token (if a dialog has been launched).
+ */
+ static public void writeCurrentStateToken(FacesContext context, String token)
+ {
+ if (token == null)
+ return;
+
+ // Locate the String array on the request map, and store the token in
+ // there if it exists. The String array is also going to be on the
+ // page flow scope map for the dialog
+ Object o =
+ context.getExternalContext().getRequestMap().get(_TARGET_FOR_STATE_TOKEN);
+ if (o instanceof String[])
+ {
+ String[] targetForToken = (String[]) o;
+ if (targetForToken.length == 1)
+ targetForToken[0] = token;
+ }
+ }
/**
+ * Store the current state token (if a dialog has been launched).
+ */
+ static public void pinPriorState(FacesContext context)
+ {
+ RequestContext rc = RequestContext.getCurrentInstance();
+ Object o = rc.getPageFlowScope().get(_TARGET_FOR_STATE_TOKEN);
+ if (o instanceof String[])
+ {
+ String[] targetForToken = (String[]) o;
+ if (targetForToken.length == 1)
+ {
+ String token = targetForToken[0];
+ if (token != null)
+ StateManagerImpl.pinStateToRequest(context, token);
+ }
+ }
+ }
+
+
+ /**
* Launch a dialog.
* @todo Don't save parameters for state-saving, page-flow scope, etc.
*/
@@ -308,6 +348,13 @@
// the renderkit to launch the dialog; which means
// we'll need to use the renderkit to close the dialog
dialogParameters.put(_USED_RENDER_KIT_KEY, Boolean.TRUE);
+
+ // We also need to pin down the current state token when we're
+ // inside the dialog. But we don't actually know what the token
+ // will be until we're done rendering. So leave a one-element
+ // String array that we can write to later.
+ String[] targetForToken = new String[1];
+ dialogParameters.put(_TARGET_FOR_STATE_TOKEN, targetForToken);
// Try to launch a window using the render kit. If that
// fails (or isn't needed), fall through to the same-window
@@ -327,6 +374,12 @@
// And we must pop the pageFlow scope immediately; it'll
// be restored later.
_context.getPageFlowScopeProvider().popPageFlowScope(context, false);
+
+ // We only need to pin the existing state when we're using the renderkit
+ // to launch the dialog - so only bother putting anything on the
+ // request in that case
+ context.getExternalContext().getRequestMap().put(_TARGET_FOR_STATE_TOKEN,
+ targetForToken);
}
else
{
@@ -454,6 +507,8 @@
"org.apache.myfaces.trinidadinternal.DialogUsedRK";
static private final String _RETURN_PARAM =
"org.apache.myfaces.trinidadinternal.ReturnParam";
+ static private final String _TARGET_FOR_STATE_TOKEN =
+ "org.apache.myfaces.trinidadinternal.StateToken";
static private final TrinidadLogger _LOG =
TrinidadLogger.createTrinidadLogger(DialogServiceImpl.class);
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreRenderKit.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreRenderKit.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreRenderKit.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/CoreRenderKit.java Wed May 16 17:39:21 2007
@@ -61,7 +61,9 @@
import org.apache.myfaces.trinidad.render.RenderUtils;
import org.apache.myfaces.trinidadinternal.agent.AgentUtil;
import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
+import org.apache.myfaces.trinidadinternal.application.StateManagerImpl;
import org.apache.myfaces.trinidadinternal.config.dispatch.DispatchResponseConfiguratorImpl;
+import org.apache.myfaces.trinidadinternal.context.DialogServiceImpl;
import org.apache.myfaces.trinidadinternal.context.TrinidadPhaseListener;
import org.apache.myfaces.trinidadinternal.io.DebugHtmlResponseWriter;
import org.apache.myfaces.trinidadinternal.io.DebugResponseWriter;
@@ -415,6 +417,8 @@
public void encodeBegin(FacesContext context)
{
/*CoreAdfRenderingContext arc = */new CoreRenderingContext();
+ // If there's any prior state, make sure our current "add" doesn't drop it
+ DialogServiceImpl.pinPriorState(context);
}
@@ -431,6 +435,11 @@
*/
public void encodeFinally(FacesContext context)
{
+ // Get the state token from the StateManager (if one is available)
+ String stateToken = StateManagerImpl.getStateToken(context);
+ // And push it onto the DialogService (in case we launched anything)
+ DialogServiceImpl.writeCurrentStateToken(context, stateToken);
+
RenderingContext arc = RenderingContext.getCurrentInstance();
if (arc != null)
{
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/LRUCache.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/LRUCache.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/LRUCache.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/LRUCache.java Wed May 16 17:39:21 2007
@@ -36,7 +36,7 @@
_maxSize = maxSize;
}
- protected void removing(Object key)
+ protected void removing(K key)
{
}
@@ -45,7 +45,7 @@
{
if (size() > _maxSize)
{
- Object key = eldest.getKey();
+ K key = eldest.getKey();
removing(key);
_LOG.finer("Discarding cached value for key {0}", key);
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/TokenCache.java Wed May 16 17:39:21 2007
@@ -24,6 +24,8 @@
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
@@ -33,7 +35,21 @@
/**
- * A simple tokenized cache
+ * A simple LRU tokenized cache. The cache is responsible for storing tokens,
+ * but the storage of the values referred to by those tokens is not handled
+ * by this class. Instead, the user of this class has to provide a Map
+ * instance to each call.
+ * <p>
+ * The design seems odd, but is intentional - this way, a session Map can be used
+ * directly as the storage target for values, while the TokenCache simply maintains
+ * the logic of which tokens should still be available. Storing values
+ * directly in the cache object (instead of directly on the session) causes
+ * HttpSession failover difficulties.
+ * <p>
+ * TokenCache also supports the concept of "pinning", whereby one token
+ * can be pinned to another. The pinned token will not be removed from
+ * the cache until all tokens that it is pinned to are also out of the
+ * cache.
*/
public class TokenCache implements Serializable
{
@@ -103,26 +119,59 @@
this(size, 0);
}
+ /**
+ * Create a TokenCache that will store the last "size" entries,
+ * and begins its tokens based on the seed (instead of always
+ * starting at "0").
+ */
public TokenCache(int size, int seed)
{
_cache = new LRU(size);
+ _pinned = new ConcurrentHashMap<String, String>(size);
_count = seed;
}
/**
* Create a new token; and use that token to store a value into
- * a target Map.
+ * a target Map. The least recently used values from the
+ * cache may be removed.
+ * @param value the value being added to the target store
+ * @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)
{
- Object remove = null;
+ return addNewEntry(value, targetStore, null);
+ }
+
+ /**
+ * Create a new token; and use that token to store a value into
+ * a target Map. The least recently used values from the
+ * cache may be removed.
+ * @param value the value being added to the target store
+ * @param targetStore the map used for storing the value
+ * @param pinnedToken a token, that if still in the cache,
+ * 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,
+ String pinnedToken)
+ {
+ String remove = null;
String token = null;
synchronized (this)
{
token = _getNextToken();
+ // If there is a request to pin one token to another,
+ // store that: the pinnedToken is the value
+ if (pinnedToken != null)
+ _pinned.put(token, pinnedToken);
+
assert(_removed == null);
// NOTE: this put() has a side-effect that can result
// in _removed being non-null afterwards
@@ -134,8 +183,10 @@
// This looks like "remove" must be null - given the
// assert above.
if (remove != null)
- targetStore.remove(remove);
-
+ {
+ _removeTokenIfReady(targetStore, remove);
+ }
+
targetStore.put(token, value);
return token;
@@ -143,17 +194,61 @@
/**
- * Returns true if an entry is still available.
+ * Returns true if an entry is still available. This
+ * method has a side-effect: by virtue of accessing the token,
+ * it is now at the top of the most-recently-used list.
*/
public boolean isAvailable(String token)
{
synchronized (this)
{
- return _cache.get(token) != null;
+ // If the token is in the LRU cache, then it's available
+ if (_cache.get(token) != null)
+ return true;
+
+ // And if the token is a value in "pinned", then it's also available
+ if (_pinned.containsValue(token))
+ return true;
+
+ return false;
}
}
/**
+ * 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,
+ String token)
+ {
+ Object removedValue;
+
+ // See if it's pinned to something still in memory
+ if (!_pinned.containsValue(token))
+ {
+ _LOG.finest("Removing token ''{0}''", token);
+ // Remove it from the target store
+ removedValue = targetStore.remove(token);
+ // Now, see if that key was pinning anything else
+ String wasPinned = _pinned.remove(token);
+ if (wasPinned != null)
+ // Yup, so see if we can remove that token
+ _removeTokenIfReady(targetStore, wasPinned);
+ }
+ else
+ {
+ _LOG.finest("Not removing pinned token ''{0}''", token);
+ // TODO: is this correct? We're not really removing
+ // the target value.
+ removedValue = targetStore.get(token);
+ }
+
+ return removedValue;
+ }
+
+
+ /**
* Removes a value from the cache.
* @return previous value associated with the token, if any
*/
@@ -165,7 +260,9 @@
{
_LOG.finest("Removing token {0} from cache", token);
_cache.remove(token);
- return targetStore.remove(token);
+ // TODO: should removing a value that is "pinned" take?
+ // Or should it stay in memory?
+ return _removeTokenIfReady(targetStore, token);
}
}
@@ -211,19 +308,24 @@
}
@Override
- protected void removing(Object key)
+ protected void removing(String key)
{
_removed = key;
}
}
private final Map<String, String> _cache;
+
+ // Map from String to String, where the keys represent tokens that are
+ // stored, and the values are the tokens that are pinned. This is
+ // an N->1 ratio: the values may appear multiple times.
+ private final Map<String, String> _pinned;
private int _count;
private transient Object _lock = new Object();
// Hack instance parameter used to communicate between the LRU cache's
// removing() method, and the addNewEntry() method that may trigger it
- private transient Object _removed;
+ private transient String _removed;
static private final boolean _USE_SESSION_TO_SEED_ID = true;
Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java?view=diff&rev=538778&r1=538777&r2=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java Wed May 16 17:39:21 2007
@@ -19,6 +19,7 @@
package org.apache.myfaces.trinidadinternal.renderkit;
import java.awt.Color;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -82,7 +83,7 @@
@Override
public Map<String, Object> getPageFlowScope()
{
- throw new UnsupportedOperationException("Not implemented yet");
+ return Collections.emptyMap();
}
@Override
@@ -301,4 +302,4 @@
static private TimeZone _FIXED_TIME_ZONE =
TimeZone.getTimeZone("America/Los_Angeles");
-}
\ No newline at end of file
+}
Added: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java?view=auto&rev=538778
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java (added)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java Wed May 16 17:39:21 2007
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.trinidadinternal.util;
+
+import junit.framework.TestCase;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Test of TokenCache.
+ * NOTE: Calling TokenCache.isAvailable() effectively bumps
+ * an item up to the top of the most-recently-used list.
+ * So the calls to assertTrue(cache.isAvailable(...)) are
+ * not without side-effects, and you can in fact break
+ * the test by removing or changing the order of assert calls!
+ */
+public class TokenCacheTest extends TestCase
+{
+ public TokenCacheTest(String testName)
+ {
+ super(testName);
+ }
+
+ public void testBasic()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ // Add a pre-existing key to the test to verify that it is untouched
+ // Slight assumption here that this key would never get hit
+ map.put("this-would-never-be-used", 17);
+
+ TokenCache cache = new TokenCache(2);
+
+ // Add first value to cache
+ String token1 = cache.addNewEntry(1, map);
+ assertEquals(Integer.valueOf(1), map.get(token1));
+ assertEquals(2, map.size());
+ assertTrue(cache.isAvailable(token1));
+
+ // Add second value to cache
+ String token2 = cache.addNewEntry(2, map);
+ assertEquals(Integer.valueOf(1), map.get(token1));
+ assertEquals(Integer.valueOf(2), map.get(token2));
+ assertEquals(3, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(cache.isAvailable(token2));
+
+ // Add third value to cache - first value is now gone
+ String token3 = cache.addNewEntry(3, map);
+ assertNull(map.get(token1));
+ assertEquals(Integer.valueOf(2), map.get(token2));
+ assertEquals(Integer.valueOf(3), map.get(token3));
+ assertEquals(3, map.size());
+ assertFalse(cache.isAvailable(token1));
+ assertTrue(cache.isAvailable(token2));
+ assertTrue(cache.isAvailable(token3));
+
+ // Remove the third value from the cache
+ Object removed = cache.removeOldEntry(token3, map);
+ assertEquals(2, map.size());
+ assertFalse(cache.isAvailable(token3));
+ assertEquals(Integer.valueOf(3), removed);
+ assertEquals(Integer.valueOf(17), map.get("this-would-never-be-used"));
+
+ // Clear the cache - only the non-cache value remains
+ cache.clear(map);
+ assertEquals(1, map.size());
+ assertEquals(Integer.valueOf(17), map.get("this-would-never-be-used"));
+ }
+
+ public void testIsAvailableAffectsOrder()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ TokenCache cache = new TokenCache(2);
+ String token1 = cache.addNewEntry(1, map);
+ String token2 = cache.addNewEntry(2, map);
+ // Check the availability of token1, which means that it
+ // is now more recently used than token2
+ assertTrue(cache.isAvailable(token1));
+
+ // Add token3: we now have 1 and 3, not 2 and 3
+ String token3 = cache.addNewEntry(3, map);
+ assertTrue(cache.isAvailable(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertTrue(cache.isAvailable(token3));
+ }
+
+ public void testPinned()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ // Add a pre-existing key to the test to verify that it is untouched
+ // Slight assumption here that this key would never get hit
+ map.put("this-would-never-be-used", 17);
+
+ TokenCache cache = new TokenCache(2);
+
+ // Add first value to cache
+ String token1 = cache.addNewEntry(1, map);
+ // Add second value to cache, but pin it to token1
+ String token2 = cache.addNewEntry(2, map, token1);
+
+ assertEquals(Integer.valueOf(1), map.get(token1));
+ assertEquals(Integer.valueOf(2), map.get(token2));
+ assertEquals(3, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(cache.isAvailable(token2));
+
+ // Add third value to cache - first value should still be present
+ // because it is pinned by token 2
+ String token3 = cache.addNewEntry(3, map);
+ assertEquals(Integer.valueOf(1), map.get(token1));
+ assertEquals(Integer.valueOf(2), map.get(token2));
+ assertEquals(Integer.valueOf(3), map.get(token3));
+ assertEquals(4, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(cache.isAvailable(token2));
+ assertTrue(cache.isAvailable(token3));
+
+ // Remove the second value from the cache, which
+ // should remove the first value as well
+ Object removed = cache.removeOldEntry(token2, map);
+ assertEquals(2, map.size());
+ assertFalse(cache.isAvailable(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertTrue(cache.isAvailable(token3));
+ assertEquals(Integer.valueOf(2), removed);
+ assertEquals(Integer.valueOf(3), map.get(token3));
+ assertEquals(Integer.valueOf(17), map.get("this-would-never-be-used"));
+
+ // Clear the cache - only the non-cache value remains
+ cache.clear(map);
+ assertEquals(1, map.size());
+ assertEquals(Integer.valueOf(17), map.get("this-would-never-be-used"));
+ }
+
+ public void testRecursivePinned()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ TokenCache cache = new TokenCache(2);
+
+ // Add first value to cache
+ String token1 = cache.addNewEntry(1, map);
+ // Add second value to cache, but pin it to token1
+ String token2 = cache.addNewEntry(2, map, token1);
+ // Add third value to cache, pinned to token2
+ String token3 = cache.addNewEntry(3, map, token2);
+ // Add fourth value to cache, pinned to token3
+ String token4 = cache.addNewEntry(4, map, token3);
+ assertEquals(4, map.size());
+
+ // Add fifth value to cache: all should still be present,
+ // because 4 and 5 are the last two, and 3, 2, and 1 stay pinned
+ String token5 = cache.addNewEntry(5, map);
+ assertEquals(5, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(map.containsKey(token1));
+ assertTrue(cache.isAvailable(token2));
+ assertTrue(map.containsKey(token2));
+ assertTrue(cache.isAvailable(token3));
+ assertTrue(map.containsKey(token3));
+ assertTrue(cache.isAvailable(token4));
+ assertTrue(map.containsKey(token4));
+ assertTrue(cache.isAvailable(token5));
+
+ // Add sixth value to cache: only two should now be present
+ String token6 = cache.addNewEntry(6, map);
+ assertFalse(cache.isAvailable(token1));
+ assertFalse(map.containsKey(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertFalse(map.containsKey(token2));
+ assertFalse(cache.isAvailable(token3));
+ assertFalse(map.containsKey(token3));
+ assertFalse(cache.isAvailable(token4));
+ assertFalse(map.containsKey(token4));
+ assertTrue(cache.isAvailable(token5));
+ assertTrue(cache.isAvailable(token6));
+ assertEquals(2, map.size());
+ }
+
+ public void testMultiplePinned()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ TokenCache cache = new TokenCache(2);
+
+ // Add first value to cache
+ String token1 = cache.addNewEntry(1, map);
+ // Add second value to cache, but pin it to token1
+ String token2 = cache.addNewEntry(2, map, token1);
+ // Add third value to cache, pinned to token1.
+ // All will be present, because 2 and 3 are the last
+ // two, but 1 is pinned.
+ String token3 = cache.addNewEntry(3, map, token1);
+ assertEquals(3, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(map.containsKey(token1));
+ assertTrue(cache.isAvailable(token2));
+ assertTrue(map.containsKey(token2));
+ assertTrue(cache.isAvailable(token3));
+ assertTrue(map.containsKey(token3));
+
+ // Add fourth value to cache, pinned to token1.
+ // Now should have 1, 3, and 4.
+ String token4 = cache.addNewEntry(4, map, token1);
+
+ assertEquals(3, map.size());
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(map.containsKey(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertFalse(map.containsKey(token2));
+ assertTrue(cache.isAvailable(token3));
+ assertTrue(map.containsKey(token3));
+ assertTrue(cache.isAvailable(token4));
+ assertTrue(map.containsKey(token4));
+
+ // Add fifth value to cache, unpinned: now should have 1, 4 and 5
+ String token5 = cache.addNewEntry(5, map);
+ assertEquals(3, map.size());
+
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(map.containsKey(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertFalse(map.containsKey(token2));
+ assertFalse(cache.isAvailable(token3));
+ assertFalse(map.containsKey(token3));
+ assertTrue(cache.isAvailable(token4));
+ assertTrue(map.containsKey(token4));
+ assertTrue(cache.isAvailable(token5));
+
+ // Add sixth value, unpinned. Should now only have 5 and 6
+ String token6 = cache.addNewEntry(6, map);
+ assertFalse(cache.isAvailable(token1));
+ assertFalse(map.containsKey(token1));
+ assertFalse(cache.isAvailable(token2));
+ assertFalse(map.containsKey(token2));
+ assertFalse(cache.isAvailable(token3));
+ assertFalse(map.containsKey(token3));
+ assertFalse(cache.isAvailable(token4));
+ assertFalse(map.containsKey(token4));
+ assertTrue(cache.isAvailable(token5));
+ assertTrue(cache.isAvailable(token6));
+ }
+
+ public void testPinnedInTinyCache()
+ {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ TokenCache cache = new TokenCache(1);
+ // Verify that adding a token that would have flushed an entry
+ // won't do so as long as it's pinning that entry
+ String token1 = cache.addNewEntry(1, map);
+ String token2 = cache.addNewEntry(2, map, token1);
+ String token3 = cache.addNewEntry(3, map, token2);
+ assertTrue(cache.isAvailable(token1));
+ assertTrue(cache.isAvailable(token2));
+ assertTrue(cache.isAvailable(token3));
+ assertEquals(3, map.size());
+ }
+}
Propchange: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/TokenCacheTest.java
------------------------------------------------------------------------------
svn:executable = *