HHH-6843 Updating LockTest to be less aggressive and more inline with actual isolation requirements

The actual changes are in the refactoring of LockTest#testContendedPessimisticLock which got split up into several methods and has the asserts changed

The formatting changes fix some generics warning and indentation. Used to be two separate commits, but after some git screw-up became one now
This commit is contained in:
Hardy Ferentschik 2011-11-23 17:24:29 +01:00 committed by Strong Liu
parent c520a81990
commit ab111b6018
1 changed files with 406 additions and 326 deletions

View File

@ -23,14 +23,6 @@
*/ */
package org.hibernate.ejb.test.lock; package org.hibernate.ejb.test.lock;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.LockTimeoutException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Query;
import javax.persistence.QueryTimeoutException;
import java.lang.RuntimeException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -38,8 +30,15 @@ import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.LockTimeoutException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Query;
import javax.persistence.QueryTimeoutException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.Test;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.Oracle10gDialect; import org.hibernate.dialect.Oracle10gDialect;
@ -48,8 +47,6 @@ import org.hibernate.ejb.AvailableSettings;
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -178,8 +175,10 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
// To get the same functionality as prior release, change the LockModeType.READ lock to: // To get the same functionality as prior release, change the LockModeType.READ lock to:
// em.lock(lock,LockModeType.PESSIMISTIC_READ); // em.lock(lock,LockModeType.PESSIMISTIC_READ);
em.lock( lock, LockModeType.READ ); em.lock( lock, LockModeType.READ );
fail("expected OptimisticLockException exception"); fail( "expected OptimisticLockException exception" );
} catch(OptimisticLockException expected) {} }
catch ( OptimisticLockException expected ) {
}
em.getTransaction().rollback(); em.getTransaction().rollback();
// the previous code block can be rewritten as follows (to get the previous behavior) // the previous code block can be rewritten as follows (to get the previous behavior)
@ -262,9 +261,13 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
EntityManager em2 = createIsolatedEntityManager(); EntityManager em2 = createIsolatedEntityManager();
em2.getTransaction().begin(); em2.getTransaction().begin();
lock = em2.find( Lock.class, lock.getId(), LockModeType.OPTIMISTIC ); lock = em2.find( Lock.class, lock.getId(), LockModeType.OPTIMISTIC );
assertEquals( "lock mode should be OPTIMISTIC ", LockModeType.OPTIMISTIC, em2.getLockMode(lock) ); assertEquals( "lock mode should be OPTIMISTIC ", LockModeType.OPTIMISTIC, em2.getLockMode( lock ) );
em2.lock( lock, LockModeType.OPTIMISTIC_FORCE_INCREMENT ); em2.lock( lock, LockModeType.OPTIMISTIC_FORCE_INCREMENT );
assertEquals( "lock mode should be OPTIMISTIC_FORCE_INCREMENT ", LockModeType.OPTIMISTIC_FORCE_INCREMENT, em2.getLockMode(lock) ); assertEquals(
"lock mode should be OPTIMISTIC_FORCE_INCREMENT ",
LockModeType.OPTIMISTIC_FORCE_INCREMENT,
em2.getLockMode( lock )
);
em2.getTransaction().commit(); em2.getTransaction().commit();
em2.getTransaction().begin(); em2.getTransaction().begin();
em2.remove( lock ); em2.remove( lock );
@ -273,73 +276,117 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
} }
@Test @Test
@SkipForDialect( value = {HSQLDialect.class,SybaseASE15Dialect.class}, jiraKey = "HHH-6820") @SkipForDialect(value = { HSQLDialect.class, SybaseASE15Dialect.class }, jiraKey = "HHH-6820")
public void testContendedPessimisticLock() throws Exception { public void testContendedPessimisticLock() throws Exception {
EntityManager em = getOrCreateEntityManager(); final EntityManager em = getOrCreateEntityManager();
final EntityManager em2 = createIsolatedEntityManager(); Lock lock = createAndPersistLockInstance( em );
Lock lock = new Lock();
Thread t = null;
try { try {
lock.setName( "testContendedPessimisticLock" ); inFirstTransactionReloadAndModifyLockInstance( em, lock );
em.getTransaction().begin(); final CountDownLatch latch = new CountDownLatch( 1 );
em.persist( lock ); FutureTask<Boolean> future = inBackgroundThreadStartSecondTransactionAndReadLockInstance( latch );
em.getTransaction().commit();
em.clear();
em.getTransaction().begin(); // wait with timeout on the background thread
lock = em.getReference( Lock.class, lock.getId() ); log.debug( "testContendedPessimisticLock: wait on BG thread" );
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); boolean backGroundThreadCompleted = latch.await( 3, TimeUnit.SECONDS );
final Integer id = lock.getId();
lock.getName(); // force entity to be read
log.info("testContendedPessimisticLock: got write lock");
final CountDownLatch latch = new CountDownLatch(1);
t = new Thread( new Runnable() { if ( backGroundThreadCompleted ) {
public void run() { // the background thread read a value. At the very least we need to assert that he did not see the
try { // changed value
em2.getTransaction().begin(); boolean backgroundThreadHasReadNewValue = future.get();
log.info("testContendedPessimisticLock: (BG) about to issue (PESSIMISTIC_READ) query against write-locked entity"); assertFalse(
// we should block on the following read "The background thread is not allowed to see the updated value while the first transaction has not committed yet",
Query query = em2.createQuery( backgroundThreadHasReadNewValue
"select L from Lock_ L where L.id < 10000 "); );
query.setLockMode(LockModeType.PESSIMISTIC_READ);
List<Lock> resultList = query.getResultList();
resultList.get(0).getName(); // force entity to be read
}
finally {
em2.getTransaction().commit();
latch.countDown(); // signal that we got the read lock
}
}
} );
t.setDaemon( true );
t.setName("LockTest read lock");
t.start();
log.info("testContendedPessimisticLock: wait on BG thread");
boolean latchSet = latch.await( 10, TimeUnit.SECONDS );
// latchSet should be false (timeout) because the background thread
// shouldn't be able to get a read lock on write locked entity.
log.info("testContendedPessimisticLock: BG thread completed transaction");
assertFalse( "shouldn't be able to get read lock while another transaction has write lock",latchSet );
em.getTransaction().commit(); em.getTransaction().commit();
} }
else {
log.debug( "The background thread was blocked" );
// commit first transaction so that background thread can continue
em.getTransaction().commit();
boolean backgroundThreadHasReadNewValue = future.get();
assertTrue(
"Background thread should read the new value after being unblocked",
backgroundThreadHasReadNewValue
);
}
}
finally { finally {
cleanup( em, lock );
}
}
private void cleanup(EntityManager em, Lock lock) throws InterruptedException {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity
t.join();
}
em.getTransaction().begin(); em.getTransaction().begin();
lock = em.getReference( Lock.class, lock.getId() ); lock = em.getReference( Lock.class, lock.getId() );
em.remove( lock ); em.remove( lock );
em.getTransaction().commit(); em.getTransaction().commit();
em.close(); em.close();
em2.close();
} }
private FutureTask<Boolean> inBackgroundThreadStartSecondTransactionAndReadLockInstance(final CountDownLatch latch) {
final EntityManager isolatedEntityManager = createIsolatedEntityManager();
FutureTask<Boolean> bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() {
try {
isolatedEntityManager.getTransaction().begin();
log.debug(
"testContendedPessimisticLock: (BG) about to issue (PESSIMISTIC_READ) query against write-locked entity"
);
// we should block on the following read
Query query = isolatedEntityManager.createQuery(
"select L from Lock_ L where L.id < 10000 "
);
query.setLockMode( LockModeType.PESSIMISTIC_READ );
List<Lock> resultList = query.getResultList();
Lock lock = resultList.get( 0 );
return lock.getName().equals( "foo" );
}
catch ( RuntimeException e ) {
fail( "An error occurred waiting while attempting to read the entity: " + e.getMessage() );
throw e;
}
finally {
isolatedEntityManager.getTransaction().commit();
isolatedEntityManager.close();
latch.countDown(); // signal that we got the read lock
}
}
}
);
Thread thread = new Thread( bgTask );
thread.setDaemon( true );
thread.setName( "LockTest read lock" );
thread.start();
return bgTask;
}
private void inFirstTransactionReloadAndModifyLockInstance(EntityManager em, Lock lock) {
em.getTransaction().begin();
lock = em.getReference( Lock.class, lock.getId() );
em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
// modify and flush, but don't commit the transaction
lock.setName( "foo" );
em.flush();
log.debug( "testContendedPessimisticLock: got write lock" );
}
private Lock createAndPersistLockInstance(EntityManager em) {
Lock lock = new Lock();
lock.setName( "testContendedPessimisticLock" );
em.getTransaction().begin();
em.persist( lock );
em.getTransaction().commit();
em.clear();
return lock;
} }
@Test @Test
@ -347,14 +394,14 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
final EntityManager em2 = createIsolatedEntityManager(); final EntityManager em2 = createIsolatedEntityManager();
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout) // TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testContendedPessimisticReadLockTimeout"); log.info( "skipping testContendedPessimisticReadLockTimeout" );
return; return;
} }
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask = null;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testContendedPessimisticReadLockTimeout" ); lock.setName( "testContendedPessimisticReadLockTimeout" );
@ -368,57 +415,65 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testContendedPessimisticReadLockTimeout: got write lock"); log.info( "testContendedPessimisticReadLockTimeout: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testContendedPessimisticReadLockTimeout: (BG) about to read write-locked entity"); log.info(
"testContendedPessimisticReadLockTimeout: (BG) about to read write-locked entity"
);
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testContendedPessimisticReadLockTimeout: (BG) read write-locked entity"); log.info( "testContendedPessimisticReadLockTimeout: (BG) read write-locked entity" );
Map<String,Object> props = new HashMap<String, Object>(); Map<String, Object> props = new HashMap<String, Object>();
// timeout is in milliseconds // timeout is in milliseconds
props.put("javax.persistence.lock.timeout", new Integer(1000)); props.put( "javax.persistence.lock.timeout", 1000 );
try { try {
em2.lock( lock2, LockModeType.PESSIMISTIC_READ, props); em2.lock( lock2, LockModeType.PESSIMISTIC_READ, props );
} }
catch( LockTimeoutException e) { catch ( LockTimeoutException e ) {
// success // success
log.info("testContendedPessimisticReadLockTimeout: (BG) got expected timeout exception"); log.info(
"testContendedPessimisticReadLockTimeout: (BG) got expected timeout exception"
);
timedOut = true; timedOut = true;
em2.getTransaction().rollback(); em2.getTransaction().rollback();
return new Boolean(timedOut); return timedOut;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("Expected LockTimeoutException but got unexpected exception", e); log.info( "Expected LockTimeoutException but got unexpected exception", e );
throw new RuntimeException("Expected LockTimeoutException but got unexpected exception",e); throw new RuntimeException(
"Expected LockTimeoutException but got unexpected exception", e
);
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean(timedOut); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName("Lock timeout Test (bg)"); t.setName( "Lock timeout Test (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -436,14 +491,14 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
final EntityManager em2 = createIsolatedEntityManager(); final EntityManager em2 = createIsolatedEntityManager();
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout) // TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testContendedPessimisticWriteLockTimeout"); log.info( "skipping testContendedPessimisticWriteLockTimeout" );
return; return;
} }
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testContendedPessimisticWriteLockTimeout" ); lock.setName( "testContendedPessimisticWriteLockTimeout" );
@ -457,54 +512,60 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testContendedPessimisticWriteLockTimeout: got write lock"); log.info( "testContendedPessimisticWriteLockTimeout: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testContendedPessimisticWriteLockTimeout: (BG) about to read write-locked entity"); log.info(
"testContendedPessimisticWriteLockTimeout: (BG) about to read write-locked entity"
);
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testContendedPessimisticWriteLockTimeout: (BG) read write-locked entity"); log.info( "testContendedPessimisticWriteLockTimeout: (BG) read write-locked entity" );
Map<String,Object> props = new HashMap<String, Object>(); Map<String, Object> props = new HashMap<String, Object>();
// timeout is in milliseconds // timeout is in milliseconds
props.put("javax.persistence.lock.timeout", new Integer(1000)); props.put( "javax.persistence.lock.timeout", 1000 );
try { try {
em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props); em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props );
} }
catch( LockTimeoutException e) { catch ( LockTimeoutException e ) {
// success // success
log.info("testContendedPessimisticWriteLockTimeout: (BG) got expected timeout exception"); log.info(
"testContendedPessimisticWriteLockTimeout: (BG) got expected timeout exception"
);
timedOut = true; timedOut = true;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("Expected LockTimeoutException but got unexpected exception", e); log.info( "Expected LockTimeoutException but got unexpected exception", e );
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean(timedOut); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName("Lock timeout Test (bg)"); t.setName( "Lock timeout Test (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -522,14 +583,14 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
final EntityManager em2 = createIsolatedEntityManager(); final EntityManager em2 = createIsolatedEntityManager();
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout) // TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testContendedPessimisticWriteLockNoWait"); log.info( "skipping testContendedPessimisticWriteLockNoWait" );
return; return;
} }
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testContendedPessimisticWriteLockNoWait" ); lock.setName( "testContendedPessimisticWriteLockNoWait" );
@ -543,54 +604,60 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testContendedPessimisticWriteLockNoWait: got write lock"); log.info( "testContendedPessimisticWriteLockNoWait: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testContendedPessimisticWriteLockNoWait: (BG) about to read write-locked entity"); log.info(
"testContendedPessimisticWriteLockNoWait: (BG) about to read write-locked entity"
);
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testContendedPessimisticWriteLockNoWait: (BG) read write-locked entity"); log.info( "testContendedPessimisticWriteLockNoWait: (BG) read write-locked entity" );
Map<String,Object> props = new HashMap<String, Object>(); Map<String, Object> props = new HashMap<String, Object>();
// timeout of zero means no wait (for lock) // timeout of zero means no wait (for lock)
props.put("javax.persistence.lock.timeout", new Integer(0)); props.put( "javax.persistence.lock.timeout", 0 );
try { try {
em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props); em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props );
} }
catch( LockTimeoutException e) { catch ( LockTimeoutException e ) {
// success // success
log.info("testContendedPessimisticWriteLockNoWait: (BG) got expected timeout exception"); log.info(
"testContendedPessimisticWriteLockNoWait: (BG) got expected timeout exception"
);
timedOut = true; timedOut = true;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("Expected LockTimeoutException but got unexpected exception", e); log.info( "Expected LockTimeoutException but got unexpected exception", e );
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean(timedOut); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName("Lock timeout Test (bg)"); t.setName( "Lock timeout Test (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -608,14 +675,14 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
final EntityManager em2 = createIsolatedEntityManager(); final EntityManager em2 = createIsolatedEntityManager();
// TODO: replace dialect instanceof test with a Dialect.hasCapability // TODO: replace dialect instanceof test with a Dialect.hasCapability
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testQueryTimeout"); log.info( "skipping testQueryTimeout" );
return; return;
} }
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testQueryTimeout" ); lock.setName( "testQueryTimeout" );
@ -629,58 +696,64 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testQueryTimeout: got write lock"); log.info( "testQueryTimeout: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testQueryTimeout: (BG) about to read write-locked entity"); log.info( "testQueryTimeout: (BG) about to read write-locked entity" );
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testQueryTimeout: (BG) read write-locked entity"); log.info( "testQueryTimeout: (BG) read write-locked entity" );
try { try {
// we should block on the following read // we should block on the following read
Query query = em2.createQuery( Query query = em2.createQuery(
"select L from Lock_ L where L.id < 10000 "); "select L from Lock_ L where L.id < 10000 "
);
query.setLockMode( LockModeType.PESSIMISTIC_READ ); query.setLockMode( LockModeType.PESSIMISTIC_READ );
query.setHint( "javax.persistence.query.timeout", new Integer(500) ); // 1 sec timeout query.setHint( "javax.persistence.query.timeout", 500 ); // 1 sec timeout
List<Lock> resultList = query.getResultList(); List<Lock> resultList = query.getResultList();
String name = resultList.get(0).getName(); // force entity to be read String name = resultList.get( 0 ).getName(); // force entity to be read
log.info("testQueryTimeout: name read =" + name); log.info( "testQueryTimeout: name read =" + name );
} }
catch( QueryTimeoutException e) { catch ( QueryTimeoutException e ) {
// success // success
log.info("testQueryTimeout: (BG) got expected timeout exception"); log.info( "testQueryTimeout: (BG) got expected timeout exception" );
timedOut = true; timedOut = true;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("testQueryTimeout: Expected LockTimeoutException but got unexpected exception", e); log.info(
"testQueryTimeout: Expected LockTimeoutException but got unexpected exception",
e
);
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean( timedOut ); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName( "testQueryTimeout (bg)" ); t.setName( "testQueryTimeout (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -695,18 +768,18 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
@Test @Test
public void testQueryTimeoutEMProps() throws Exception { public void testQueryTimeoutEMProps() throws Exception {
// TODO: replace dialect instanceof test with a Dialect.hasCapability // TODO: replace dialect instanceof test with a Dialect.hasCapability
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testQueryTimeout"); log.info( "skipping testQueryTimeout" );
return; return;
} }
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
Map queryTimeoutProps = new HashMap(); Map<String, Object> queryTimeoutProps = new HashMap<String, Object>();
queryTimeoutProps.put("javax.persistence.query.timeout", new Integer(500) ); // 1 sec timeout (should round up) queryTimeoutProps.put( "javax.persistence.query.timeout", 500 ); // 1 sec timeout (should round up)
final EntityManager em2 = createIsolatedEntityManager(queryTimeoutProps); final EntityManager em2 = createIsolatedEntityManager( queryTimeoutProps );
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testQueryTimeout" ); lock.setName( "testQueryTimeout" );
@ -720,57 +793,63 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testQueryTimeout: got write lock"); log.info( "testQueryTimeout: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testQueryTimeout: (BG) about to read write-locked entity"); log.info( "testQueryTimeout: (BG) about to read write-locked entity" );
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testQueryTimeout: (BG) read write-locked entity"); log.info( "testQueryTimeout: (BG) read write-locked entity" );
try { try {
// we should block on the following read // we should block on the following read
Query query = em2.createQuery( Query query = em2.createQuery(
"select L from Lock_ L where L.id < 10000 "); "select L from Lock_ L where L.id < 10000 "
);
query.setLockMode( LockModeType.PESSIMISTIC_READ ); query.setLockMode( LockModeType.PESSIMISTIC_READ );
List<Lock> resultList = query.getResultList(); List<Lock> resultList = query.getResultList();
String name = resultList.get(0).getName(); // force entity to be read String name = resultList.get( 0 ).getName(); // force entity to be read
log.info("testQueryTimeout: name read =" + name); log.info( "testQueryTimeout: name read =" + name );
} }
catch( QueryTimeoutException e) { catch ( QueryTimeoutException e ) {
// success // success
log.info("testQueryTimeout: (BG) got expected timeout exception"); log.info( "testQueryTimeout: (BG) got expected timeout exception" );
timedOut = true; timedOut = true;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("testQueryTimeout: Expected LockTimeoutException but got unexpected exception", e); log.info(
"testQueryTimeout: Expected LockTimeoutException but got unexpected exception",
e
);
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean( timedOut ); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName( "testQueryTimeout (bg)" ); t.setName( "testQueryTimeout (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -782,23 +861,22 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
} }
} }
@Test @Test
public void testLockTimeoutEMProps() throws Exception { public void testLockTimeoutEMProps() throws Exception {
EntityManager em = getOrCreateEntityManager(); EntityManager em = getOrCreateEntityManager();
Map TimeoutProps = new HashMap(); Map<String, Object> TimeoutProps = new HashMap<String, Object>();
TimeoutProps.put("javax.persistence.lock.timeout", new Integer(1000) ); // 1 second timeout TimeoutProps.put( "javax.persistence.lock.timeout", 1000 ); // 1 second timeout
final EntityManager em2 = createIsolatedEntityManager(TimeoutProps); final EntityManager em2 = createIsolatedEntityManager( TimeoutProps );
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout) // TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
if ( ! (getDialect() instanceof Oracle10gDialect)) { if ( !( getDialect() instanceof Oracle10gDialect ) ) {
log.info("skipping testLockTimeoutEMProps"); log.info( "skipping testLockTimeoutEMProps" );
return; return;
} }
Lock lock = new Lock(); Lock lock = new Lock();
Thread t = null; Thread t = null;
FutureTask<Boolean> bgTask = null; FutureTask<Boolean> bgTask;
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch( 1 );
try { try {
lock.setName( "testLockTimeoutEMProps" ); lock.setName( "testLockTimeoutEMProps" );
@ -812,52 +890,54 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
em.lock( lock, LockModeType.PESSIMISTIC_WRITE ); em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
final Integer id = lock.getId(); final Integer id = lock.getId();
lock.getName(); // force entity to be read lock.getName(); // force entity to be read
log.info("testLockTimeoutEMProps: got write lock"); log.info( "testLockTimeoutEMProps: got write lock" );
bgTask = new FutureTask<Boolean>( new Callable() { bgTask = new FutureTask<Boolean>(
new Callable<Boolean>() {
public Boolean call() { public Boolean call() {
try { try {
boolean timedOut = false; // true (success) if LockTimeoutException occurred boolean timedOut = false; // true (success) if LockTimeoutException occurred
em2.getTransaction().begin(); em2.getTransaction().begin();
log.info("testLockTimeoutEMProps: (BG) about to read write-locked entity"); log.info( "testLockTimeoutEMProps: (BG) about to read write-locked entity" );
// we should block on the following read // we should block on the following read
Lock lock2 = em2.getReference( Lock.class, id ); Lock lock2 = em2.getReference( Lock.class, id );
lock2.getName(); // force entity to be read lock2.getName(); // force entity to be read
log.info("testLockTimeoutEMProps: (BG) read write-locked entity"); log.info( "testLockTimeoutEMProps: (BG) read write-locked entity" );
// em2 already has javax.persistence.lock.timeout of 1 second applied // em2 already has javax.persistence.lock.timeout of 1 second applied
try { try {
em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE); em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE );
} }
catch( LockTimeoutException e) { catch ( LockTimeoutException e ) {
// success // success
log.info("testLockTimeoutEMProps: (BG) got expected timeout exception"); log.info( "testLockTimeoutEMProps: (BG) got expected timeout exception" );
timedOut = true; timedOut = true;
} }
catch ( Throwable e) { catch ( Throwable e ) {
log.info("Expected LockTimeoutException but got unexpected exception", e); log.info( "Expected LockTimeoutException but got unexpected exception", e );
} }
em2.getTransaction().commit(); em2.getTransaction().commit();
return new Boolean(timedOut); return timedOut;
} }
finally { finally {
latch.countDown(); // signal that we finished latch.countDown(); // signal that we finished
} }
} }
} ); }
t = new Thread(bgTask); );
t = new Thread( bgTask );
t.setDaemon( true ); t.setDaemon( true );
t.setName("Lock timeout Test (bg)"); t.setName( "Lock timeout Test (bg)" );
t.start(); t.start();
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
assertTrue( "background test thread finished (lock timeout is broken)", latchSet); assertTrue( "background test thread finished (lock timeout is broken)", latchSet );
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() );
em.getTransaction().commit(); em.getTransaction().commit();
} }
finally { finally {
if ( em.getTransaction().isActive() ) { if ( em.getTransaction().isActive() ) {
em.getTransaction().rollback(); em.getTransaction().rollback();
} }
if ( t != null) { // wait for background thread to finish before deleting entity if ( t != null ) { // wait for background thread to finish before deleting entity
t.join(); t.join();
} }
em.getTransaction().begin(); em.getTransaction().begin();
@ -871,7 +951,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
@Override @Override
public Class[] getAnnotatedClasses() { public Class[] getAnnotatedClasses() {
return new Class[]{ return new Class[] {
Lock.class, Lock.class,
UnversionedLock.class UnversionedLock.class
}; };