package org.jactr.tools.tracer.sinks.trace; /* * default logging */ import java.io.File; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Executor; import java.util.concurrent.locks.ReentrantReadWriteLock; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.util.LockUtilities; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.tools.tracer.ITraceSink; import org.jactr.tools.tracer.sinks.trace.internal.TraceFileManager; import org.jactr.tools.tracer.transformer.ITransformedEvent; /** * full trace sink saves events to a series of timestamped files that can be * used later for playback * * @author harrison */ public class ArchivalSink implements ITraceSink { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(ArchivalSink.class); private volatile boolean _isActive = true; private Executor _executor = ExecutorServices.INLINE_EXECUTOR; private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(); private Map<String, TraceFileManager> _fileManagers = new TreeMap<String, TraceFileManager>(); public ArchivalSink() { initializeCleanup(); // _executor = ExecutorServices.getExecutor("archivalSink"); // if (_executor == null) // { // _executor = Executors.newSingleThreadExecutor(); // ExecutorServices.addExecutor("archivalSink", (ExecutorService) // _executor); // } } public File getOutputDirectory() { File outputDirectory = new File(ACTRRuntime.getRuntime() .getWorkingDirectory(), "sessionData"); outputDirectory.mkdirs(); return outputDirectory; } /** * to maximize recoverability of the data, we install a shutdown hook */ private void initializeCleanup() { Runnable cleaner = new Runnable() { public void run() { // no need to lock _fileManagers.values().forEach((fm) -> { try { fm.flush(); fm.close(); } catch (Exception e) { LOGGER.error("Failed to close file manager ", e); } }); _fileManagers.clear(); } }; Runtime.getRuntime().addShutdownHook(new Thread(cleaner)); } public void add(final ITransformedEvent event) { if (event == null) { LOGGER.error("null message received ", new NullPointerException()); return; } String model = event.getModelName(); TraceFileManager tfm = null; try { tfm = LockUtilities.runLocked( _lock.writeLock(), () -> { TraceFileManager fm = _fileManagers.get(model.toLowerCase()); if (fm == null) { fm = new TraceFileManager(new File(getOutputDirectory(), model .toLowerCase())); fm.open(); _fileManagers.put(model.toLowerCase(), fm); } return fm; }); } catch (Exception e) { LOGGER.error("Failed to create trace file manager"); } if (tfm != null) { final TraceFileManager fTFM = tfm; _executor.execute(new Runnable() { public void run() { _isActive = fTFM.record(event); } }); } else if (LOGGER.isWarnEnabled()) LOGGER.warn(String.format("Could not save events for %s", model)); } public void flush() throws Exception { _executor.execute(new Runnable() { public void run() { _isActive = true; try { FastList<TraceFileManager> managers = FastList.newInstance(); LockUtilities.runLocked(_lock.writeLock(), () -> { managers.addAll(_fileManagers.values()); }); managers.forEach((fm) -> { try { fm.flush(); } catch (Exception e) { _isActive = false; LOGGER.error("Failed to flush fileManager", e); } }); } catch (Exception e) { _isActive = false; LOGGER.error("Failed to flush", e); } } }); } public boolean isOpen() { return _isActive; } }