/*
* Created on Nov 30, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.core.runtime.controller;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.executor.GeneralThreadFactory;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.model.IModel;
import org.jactr.core.model.event.IModelListener;
import org.jactr.core.model.event.ModelEvent;
import org.jactr.core.model.event.ModelListenerAdaptor;
import org.jactr.core.model.six.DefaultCycleProcessor6;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.runtime.DefaultModelRunner;
import org.jactr.core.runtime.event.ACTRRuntimeEvent;
import org.jactr.core.runtime.event.IACTRRuntimeListener;
import org.jactr.core.runtime.profile.ProfilingModelRunner;
/**
* @author developer
*/
public class OldController //implements IController
{
/**
* logger definition
*/
static private final Log LOGGER = LogFactory
.getLog(OldController.class);
protected Set<IModel> _runningModels;
protected Set<IModel> _terminatedModels;
protected Set<IModel> _suspendedModels;
protected Map<IModel, Suspender> _suspensionState;
protected boolean _suspendOnStart = false;
protected Map<IModel, ExecutorService> _executors;
private IModelListener _defaultModelListener;
private IACTRRuntimeListener _defaultRuntimeListener;
protected ReentrantLock _lock = new ReentrantLock();
protected Condition _start = _lock.newCondition();
protected Condition _stop = _lock.newCondition();
protected Condition _suspend = _lock.newCondition();
protected Condition _resume = _lock.newCondition();
public OldController()
{
_executors = new HashMap<IModel, ExecutorService>();
_runningModels = new HashSet<IModel>();
_terminatedModels = new HashSet<IModel>();
_suspendedModels = new HashSet<IModel>();
_suspensionState = new HashMap<IModel, Suspender>();
_defaultModelListener = createModelListener();
_defaultRuntimeListener = createRuntimeListener();
}
/**
* return the model listener that we use in order to control execution
*
* @return
*/
final protected IModelListener getModelListener()
{
return _defaultModelListener;
}
/**
* return the runtime listener that we use in order to signal the controller
* events (awake from wait)
*
* @return
*/
final protected IACTRRuntimeListener getRuntimeListener()
{
return _defaultRuntimeListener;
}
/**
* create the model listener that will be used to controller the model. if you
* are subclassing this controller, and need to customize the listener, use
* new DecoratedModelListener(super.createModelListener()); and please, oh,
* please, make sure you call the super.XXXX() in your versions too
*
* @return
*/
protected IModelListener createModelListener()
{
return new ModelListenerAdaptor() {
/**
* if the controller has called suspend, models will block at the
* cycleStart
*
* @see org.jactr.core.model.event.ModelListenerAdaptor#cycleStarted(org.jactr.core.model.event.ModelEvent)
*/
@Override
public void cycleStarted(ModelEvent event)
{
Suspender suspender = getSuspender(event.getSource());
if (suspender == null)
LOGGER.warn("No model control block found for " + event.getSource());
else
try
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Checking to see if we should suspend");
suspender.suspendIfNecessary();
}
catch (Exception e)
{
LOGGER.error("Could not suspend " + event.getSource(), e);
}
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelResumed(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
@Override
public void modelResumed(ModelEvent event)
{
IModel model = event.getSource();
if (LOGGER.isDebugEnabled()) LOGGER.debug(model + " has been resumed");
if (!isSuspended(model))
throw new RuntimeException(model + " isn't suspended, cannot resume");
try
{
_lock.lock();
_suspendedModels.remove(model);
/*
* everyone is awake.. fire the runtime resumed event
*/
if (_suspendedModels.size() == 0)
{
/*
* condition was originally if(!isSuspended()) which isn't right,
* since isSuspended only returns true if all the models are
* suspended, which means if one is resumed, this would have always
* fired, resulting in a resumed event for every running model. AMH
* 03.15.07
*/
ACTRRuntime runtime = ACTRRuntime.getRuntime();
if (runtime.hasListeners())
runtime.dispatch(new ACTRRuntimeEvent(
ACTRRuntimeEvent.Type.RESUMED));
}
_resume.signalAll();
}
finally
{
_lock.unlock();
}
}
/**
* we use this to snag the model thread for this model's suspender
*
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelStarted(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
@Override
public void modelStarted(ModelEvent event)
{
IModel model = event.getSource();
if (LOGGER.isDebugEnabled())
LOGGER
.debug(model + " has been started on " + Thread.currentThread());
try
{
_lock.lock();
if (!isRunning(model))
{
_runningModels.add(model);
getSuspender(model).setModelThread(Thread.currentThread());
//make sure word gets out
_start.signalAll();
_stop.signalAll();
}
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelStopped(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
@Override
public void modelStopped(ModelEvent event)
{
/*
* logic moved to disconnected so that we can be sure all events are
* delivered before killing the executor - also it ensures that the
* runtime doesn't hit stopped until everything is done..
*/
}
@Override
public void modelDisconnected(ModelEvent event)
{
IModel model = event.getSource();
if (LOGGER.isDebugEnabled()) LOGGER.debug(model + " has stopped");
try
{
_lock.lock();
_terminatedModels.add(model);
_runningModels.remove(model);
/*
* remove the listener
*/
model.removeListener(getModelListener());
getSuspender(model).setModelThread(null);
/*
* this will just change the suspender state..
*/
resumeModel(model);
/*
* everyone has stopped... fire the onStop and signal. I'd rather
* these not fire on the modelThread - but there is nothing that I can
* do about this short of starting another thread just for the
* controller
*/
if (!isRunning())
{
ACTRRuntime runtime = ACTRRuntime.getRuntime();
if (runtime.hasListeners())
runtime.dispatch(new ACTRRuntimeEvent(
ACTRRuntimeEvent.Type.STOPPED));
}
/*
* and kill the model's executor
*/
ExecutorService service = _executors.remove(model);
if (service == null)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn("No valid execution service is associated with "
+ model);
}
else
destroyExecutorService(service, model);
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelSuspended(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
@Override
public void modelSuspended(ModelEvent event)
{
IModel model = event.getSource();
if (LOGGER.isDebugEnabled()) LOGGER.debug(model + " suspended");
try
{
_lock.lock();
if (!isRunning(model))
throw new RuntimeException(
"Cannot suspend a model that isnt running");
_suspendedModels.add(model);
/*
* everyone is suspended..
*/
if (isSuspended())
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Everyone has suspended, signalling runtime");
ACTRRuntime runtime = ACTRRuntime.getRuntime();
if (runtime.hasListeners())
runtime.dispatch(new ACTRRuntimeEvent(
ACTRRuntimeEvent.Type.SUSPENDED));
}
_suspend.signalAll();
}
finally
{
_lock.unlock();
}
}
};
}
/**
* create the runtime listener that handles controller signalling.
*
* @return
*/
protected IACTRRuntimeListener createRuntimeListener()
{
return new IACTRRuntimeListener() {
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelAdded(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void modelAdded(ACTRRuntimeEvent event)
{
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#modelRemoved(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void modelRemoved(ACTRRuntimeEvent event)
{
/*
* let's be sure there is no executor
*/
ExecutorService service = _executors.remove(event.getModel());
if (service != null) destroyExecutorService(service, event.getModel());
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#runtimeResumed(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void runtimeResumed(ACTRRuntimeEvent event)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("runtime resumed");
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#runtimeStarted(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void runtimeStarted(ACTRRuntimeEvent event)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("runtime started");
try
{
_lock.lock();
_start.signalAll();
_stop.signalAll();
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#runtimeStopped(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void runtimeStopped(ACTRRuntimeEvent evnet)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("runtime stopped");
ACTRRuntime runtime = ACTRRuntime.getRuntime();
Runnable stop = runtime.getOnStop();
if (LOGGER.isDebugEnabled()) LOGGER.debug("Running onStop " + stop);
if (stop != null) stop.run();
if (LOGGER.isDebugEnabled()) LOGGER.debug("Stopping common reality");
try
{
runtime.getConnector().stop();
}
catch (Exception e)
{
LOGGER.warn("Could not cleanly stop connector connection. ", e);
}
try
{
_lock.lock();
_start.signalAll();
_stop.signalAll();
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.event.IACTRRuntimeListener#runtimeSuspended(org.jactr.core.runtime.event.ACTRRuntimeEvent)
*/
public void runtimeSuspended(ACTRRuntimeEvent event)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("runtime suspended");
}
};
}
/**
* if true, after start() is called, the models will immediately suspend
*
* @param suspendImmediately
*/
final public void setSuspendImmediately(boolean suspendImmediately)
{
_suspendOnStart = suspendImmediately;
}
/**
* return all the models that are currently running
*
* @see org.jactr.core.runtime.controller.IController#getRunningModels()
*/
final public Collection<IModel> getRunningModels()
{
try
{
_lock.lock();
return new ArrayList<IModel>(_runningModels);
}
finally
{
_lock.unlock();
}
}
/**
* return all the models that are suspended
*
* @see org.jactr.core.runtime.controller.IController#getSuspendedModels()
*/
final public Collection<IModel> getSuspendedModels()
{
try
{
_lock.lock();
return new ArrayList<IModel>(_suspendedModels);
}
finally
{
_lock.unlock();
}
}
/**
* return all the models that have termianted
*
* @see org.jactr.core.runtime.controller.IController#getTerminatedModels()
*/
final public Collection<IModel> getTerminatedModels()
{
try
{
_lock.lock();
return new ArrayList<IModel>(_terminatedModels);
}
finally
{
_lock.unlock();
}
}
/**
* return the suspender which is responsible for flaging a model for
* suspension, doing it and propogating the events.
*
* @param model
* @return
*/
final protected Suspender getSuspender(IModel model)
{
try
{
_lock.lock();
return _suspensionState.get(model);
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.controller.IController#isRunning()
*/
final public boolean isRunning()
{
try
{
_lock.lock();
return _runningModels.size() != 0;
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.controller.IController#isSuspended()
*/
final public boolean isSuspended()
{
try
{
_lock.lock();
return isRunning() && _suspendedModels.containsAll(_runningModels);
}
finally
{
_lock.unlock();
}
}
/**
* reset a competed runtime to its prefrun state
*
* @see org.jactr.core.runtime.controller.IController#reset()
*/
final public void reset()
{
if (isRunning()) try
{
waitForCompletion();
}
catch (InterruptedException ie)
{
LOGGER.error("interrupted while waiting for completion", ie);
}
try
{
_lock.lock();
_runningModels.clear();
_terminatedModels.clear();
_suspendedModels.clear();
}
finally
{
_lock.unlock();
}
}
/**
* request that a suspended runtime resume
*
* @see org.jactr.core.runtime.controller.IController#resume()
*/
final public void resume()
{
if (!isRunning())
throw new RuntimeException(
"Cannot resume a runtime that isnt even running");
/*
* note: this is resuming any models - whether they have been suspended or
* not. We could be more strict and only resume suspended models, but it
* doesn't really make a difference given the way Suspender works
*/
try
{
_lock.lock();
for (IModel model : _runningModels)
resumeModel(model);
}
finally
{
_lock.unlock();
}
}
/**
* attach this controller to the runtime. called by ACRRuntime.setController()
* If you override, please call super.attach()
*
* @see org.jactr.core.runtime.controller.IController#attach()
*/
public void attach()
{
ACTRRuntime runtime = ACTRRuntime.getRuntime();
if (runtime.getController() == this)
{
if (LOGGER.isWarnEnabled()) LOGGER.warn("already connected to runtime");
return;
}
IACTRRuntimeListener listener = getRuntimeListener();
runtime.addListener(listener, ExecutorServices.INLINE_EXECUTOR);
// we may have missed the addModel callbacks..
for (IModel model : runtime.getModels())
listener.modelAdded(new ACTRRuntimeEvent(model,
ACTRRuntimeEvent.Type.MODEL_ADDED));
}
/**
* detach from the runtime. if you override, please call super.detach()
*
* @see org.jactr.core.runtime.controller.IController#detach()
*/
public void detach()
{
if (isRunning())
throw new RuntimeException(
"Cannot detach until the models complete their runs");
/*
* remove our listener
*/
ACTRRuntime runtime = ACTRRuntime.getRuntime();
runtime.removeListener(getRuntimeListener());
/*
* the model listener will be removed when the model stops
*/
reset();
}
/**
* @see org.jactr.core.runtime.controller.IController#start()
*/
final public void start()
{
start(_suspendOnStart);
}
/**
* this will fire the onStart if present, signal the start of the runtime and
* then start the models
*
* @see org.jactr.core.runtime.controller.IController#start(boolean)
*/
final public void start(boolean suspendImmediately)
{
if (isRunning())
throw new RuntimeException("Runtime is currently running");
ACTRRuntime runtime = ACTRRuntime.getRuntime();
/*
* start common reality if necessary
*/
runtime.getConnector().start();
/*
* fire the onStart. we do this outside of the lock in case the onStart
* needs to access controller
*/
Runnable start = runtime.getOnStart();
if (LOGGER.isDebugEnabled()) LOGGER.debug("Running onStart" + start);
if (start != null) start.run();
/*
* fire the runtime event. outside of the lock in case asynch listeners will
* access the controller
*/
if (runtime.hasListeners())
runtime.dispatch(new ACTRRuntimeEvent(ACTRRuntimeEvent.Type.STARTED));
try
{
_lock.lock();
setSuspendImmediately(suspendImmediately);
for (IModel model : runtime.getModels())
startModel(model, _suspendOnStart);
}
finally
{
_lock.unlock();
}
}
/**
* request that everyone stop.
*
* @see org.jactr.core.runtime.controller.IController#stop()
*/
final public void stop()
{
try
{
_lock.lock();
if (!isRunning()) return;
/*
* request that everyone quit..
*/
for (IModel model : _runningModels)
stopModel(model);
}
finally
{
_lock.unlock();
}
}
public Future<Boolean> terminate()
{
if (LOGGER.isWarnEnabled()) LOGGER.warn("NO OP");
FutureTask<Boolean> rtn = new FutureTask<Boolean>(new Callable<Boolean>(){
public Boolean call() throws Exception
{
try
{
return true;
}
catch(Exception e)
{
LOGGER.error("Failed to terminate runtime correctly ",e);
return false;
}
}
});
rtn.run();
return rtn;
}
/**
* request that everyone suspend
*
* @see org.jactr.core.runtime.controller.IController#suspend()
*/
final public void suspend()
{
try
{
_lock.lock();
/*
* request that everyone suspend
*/
for (IModel model : _runningModels)
suspendModel(model);
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.controller.IController#waitForCompletion()
*/
final public void waitForCompletion() throws InterruptedException
{
try
{
_lock.lock();
while (isRunning())
_stop.await();
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.controller.IController#waitForResumption()
*/
final public void waitForResumption() throws InterruptedException
{
try
{
_lock.lock();
while (isRunning() && isSuspended())
_resume.await();
}
finally
{
_lock.unlock();
}
}
/**
* @see org.jactr.core.runtime.controller.IController#waitForSuspension()
*/
final public void waitForSuspension() throws InterruptedException
{
try
{
_lock.lock();
while (isRunning() && !isSuspended())
_suspend.await();
}
finally
{
_lock.unlock();
}
}
final public void waitForStart() throws InterruptedException
{
try
{
_lock.lock();
/*
* this is a tad complicated since a model might terminate before wait for
* Start is even called..
*/
HashSet<IModel> runningOrTerminated = new HashSet<IModel>(_runningModels);
runningOrTerminated.addAll(_terminatedModels);
while (!runningOrTerminated.containsAll(ACTRRuntime.getRuntime()
.getModels()))
{
_start.await();
// update the contents
runningOrTerminated.addAll(_runningModels);
runningOrTerminated.addAll(_terminatedModels);
}
}
finally
{
_lock.unlock();
}
}
/**
* override to provide ProfilingModelRunner if you'd like to track performance
* stats
*
* @param model
* @param service
* @return
*/
protected Runnable createModelRunner(IModel model, ExecutorService service)
{
if (Boolean.getBoolean("jactr.profiling"))
return new ProfilingModelRunner(service, model,
new DefaultCycleProcessor6());
return new DefaultModelRunner(service, model, new DefaultCycleProcessor6());
}
/**
* if you override instantiateModelRunner and need to clean up said runner,
* clean up here
*
* @param runner
* @param model
*/
protected void destroyModelRunner(Runnable runner, IModel model)
{
if (runner instanceof ProfilingModelRunner)
{
ProfilingModelRunner prm = (ProfilingModelRunner) runner;
StringBuilder sb = new StringBuilder("Profile Stats for ");
sb.append(model.getName()).append("\n");
sb.append(" Total actual processing cycles \t").append(
prm.getTotalCycles()).append("\n");
sb.append(" Simulated processing cycles \t\t").append(model.getCycle())
.append("\n");
sb.append(" Total actual time \t\t\t").append(prm.getTotalCycleTime())
.append("s\n");
sb.append(" Simulate time \t\t\t\t").append(prm.getSimulatedTime())
.append("s\n");
sb.append(" Average sleep time (wait for clock) \t").append(
prm.getActualWaitTime() / prm.getTotalCycles() * 1000d)
.append("ms\n");
sb.append(" Average time processing events \t").append(
prm.getActualEventTime() / prm.getTotalCycles() * 1000d).append(
"ms\n");
sb.append(" Average production cycle time \t\t").append(
prm.getActualCycleTime() / prm.getTotalCycles() * 1000d).append(
"ms\n");
sb.append(" Average production time + waits \t").append(
prm.getTotalCycleTime() / prm.getTotalCycles() * 1000d)
.append("ms\n");
sb.append(" Realtime factor \t\t\t").append(prm.getRealTimeFactor())
.append(" X \n");
System.out.println(sb.toString());
System.out.flush();
}
}
/**
* create the executor service that will run the model. The service is created
* and then added to ExecutorServices under model.getName(). Note, that model
* threads must be run on ITimeDependentThreads. If you need a thread factory,
* check ExecutorServices.TIME_DEPENDENT_THREAD_FACTORY
*
* @param model
* @return
*/
protected ExecutorService createExecutorService(IModel model)
{
ExecutorService service = Executors
.newSingleThreadExecutor(new GeneralThreadFactory(model.getName()));
ExecutorServices.addExecutor(model.getName(), service);
return service;
}
/**
* destory the executor service.
*
* @param service
* @param model
*/
protected void destroyExecutorService(ExecutorService service, IModel model)
{
if (!service.isShutdown()) service.shutdown();
if (service instanceof ThreadPoolExecutor)
{
ThreadFactory factory = ((ThreadPoolExecutor) service).getThreadFactory();
if (factory instanceof GeneralThreadFactory)
((GeneralThreadFactory) factory).dispose();
}
ExecutorServices.removeExecutor(model.getName());
}
/**
* actually start the model
*
* @param model
* @param suspendOnStart
*/
final protected void startModel(final IModel model, boolean suspendOnStart)
{
try
{
_lock.lock();
/*
* attach the model listener that lets us block at the start of a cycle..
*/
model.addListener(getModelListener(), ExecutorServices.INLINE_EXECUTOR);
/*
* create the executor on which the model will run
*/
ExecutorService executor = createExecutorService(model);
_executors.put(model, executor);
/*
* create the suspender that will control the model execution
*/
Suspender suspender = new Suspender(model);
suspender.setShouldSuspend(suspendOnStart);
_suspensionState.put(model, suspender);
/*
* now the actual runnable
*/
final Runnable modelRunner = createModelRunner(model, executor);
Runnable actual = new Runnable() {
public void run()
{
try
{
modelRunner.run();
}
finally
{
destroyModelRunner(modelRunner, model);
}
}
};
/*
* and start the beatch up.
*/
executor.execute(actual);
}
finally
{
_lock.unlock();
}
}
/**
* force the model to stop
*/
final protected void stopModel(IModel model)
{
ExecutorService executor = _executors.get(model);
if (executor != null)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Shuting down " + model);
executor.shutdown();
}
/*
* wake it up just in case
*/
resumeModel(model);
}
/**
* is this model running?
*
* @param model
* @return
*/
final protected boolean isRunning(IModel model)
{
try
{
_lock.lock();
return _runningModels.contains(model);
}
finally
{
_lock.unlock();
}
}
/**
* is this model suspended
*
* @param model
* @return
*/
final protected boolean isSuspended(IModel model)
{
try
{
_lock.lock();
return _suspendedModels.contains(model);
}
finally
{
_lock.unlock();
}
}
/**
* request that this model be suspended
*
* @param model
*/
final protected void suspendModel(IModel model)
{
Suspender suspender = getSuspender(model);
if (suspender == null)
LOGGER.warn("Could not find model control block for " + model
+ ", ignoring suspension request");
else
suspender.setShouldSuspend(Boolean.TRUE);
}
/**
* request that this model resume
*/
final protected void resumeModel(IModel model)
{
Suspender suspender = getSuspender(model);
if (suspender == null)
LOGGER.warn("Could not find model control block for " + model
+ ", ignoring resumption request");
else
suspender.setShouldSuspend(Boolean.FALSE);
}
/**
* actual class that will block a model's execution. It is also responsible
* for firing the modelSuspend/resume events
*
* @author developer
*/
static protected class Suspender
{
private Lock _lock;
private boolean _shouldSuspend;
private Condition _suspend;
private IModel _model;
private Thread _thread;
public Suspender(IModel model)
{
_lock = new ReentrantLock();
_model = model;
_suspend = _lock.newCondition();
}
public Thread getModelThread()
{
return _thread;
}
protected void setModelThread(Thread thread)
{
_thread = thread;
}
public void setShouldSuspend(boolean suspend)
{
try
{
_lock.lock();
if (LOGGER.isDebugEnabled())
LOGGER.debug("Marking " + _model + " should suspend : " + suspend);
_shouldSuspend = suspend;
_suspend.signalAll();
}
finally
{
_lock.unlock();
}
}
public boolean willSuspend()
{
try
{
_lock.lock();
return _shouldSuspend;
}
finally
{
_lock.unlock();
}
}
public void suspendIfNecessary() throws InterruptedException
{
boolean wasSuspended = false;
if (Thread.currentThread() != getModelThread())
throw new RuntimeException(
"Cannot suspend current thread since it is not the model thread "
+ getModelThread());
try
{
_lock.lock();
if (willSuspend())
_model.dispatch(new ModelEvent(_model, ModelEvent.Type.SUSPENDED));
while (willSuspend())
{
wasSuspended = true;
_suspend.await();
}
}
finally
{
if (wasSuspended)
_model.dispatch(new ModelEvent(_model, ModelEvent.Type.RESUMED));
_lock.unlock();
}
}
}
}