package org.jactr.tools.masterslave.master;
/*
* default logging
*/
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import org.antlr.runtime.tree.CommonTree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.chunktype.ISymbolicChunkType;
import org.jactr.core.extensions.IExtension;
import org.jactr.core.model.IModel;
import org.jactr.core.model.IllegalModelStateException;
import org.jactr.core.model.basic.BasicModel;
import org.jactr.core.production.IProduction;
import org.jactr.core.queue.timedevents.RunnableTimedEvent;
import org.jactr.core.queue.timedevents.TerminationTimedEvent;
import org.jactr.core.reality.connector.IClockConfigurator;
import org.jactr.core.reality.connector.IConnector;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.runtime.controller.IController;
import org.jactr.core.runtime.event.ACTRRuntimeAdapter;
import org.jactr.core.runtime.event.ACTRRuntimeEvent;
import org.jactr.core.runtime.event.IACTRRuntimeListener;
import org.jactr.core.slot.BasicSlot;
import org.jactr.core.slot.IMutableSlot;
import org.jactr.core.slot.ISlot;
import org.jactr.core.slot.IUniqueSlotContainer;
import org.jactr.core.slot.UniqueSlotContainer;
import org.jactr.io.IOUtilities;
import org.jactr.tools.masterslave.clock.MasterSlaveClockConfigurator;
import org.jactr.tools.masterslave.slave.SlaveExtension;
import org.jactr.tools.masterslave.slave.SlaveStateCondition;
public class MasterExtension implements IExtension
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(MasterExtension.class);
private IModel _model;
private Map<String, IModel> _slaveModels = new TreeMap<String, IModel>();
private Map<String, IUniqueSlotContainer> _slaveVariables = new TreeMap<String, IUniqueSlotContainer>();
private IACTRRuntimeListener _runtimeListener;
/**
* finds the installed instanceof the master extension in the given model
*
* @param model
* @return
*/
static public MasterExtension getMaster(IModel model)
{
return (MasterExtension) model.getExtension(MasterExtension.class);
}
public IModel getModel()
{
return _model;
}
public String getName()
{
return "master";
}
@SuppressWarnings("unused")
public void install(IModel model)
{
if (true)
throw new IllegalModelStateException(
String
.format(
"%s is currently nonfunctional until the underlying clock wrapper can be updated.",
getClass().getSimpleName()));
_model = model;
/*
* the masterslave also requires some tricky clock control, we need the
* IConnector to do taht.
*/
IConnector connector = ACTRRuntime.getRuntime().getConnector();
IClockConfigurator original = connector.getClockConfigurator();
if (!(original instanceof MasterSlaveClockConfigurator))
{
MasterSlaveClockConfigurator mscc = new MasterSlaveClockConfigurator(
original);
connector.setClockConfigurator(mscc);
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Installed custom clock configurator"));
}
_runtimeListener = new ACTRRuntimeAdapter() {
public void modelStopped(ACTRRuntimeEvent event)
{
IModel model = event.getModel();
String modelName = model.getName();
if (_slaveModels.containsKey(modelName))
{
IUniqueSlotContainer container = getSlaveVariables(modelName);
((IMutableSlot) container
.getSlot(SlaveStateCondition.HAS_COMPLETED_SLOT))
.setValue(Boolean.TRUE);
((IMutableSlot) container
.getSlot(SlaveStateCondition.IS_RUNNING_SLOT))
.setValue(Boolean.FALSE);
}
}
public void modelStarted(ACTRRuntimeEvent event)
{
IModel model = event.getModel();
String modelName = model.getName();
if (_slaveModels.containsKey(modelName))
{
IUniqueSlotContainer container = getSlaveVariables(modelName);
((IMutableSlot) container
.getSlot(SlaveStateCondition.IS_RUNNING_SLOT))
.setValue(Boolean.TRUE);
((IMutableSlot) container
.getSlot(SlaveStateCondition.HAS_COMPLETED_SLOT))
.setValue(Boolean.FALSE);
}
}
};
ACTRRuntime.getRuntime().addListener(_runtimeListener, null);
}
public void uninstall(IModel model)
{
_model = null;
ACTRRuntime.getRuntime().removeListener(_runtimeListener);
}
public String getParameter(String key)
{
return null;
}
public Collection<String> getPossibleParameters()
{
return Collections.EMPTY_LIST;
}
public Collection<String> getSetableParameters()
{
return Collections.EMPTY_LIST;
}
public void setParameter(String key, String value)
{
}
public void initialize() throws Exception
{
}
public IModel getSlaveModel(String alias)
{
return _slaveModels.get(alias.toLowerCase());
}
public IUniqueSlotContainer getSlaveVariables(String alias)
{
IUniqueSlotContainer container = _slaveVariables.get(alias.toLowerCase());
if (container == null) container = createSlaveVariables(alias);
return container;
}
private IUniqueSlotContainer createSlaveVariables(String alias)
{
// make sure we use mutables
IUniqueSlotContainer container = new UniqueSlotContainer(true);
container.addSlot(new BasicSlot(SlaveStateCondition.ALIAS_SLOT, alias));
container.addSlot(new BasicSlot(SlaveStateCondition.IS_LOADED_SLOT,
Boolean.FALSE));
container.addSlot(new BasicSlot(SlaveStateCondition.IS_RUNNING_SLOT,
Boolean.FALSE));
container.addSlot(new BasicSlot(SlaveStateCondition.HAS_COMPLETED_SLOT,
Boolean.FALSE));
return container;
}
protected IModel loadModelAs(URL modelFile, String alias) throws Exception
{
Collection<Exception> warnings = new HashSet<Exception>();
Collection<Exception> errors = new HashSet<Exception>();
IModel model = null;
CommonTree modelDescriptor = IOUtilities.loadModelFile(modelFile, warnings,
errors);
if (errors.size() != 0) throw errors.iterator().next();
if (IOUtilities.compileModelDescriptor(modelDescriptor, warnings, errors))
model = IOUtilities.constructModel(modelDescriptor, warnings, errors);
if (errors.size() != 0) throw errors.iterator().next();
if (model instanceof BasicModel) ((BasicModel) model).setName(alias);
_slaveModels.put(alias.toLowerCase(), model);
/*
* link them together
*/
SlaveExtension se = (SlaveExtension) model
.getExtension(SlaveExtension.class);
if (se == null)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn(String.format(
"%s has no SlaveExtension installed, forcing installation", alias));
se = new SlaveExtension();
model.install(se);
}
se.setMaster(this);
IUniqueSlotContainer container = createSlaveVariables(alias);
((IMutableSlot) container.getSlot(SlaveStateCondition.IS_LOADED_SLOT))
.setValue(Boolean.TRUE);
container.addSlot(new BasicSlot(SlaveStateCondition.MODEL_SLOT, model));
_slaveVariables.put(alias, container);
return model;
}
protected void startModel(IModel model)
{
/*
* since we can be certain the master is already running, we should just
* need to add the model..
*/
ACTRRuntime runtime = ACTRRuntime.getRuntime();
IController controller = runtime.getController();
if (controller.getRunningModels().contains(model))
throw new IllegalStateException("Already running");
else if (!runtime.getModels().contains(model))
{
/*
* temporarily mark it as 'starting', to prevent models from starting
* multiple times
*/
IUniqueSlotContainer container = getSlaveVariables(model.getName());
((IMutableSlot) container.getSlot(SlaveStateCondition.IS_RUNNING_SLOT))
.setValue("starting");
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Starting %s", model.getName()));
runtime.addModel(model);
}
if (!controller.isRunning()) controller.start();
}
protected void stopModel(IModel model)
{
ACTRRuntime runtime = ACTRRuntime.getRuntime();
IController controller = runtime.getController();
if (controller.getRunningModels().contains(model))
{
double now = runtime.getClock(model).getTime();
model.getTimedEventQueue().enqueue(new TerminationTimedEvent(now, now));
}
/*
* we can't block
*/
}
protected void cleanUp(final IModel model) throws IllegalStateException
{
String modelName = model.getName().toLowerCase();
boolean wasSlave = _slaveModels.remove(modelName) != null;
_slaveVariables.remove(modelName);
if (!wasSlave)
{
if (LOGGER.isWarnEnabled())
LOGGER.warn(String.format(
"%s was not a slave model, or was already cleanedup", modelName));
return;
}
Runnable cleanUpRunner = new Runnable() {
public void run()
{
ACTRRuntime runtime = ACTRRuntime.getRuntime();
Collection<IModel> allModels = runtime.getModels();
Collection<IModel> terminated = runtime.getController()
.getTerminatedModels();
if (!allModels.contains(model))
{
if (LOGGER.isDebugEnabled())
LOGGER
.debug(String.format(
"%s is not in the runtime, can safely ignore",
model.getName()));
return;
}
if (terminated.contains(model))
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("%s has terminated, cleaning up",
model.getName()));
// clean up
runtime.removeModel(model);
model.dispose();
}
else
// while we are in the cleanup, but the model hasn't fully terminated
{
if (LOGGER.isWarnEnabled())
LOGGER
.warn(String
.format(
"%s is not in the terminated set %s, but is in the active set. Not completely terminated, will try again later.",
model, terminated));
_model.getTimedEventQueue().enqueue(
new RunnableTimedEvent(ACTRRuntime.getRuntime().getClock(_model)
.getTime() + 0.05, this));
}
}
};
_model.getTimedEventQueue().enqueue(
new RunnableTimedEvent(ACTRRuntime.getRuntime().getClock(_model)
.getTime() + 0.05, cleanUpRunner));
}
public void copyInto(IChunkType chunkType, IModel destination)
{
}
public void copyInto(IChunk chunk, IModel destination)
{
}
/**
* @bug this code will not work w/ multiple inheritance
* @param source
* @param destination
* @param sourceToCopy
*/
protected void createChunkType(IChunkType source, IModel destination,
Map sourceToCopy)
{
/*
* do we already have it? just using name test
*/
try
{
ISymbolicChunkType sct = source.getSymbolicChunkType();
IChunkType copy = destination.getDeclarativeModule()
.getChunkType(sct.getName()).get();
if (copy != null) return;
/*
* it may not be encoded yet, but it could be in the sourceToCopy map
*/
if (sourceToCopy.get(source) != null) return;
/*
* check its parent
*/
IChunkType parent = sct.getParent();
if (parent != null)
{
createChunkType(parent, destination, sourceToCopy);
// make it the copy parent now
parent = (IChunkType) sourceToCopy.get(parent);
}
/*
* now lets create
*/
copy = destination.getDeclarativeModule()
.createChunkType(parent, sct.getName()).get();
sourceToCopy.put(source, copy);
/*
* lets traverse the slots, looking for chunks that we have to handle.. we
* wont assign slot values until after creation
*/
for (ISlot slot : sct.getSlots())
if (slot.getValue() instanceof IChunk)
createChunk((IChunk) slot.getValue(), destination, sourceToCopy);
else if (slot.getValue() instanceof IChunkType)
createChunkType((IChunkType) slot.getValue(), destination,
sourceToCopy);
else if (slot.getValue() instanceof IProduction)
LOGGER
.warn("Copying of productions between models is not currently supported");
}
catch (Exception e)
{
LOGGER.error("unknown exception while getting chunktype ", e);
}
}
protected void createChunk(IChunk source, IModel destination, Map sourceToCopy)
{
}
}