/*
* Created on May 8, 2006 Copyright (C) 2001-5, 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;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.time.IClock;
import org.jactr.core.event.ACTREventDispatcher;
import org.jactr.core.model.IModel;
import org.jactr.core.reality.connector.IConnector;
import org.jactr.core.reality.connector.LocalConnector;
import org.jactr.core.runtime.controller.IController;
import org.jactr.core.runtime.event.ACTRRuntimeEvent;
import org.jactr.core.runtime.event.IACTRRuntimeListener;
public class ACTRRuntime
{
/**
* logger definition
*/
static public final Log LOGGER = LogFactory
.getLog(ACTRRuntime.class);
static private ACTRRuntime _instance;
private Collection<IModel> _allModels;
private Runnable _onStart;
private Runnable _onStop;
private Object _applicationData;
private IController _controller;
ACTREventDispatcher<ACTRRuntime, IACTRRuntimeListener> _eventDispatcher;
private IConnector _commonRealityConnector;
private File _workingDirectory;
/**
* return the ACTRRuntime singleton
*
* @return
*/
static public ACTRRuntime getRuntime()
{
synchronized (ACTRRuntime.class)
{
if (_instance == null) _instance = new ACTRRuntime();
return _instance;
}
}
protected ACTRRuntime()
{
_allModels = Collections.synchronizedList(new ArrayList<IModel>());
_eventDispatcher = new ACTREventDispatcher<ACTRRuntime, IACTRRuntimeListener>();
setConnector(new LocalConnector());
setWorkingDirectory(new File(System.getProperty("user.dir")));
}
public File getWorkingDirectory()
{
return _workingDirectory;
}
public void setWorkingDirectory(File workingDirectory)
{
_workingDirectory = workingDirectory;
}
public IConnector getConnector()
{
return _commonRealityConnector;
}
public void setConnector(IConnector connector)
{
_commonRealityConnector = connector;
}
public IController getController()
{
return _controller;
}
/**
* will call IController.attach()
*
* @param controller
*/
public void setController(IController controller)
{
IController oldController = _controller;
if (oldController == controller)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Already attached");
return;
}
if (oldController != null) oldController.detach();
if (controller != null) try
{
controller.attach();
}
catch (Exception e)
{
LOGGER.error("Could not attach new controller " + controller, e);
controller = null;
}
_controller = controller;
}
/**
* return the clock for the model in this runtime. originally this was a zero
* parameter method but since when connected to common reality, each model has
* its own clock instance, we use the proxy clock to return a valid clock if
* connected to common reality, otherwise it returns a default shared clock
*
* @param model
* if null will always return the default shared clock
* @return
*/
public IClock getClock(IModel model)
{
return getConnector().getClock(model);
}
/**
* add a model to this runtime. if the runtime is already actively running,
* this model will be added and immediately started. The model should have had
* all of its listeners attached before being added to the runtime. this will
* ensure that if the model is suspended that all events are delivered
* appropriately
*
* @param model
*/
public void addModel(IModel model)
{
_allModels.add(model);
/*
* fire the event
*/
if (hasListeners())
dispatch(new ACTRRuntimeEvent(model, ACTRRuntimeEvent.Type.MODEL_ADDED));
}
/**
* remove this model, assuming that it is not running. If it is, a runtime
* excepiton will be thrown
*
* @param model
*/
public void removeModel(IModel model)
{
try
{
/*
* why wrap this? when the event fires, the controller, which will receive
* the event may throw an exception if you are removing from a running
* runtime
*/
if (hasListeners())
dispatch(new ACTRRuntimeEvent(model,
ACTRRuntimeEvent.Type.MODEL_REMOVED));
_allModels.remove(model);
}
catch (Exception e)
{
}
}
/**
* return all the models associated with the runtime
*
* @return
*/
public Collection<IModel> getModels()
{
return Collections.unmodifiableCollection(_allModels);
}
/**
* runnable to be executed after the runtime is started by the controller
*
* @param onStart
*/
public void setOnStart(Runnable onStart)
{
_onStart = onStart;
}
public Runnable getOnStart()
{
return _onStart;
}
/**
* runnable to be executed after the runtime has stopped. ie. all models
* terminated
*/
public void setOnStop(Runnable onStop)
{
_onStop = onStop;
}
/**
* called by controller
*
* @return
*/
public Runnable getOnStop()
{
return _onStop;
}
/**
* @param applicationData
*/
public void setApplicationData(Object applicationData)
{
_applicationData = applicationData;
}
public Object getApplicationData()
{
return _applicationData;
}
public boolean hasListeners()
{
return _eventDispatcher.hasListeners();
}
public Collection<IACTRRuntimeListener> getListeners()
{
return _eventDispatcher.getListeners();
}
public void addListener(IACTRRuntimeListener listener, Executor executor)
{
_eventDispatcher.addListener(listener, executor);
}
public void removeListener(IACTRRuntimeListener listener)
{
_eventDispatcher.removeListener(listener);
}
public void dispatch(ACTRRuntimeEvent event)
{
if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(event);
}
}