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