org.jactr.core.runtime.controller.OldController.java Source code

Java tutorial

Introduction

Here is the source code for org.jactr.core.runtime.controller.OldController.java

Source

/*
 * 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();
            }
        }
    }

}