/* * Created on Oct 21, 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.model.basic; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.Executor; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.event.ACTREventDispatcher; import org.jactr.core.event.IParameterListener; import org.jactr.core.event.ParameterEvent; import org.jactr.core.extensions.IExtension; import org.jactr.core.model.ICycleProcessor; import org.jactr.core.model.IModel; import org.jactr.core.model.IllegalModelStateException; import org.jactr.core.model.event.IModelListener; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.model.six.DefaultCycleProcessor6; import org.jactr.core.module.IModule; import org.jactr.core.module.declarative.IDeclarativeModule; import org.jactr.core.module.procedural.IProceduralModule; import org.jactr.core.queue.TimedEventQueue; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.utils.collections.CachedCollection; import org.jactr.core.utils.collections.CachedMap; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.core.utils.parameter.ParameterHandler; import org.jactr.instrument.IInstrument; public class BasicModel implements IModel { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(BasicModel.class); static public final String CYCLE_SKIPPING_PARAM = "EnableUnusedCycleSkipping"; static public final String PERSISTENT_PARAM = "EnablePersistentExecution"; static public final String AGE_PARAM = "Age"; static private final Collection<String> READABLE_PARAMETERS = Collections .unmodifiableCollection(Arrays .asList( AGE_PARAM, CYCLE_SKIPPING_PARAM, PERSISTENT_PARAM)); static private final Collection<String> WRITABLE_PARAMETERS = Collections .unmodifiableCollection(Arrays .asList( AGE_PARAM, CYCLE_SKIPPING_PARAM, PERSISTENT_PARAM)); protected boolean _isInitialized = false; protected TimedEventQueue _timedEventQueue; protected IDeclarativeModule _declarativeModule; protected IProceduralModule _proceduralModule; protected CachedMap<String, IActivationBuffer> _buffers; protected CachedCollection<IModule> _modules; protected CachedCollection<IExtension> _extensions; protected CachedCollection<IInstrument> _installedInstruments; protected boolean _cycleSkippingEnabled = true; protected boolean _persistentExecution = false; protected ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(); protected ACTREventDispatcher<IModel, IModelListener> _eventDispatcher; protected ACTREventDispatcher<IParameterized, IParameterListener> _parameterEventDispatcher; protected Map<String, Object> _metaData; protected Map<String, Object> _parameterMap; protected String _name; protected boolean _modulesInitialized = false; protected long _cycle; protected double _age; protected ICycleProcessor _cycleProcessor; public BasicModel() { _timedEventQueue = new TimedEventQueue(this); _buffers = new CachedMap<String, IActivationBuffer>( new TreeMap<String, IActivationBuffer>()); _modules = new CachedCollection<IModule>(new ArrayList<IModule>()); _extensions = new CachedCollection<IExtension>(new ArrayList<IExtension>()); _installedInstruments = new CachedCollection<IInstrument>( new ArrayList<IInstrument>()); _eventDispatcher = new ACTREventDispatcher<IModel, IModelListener>(); _parameterEventDispatcher = new ACTREventDispatcher<IParameterized, IParameterListener>(); _metaData = new TreeMap<String, Object>(); _parameterMap = new TreeMap<String, Object>(); setCycleProcessor(createCycleProcessor()); } public void setCycleProcessor(ICycleProcessor processor) { _cycleProcessor = processor; } public ICycleProcessor getCycleProcessor() { return _cycleProcessor; } protected ICycleProcessor createCycleProcessor() { return new DefaultCycleProcessor6(); } public BasicModel(String name) { this(); setName(name); } public void dispose() { /* * explicitly uninstall */ for (IInstrument instrument : new ArrayList<IInstrument>( _installedInstruments)) uninstall(instrument); /* * modules are responsible for buffer disposal */ for (IModule module : _modules) module.dispose(); try { // acquire lock _lock.writeLock().lock(); /* * no more events */ _eventDispatcher.clear(); _eventDispatcher = null; _extensions.clear(); _extensions = null; _modules.clear(); _modules = null; _buffers.clear(); _buffers = null; _metaData.clear(); _metaData = null; _parameterMap.clear(); _parameterMap = null; _timedEventQueue.dispose(); _timedEventQueue = null; _declarativeModule = null; _proceduralModule = null; } finally { // release _lock.writeLock().unlock(); } } public void addActivationBuffer(IActivationBuffer buffer) { boolean added = false; try { // acquire lock _lock.writeLock().lock(); _buffers.put(buffer.getName().toLowerCase(), buffer); added = true; } finally { // release _lock.writeLock().unlock(); if (added) dispatch(new ModelEvent(this, buffer)); } } public IActivationBuffer getActivationBuffer(String name) { try { // lock _lock.readLock().lock(); return _buffers.get(name.toLowerCase()); } finally { // unlock _lock.readLock().unlock(); } } public Collection<IActivationBuffer> getActivationBuffers() { ArrayList<IActivationBuffer> buffers = new ArrayList<IActivationBuffer>( _buffers.size()); getActivationBuffers(buffers); return buffers; } public void getActivationBuffers(Collection<IActivationBuffer> container) { try { // lock _lock.readLock().lock(); _buffers.getValues(container); } finally { // unlock _lock.readLock().unlock(); } } public IDeclarativeModule getDeclarativeModule() { try { // lock _lock.readLock().lock(); return _declarativeModule; } finally { // unlock _lock.readLock().unlock(); } } public IExtension getExtension(Class<? extends IExtension> extensionClass) { try { // lock _lock.readLock().lock(); for (IExtension extension : _extensions) if (extensionClass.isAssignableFrom(extension.getClass())) return extension; return null; } finally { // unlock _lock.readLock().unlock(); } } public Collection<IExtension> getExtensions() { try { // lock _lock.readLock().lock(); return Collections.unmodifiableCollection(_extensions); } finally { // unlock _lock.readLock().unlock(); } } public IInstrument getInstrument(Class<? extends IInstrument> instrumentClass) { try { // lock _lock.readLock().lock(); for (IInstrument installable : _installedInstruments) if (instrumentClass.isAssignableFrom(installable.getClass())) return installable; return null; } finally { // unlock _lock.readLock().unlock(); } } public Collection<IInstrument> getInstruments() { try { // lock _lock.readLock().lock(); return Collections.unmodifiableCollection(_installedInstruments); } finally { // unlock _lock.readLock().unlock(); } } public IModule getModule(Class<? extends IModule> moduleClass) { try { // lock _lock.readLock().lock(); for (IModule module : _modules) if (moduleClass.isAssignableFrom(module.getClass())) return module; return null; } finally { // unlock _lock.readLock().unlock(); } } public Collection<IModule> getModules() { try { // lock _lock.readLock().lock(); return Collections.unmodifiableCollection(_modules); } finally { // unlock _lock.readLock().unlock(); } } public IProceduralModule getProceduralModule() { try { // lock _lock.readLock().lock(); return _proceduralModule; } finally { // unlock _lock.readLock().unlock(); } } public void install(IModule module) { boolean added = false; try { // acquire lock _lock.writeLock().lock(); if (module instanceof IDeclarativeModule) { if (_declarativeModule != null) throw new IllegalModelStateException( "Only one declarative module may exist in a model at a time"); _declarativeModule = (IDeclarativeModule) module; } if (module instanceof IProceduralModule) { if (_proceduralModule != null) throw new IllegalModelStateException( "Only one procedural module may exist in a model at a time"); _proceduralModule = (IProceduralModule) module; } _modules.add(module); added = true; } finally { // release _lock.writeLock().unlock(); if (added) { /* * install out of the lock in case this takes a sig amount of time */ module.install(this); if (hasBeenInitialized()) module.initialize(); dispatch(new ModelEvent(this, module)); } } } public void install(IExtension extension) { if (!modulesAreInitialized()) initializeModules(); boolean added = false; try { // acquire lock _lock.writeLock().lock(); _extensions.add(extension); added = true; } finally { // release _lock.writeLock().unlock(); if (added) { /* * install out of the lock in case this takes a sig amount of time */ extension.install(this); if (hasBeenInitialized()) try { extension.initialize(); } catch (Exception e) { throw new IllegalModelStateException( "Exception while initializing extension " + extension, e); } dispatch(new ModelEvent(this, extension)); } } } public void install(IInstrument instrument) { if (!modulesAreInitialized()) initializeModules(); boolean added = false; try { // acquire lock _lock.writeLock().lock(); _installedInstruments.add(instrument); added = true; } finally { // release _lock.writeLock().unlock(); if (added) { /* * perform the instll out of the lock */ instrument.install(this); if (hasBeenInitialized()) instrument.initialize(); dispatch(new ModelEvent(this, instrument)); } } } /** * @see org.jactr.core.model.IModel#uninstall(org.jactr.instrument.IInstrument) */ public void uninstall(IInstrument installable) { try { _lock.writeLock().lock(); _installedInstruments.remove(installable); } finally { _lock.writeLock().unlock(); } /* * pull it out of the lock, since this may take some time */ installable.uninstall(this); } public String getParameter(String key) { if (CYCLE_SKIPPING_PARAM.equalsIgnoreCase(key)) return ParameterHandler.booleanInstance().toString( isCycleSkippingEnabled()); else if (AGE_PARAM.equalsIgnoreCase(key)) return "" + getAge(); else if (PERSISTENT_PARAM.equalsIgnoreCase(key)) return "" + isPersistentExecutionEnabled(); if (_parameterMap.containsKey(key)) return _parameterMap.get(key).toString(); if (LOGGER.isWarnEnabled()) LOGGER.warn("Unknown parameter " + key + " requested, returning null"); return null; } public Collection<String> getPossibleParameters() { TreeSet<String> possible = new TreeSet<String>(READABLE_PARAMETERS); possible.addAll(WRITABLE_PARAMETERS); return possible; } public Collection<String> getSetableParameters() { return WRITABLE_PARAMETERS; } public void setParameter(String key, String value) { boolean handled = false; if (CYCLE_SKIPPING_PARAM.equalsIgnoreCase(key)) { setCycleSkippingEnabled(ParameterHandler.booleanInstance().coerce(value)); handled = true; } else if (AGE_PARAM.equalsIgnoreCase(key)) { setAge(ParameterHandler.numberInstance().coerce(value).doubleValue()); handled = true; } else if (PERSISTENT_PARAM.equalsIgnoreCase(key)) { setPersistentExecutionEnabled(ParameterHandler.booleanInstance().coerce( value)); handled = true; } else // check the modules.. for (IModule module : _modules) if (module instanceof IParameterized) if (((IParameterized) module).getSetableParameters().contains(key)) { // route it if (LOGGER.isWarnEnabled()) LOGGER .warn("You should not use the model as the parameter nexus, rather assigned it to the module directly"); ((IParameterized) module).setParameter(key, value); handled = true; } if (!handled) { if (LOGGER.isDebugEnabled()) LOGGER.debug("No registered handler for " + key + " = " + value); _parameterMap.put(key, value); } } public Object getMetaData(String key) { try { // lock _lock.readLock().lock(); return _metaData.get(key); } finally { // unlock _lock.readLock().unlock(); } } public void setMetaData(String key, Object value) { try { // acquire lock _lock.writeLock().lock(); _metaData.put(key, value); } finally { // release _lock.writeLock().unlock(); } } public Collection<String> getMetaDataKeys() { return new ArrayList<String>(_metaData.keySet()); } public void setAge(double age) { _age = age; } public double getAge() { return _age; } public void setCycleSkippingEnabled(boolean skipping) { if (skipping == _cycleSkippingEnabled) return; boolean old = _cycleSkippingEnabled; _cycleSkippingEnabled = skipping; fireParameterEvent(CYCLE_SKIPPING_PARAM, old, skipping); } public boolean isCycleSkippingEnabled() { return _cycleSkippingEnabled; } public void setPersistentExecutionEnabled(boolean persistent) { if (_persistentExecution == persistent) return; boolean old = _persistentExecution; _persistentExecution = persistent; fireParameterEvent(PERSISTENT_PARAM, old, persistent); } public boolean isPersistentExecutionEnabled() { return _persistentExecution; } public TimedEventQueue getTimedEventQueue() { return _timedEventQueue; } public boolean hasBeenInitialized() { return _isInitialized; } /** * initialize the model, calling initialize on buffers, and extensions in that * order. this is called before the model starts to run Modules will have * already been initialized, as they are initialized before extensions or * instruments are installed.. * * @see org.jactr.core.model.IModel#initialize() */ public void initialize() throws Exception { if (!modulesAreInitialized()) initializeModules(); try { _lock.writeLock().lock(); if (hasBeenInitialized()) throw new IllegalModelStateException( "Model has already been initialized"); _isInitialized = true; for (IActivationBuffer buffer : _buffers.values()) buffer.initialize(); for (IExtension extension : _extensions) extension.initialize(); for (IInstrument instruments : _installedInstruments) instruments.initialize(); } finally { _lock.writeLock().unlock(); dispatch(new ModelEvent(this, ModelEvent.Type.INITIALIZED)); } } protected boolean modulesAreInitialized() { return _modulesInitialized; } protected void initializeModules() { if (modulesAreInitialized()) return; for (IModule module : _modules) module.initialize(); _modulesInitialized = true; } public void addListener(IModelListener listener, Executor executor) { _eventDispatcher.addListener(listener, executor); } public void removeListener(IModelListener listener) { _eventDispatcher.removeListener(listener); } public boolean hasListeners() { return _eventDispatcher.hasListeners(); } public void dispatch(ModelEvent modelEvent) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(modelEvent); } public void addListener(IParameterListener listener, Executor executor) { _parameterEventDispatcher.addListener(listener, executor); } public void removeListener(IParameterListener listener) { _parameterEventDispatcher.removeListener(listener); } public boolean hasParameterListeners() { return _parameterEventDispatcher.hasListeners(); } public void dispatch(ParameterEvent modelEvent) { if (_parameterEventDispatcher.hasListeners()) _parameterEventDispatcher.fire(modelEvent); } protected void fireParameterEvent(String parameterName, Object oldValue, Object newValue) { if (hasParameterListeners()) dispatch(new ParameterEvent(this, ACTRRuntime.getRuntime().getClock(this) .getTime(), parameterName, oldValue, newValue)); } public String getName() { return _name; } public void setName(String modelName) { _name = modelName; } public long getCycle() { return _cycle; } public void setCycle(long cycle) { if (cycle <= _cycle) return; _cycle = cycle; } @Override public String toString() { return getName(); } }