package org.jactr.tools.misc;
/*
* default logging
*/
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.concurrent.ModelCycleExecutor;
import org.jactr.core.model.IModel;
import org.jactr.core.queue.ITimedEvent;
import org.jactr.core.queue.TimedEventQueue;
import org.jactr.core.queue.timedevents.RunnableTimedEvent;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.runtime.controller.IController;
/**
* some general purpose utilities invoking code on the model thread.
* {@link #executeLater(IModel, Runnable)} and
* {@link #executeLater(IModel, Runnable, Runnable)} both wrap the runnables in
* {@link ITimedEvent}s that are posted onto the {@link IModel}'s
* {@link TimedEventQueue}. <br/> {@link #executeNow(IModel, Runnable)} uses the
* {@link ModelCycleExecutor} to fire the runnable
* {@link ModelCycleExecutor.When#ASAP}.
*
* @author harrison
*/
public class ExecutionUtilities
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(ExecutionUtilities.class);
/**
* will execute the runnable on the model thread at the earliest possible
* moment. This allows you to specify different runnables for the timedevent
* if it is fired or aborted. This method uses {@link ITimedEvent}s to handle
* the execution
*
* @param model
* @param runnable
* @return future boolean if the runnable was fired (false if aborted)
* @throws IllegalStateException
* if the model is not running
*/
static public Future<Boolean> executeLater(IModel model,
final Runnable onFire, final Runnable onAbort)
throws IllegalStateException
{
IController controller = ACTRRuntime.getRuntime().getController();
if (controller == null || !controller.isRunning())
throw new IllegalStateException("Model is not running");
double now = ACTRRuntime.getRuntime().getClock(model).getTime();
final ExecuteLater<Boolean> future = new ExecuteLater<Boolean>();
Runnable fire = new Runnable() {
public void run()
{
try
{
onFire.run();
future.setResult(true);
}
catch (Exception e)
{
future.setException(e);
}
}
};
Runnable abort = new Runnable() {
public void run()
{
try
{
if (onAbort != null) onAbort.run();
future.setResult(false);
}
catch (Exception e)
{
future.setException(e);
}
}
};
model.getTimedEventQueue()
.enqueue(
new RunnableTimedEvent(now, fire, abort) {
@Override
protected boolean shouldWarnOnTimeSlips()
{
return false;
}
});
return future;
}
/**
* post this runnable to execute after the current cycle finishes
*
* @param model
* @param onFire
* @return
*/
static public Future<Boolean> executeNow(IModel model, final Runnable onFire)
{
IController controller = ACTRRuntime.getRuntime().getController();
if (controller == null || !controller.isRunning())
throw new IllegalStateException("Model is not running");
final ExecuteLater<Boolean> future = new ExecuteLater<Boolean>();
Runnable fire = new Runnable() {
public void run()
{
try
{
onFire.run();
future.setResult(true);
}
catch (Exception e)
{
future.setException(e);
}
}
};
new ModelCycleExecutor(model, ModelCycleExecutor.When.ASAP).execute(fire);
return future;
}
static public Future<Boolean> executeLater(IModel model, Runnable onFire)
throws IllegalStateException
{
return executeLater(model, onFire, null);
}
static private class ExecuteLater<T> extends FutureTask<T>
{
public ExecuteLater()
{
super(new Runnable() {
public void run()
{
}
}, null);
}
public void setResult(T result)
{
set(result);
}
@Override
public void setException(Throwable thrown)
{
super.setException(thrown);
}
}
}