package org.jactr.modules.pm.common.memory.impl; /* * default logging */ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.agents.IAgent; import org.commonreality.identifier.IIdentifier; import org.commonreality.object.IAfferentObject; import org.commonreality.object.delta.IObjectDelta; import org.jactr.core.buffer.BufferUtilities; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.event.ChunkEvent; import org.jactr.core.chunk.event.IChunkListener; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.modules.pm.common.afferent.DefaultAfferentObjectListener; import org.jactr.modules.pm.common.afferent.IAfferentObjectListener; import org.jactr.modules.pm.common.memory.IPerceptualEncoder; import org.jactr.modules.pm.common.memory.event.ActivePerceptEvent; /** * simple delegate that sits between the afferent object listener * {@link DefaultAfferentObjectListener} and the {@link IPerceptualEncoder} to * manage notification and caching.. * * @author harrison */ public class PerceptualEncoderBridge implements IAfferentObjectListener { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(PerceptualEncoderBridge.class); private final IPerceptualEncoder _encoder; private final Map<IIdentifier, IChunk> _cache; private final IChunkListener _chunkListener; /** * to track add/remove of cached chunks */ // private final IActivationBufferListener _bufferListener; // private final ConcurrentHashMap<IChunk, AtomicInteger> _activeChunks; private final AbstractPerceptualMemory _memory; public PerceptualEncoderBridge(IPerceptualEncoder encoder, AbstractPerceptualMemory memory) { _memory = memory; _encoder = encoder; _cache = new HashMap<IIdentifier, IChunk>(); // _activeChunks = new ConcurrentHashMap<IChunk, AtomicInteger>(); /** * we remove the cached element when it is encoded */ _chunkListener = new IChunkListener() { public void chunkAccessed(ChunkEvent event) { } public void chunkEncoded(ChunkEvent event) { /* * remove from the cache. */ remove(getIdentifier(event.getSource())); } public void mergingInto(ChunkEvent event) { remove(getIdentifier(event.getSource())); } public void mergingWith(ChunkEvent event) { // noop } public void similarityChanged(ChunkEvent event) { } public void slotChanged(ChunkEvent event) { } }; /** * buffer listener is used to managed the activeChunks container which * allows us to keep track of which chunks of ours are in buffers which we * use to signal a reencoding event */ // _bufferListener = new IActivationBufferListener() { // // public void chunkMatched(ActivationBufferEvent abe) // { // // } // // public void requestAccepted(ActivationBufferEvent abe) // { // // } // // public void sourceChunkAdded(ActivationBufferEvent abe) // { // checkContents(abe.getSourceChunks(), true); // } // // public void sourceChunkRemoved(ActivationBufferEvent abe) // { // checkContents(abe.getSourceChunks(), false); // } // // public void sourceChunksCleared(ActivationBufferEvent abe) // { // checkContents(abe.getSourceChunks(), false); // } // // public void statusSlotChanged(ActivationBufferEvent abe) // { // // } // // @SuppressWarnings("unchecked") // public void parameterChanged(IParameterEvent pe) // { // // } // // }; // // for (IActivationBuffer buffer : _memory.getModule().getModel() // .getActivationBuffers()) // buffer.addListener(_bufferListener, ExecutorServices.INLINE_EXECUTOR); } final public void clear() { // _activeChunks.clear(); FastList<IIdentifier> ids = FastList.newInstance(); ids.addAll(_cache.keySet()); for (IIdentifier id : ids) remove(id); _cache.clear(); FastList.recycle(ids); } final public IPerceptualEncoder getEncoder() { return _encoder; } final public Set<IIdentifier> getCachedIdentifiers(Set<IIdentifier> container) { if (container == null) container = new HashSet<IIdentifier>(); container.addAll(_cache.keySet()); return container; } final public Set<IChunk> getCacheContents(Set<IChunk> container) { if (container == null) container = new HashSet<IChunk>(); container.addAll(_cache.values()); return container; } final public void afferentObjectAdded(IAfferentObject object) { if (_encoder.isInterestedIn(object)) { IIdentifier id = object.getIdentifier(); IChunk chunk = _encoder.encode(object, _memory); if (chunk != null) { add(id, chunk); if (_memory.hasListeners()) _memory.dispatch(new ActivePerceptEvent(_memory, ActivePerceptEvent.Type.NEW, id, chunk)); } } } final public void afferentObjectRemoved(IAfferentObject object) { if (_encoder.isInterestedIn(object)) { IIdentifier identifier = object.getIdentifier(); IChunk oldChunk = remove(identifier); // if (_activeChunks.containsKey(oldChunk)) if (oldChunk != null && !oldChunk.isEncoded() && BufferUtilities.getContainingBuffers(oldChunk, true).size() != 0) if (_memory.hasListeners()) _memory.dispatch(new ActivePerceptEvent(_memory, ActivePerceptEvent.Type.REMOVED, identifier, oldChunk)); } } final public void afferentObjectUpdated(IAfferentObject object, IObjectDelta delta) { if (_encoder.isInterestedIn(object)) { IIdentifier id = object.getIdentifier(); IChunk oldChunk = get(id, false); /* * could be null if it was attended and the percept was encoded (cache * emptied), so we need to reencode it */ if (oldChunk == null || oldChunk.isEncoded() || oldChunk.hasBeenDisposed()) { oldChunk = _encoder.encode(object, _memory); if (oldChunk != null) add(id, oldChunk); // _listener.newPercept(id, oldChunk); } else { /* * we need to lock old chunk it we are going to update it */ boolean isDirty = _encoder.isDirty(object, oldChunk, _memory); if (isDirty) { IChunk updated = null; try { updated = _encoder.update(object, oldChunk, _memory); } catch (Exception e) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Failed to update " + oldChunk + " reencoding instead", e); /* * it's not uncommon for this to fail because the chunk has been * encoded.. we could use the chunk lock but that is for fine-graned * locking.. instead, we'll just create a new chunk */ updated = _encoder.encode(object, _memory); } if (updated != oldChunk) { // if (_activeChunks.containsKey(oldChunk)) /* * since we are operating in a separate thread (CR), it is possible * that this condition will be true and then false by the time the * processing is actually completed. so, it's just an early test.. */ if (!oldChunk.isEncoded() && BufferUtilities.getContainingBuffers(oldChunk, true).size() != 0) if (_memory.hasListeners()) _memory.dispatch(new ActivePerceptEvent(_memory, id, updated, oldChunk)); remove(id); add(id, updated); } else if (BufferUtilities.getContainingBuffers(oldChunk, true).size() != 0) if (_memory.hasListeners()) _memory.dispatch(new ActivePerceptEvent(_memory, ActivePerceptEvent.Type.UPDATED, id, oldChunk)); } } } } final public boolean isInterestedIn(IAfferentObject object) { return _encoder.isInterestedIn(object); } final protected void add(IIdentifier identifier, IChunk chunk) { chunk.setMetaData(IPerceptualEncoder.COMMONREALITY_IDENTIFIER_META_KEY, identifier); chunk.addListener(_chunkListener, ExecutorServices.INLINE_EXECUTOR); _cache.put(identifier, chunk); } final protected IChunk remove(IIdentifier identifier) { IChunk chunk = _cache.remove(identifier); if (chunk != null) { chunk.removeListener(_chunkListener); if (!chunk.isEncoded() && !chunk.hasBeenDisposed() && BufferUtilities.getContainingBuffers(chunk, true).size() == 0) _memory.getModule().getModel().getDeclarativeModule().dispose(chunk); } return chunk; } /** * fetch cached encoding * * @param identifier * @return */ final public IChunk get(IIdentifier identifier, boolean createIfAbsent) { IChunk chunk = _cache.get(identifier); if (chunk == null && createIfAbsent) { IAgent agent = ACTRRuntime.getRuntime().getConnector().getAgent( _memory.getModule().getModel()); if (agent == null) return null; IAfferentObject afferentObject = agent.getAfferentObjectManager().get( identifier); if (afferentObject == null) return null; if (_encoder.isInterestedIn(afferentObject)) { chunk = _encoder.encode(afferentObject, _memory); if (chunk != null) add(identifier, chunk); } } return chunk; } final protected IIdentifier getIdentifier(IChunk chunk) { return (IIdentifier) chunk .getMetaData(IPerceptualEncoder.COMMONREALITY_IDENTIFIER_META_KEY); } // final protected void checkContents(Collection<IChunk> sourceChunks, // boolean added) // { // for (IChunk chunk : sourceChunks) // { // IIdentifier identifier = getIdentifier(chunk); // if (identifier == null) continue; // // /* // * do we have this chunk cached? // */ // IChunk cached = get(identifier, false); // if (cached == null) continue; // // if (added) // { // AtomicInteger latch = _activeChunks.putIfAbsent(chunk, // new AtomicInteger(1)); // if (latch != null) latch.incrementAndGet(); // } // else // { // AtomicInteger latch = _activeChunks.get(chunk); // if (latch != null && latch.decrementAndGet() <= 0) // _activeChunks.remove(chunk); // } // // } // } }