Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 242   Methods: 26
NCLOC: 149   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
InterceptorSynchronizationTest.java 100% 77.4% 30.8% 57.6%
coverage coverage
 1    package org.jboss.cache.loader;
 2   
 3    import junit.framework.TestCase;
 4    import org.jboss.cache.CacheException;
 5    import org.jboss.cache.CacheImpl;
 6    import org.jboss.cache.DefaultCacheFactory;
 7    import org.jboss.cache.Fqn;
 8    import org.jboss.cache.Modification;
 9    import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
 10   
 11    import java.io.ObjectInputStream;
 12    import java.io.ObjectOutputStream;
 13    import java.util.ArrayList;
 14    import java.util.Collection;
 15    import java.util.Collections;
 16    import java.util.List;
 17    import java.util.Map;
 18    import java.util.Set;
 19   
 20    /**
 21    * Test case that proves that the CacheImpl is serialized inside the CacheLoaderInterceptor
 22    * when the node is empty. ANY call to retrieve a node that is not loaded will lock
 23    * up inside CacheLoaderInterceptor while the node is loaded and any other thread
 24    * that wants a node out of the cache will wait for the first one to finish before it can even _start_ loading.
 25    *
 26    * @author paulsmith
 27    */
 28    public class InterceptorSynchronizationTest extends TestCase
 29    {
 30   
 31    final int CACHELOADER_WAITTIME = 2000;// lets say loading a node takes 2 seconds
 32    final int numThreadsPerTopLevelNode = 5;// we'll have 5 requests for nodes within a branch
 33   
 34   
 35  1 public void testBlockingProblem() throws Exception
 36    {
 37   
 38  1 CacheImpl cache = (CacheImpl) DefaultCacheFactory.getInstance().createCache(false);
 39  1 cache.setCacheLoader(new TestSlowCacheLoader());
 40  1 cache.start();
 41   
 42  1 long begin = System.currentTimeMillis();
 43  1 Collection<Thread> threads = new ArrayList<Thread>();
 44   
 45    /*
 46    * Create lots of threads all trying to load DIFFERENT fqn's, as well as a set with different top level nodes.
 47    */
 48   
 49  1 for (int i = 0; i < numThreadsPerTopLevelNode; i++)
 50    {
 51  5 Thread thread = new Thread(new Retriever(cache, "/Moo/" + i));
 52  5 threads.add(thread);
 53  5 Thread thread2 = new Thread(new Retriever(cache, "/Meow/" + i));
 54  5 threads.add(thread2);
 55    }
 56  1 for (Thread thread : threads)
 57    {
 58  10 thread.start();
 59    }
 60   
 61  1 for (Thread thread : threads)
 62    {
 63  10 thread.join();
 64    }
 65   
 66  1 long end = System.currentTimeMillis();
 67  1 long timeTaken = (end - begin);
 68   
 69    /*
 70    * My expectation is that if there are 2 top level nodes they should be loaded in parallel at the very least,
 71    * but even bottom level nodes that are different should be able to be loaded concurrently.
 72    *
 73    * In this test, NONE of the threads operate in parallel once entered into CacheLoaderInterceptor.
 74    */
 75  1 int totalTimeExpectedToWaitIfNotSerialized = 3 * CACHELOADER_WAITTIME;// i'm being very generous here. 3 times the wait time for each node is more than enough if it was in parallel.
 76  1 assertTrue("If it was parallel, it should have finished quicker than this:" + timeTaken, timeTaken < totalTimeExpectedToWaitIfNotSerialized);
 77    }
 78   
 79    /**
 80    * Dummy cache loader that emulates a slow loading of any node from a virtual backing store.
 81    *
 82    * @author paulsmith
 83    */
 84    public class TestSlowCacheLoader extends AbstractCacheLoader
 85    {
 86  0 public void setConfig(IndividualCacheLoaderConfig config)
 87    {
 88    }
 89   
 90  0 public IndividualCacheLoaderConfig getConfig()
 91    {
 92  0 return null;
 93    }
 94   
 95  0 public Set getChildrenNames(Fqn arg0) throws Exception
 96    {
 97  0 return null;
 98    }
 99   
 100  0 public Object get(Fqn arg0, Object arg1) throws Exception
 101    {
 102  0 return null;
 103    }
 104   
 105  10 public Map get(Fqn arg0) throws Exception
 106    {
 107  10 Thread.sleep(CACHELOADER_WAITTIME);
 108  10 return Collections.singletonMap("foo", "bar");
 109    }
 110   
 111  0 public boolean exists(Fqn arg0) throws Exception
 112    {
 113  0 return true;
 114    }
 115   
 116  0 public Object put(Fqn arg0, Object arg1, Object arg2) throws Exception
 117    {
 118  0 return null;
 119    }
 120   
 121  0 public void put(Fqn arg0, Map arg1) throws Exception
 122    {
 123   
 124    }
 125   
 126  0 public void put(List<Modification> modifications) throws Exception
 127    {
 128    }
 129   
 130  0 public Object remove(Fqn arg0, Object arg1) throws Exception
 131    {
 132  0 return null;
 133    }
 134   
 135  0 public void remove(Fqn arg0) throws Exception
 136    {
 137   
 138    }
 139   
 140  0 public void removeData(Fqn arg0) throws Exception
 141    {
 142   
 143    }
 144   
 145  0 public void prepare(Object tx, List<Modification> modifications, boolean one_phase) throws Exception
 146    {
 147    }
 148   
 149  0 public void commit(Object arg0) throws Exception
 150    {
 151   
 152    }
 153   
 154  0 public void rollback(Object arg0)
 155    {
 156    }
 157   
 158    /*public byte[] loadEntireState() throws Exception {
 159    return null;
 160    }
 161   
 162    public void storeEntireState(byte[] arg0) throws Exception {
 163   
 164    }
 165   
 166    public byte[] loadState(Fqn subtree) throws Exception
 167    {
 168    return new byte[0];
 169    }
 170   
 171    public void storeState(byte[] state, Fqn subtree) throws Exception
 172    {
 173    }*/
 174   
 175  0 public void loadEntireState(ObjectOutputStream os) throws Exception
 176    {
 177    // nothing to do here
 178    }
 179   
 180  0 public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception
 181    {
 182    // nothing to do here
 183    }
 184   
 185  0 public void storeEntireState(ObjectInputStream is) throws Exception
 186    {
 187    // nothing to do here
 188    }
 189   
 190  0 public void storeState(Fqn subtree, ObjectInputStream is) throws Exception
 191    {
 192    // nothing to do here
 193    }
 194   
 195  1 public void create() throws Exception
 196    {
 197   
 198    }
 199   
 200  1 public void start() throws Exception
 201    {
 202   
 203    }
 204   
 205  1 public void stop()
 206    {
 207   
 208    }
 209   
 210  1 public void destroy()
 211    {
 212   
 213    }
 214   
 215    }
 216   
 217   
 218    private static class Retriever implements Runnable
 219    {
 220    private final String fqn;
 221    private CacheImpl cache;
 222   
 223  10 private Retriever(CacheImpl cache, String fqn)
 224    {
 225  10 this.fqn = fqn;
 226  10 this.cache = cache;
 227    }
 228   
 229  10 public void run()
 230    {
 231  10 try
 232    {
 233  10 cache.get(fqn, "foo");
 234    }
 235    catch (CacheException e)
 236    {
 237  0 throw new RuntimeException("Unexpected", e);
 238    }
 239   
 240    }
 241    }
 242    }