package er.erxtest.tests;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.log4j.Logger;
import com.webobjects.foundation.NSForwardException;
import er.erxtest.ERXTestCase;
import er.extensions.appserver.ERXApplication;
import er.extensions.eof.ERXEC;
/*
This class should test locking of ECs:
- lock() when not locked in same thread
- lock() when locked in same thread
- lock() when saveChanges() delegate also locks
- autolock() when not locked in same thread
- autolock() when locked in same thread
- lock() when not locked in other thread
- lock() when locked in other thread
- autolock() when not locked in other thread
- autolock() when locked in other thread
- lock() when not autolocked in other thread
- lock() when autolocked in other thread
- autolock() when not locked in other thread
- autolock() when locked in other thread
- finalize()
and all variations with coalesce() true/false
and all variations with shouldAutolock() true/false
expected results are:
- all should have been locked()
- lock() context should not been autolocked()
- autolock() context should not been autolocked() if shouldAutolock() is false
- autolock() context should been autolocked() if shouldAutolock() is true
- autolock() context should not been called in finalize()
- if coalesce, autolock should stay open?
*/
public class ERXECLockingTestCase extends ERXTestCase {
static ExecutorService executor = Executors.newCachedThreadPool();
protected static Object call(Callable<? extends Object> aCallable, long timeout) throws TimeoutException {
Future<? extends Object> future = executor.submit(aCallable);
try {
return future.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e1) {
throw new TimeoutException();
} catch (ExecutionException e1) {
throw NSForwardException._runtimeExceptionForThrowable(e1.getCause());
}
}
public static final Logger log = Logger.getLogger(ERXECLockingTestCase.class);
private static final long JOIN_TIME = 2000L;
public static class EC extends ERXEC {
public boolean beforeLock;
public boolean afterLock;
public boolean beforeUnlock;
public boolean afterUnlock;
public boolean beforeAutoLock;
public boolean afterAutoLock;
public boolean beforeAutoUnlock;
public boolean afterAutoUnlock;
public boolean wasAutolocked;
public int autoLocks;
public long waitTime;
@Override
public void lock() {
beforeLock = true;
super.lock();
afterLock = true;
}
@Override
public void unlock() {
beforeUnlock = true;
super.unlock();
afterUnlock = true;
}
@Override
protected boolean autoLock(String method) {
beforeAutoLock = true;
boolean result = super.autoLock(method);
if(result) {
wasAutolocked = true;
autoLocks++;
}
afterAutoLock = true;
return result;
}
@Override
protected void autoUnlock(boolean wasLocked) {
beforeAutoUnlock = true;
super.autoUnlock(wasLocked);
afterAutoUnlock = true;
}
public void saveChangesWithWait() {
waitTime = 400;
saveChanges();
}
@Override
public void _saveChanges() {
if(waitTime > 0) {
synchronized(this) {
try {
wait(waitTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
super._saveChanges();
}
}
private EC createEC() {
return new EC() ;
}
private void assertLockable(final EC ec) {
assertTrue(isLockable(ec));
}
@SuppressWarnings("boxing")
private boolean isLockable(final EC ec) {
try {
return (Boolean) call(new Callable<Boolean>() {
public Boolean call() throws Exception {
Boolean r = ec.tryLock();
if(r) {
ec.unlock();
}
return r;
}
}, 20);
} catch (TimeoutException e) {
fail(e.getMessage());
}
return false;
}
private void assertNotLockable(EC ec) {
assertFalse(isLockable(ec));
}
public void test() {
EC ec = createEC();
assertFalse(ec.beforeLock);
assertFalse(ec.beforeAutoLock);
assertFalse(ec.beforeAutoUnlock);
assertFalse(ec.beforeUnlock);
assertFalse(ec.isAutoLocked());
assertFalse(ec.wasAutolocked);
assertLockable(ec);
}
public void testPlainOneThreadLocking() {
EC ec = createEC();
ec.lock();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertFalse(ec.beforeAutoLock);
assertFalse(ec.beforeAutoUnlock);
assertFalse(ec.beforeUnlock);
ec.unlock();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.beforeAutoLock);
assertFalse(ec.beforeAutoUnlock);
assertFalse(ec.isAutoLocked());
assertFalse(ec.wasAutolocked);
assertEquals(ec.autoLocks, 0);
assertLockable(ec);
}
public void testPlainOneThreadAutoLocking() {
EC ec = createEC();
ec.saveChanges();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
assertTrue("autoLocks: " + ec.autoLocks, ec.autoLocks >= 1);
assertLockable(ec);
}
public void testPlainOneThreadAutoLockingCoalesceNotInRequest() {
// AK: not in a request->coalesce doesn't work
EC ec = createEC();
ec.setCoalesceAutoLocks(true);
ec.saveChanges();
ec.saveChanges();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
assertEquals(2, ec.autoLocks);
assertLockable(ec);
}
public void testPlainOneThreadAutoLockingCoalesceInRequest() {
EC ec = createEC();
ERXApplication._startRequest();
ec.setCoalesceAutoLocks(true);
ec.saveChanges();
ec.saveChanges();
assertNotLockable(ec);
assertTrue(ec.isAutoLocked());
ERXApplication._endRequest();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertEquals(1, ec.autoLocks);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
assertLockable(ec);
}
public void testPlainOneThreadAutoLockingWithLock() {
EC ec = createEC();
ec.lock();
ec.saveChanges();
ec.unlock();
assertFalse(ec.wasAutolocked);
ec.saveChanges();
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
}
public void testTwoThreadWithAutoLock() {
final EC ec = createEC();
Runnable r = new Runnable() {
public void run() {
log.info("Saving: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
try {
t1.join(JOIN_TIME);
t2.join(JOIN_TIME);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
} catch (InterruptedException e) {
assertTrue(false);
}
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
}
public void testTwoThreadWithLock() {
final EC ec = createEC();
Runnable r = new Runnable() {
public void run() {
log.info("Before lock: " + Thread.currentThread().getName());
ec.lock();
log.info("After lock: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
ec.unlock();
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
try {
t1.join(JOIN_TIME);
t2.join(JOIN_TIME);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
} catch (InterruptedException e) {
assertTrue(false);
}
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertFalse(ec.wasAutolocked);
}
public void testTwoThreadWithLockAutoLock() {
final EC ec = createEC();
Runnable r1 = new Runnable() {
public void run() {
log.info("Before save: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
assertFalse(ec.isAutoLocked());
}
};
Runnable r2 = new Runnable() {
public void run() {
log.info("Before lock: " + Thread.currentThread().getName());
ec.lock();
log.info("After lock: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
ec.unlock();
assertFalse(ec.isAutoLocked());
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
t1.join(JOIN_TIME);
t2.join(JOIN_TIME);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
} catch (InterruptedException e) {
assertTrue(false);
}
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
}
public void testTwoThreadWithAutoLockLock() {
final EC ec = createEC();
Runnable r2 = new Runnable() {
public void run() {
log.info("Before save: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
assertFalse(ec.isAutoLocked());
}
};
Runnable r1 = new Runnable() {
public void run() {
log.info("Before lock: " + Thread.currentThread().getName());
ec.lock();
log.info("After lock: " + Thread.currentThread().getName());
ec.saveChangesWithWait();
log.info("Saved: " + Thread.currentThread().getName());
ec.unlock();
assertFalse(ec.isAutoLocked());
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
t1.join(JOIN_TIME);
t2.join(JOIN_TIME);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
} catch (InterruptedException e) {
assertTrue(false);
}
assertTrue(ec.beforeLock);
assertTrue(ec.afterLock);
assertTrue(ec.beforeAutoLock);
assertTrue(ec.afterAutoLock);
assertTrue(ec.beforeAutoUnlock);
assertTrue(ec.afterAutoUnlock);
assertTrue(ec.beforeUnlock);
assertTrue(ec.afterUnlock);
assertFalse(ec.isAutoLocked());
assertTrue(ec.wasAutolocked);
}
}