package org.jactr.tools.tracer.listeners; /* * default logging */ import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Executor; import javolution.util.FastSet; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.BufferUtilities; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.buffer.event.ActivationBufferEvent; import org.jactr.core.buffer.event.ActivationBufferListenerAdaptor; import org.jactr.core.buffer.event.IActivationBufferListener; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.event.ChunkEvent; import org.jactr.core.chunk.event.ChunkListenerAdaptor; import org.jactr.core.chunk.event.IChunkListener; import org.jactr.core.model.IModel; import org.jactr.core.model.event.IModelListener; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.model.event.ModelListenerAdaptor; import org.jactr.core.module.procedural.event.IProceduralModuleListener; import org.jactr.core.module.procedural.event.ProceduralModuleEvent; import org.jactr.core.module.procedural.event.ProceduralModuleListenerAdaptor; import org.jactr.core.slot.ISlotContainer; import org.jactr.io.antlr3.builder.JACTRBuilder; import org.jactr.io.antlr3.misc.ASTSupport; import org.jactr.io.resolver.ASTResolver; import org.jactr.tools.tracer.transformer.buffer.BulkBufferEvent; /** * buffer tracer that monitors the buffers and contents for any changes. The AST * for each (dirty) buffer is sent out at conflict resolution and after the * cycle has elapsed. <br/> * As currently implemented, we mark buffers as dirty inline, but then flush * them on the background thread. This opens the possibility to mismatches due * to a late flush. It would probably be better to mark them inline, package * them inline, but dispatch the message on the background thread (hopefully * ensurely in order receipt) * * @author harrison */ public class BufferTracer extends BaseTraceListener { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(BufferTracer.class); private Map<IModel, Set<String>> _dirtyBuffers; private IProceduralModuleListener _proceduralListener; private IModelListener _modelListener; private IChunkListener _chunkListener; private IActivationBufferListener _bufferListener; private Executor _executor; public BufferTracer() { _dirtyBuffers = new HashMap<IModel, Set<String>>(); _modelListener = new ModelListenerAdaptor() { @Override public void cycleStopped(ModelEvent me) { flush(me.getSource(), me.getSimulationTime(), true); } }; _proceduralListener = new ProceduralModuleListenerAdaptor() { @Override public void conflictSetAssembled(ProceduralModuleEvent pme) { flush(pme.getSource().getModel(), pme.getSimulationTime(), false); } }; _chunkListener = new ChunkListenerAdaptor() { @Override public void slotChanged(ChunkEvent event) { IChunk chunk = event.getSource(); for (IActivationBuffer buffer : BufferUtilities.getContainingBuffers( chunk, true)) markDirty(buffer); } }; /* * to track status changes and content changes (where we add and remove the * chunk listener) */ _bufferListener = new ActivationBufferListenerAdaptor() { @Override public void statusSlotChanged(ActivationBufferEvent abe) { markDirty(abe.getSource()); } @Override public void sourceChunkAdded(ActivationBufferEvent abe) { markDirty(abe.getSource()); for (IChunk chunk : abe.getSourceChunks()) chunk.addListener(_chunkListener, null); } @Override public void sourceChunkRemoved(ActivationBufferEvent abe) { markDirty(abe.getSource()); detachListener(abe); } @Override public void sourceChunksCleared(ActivationBufferEvent abe) { markDirty(abe.getSource()); detachListener(abe); } private void detachListener(ActivationBufferEvent abe) { for (IChunk chunk : abe.getSourceChunks()) chunk.removeListener(_chunkListener); } }; } public void install(IModel model, Executor executor) { _executor = executor; /* * we flush asynch, but mark dirty synchronously. */ model.addListener(_modelListener, _executor); model.getProceduralModule().addListener(_proceduralListener, _executor); TreeSet<String> buffers = new TreeSet<String>(); for (IActivationBuffer buffer : model.getActivationBuffers()) { buffer.addListener(_bufferListener, null); buffers.add(buffer.getName()); for (IChunk chunk : buffer.getSourceChunks()) chunk.addListener(_chunkListener, null); } _dirtyBuffers.put(model, buffers); } public void uninstall(IModel model) { _dirtyBuffers.remove(model); model.removeListener(_modelListener); model.getProceduralModule().removeListener(_proceduralListener); for (IActivationBuffer buffer : model.getActivationBuffers()) { buffer.removeListener(_bufferListener); for (IChunk chunk : buffer.getSourceChunks()) chunk.removeListener(_chunkListener); } } protected void markDirty(IActivationBuffer buffer) { synchronized (_dirtyBuffers) { _dirtyBuffers.get(buffer.getModel()).add(buffer.getName()); } } protected void flush(IModel model, double time, boolean isEndOfCycle) { FastSet<String> buffers = FastSet.newInstance(); synchronized (_dirtyBuffers) { buffers.addAll(_dirtyBuffers.get(model)); _dirtyBuffers.get(model).clear(); } ASTSupport support = new ASTSupport(); CommonTree buffersAST = support.createTree(JACTRBuilder.BUFFERS, "buffers"); /* * extract the ASTs and send them out. Normal buffer AST does not have slots * or chunks (fully defined) */ for (String bufferName : buffers) { IActivationBuffer buffer = model.getActivationBuffer(bufferName); CommonTree bufferAST = ASTResolver.toAST(buffer); /* * we replace the content of CHUNKS with actual chunk ASTs */ CommonTree chunksAST = ASTSupport.getFirstDescendantWithType(bufferAST, JACTRBuilder.CHUNKS); while (chunksAST.getChildCount() > 0) chunksAST.deleteChild(0); for (IChunk chunk : buffer.getSourceChunks()) { // resolve, leaving out parameters CommonTree chunkAST = ASTResolver.toAST(chunk, true); chunksAST.addChild(chunkAST); // strip paraemters node entirely chunkAST.deleteChild(3); } /* * to be nice, lets strip the parameters (name chunks parameters) */ bufferAST.deleteChild(2); /* * now the slots, if applicable */ ISlotContainer slotContainer = (ISlotContainer) buffer .getAdapter(ISlotContainer.class); if (slotContainer != null) { CommonTree slotsAST = support.createSlotsTree(); bufferAST.addChild(slotsAST); ASTResolver.setSlots(slotsAST, slotContainer); } /* * add to the bulk buffer event */ buffersAST.addChild(bufferAST); } // and send it out if (buffers.size() > 0) sink(new BulkBufferEvent(model.getName(), time, buffersAST, isEndOfCycle)); FastSet.recycle(buffers); } }