package org.jactr.embed; /* * default logging */ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.agents.IAgent; import org.commonreality.agents.ThinAgent; import org.commonreality.time.IClock; import org.commonreality.util.LockUtilities; import org.jactr.core.model.IModel; import org.jactr.core.reality.connector.LocalConnector; import org.jactr.core.runtime.ACTRRuntime; public class EmbedConnector extends LocalConnector { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(EmbedConnector.class); static private final String EMBED_AGENT_KEY = "embedConnector.thinAgent"; private boolean _running = false; private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(); public EmbedConnector() { super(); } public EmbedConnector(boolean useIndependentClocks) { super(useIndependentClocks); } /** * @see org.jactr.core.reality.connector.IConnector#connect(org.jactr.core.model.IModel) */ @Override public void connect(IModel model) { try { LockUtilities.runLocked(_lock.writeLock(), () -> { if (!isConnected(model)) { super.connect(model); startThinAgent(model, getClock(model)); } }); } catch (InterruptedException e) { // totally expected if we are haulting, but very unlikely LOGGER.debug("EmbedConnector.connect threw InterruptedException : ", e); } catch (Exception e) { LOGGER.error("EmbedConnector.connect threw Exception : ", e); } } /** * @see org.jactr.core.reality.connector.IConnector#disconnect(org.jactr.core.model.IModel) */ @Override public void disconnect(IModel model) { try { LockUtilities.runLocked(_lock.writeLock(), () -> { if (isConnected(model)) { super.disconnect(model); stopThinAgent(model); } }); } catch (InterruptedException e) { LOGGER.debug("EmbedConnector.connect threw InterruptedException : ", e); } catch (Exception e) { LOGGER.error("EmbedConnector.connect threw Exception : ", e); } } public boolean isConnected(IModel model) { return getAgent(model) != null; } /** * @see org.jactr.core.reality.connector.IConnector#getAgentInterface(org.jactr.core.model.IModel) */ @Override public IAgent getAgent(IModel model) { return (IAgent) model.getMetaData(EMBED_AGENT_KEY); } /** * @see org.jactr.core.reality.connector.IConnector#isRunning() */ @Override public boolean isRunning() { return _running; } /** * We create and start the thin agents at start, not connect, so that we are * sure they are available immediately. It is common to require the thinagent * slightly before the model has fully started. * * @see org.jactr.core.reality.connector.IConnector#start() */ @Override public void start() { try { LockUtilities.runLocked(_lock.writeLock(), () -> { if (!_running) for (IModel model : ACTRRuntime.getRuntime().getModels()) connect(model); _running = true; }); } catch (InterruptedException e) { // TODO Auto-generated catch block LOGGER.error("EmbedConnector.start threw InterruptedException : ", e); } } /** * @see org.jactr.core.reality.connector.IConnector#stop() */ @Override public void stop() { try { LockUtilities.runLocked(_lock.writeLock(), () -> { if (_running) for (IModel model : ACTRRuntime.getRuntime().getModels()) disconnect(model); _running = false; }); } catch (InterruptedException e) { // TODO Auto-generated catch block LOGGER.error("EmbedConnector.start threw InterruptedException : ", e); } } protected void startThinAgent(IModel model, IClock clock) { ThinAgent agent = new ThinAgent(model.getName(), clock); try { agent.connect(); // noop agent.initialize(); agent.start(); model.setMetaData(EMBED_AGENT_KEY, agent); } catch (Exception e) { LOGGER .error(String.format("Failed to start thin agent for %s", model), e); } } protected void stopThinAgent(IModel model) { ThinAgent agent = (ThinAgent) model.getMetaData(EMBED_AGENT_KEY); model.setMetaData(EMBED_AGENT_KEY, null); if (agent != null) try { agent.stop(); agent.disconnect(); agent.shutdown(); } catch (Exception e) { LOGGER.error(String.format("Failed to stop thin agent for %s", model), e); } } }