package fitnesse.slim;
import java.util.concurrent.*;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static java.util.concurrent.TimeUnit.SECONDS;
public class StatementTimeoutExecutor implements StatementExecutorInterface {
private final StatementExecutorInterface inner;
private final Integer timeout;
private final ExecutorService service;
private StatementTimeoutExecutor(StatementExecutorInterface inner, Integer timeout, ExecutorService service) {
this.inner = inner;
this.timeout = timeout;
this.service = service;
}
public static StatementExecutorInterface decorate(StatementExecutorInterface inner, Integer timeout) {
return decorate(inner, timeout, newSingleThreadExecutor());
}
public static StatementExecutorInterface decorate(StatementExecutorInterface inner, Integer timeout, ExecutorService service) {
return new StatementTimeoutExecutor(inner, timeout, service);
}
@Override
public void assign(final String name, final Object value) {
inner.assign(name, value);
}
@Override
public Object getSymbol(String symbolName) {
return inner.getSymbol(symbolName);
}
@Override
public Object getInstance(String instanceName) {
return inner.getInstance(instanceName);
}
@Override
public boolean stopHasBeenRequested() {
return inner.stopHasBeenRequested();
}
@Override
public void reset() {
inner.reset();
}
@Override
public void setInstance(String actorInstanceName, Object actor) {
inner.setInstance(actorInstanceName, actor);
}
@Override
public void addPath(String path) throws SlimException {
inner.addPath(path);
}
@Override
public void create(final String instanceName, final String className, final Object... constructorArgs) throws SlimException {
Future<?> submit = service.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
inner.create(instanceName, className, constructorArgs);
return true;
}
});
getWithTimeout(submit);
}
@Override
public Object callAndAssign(final String symbolName, final String instanceName, final String methodsName, final Object... arguments) throws SlimException {
Future<Object> submit = service.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
return inner.callAndAssign(symbolName, instanceName, methodsName, arguments);
}
});
return getWithTimeout(submit);
}
@Override
public Object call(final String instanceName, final String methodName, final Object... arguments) throws SlimException {
Future<Object> submit = service.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
return inner.call(instanceName, methodName, arguments);
}
});
return getWithTimeout(submit);
}
private <T> T getWithTimeout(Future<T> submit) throws SlimException {
try {
return submit.get(timeout, SECONDS);
} catch (TimeoutException e) {
throw new SlimException(Integer.toString(timeout), SlimServer.TIMED_OUT, true);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SlimError("Statement execution was interrupted", e);
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof SlimException) {
throw (SlimException) cause;
} else {
throw new SlimException(e.getCause());
}
}
}
}