Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 333   Methods: 9
NCLOC: 227   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
IsolationLevelReadCommittedTest.java 50% 85.6% 100% 82.7%
coverage coverage
 1    package org.jboss.cache.transaction;
 2   
 3    import junit.framework.AssertionFailedError;
 4    import junit.framework.Test;
 5    import junit.framework.TestCase;
 6    import junit.framework.TestSuite;
 7    import org.jboss.cache.Cache;
 8    import org.jboss.cache.CacheImpl;
 9    import org.jboss.cache.DefaultCacheFactory;
 10    import org.jboss.cache.Fqn;
 11    import org.jboss.cache.config.Configuration;
 12    import org.jboss.cache.config.Configuration.CacheMode;
 13    import org.jboss.cache.lock.IsolationLevel;
 14    import org.jboss.cache.lock.TimeoutException;
 15   
 16    import javax.transaction.NotSupportedException;
 17    import javax.transaction.SystemException;
 18    import javax.transaction.Transaction;
 19    import java.util.concurrent.CountDownLatch;
 20    import java.util.concurrent.TimeUnit;
 21   
 22   
 23    /**
 24    * Tests READ_COMMITED isolation level.
 25    *
 26    * @author <a href="mailto:ovidiu@jboss.org">Ovidiu Feodorov</a>
 27    * @version $Id: IsolationLevelReadCommittedTest.java,v 1.16 2007/02/07 22:06:43 genman Exp $
 28    */
 29   
 30    public class IsolationLevelReadCommittedTest extends TestCase
 31    {
 32   
 33    private Cache cache = null;
 34    private final Fqn FQN = Fqn.fromString("/a/b/c");
 35    private final Fqn PARENT_FQN = FQN.getParent();
 36    private final String KEY = "key";
 37    private final String VALUE = "value";
 38   
 39    private volatile boolean writerFailed;
 40    private volatile boolean readerFailed;
 41    private volatile AssertionFailedError writerError;
 42    private volatile AssertionFailedError readerError;
 43   
 44  2 protected void setUp() throws Exception
 45    {
 46  2 super.setUp();
 47   
 48  2 writerFailed = false;
 49  2 readerFailed = false;
 50   
 51  2 writerError = null;
 52  2 readerError = null;
 53   
 54  2 Configuration config = new Configuration();
 55  2 config.setCacheMode(CacheMode.LOCAL);
 56  2 config.setIsolationLevel(IsolationLevel.READ_COMMITTED);
 57  2 config.setLockAcquisitionTimeout(1000);
 58  2 config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
 59  2 cache = DefaultCacheFactory.getInstance().createCache(config);
 60    }
 61   
 62   
 63  2 protected void tearDown() throws Exception
 64    {
 65  2 super.tearDown();
 66   
 67  2 cache.stop();
 68  2 cache.destroy();
 69  2 cache = null;
 70    }
 71   
 72   
 73    /**
 74    * The test starts a reader and writer thread and coordinates access to the cache so the reader
 75    * reads after the writer changes a node in a transaction, but before roll back. The reader
 76    * should never read anything else than the original value (uncommitted values should be
 77    * isolated)
 78    */
 79  1 public void testReadCommitted() throws Exception
 80    {
 81  1 final CountDownLatch readerCanRead = new CountDownLatch(1);
 82  1 final CountDownLatch readerDone = new CountDownLatch(1);
 83  1 final CountDownLatch writerCanWrite = new CountDownLatch(1);
 84  1 final CountDownLatch writerCanRollback = new CountDownLatch(1);
 85  1 final CountDownLatch writerDone = new CountDownLatch(1);
 86   
 87  1 cache.put(FQN, KEY, VALUE);
 88  1 assertEquals(VALUE, cache.get(FQN, KEY));
 89   
 90    // start a reader thread; need a transaction so its initial
 91    // read lock is maintained while the writer does its work
 92   
 93  1 Thread readerThread = new Thread(new Runnable()
 94    {
 95  1 public void run()
 96    {
 97  1 Transaction tx = null;
 98  1 try
 99    {
 100  1 tx = startTransaction();
 101   
 102    // Read the value, thus acquiring a read lock on FQN
 103  1 assertEquals("Could not read node with expected value!", VALUE, cache.get(FQN, KEY));
 104   
 105  1 writerCanWrite.countDown();
 106   
 107    // wait until the writer thread changes the value in a transaction, but it did not
 108    // yet commit or roll back.
 109  1 readerCanRead.await();
 110   
 111  1 try
 112    {
 113    // I shouldn't be able to see the "dirty" value
 114  1 assertEquals("thread w/ read lock can see subsequent uncommitted changes!!", VALUE, cache.get(FQN, KEY));
 115    }
 116    catch (TimeoutException good)
 117    {
 118    // this is what should happen due to writer's WL
 119    }
 120   
 121    // let the writer know it can rollback
 122  1 writerCanRollback.countDown();
 123   
 124    // I should still be able to see the "clean" value
 125  1 assertEquals("Could not read node with expected value!", VALUE, cache.get(FQN, KEY));
 126    }
 127    catch (AssertionFailedError e)
 128    {
 129  0 readerError = e;
 130    }
 131    catch (Throwable t)
 132    {
 133  0 t.printStackTrace();
 134  0 readerFailed = true;
 135    }
 136    finally
 137    {
 138  1 System.out.println("reader thread exits");
 139  1 if (tx != null)
 140    {
 141  1 try { tx.commit(); } catch (Exception e) {}
 142    }
 143  1 writerCanWrite.countDown();
 144  1 writerCanRollback.countDown();
 145  1 readerDone.countDown();
 146    }
 147    }
 148    }, "READER");
 149  1 readerThread.start();
 150   
 151    // start a writer thread and a transaction
 152   
 153  1 Thread writerThread = new Thread(new Runnable()
 154    {
 155  1 public void run()
 156    {
 157  1 try
 158    {
 159    // wait until the reader thread reads and allows me to start
 160   
 161  1 writerCanWrite.await(3, TimeUnit.SECONDS);
 162   
 163  1 Transaction tx2 = startTransaction();
 164   
 165    // change VALUE in a transaction
 166  1 cache.put(FQN, KEY, "this-shouldnt-be-visible");
 167   
 168    // notify the reading thread
 169  1 readerCanRead.countDown();
 170   
 171    // wait until the reader thread reads and allows me to rollback or until I timeout
 172  1 writerCanWrite.await(3, TimeUnit.SECONDS);
 173   
 174  1 System.out.println("rolling back");
 175   
 176  1 tx2.rollback();
 177    }
 178    catch (AssertionFailedError e)
 179    {
 180  0 writerError = e;
 181    }
 182    catch (Throwable t)
 183    {
 184  0 t.printStackTrace();
 185  0 writerFailed = true;
 186    }
 187    finally
 188    {
 189  1 System.out.println("writer thread exits");
 190  1 readerCanRead.countDown();
 191  1 writerDone.countDown();
 192    }
 193    }
 194    }, "WRITER");
 195  1 writerThread.start();
 196   
 197    // wait for both threads to finish
 198  1 readerDone.await();
 199  1 writerDone.await();
 200   
 201    // If any assertion failed, throw on the AssertionFailedError
 202  1 if (readerError != null)
 203    {
 204  0 throw readerError;
 205    }
 206   
 207  1 if (writerError != null)
 208    {
 209  0 throw writerError;
 210    }
 211   
 212  1 if (readerFailed)
 213    {
 214  0 fail("The reader thread exited incorrectly. Watch the log for previous stack traces");
 215    }
 216   
 217  1 if (writerFailed)
 218    {
 219  0 fail("The writer thread exited incorrectly. Watch the log for previous stack traces");
 220    }
 221    }
 222   
 223    /**
 224    * Test creates a cache node then starts a separate thread that removes
 225    * the node inside a tx. Test confirms that the removal cannot be seen
 226    * before the test commits.
 227    *
 228    * @throws Exception
 229    */
 230  1 public void testNodeRemoved() throws Exception
 231    {
 232  1 final CountDownLatch readerCanRead = new CountDownLatch(1);
 233  1 final CountDownLatch readerDone = new CountDownLatch(1);
 234  1 final CountDownLatch writerDone = new CountDownLatch(1);
 235   
 236  1 cache.put(FQN, KEY, VALUE);
 237  1 assertEquals(VALUE, cache.get(FQN, KEY));
 238   
 239    // start a writer thread and a transaction
 240   
 241  1 Thread writerThread = new Thread(new Runnable()
 242    {
 243  1 public void run()
 244    {
 245  1 try
 246    {
 247  1 Transaction tx = startTransaction();
 248   
 249    // change VALUE in a transaction
 250  1 cache.removeNode(PARENT_FQN);
 251   
 252    // notify the reading thread
 253  1 readerCanRead.countDown();
 254   
 255  1 readerDone.await();
 256   
 257  1 tx.commit();
 258    }
 259    catch (AssertionFailedError e)
 260    {
 261  0 writerError = e;
 262    }
 263    catch (Throwable t)
 264    {
 265  0 t.printStackTrace();
 266  0 writerFailed = true;
 267    }
 268    finally
 269    {
 270  1 System.out.println("writer thread exits");
 271  1 readerCanRead.countDown();
 272  1 writerDone.countDown();
 273    }
 274    }
 275    }, "WRITER");
 276  1 writerThread.start();
 277   
 278  1 try
 279    {
 280    // wait until the writer thread changes the value in a transaction,
 281    // but it did not yet commit or roll back.
 282  1 readerCanRead.await();
 283   
 284    // I shouldn't be able to see the "dirty" value
 285  1 assertEquals("2nd thread cannot see uncommitted changes",
 286    VALUE, cache.get(FQN, KEY));
 287    }
 288    catch (TimeoutException t)
 289    {
 290    // ignore, this is good
 291    }
 292    finally
 293    {
 294  1 System.out.println("reader thread exits");
 295  1 readerDone.countDown();
 296    }
 297   
 298    // wait for the writer to finish
 299  1 writerDone.await();
 300   
 301  1 assertNull("Node was removed", ((CacheImpl) cache).get(FQN));
 302   
 303    // If any assertion failed, throw on the AssertionFailedError
 304   
 305  1 if (writerError != null)
 306    {
 307  0 throw writerError;
 308    }
 309   
 310  1 if (writerFailed)
 311    {
 312  0 fail("The writer thread exited incorrectly. Watch the log for previous stack traces");
 313    }
 314   
 315    }
 316   
 317  3 private Transaction startTransaction() throws SystemException, NotSupportedException
 318    {
 319  3 DummyTransactionManager mgr = DummyTransactionManager.getInstance();
 320  3 mgr.begin();
 321  3 return mgr.getTransaction();
 322    }
 323   
 324   
 325  1 public static Test suite()
 326    {
 327   
 328  1 return new TestSuite(IsolationLevelReadCommittedTest.class);
 329   
 330    }
 331   
 332    }
 333