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