/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.loader;
import junit.framework.Assert;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.TimeoutException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
import EDU.oswego.cs.dl.util.concurrent.CountDown;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
/**
* Tests ClusteredCacheLoader
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
*/
public class ClusteredCacheLoaderTest extends AbstractCacheLoaderTestBase
{
private static Log log = LogFactory.getLog(ClusteredCacheLoaderTest.class);
private TreeCache cache1, cache2;
private CacheLoader loader1, loader2;
private Fqn fqn = Fqn.fromString("/a");
private String key = "key";
protected boolean useRegionBasedMarshalling = false;
protected void setUp() throws Exception
{
if (cache1 != null || cache2 != null) tearDown();
cache1 = new TreeCache("CCL-Test", null, 2000);
cache2 = new TreeCache("CCL-Test", null, 2000);
cache1.setCacheMode(TreeCache.REPL_SYNC);
cache2.setCacheMode(TreeCache.REPL_SYNC);
cache1.setCacheLoaderConfiguration( getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=500", false, false, false) );
cache2.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=500", false, false, false));
cache1.setUseRegionBasedMarshalling(useRegionBasedMarshalling);
cache2.setUseRegionBasedMarshalling(useRegionBasedMarshalling);
if (useRegionBasedMarshalling)
{
cache1.getRegionManager().createRegion(fqn, this.getClass().getClassLoader(), false);
cache2.getRegionManager().createRegion(fqn, this.getClass().getClassLoader(), false);
}
cache1.startService();
cache2.startService();
loader1 = cache1.getCacheLoader();
loader2 = cache2.getCacheLoader();
}
protected void tearDown()
{
if (cache1 != null)
{
cache1.stopService();
cache1 = null;
loader1 = null;
}
if (cache2 != null)
{
cache2.stopService();
cache2 = null;
loader2 = null;
}
}
public void testGetKeyValue() throws Exception
{
cache1.put(fqn, key, "value");
log.info("Finished put");
// test that this has propagated.
Assert.assertEquals("value", loader1.get(fqn).get(key));
Assert.assertEquals("value", loader2.get(fqn).get(key));
cache1.evict(fqn);
// now cache 1 should not have this but cache 2 should.
// loader1 looks at cache2 while loader2 looks at cache1
Assert.assertEquals("value", loader1.get(fqn).get(key));
Assert.assertNull("Expecting null", loader2.get(fqn));
// // calling a get on cache1 should cause the loader to retrieve the node from cache2
Assert.assertEquals("value", cache1.get(fqn, key));
// // and now loader2 should see it
// Assert.assertEquals("value", loader2.get(fqn).get(key));
}
public void testGet() throws Exception
{
cache1.put(fqn, key, "value");
// test that this has propagated.
Map map = loader1.get(fqn);
Assert.assertTrue("Should contain key", map.containsKey(key));
Assert.assertEquals("value", map.get(key));
Assert.assertEquals(1, map.size());
map = loader2.get(fqn);
Assert.assertTrue("Should contain key", map.containsKey(key));
Assert.assertEquals("value", map.get(key));
Assert.assertEquals(1, map.size());
cache1.evict(fqn);
// now cache 1 should not have this but cache 2 should.
// loader1 looks at cache2 while loader2 looks at cache1
map = loader1.get(fqn);
Assert.assertTrue(map.containsKey(key));
Assert.assertEquals("value", map.get(key));
Assert.assertEquals(1, map.size());
Assert.assertNull("Expecting null", loader2.get(fqn));
map = loader2.get(fqn);
Assert.assertNull( "Should be null", map );
// calling a get on cache1 should cause the loader to retrieve the node from cache2
Assert.assertEquals("value", cache1.get(fqn, key));
// and now loader2 should see it
map = loader2.get(fqn);
Assert.assertTrue(map.containsKey(key));
Assert.assertEquals("value", map.get(key));
Assert.assertEquals(1, map.size());
}
public void testGetChildrenNames() throws Exception
{
cache1.put(fqn, key, "value");
Fqn child1 = new Fqn(fqn, "child1");
Fqn child2 = new Fqn(fqn, "child2");
Fqn child3 = new Fqn(fqn, "child3");
cache1.put(child1, key, "value");
cache1.put(child2, key, "value");
cache1.put(child3, key, "value");
// test that this has propagated.
Set childNames = loader1.getChildrenNames(fqn);
Assert.assertEquals(3, childNames.size());
childNames = loader2.getChildrenNames(fqn);
Assert.assertEquals(3, childNames.size());
cache1.evict(child1);
cache1.evict(child2);
cache1.evict(child3);
cache1.evict(fqn);
// now cache 1 should not have this but cache 2 should.
// loader1 looks at cache2 while loader2 looks at cache1
childNames = loader1.getChildrenNames(fqn);
Assert.assertEquals(3, childNames.size());
childNames = loader2.getChildrenNames(fqn);
Assert.assertNull("should be null", childNames);
// calling a get on cache1 should cause the loader to retrieve the node from cache2
Assert.assertEquals("value", cache1.get(fqn, key));
// load up children
Assert.assertEquals("value", cache1.get(child1, key));
Assert.assertEquals("value", cache1.get(child2, key));
Assert.assertEquals("value", cache1.get(child3, key));
// and now loader2 should see it
childNames = loader2.getChildrenNames(fqn);
Assert.assertEquals(3, childNames.size());
}
public void testExists() throws Exception
{
cache1.put(fqn, key, "value");
// test that this has propagated.
Assert.assertTrue("should exist", loader1.exists(fqn));
Assert.assertTrue("should exist", loader2.exists(fqn));
cache1.evict(fqn);
// now cache 1 should not have this but cache 2 should.
// loader1 looks at cache2 while loader2 looks at cache1
Assert.assertTrue("should exist", loader1.exists(fqn));
Assert.assertTrue("should not exist", !loader2.exists(fqn));
// calling a get on cache1 should cause the loader to retrieve the node from cache2
Assert.assertEquals("value", cache1.get(fqn, key));
// and now loader2 should see it
Assert.assertTrue("should exist", loader2.exists(fqn));
}
public void testCacheLoaderThreadSafety() throws Exception
{
threadSafetyTest(true);
}
public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception
{
threadSafetyTest(false);
}
protected void threadSafetyTest(final boolean singleFqn) throws Exception
{
final CountDown latch = new CountDown(1);
final Fqn fqn = Fqn.fromString("/a/b/c");
final List fqns = new ArrayList(30);
final Random r = new Random();
if (!singleFqn)
{
for (int i = 0; i < 30; i++)
{
Fqn f = Fqn.fromString("/a/b/c/" + i);
fqns.add(f);
cache2.put(f, "k", "v");
cache1.evict(f);
}
}
else
{
cache2.put(fqn, "k", "v");
cache1.evict(fqn);
}
final int loops = 10000;
final Set exceptions = new CopyOnWriteArraySet();
Thread evictor = new Thread("Evictor")
{
public void run()
{
try
{
latch.acquire();
for (int i = 0; i < loops; i++)
{
Fqn f = singleFqn ? fqn : (Fqn)fqns.get(r.nextInt(fqns.size()));
cache1.evict(f);
}
}
catch (TimeoutException te)
{
// doesn't matter if we hit these on occasion
}
catch (Exception e)
{
exceptions.add(e);
}
}
};
evictor.start();
Thread writer = new Thread("Writer")
{
public void run()
{
try
{
latch.acquire();
for (int i = 0; i < loops; i++)
{
Fqn f = singleFqn ? fqn : (Fqn)fqns.get(r.nextInt(fqns.size()));
cache2.put(f, "k", "v");
}
}
catch (Exception e)
{
exceptions.add(e);
}
}
};
writer.start();
Thread reader1 = new Thread("Reader-1")
{
public void run()
{
try
{
latch.acquire();
for (int i = 0; i < loops; i++)
{
loader1.get(singleFqn ? fqn : (Fqn)fqns.get(r.nextInt(fqns.size())));
}
}
catch (Exception e)
{
exceptions.add(e);
}
}
};
reader1.start();
Thread reader2 = new Thread("Reader-2")
{
public void run()
{
try
{
latch.acquire();
for (int i = 0; i < loops; i++)
{
loader1.getChildrenNames(singleFqn ? fqn.getParent() : ((Fqn)fqns.get(r.nextInt(fqns.size()))).getParent());
}
}
catch (Exception e)
{
exceptions.add(e);
}
}
};
reader2.start();
Thread reader3 = new Thread("Reader-3")
{
public void run()
{
try
{
latch.acquire();
for (int i = 0; i < loops; i++)
{
loader1.getChildrenNames(singleFqn ? fqn : (Fqn)fqns.get(r.nextInt(fqns.size())));
}
}
catch (Exception e)
{
exceptions.add(e);
}
}
};
reader3.start();
latch.release();
reader1.join();
reader2.join();
reader3.join();
evictor.join();
writer.join();
Exception e;
for(Iterator it = exceptions.iterator(); it.hasNext();)
{
e = (Exception)it.next();
throw e;
}
}
}