JBoss Cache SVN: r4628 - in core/trunk/src: test/java/org/jboss/cache/util and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: genman
Date: 2007-10-16 23:11:06 -0400 (Tue, 16 Oct 2007)
New Revision: 4628
Added:
core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java
core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java
Log:
Wrapper for a Map that tracks changes against the wrapped map
Saves data copying for a small set of modifications
Added: core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java 2007-10-17 03:11:06 UTC (rev 4628)
@@ -0,0 +1,252 @@
+package org.jboss.cache.util;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Wraps an existing map, which is not modified, reflecting modifications
+ * in an internal modification set.
+ *
+ * This is to minimize the amount of data copying, for instance in the
+ * case few changes are applied.
+ *
+ * Example usage:
+ <pre>
+HashMap<String, String> hm = new HashMap<String, String>();
+hm.put("a", "apple");
+DeltaMap<String, String> dm = DeltaMap.create(hm);
+dm.remove("a");
+assert hm.containsKey("a");
+assert !dm.containsKey("a");
+dm.commit();
+assert !hm.containsKey("a");
+</pre>
+ *
+ * @author Elias Ross
+ * @param <K> key type
+ * @param <V> value type
+ */
+public class DeltaMap<K, V> extends AbstractMap<K, V>
+{
+
+ /**
+ * Wrapped instance.
+ */
+ private Map<K, V> original;
+
+ /**
+ * Keys removed.
+ * This should not exceed the size of the original map.
+ */
+ private Set<K> removed = new HashSet<K>();
+
+ /**
+ * Keys changed.
+ * This may contain new entries or entries modified.
+ */
+ private Map<K, V> changed = new HashMap<K, V>();
+
+ /**
+ * Constructs a new DeltaMap.
+ * @param original will not be modified.
+ */
+ private DeltaMap(Map<K, V> original)
+ {
+ if (original == null)
+ throw new NullPointerException("original");
+ this.original = original;
+ }
+
+ /**
+ * Creates and returns a DeltaMap for an original map.
+ *
+ * @param original will not be modified, except by {@link #commit()}
+ * @return a new instance
+ */
+ public static <K, V> DeltaMap<K, V> create(Map<K, V> original)
+ {
+ return new DeltaMap<K, V>(original);
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet()
+ {
+ return new AbstractSet<Entry<K, V>>()
+ {
+
+ @Override
+ public Iterator<java.util.Map.Entry<K, V>> iterator()
+ {
+ return new WrappedIterator();
+ }
+
+ @Override
+ public int size()
+ {
+ int size = original.size() - removed.size();
+ for (Object o : changed.keySet())
+ {
+ if (!original.containsKey(o))
+ size++;
+ }
+ return size;
+ }
+ };
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ if (removed.contains(key))
+ return false;
+ if (changed.containsKey(key))
+ return true;
+ return original.containsKey(key);
+ }
+
+ @Override
+ public V get(Object key)
+ {
+ if (removed.contains(key))
+ return null;
+ if (changed.containsKey(key))
+ return changed.get(key);
+ return original.get(key);
+ }
+
+ @Override
+ public V put(K key, V value)
+ {
+ V old;
+ if (changed.containsKey(key))
+ old = changed.get(key);
+ else
+ old = original.get(key);
+ changed.put(key, value);
+ if (removed.contains(key))
+ {
+ removed.remove(key);
+ return null;
+ }
+ return old;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public V remove(Object key)
+ {
+ if (changed.containsKey(key))
+ {
+ if (original.containsKey(key))
+ removed.add((K) key);
+ return changed.remove(key);
+ }
+ if (removed.contains(key))
+ {
+ return null;
+ }
+ if (original.containsKey(key))
+ {
+ removed.add((K) key);
+ return original.get(key);
+ }
+ return null;
+ }
+
+ /**
+ * Commits the changes to the original map.
+ * Clears the list of removed keys.
+ */
+ public void commit()
+ {
+ original.keySet().removeAll(removed);
+ original.putAll(changed);
+ removed.clear();
+ changed.clear();
+ }
+
+ /**
+ * Iterator that skips over removed entries.
+ * @author Elias Ross
+ */
+ private class WrappedIterator implements Iterator<Entry<K, V>>
+ {
+
+ private boolean orig = true;
+ private boolean nextSet = false;
+ private Entry<K, V> next;
+ private Iterator<Entry<K, V>> i = original.entrySet().iterator();
+
+ private boolean redef(Entry<K, V> e)
+ {
+ K key = e.getKey();
+ return removed.contains(key) || changed.containsKey(key);
+ }
+
+ public boolean hasNext()
+ {
+ if (nextSet)
+ return true;
+ if (orig)
+ {
+ while (i.hasNext() && redef(next = i.next()));
+ nextSet = true;
+ if (!i.hasNext())
+ {
+ orig = false;
+ i = changed.entrySet().iterator();
+ }
+ }
+ else
+ {
+ if (!i.hasNext())
+ return false;
+ next = i.next();
+ nextSet = true;
+ return true;
+ }
+ return i.hasNext();
+ }
+
+ public java.util.Map.Entry<K, V> next()
+ {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ try
+ {
+ return next;
+ }
+ finally
+ {
+ nextSet = false;
+ }
+ }
+
+ public void remove()
+ {
+ DeltaMap.this.remove(next.getKey());
+ }
+
+ }
+
+ /**
+ * Returns a debug string.
+ */
+ public String toDebugString() {
+ return "DeltaMap original=" + original + " removed=" + removed + " changed=" + changed;
+ }
+
+ @Override
+ public void clear()
+ {
+ removed.addAll(original.keySet());
+ changed.clear();
+ }
+
+}
Added: core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java 2007-10-17 03:11:06 UTC (rev 4628)
@@ -0,0 +1,111 @@
+package org.jboss.cache.util;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.fail;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Map.Entry;
+
+import org.testng.annotations.Test;
+
+@Test(groups = {"functional", "transaction"})
+public class DeltaMapTest
+{
+
+ public static String Y = "y";
+
+ public static String Z = "z";
+
+ public static String K = "k";
+
+ HashMap<String, String> hm = new HashMap<String, String>();
+ {
+ hm.put(null, null);
+ hm.put(Y, Z);
+ hm.put(K, Y);
+ }
+
+ HashMap<String, String> backup = new HashMap<String, String>(hm);
+
+ DeltaMap<String, String> dm = DeltaMap.create(hm);
+
+ public void testChanges() throws Exception
+ {
+ assertEquals(backup, dm);
+ assertEquals(Z, dm.remove(Y));
+ assertEquals(null, dm.remove(Y));
+ assertEquals("changes not made to underlying map", backup, hm);
+ assertEquals(false, dm.containsKey(Y));
+ assertEquals(false, dm.containsValue(Z));
+ assertEquals(null, dm.put(Y, Z));
+ assertEquals(Z, dm.put(Y, Z));
+ assertEquals("changes not made to underlying map", backup, hm);
+ assertEquals(backup.size(), dm.size());
+ assertEquals(backup, dm);
+ dm.commit();
+ assertEquals(hm, dm);
+ dm.commit();
+ assertEquals(hm, dm);
+ }
+
+ public void testAddRemove() throws Exception
+ {
+ dm.remove(K);
+ dm.put(K, Z);
+ assertEquals(Z, dm.get(K));
+ assertEquals(Z, dm.remove(K));
+ assertEquals(null, dm.remove(K));
+ }
+
+ public void testClear() throws Exception
+ {
+ dm.clear();
+ assertEquals(0, dm.size());
+ assertEquals(backup, hm);
+ assertEquals(null, dm.remove(Y));
+ }
+
+ public void testIterator() throws Exception
+ {
+ dm.remove(null);
+ dm.put(K, Y);
+ System.out.println(dm.toDebugString());
+ System.out.println(dm.toString());
+ Iterator<Entry<String, String>> i = dm.entrySet().iterator();
+ assertEquals(true, i.hasNext());
+ assertEquals(true, i.hasNext());
+ i.next();
+ assertEquals(true, i.hasNext());
+ i.next();
+ assertEquals("" + dm, false, i.hasNext());
+ try
+ {
+ i.next();
+ fail("no next");
+ }
+ catch (NoSuchElementException e)
+ {
+ }
+ try
+ {
+ i.next();
+ fail("no next");
+ }
+ catch (NoSuchElementException e)
+ {
+ }
+ }
+
+ public void testEx() {
+HashMap<String, String> hm = new HashMap<String, String>();
+hm.put("a", "apple");
+DeltaMap<String, String> dm = DeltaMap.create(hm);
+dm.remove("a");
+assert hm.containsKey("a");
+assert !dm.containsKey("a");
+dm.commit();
+assert !hm.containsKey("a");
+ }
+}
17 years
JBoss Cache SVN: r4627 - core/trunk/src/test/java/org/jboss/cache/api/pfer.
by jbosscache-commits@lists.jboss.org
Author: bstansberry(a)jboss.com
Date: 2007-10-16 12:54:42 -0400 (Tue, 16 Oct 2007)
New Revision: 4627
Modified:
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java
Log:
Fix the cache mode's
Modified: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java 2007-10-16 16:27:34 UTC (rev 4626)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java 2007-10-16 16:54:42 UTC (rev 4627)
@@ -19,6 +19,6 @@
public PFERPessimisticInvalidationAsyncTest()
{
optimistic = false;
- cacheMode = Configuration.CacheMode.INVALIDATION_SYNC;
+ cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC;
}
}
Modified: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java 2007-10-16 16:27:34 UTC (rev 4626)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java 2007-10-16 16:54:42 UTC (rev 4627)
@@ -7,6 +7,7 @@
package org.jboss.cache.api.pfer;
import org.jboss.cache.config.Configuration;
+import org.testng.annotations.Test;
/**
* Test putForExternalRead with pessimistic locking and INVALIDATION_ASYNC.
@@ -19,7 +20,9 @@
public PFERPessimisticInvalidationSyncTest()
{
optimistic = false;
- cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC;
+ cacheMode = Configuration.CacheMode.INVALIDATION_SYNC;
}
+ @Test
+ public void blah() {}
}
17 years
JBoss Cache SVN: r4626 - core/trunk/src/main/java/org/jboss/cache/interceptors.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-10-16 12:27:34 -0400 (Tue, 16 Oct 2007)
New Revision: 4626
Modified:
core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java
Log:
Unnecessary sync?
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java 2007-10-16 14:30:18 UTC (rev 4625)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java 2007-10-16 16:27:34 UTC (rev 4626)
@@ -110,9 +110,12 @@
List<Fqn> nodesCreated = new ArrayList<Fqn>();
// synchronize on the workspace so that more than one thread doesn't attempt to put stuff into the workspace for
- // the same transaction.
+ // the same transaction. Isn't this unlikely though, given that each transaction would only occur in a single thread
+ // at a time?
+/*
synchronized (workspace)
{
+*/
DataVersion version = null;
if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null)
{
@@ -223,7 +226,7 @@
}
}
- }// end sync block
+ //}// end sync block
if (!suppressNotification)
{
17 years
JBoss Cache SVN: r4625 - core/trunk/src/test/java/org/jboss/cache/api/pfer.
by jbosscache-commits@lists.jboss.org
Author: bstansberry(a)jboss.com
Date: 2007-10-16 10:30:18 -0400 (Tue, 16 Oct 2007)
New Revision: 4625
Added:
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.java
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.java
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java
core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java
Log:
[JBCACHE-1027] Add tests with invalidation
Added: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.java 2007-10-16 14:30:18 UTC (rev 4625)
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.cache.api.pfer;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * Test putForExternalRead with optimistic locking and INVALIDATION_ASYNC.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class PFEROptimisticInvalidationAsyncTest extends PutForExternalReadTestBase
+{
+ public PFEROptimisticInvalidationAsyncTest()
+ {
+ optimistic = true;
+ cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC;
+ }
+}
Added: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.java 2007-10-16 14:30:18 UTC (rev 4625)
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.cache.api.pfer;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * Test putForExternalRead with optimistic locking and INVALIDATION_SYNC.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class PFEROptimisticInvalidationSyncTest extends PutForExternalReadTestBase
+{
+ public PFEROptimisticInvalidationSyncTest()
+ {
+ optimistic = true;
+ cacheMode = Configuration.CacheMode.INVALIDATION_SYNC;
+ }
+}
Added: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.java 2007-10-16 14:30:18 UTC (rev 4625)
@@ -0,0 +1,24 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.cache.api.pfer;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * Test putForExternalRead with pessimistic locking and INVALIDATION_SYNC.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class PFERPessimisticInvalidationAsyncTest extends PutForExternalReadTestBase
+{
+ public PFERPessimisticInvalidationAsyncTest()
+ {
+ optimistic = false;
+ cacheMode = Configuration.CacheMode.INVALIDATION_SYNC;
+ }
+}
Added: core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.java 2007-10-16 14:30:18 UTC (rev 4625)
@@ -0,0 +1,25 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.cache.api.pfer;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * Test putForExternalRead with pessimistic locking and INVALIDATION_ASYNC.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class PFERPessimisticInvalidationSyncTest extends PutForExternalReadTestBase
+{
+ public PFERPessimisticInvalidationSyncTest()
+ {
+ optimistic = false;
+ cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC;
+ }
+
+}
17 years
JBoss Cache SVN: r4624 - core/trunk/src/test/java/org/jboss/cache/api/pfer.
by jbosscache-commits@lists.jboss.org
Author: bstansberry(a)jboss.com
Date: 2007-10-16 10:27:56 -0400 (Tue, 16 Oct 2007)
New Revision: 4624
Modified:
core/trunk/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java
Log:
[JBCACHE-1027] Add tests with invalidation
Add tests of cacheModeLocal
Modified: core/trunk/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java 2007-10-16 14:19:21 UTC (rev 4623)
+++ core/trunk/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java 2007-10-16 14:27:56 UTC (rev 4624)
@@ -31,6 +31,7 @@
import org.jboss.cache.RPCManager;
import org.jboss.cache.RPCManagerImpl;
import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.lock.NodeLock;
import org.jboss.cache.misc.TestingUtil;
@@ -156,7 +157,10 @@
asyncWait();
assertEquals("Parent node write should have succeeded", value2, cache1.get(parentFqn, key));
- assertEquals("Parent node write should have replicated", value2, cache2.get(parentFqn, key));
+ if (isUsingInvalidation())
+ assertNull("Parent node write should have invalidated", cache2.get(parentFqn, key));
+ else
+ assertEquals("Parent node write should have replicated", value2, cache2.get(parentFqn, key));
if (!optimistic)
{
@@ -172,7 +176,10 @@
asyncWait();
assertEquals("PFER should have succeeded", value, cache1.get(fqn, key));
- assertEquals("PFER should have replicated", value, cache2.get(fqn, key));
+ if (isUsingInvalidation())
+ assertNull("PFER should not have effected cache2", cache2.get(fqn, key));
+ else
+ assertEquals("PFER should have replicated", value, cache2.get(fqn, key));
// reset
cache1.removeNode(fqn);
@@ -188,7 +195,10 @@
cache1.putForExternalRead(fqn, key, value2);
assertEquals("PFER should have been a no-op", value, cache1.get(fqn, key));
- assertEquals("PFER should have been a no-op", value, cache2.get(fqn, key));
+ if (isUsingInvalidation())
+ assertNull("PFER should have been a no-op", cache2.get(fqn, key));
+ else
+ assertEquals("PFER should have been a no-op", value, cache2.get(fqn, key));
}
private List<Address> anyAddresses()
@@ -242,7 +252,10 @@
assertLocked(parentFqn, parentNode, workspace, false);
assertEquals("PFER should have completed", value, cache1.get(fqn, key));
- assertEquals("PFER should have completed", value, cache2.get(fqn, key));
+ if (isUsingInvalidation())
+ assertNull("PFER should not have effected cache2", cache2.get(fqn, key));
+ else
+ assertEquals("PFER should have completed", value, cache2.get(fqn, key));
tm1.resume(t);
tm1.commit();
@@ -250,7 +263,10 @@
asyncWait();
assertEquals("parent fqn tx should have completed", value, cache1.get(parentFqn, key));
- assertEquals("parent fqn tx should have completed", value, cache2.get(parentFqn, key));
+ if (isUsingInvalidation())
+ assertNull("parent fqn tx should have invalidated cache2", cache2.get(parentFqn, key));
+ else
+ assertEquals("parent fqn tx should have completed", value, cache2.get(parentFqn, key));
}
public void testExceptionSuppression()
@@ -298,7 +314,79 @@
// should not barf
cache1.putForExternalRead(fqn, key, value);
}
+
+ public void testBasicPropagation() throws Exception {
+
+ cache1.putForExternalRead(fqn, key, value);
+
+ asyncWait();
+
+ assertEquals("PFER updated cache1", value, cache1.get(fqn, key));
+ Object expected = isUsingInvalidation() ? null : value;
+ assertEquals("PFER propagated to cache2 as expected", expected, cache2.get(fqn, key));
+ cache2.putForExternalRead(fqn, key, value);
+
+ asyncWait();
+
+ assertEquals("PFER updated cache2", value, cache2.get(fqn, key));
+ assertEquals("PFER propagated to cache1 as expected", value, cache1.get(fqn, key));
+ }
+
+ /**
+ * Tests that setting a cacheModeLocal=true Option prevents propagation
+ * of the putForExternalRead().
+ *
+ * @throws Exception
+ */
+ public void testSimpleCacheModeLocal() throws Exception
+ {
+ cacheModeLocalTest(false);
+ }
+
+ /**
+ * Tests that setting a cacheModeLocal=true Option prevents propagation
+ * of the putForExternalRead() when the call occurs inside a transaction.
+ *
+ * @throws Exception
+ */
+ public void testCacheModeLocalInTx() throws Exception
+ {
+ cacheModeLocalTest(true);
+ }
+
+ /**
+ * Tests that setting a cacheModeLocal=true Option prevents propagation
+ * of the putForExternalRead().
+ * @throws Exception
+ */
+ private void cacheModeLocalTest(boolean transactional) throws Exception
+ {
+ RPCManager rpcManager = EasyMock.createMock(RPCManager.class);
+ RPCManager originalRpcManager = cache1.getConfiguration().getRuntimeConfig().getRPCManager();
+
+ // inject a mock RPC manager so that we can test whether calls made are sync or async.
+ cache1.getConfiguration().getRuntimeConfig().setRPCManager(rpcManager);
+
+ // specify that we expect nothing will be called on the mock Rpc Manager.
+ replay(rpcManager);
+
+ // now try a simple replication. Since the RPCManager is a mock object it will not actually replicate anything.
+ if (transactional)
+ tm1.begin();
+
+ cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+ cache1.putForExternalRead(fqn, key, value);
+
+ if (transactional)
+ tm1.commit();
+
+ verify(rpcManager);
+ // cleanup
+ cache1.getConfiguration().getRuntimeConfig().setRPCManager(originalRpcManager);
+ cache1.removeNode(fqn);
+ }
+
protected void assertLocked(Fqn fqn, NodeSPI n, TransactionWorkspace workspace, boolean write_locked) throws Exception
{
// this needs to cater for "optimistically locked" nodes as well.
@@ -343,4 +431,10 @@
{
TestingUtil.sleepThread(500);
}
+
+ protected boolean isUsingInvalidation()
+ {
+ return cacheMode == CacheMode.INVALIDATION_ASYNC
+ || cacheMode == CacheMode.INVALIDATION_SYNC;
+ }
}
17 years
JBoss Cache SVN: r4623 - in core/trunk/src: test/java/org/jboss/cache/marshall and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-10-16 10:19:21 -0400 (Tue, 16 Oct 2007)
New Revision: 4623
Modified:
core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java
core/trunk/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java
Log:
JBCACHE-1170 - Leakage of marshalling region stored in thread local
Modified: core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java 2007-10-16 14:17:46 UTC (rev 4622)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java 2007-10-16 14:19:21 UTC (rev 4623)
@@ -80,7 +80,7 @@
init(manager, defaultInactive, useRegionBasedMarshalling);
if (useRegionBasedMarshalling)
{
- log.debug("Using region based marshalling logic : marshalling Fqn as a String first for every call.");
+ log.debug("Using region based marshalling logic : marshalling Fqn first for every call.");
}
}
@@ -106,6 +106,8 @@
{
// if the return value we're trying to marshall is null we're easy ...
region = null;
+ // we still need to clear the thread local though.
+ regionForCall.remove();
}
else if (isReturnValue(o))
{
@@ -113,6 +115,8 @@
// let's see if an incoming unmarshalling call for this exists, in the same thread stack and had registered
// a Fqn region.
region = regionForCall.get();
+ regionForCall.remove();
+ if (log.isTraceEnabled()) log.trace("Suspect this is a return value. Extract region from ThreadLocal as " + region);
// otherwise, we need to marshall the retval.
}
@@ -188,6 +192,7 @@
}
if (region == null)
{
+ if (log.isDebugEnabled()) log.debug("Region does not exist for Fqn " + regionFqn + " - not using a context classloader.");
retValue = unmarshallObject(in, refMap);
}
else
@@ -655,7 +660,7 @@
return gtx;
}
- private Fqn unmarshallFqn(ObjectInputStream in, Map<Integer, Object> refMap) throws Exception
+ Fqn unmarshallFqn(ObjectInputStream in, Map<Integer, Object> refMap) throws Exception
{
boolean isRoot = in.readBoolean();
Modified: core/trunk/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java 2007-10-16 14:17:46 UTC (rev 4622)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java 2007-10-16 14:19:21 UTC (rev 4623)
@@ -6,11 +6,23 @@
*/
package org.jboss.cache.marshall;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Region;
+import org.jboss.cache.RegionManager;
import static org.testng.AssertJUnit.assertEquals;
-
-import org.jboss.cache.RegionManager;
import org.testng.annotations.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
@Test(groups = {"functional"})
public class CacheMarshaller200Test extends CacheMarshallerTestBase
{
@@ -33,4 +45,165 @@
assertEquals("hello", unmarshalled);
}
+ public void testRegionalisedStream() throws Exception
+ {
+ // need to test what's going on with
+ CacheMarshaller200 cm200 = new CacheMarshaller200(new RegionManager(), false, true);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ cm200.objectToObjectStream("Hello World", oos, Fqn.fromString("/hello"));
+ oos.close();
+
+ ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
+
+ // test that the first thing on the stream is the fqn!
+ byte magic = ois.readByte();
+ short ref = ois.readShort();
+ System.out.println("Magic number is " + magic);
+
+ assert magic == CacheMarshaller200.MAGICNUMBER_FQN;
+
+ // now the chunks of an Fqn
+ Fqn f = cm200.unmarshallFqn(ois, new HashMap<Integer, Object>());
+
+ assert f.equals(Fqn.fromString("/hello"));
+ }
+
+ /**
+ * This test emulates the behaviour observed when an incoming method call is unmarshalled (using objectFromOutputStream),
+ * region based marshalling is used, and it is expected that we would need to marshall the response using the same
+ * region as well. To deal with this, the region (as an Fqn) is read off the stream when unmarshalling and then
+ * stored in a ThreadLocal so it can be accessed for when the response needs to be marshalled again.
+ *
+ * The problem here - registered as JBCACHE-1170 - is that this has a tendency to leak scope and affect more than the
+ * required method call.
+ *
+ */
+ public void testLeakageOfFqn() throws Throwable
+ {
+ // Use a thread pool so that we know threads will be reused.
+ // You don't need any concurrency here to demonstrate the failure - a single thread is enough.
+ ExecutorService e = Executors.newFixedThreadPool(1);
+ // to capture throwables
+ final List<Throwable> throwables = new CopyOnWriteArrayList<Throwable>();
+
+ // each thread will perform 1 of 3 tasks:
+ // 1. unmarshall a stream, and marshall a response - typical case such as a clustered get
+ // 2. unmarshall a stream, and marshall a 'void' response.
+ // 3. marshall a (primitive response) - case such as a state transfer sending out boolean status
+
+ // first create a stream to unmarshall.
+ RegionManager rm = new RegionManager();
+ final CacheMarshaller200 cm200 = new CacheMarshaller200(rm, false, true);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ final Fqn region = Fqn.fromString("/hello");
+ Region r = rm.getRegion(region, true);
+ r.registerContextClassLoader(this.getClass().getClassLoader());
+ cm200.objectToObjectStream(MethodCallFactory.create(MethodDeclarations.clusteredGetMethod, null, null), oos, region);
+ oos.close();
+
+ final byte[] stream = baos.toByteArray();
+ // so now the stream starts with the Fqn "/hello".
+
+ // repeat 100 times
+ for (int i=0; i<100; i++)
+ {
+ if (i % 3 == 0)
+ {
+ // task 1 above
+ e.execute(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ cm200.objectFromObjectStream(new ObjectInputStream(new ByteArrayInputStream(stream)));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream outStream = new ObjectOutputStream(out);
+ cm200.objectToObjectStream("A result", outStream);
+ outStream.close();
+ out.close();
+ // test that the output stream has got "/hello" as it's region Fqn.
+ ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
+ assert in.readByte() == CacheMarshaller200.MAGICNUMBER_FQN : "The stream should start with an Fqn";
+ // discard the nest refId short
+ in.readShort();
+ Fqn f = cm200.unmarshallFqn(in, new HashMap<Integer, Object>());
+ assert region.equals(f) : "Should use the same region for the response as was used for the request!";
+
+ }
+ catch (Throwable t)
+ {
+ throwables.add(t);
+ }
+ }
+ });
+ }
+ else if (i % 3 == 1)
+ {
+ // task 2 above
+ e.execute(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ cm200.objectFromObjectStream(new ObjectInputStream(new ByteArrayInputStream(stream)));
+ // and now just send back a 'void' return type (In JGroups this is treated as a null)
+ cm200.objectToObjectStream(null, new ObjectOutputStream(new ByteArrayOutputStream()));
+ }
+ catch (Throwable t)
+ {
+ throwables.add(t);
+ }
+ }
+ });
+ }
+ else if (i % 3 == 2)
+ {
+ // task 3 above
+ e.execute(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+
+ // and now don't bother with any umarshalling
+ // directly marshall a boolean.
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream outStream = new ObjectOutputStream(out);
+ cm200.objectToObjectStream(true, outStream);
+ outStream.close();
+ out.close();
+ // test that the output stream has got "/hello" as it's region Fqn.
+ ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
+ byte magic = in.readByte();
+
+ assert magic != CacheMarshaller200.MAGICNUMBER_FQN : "The stream should NOT start with an Fqn!";
+ assert magic == CacheMarshaller200.MAGICNUMBER_NULL : "Should start with a NULL";
+ assert in.readByte() == CacheMarshaller200.MAGICNUMBER_BOOLEAN : "Should have a boolean magic number before the boolean value";
+ assert in.readBoolean() : "The boolean written to the stream should be true";
+
+ }
+ catch (Throwable t)
+ {
+ throwables.add(t);
+ }
+ }
+ });
+ }
+ }
+
+ e.shutdown();
+ e.awaitTermination(60, TimeUnit.SECONDS);
+
+ for (Throwable t : throwables)
+ {
+ t.printStackTrace();
+ }
+
+ assert throwables.size() == 0 : "Should not have caught any exceptions!";
+ }
}
17 years