package book.example.threading.executor;
import org.jmock.api.Imposteriser;
import org.jmock.api.Invocation;
import org.jmock.api.Invokable;
import org.jmock.internal.StatePredicate;
import org.jmock.lib.JavaReflectionImposteriser;
import org.junit.Assert;
import static org.hamcrest.StringDescription.asString;
/**
* A Decorator that wraps an Imposteriser and makes the Mockery thread-safe.
*
* In the latest version of jMock, this is performed by plugging a Synchroniser into the Mockery
*
* @author Nat Pryce
*/
public class SynchronisingImposteriser extends DecoratingImposteriser {
private final Object sync = new Object();
private Error firstError = null;
public SynchronisingImposteriser() {
this(JavaReflectionImposteriser.INSTANCE);
}
public SynchronisingImposteriser(Imposteriser imposteriser) {
super(imposteriser);
}
/**
* Waits for a StatePredicate to become active.
* <p/>
* Warning: this will wait forever unless the test itself has a timeout.
*
* @param p the StatePredicate to wait for
* @throws InterruptedException
*/
public void waitUntil(StatePredicate p) throws InterruptedException {
synchronized (sync) {
while (!p.isActive()) {
checkForFailure();
sync.wait();
}
}
}
/**
* Waits up to a timeout for a StatePredicate to become active. Fails the test
* if the timeout expires.
*
* @param p the StatePredicate to wait for
* @param timeoutMs the timeout in milliseconds
* @throws InterruptedException
*/
public void waitUntil(StatePredicate p, long timeoutMs) throws InterruptedException {
long start = System.currentTimeMillis();
synchronized (sync) {
while (!p.isActive()) {
checkForFailure();
long now = System.currentTimeMillis();
long timeLeft = timeoutMs - (now - start);
if (timeLeft <= 0) {
Assert.fail("timeout waiting for " + asString(p));
}
sync.wait(timeLeft);
}
}
}
@Override
protected Object applyInvocation(Invokable imposter, Invocation invocation) throws Throwable {
synchronized (sync) {
try {
return imposter.invoke(invocation);
} catch (Error e) {
if (firstError == null) {
firstError = e;
}
sync.notifyAll();
throw e;
} finally {
sync.notifyAll();
}
}
}
private void checkForFailure() {
if (firstError != null) {
throw firstError;
}
}
}