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:
parent
c520a81990
commit
ab111b6018
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue