package nbtool.data.log; import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import nbtool.data.group.Group; import nbtool.nio.FileIO; import nbtool.util.Debug; import nbtool.util.Debug.DebugSettings; import nbtool.util.SharedConstants; import nbtool.util.ToolSettings; import nbtool.util.Utility; import nbtool.util.test.TestBase; import nbtool.util.test.Tests; public class LogReference { private static final DebugSettings debug = new DebugSettings(true, true, true, Debug.WARN, null); private static final Path TEMP_DIR = getTempDir(); private static Path getTempDir() { return FileSystems.getDefault().getPath(ToolSettings.NBITES_DIR, ".nbtemp/nbtool/"); } public static void _NBL_REQUIRED_START_() { debug.warn("clearing/creating log temp directory..."); try { if (Files.exists(TEMP_DIR)) { Files.walkFileTree(TEMP_DIR, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { debug.info("deleting: %s", file.toString()); Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path file, IOException e) throws IOException { if (e != null) { debug.error("error deleting temp dir!"); e.printStackTrace(); throw e; } debug.info("deleting dir: %s", file.toString()); Files.delete(file); return FileVisitResult.CONTINUE; } }); } assert (!Files.exists(TEMP_DIR)); Files.createDirectories(TEMP_DIR); } catch (IOException e) { e.printStackTrace(); debug.error("LogReference could not set up temporary directory."); System.exit(-1); } assert (Files.exists(TEMP_DIR)); assert (Files.isReadable(TEMP_DIR)); assert (Files.isWritable(TEMP_DIR)); } public long createdWhen = 0; public String logClass = ""; public String host_type = null; public String host_name = null; public String host_addr = null; public String description = null; public long savedID = -1; public long thisID = Utility.getNextIndex(this); private WeakReference<Log> theLog = null; private boolean tempWritten = false; private Path tempPath = null; private synchronized Path useTempPath() { while (!tempWritten) { debug.warn("reference %d waiting for temp write to finish...", thisID); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return tempPath; } private Path loadPath = null; public Group container = null; public synchronized Log get() { if (theLog != null) { Log cur = theLog.get(); if (cur != null) { assert (cur.logReference == this); return cur; } else { theLog = null; } } assert (theLog == null); assert (tempPath != null); debug.info("get() usetemp()"); useTempPath(); assert (Files.exists(tempPath)); Log ret = null; try { ret = FileIO.readLogFromPath(tempPath); } catch (IOException e) { debug.error("LogReference(%d) could not load log from tempPath(%s)!", savedID, tempPath.toString()); e.printStackTrace(); System.exit(-1); } assert (!manages(ret)); this.savedID = ret.jvm_unique_id; updateInternal(ret); ret.logReference = this; theLog = new WeakReference<>(ret); return ret; } public synchronized void copyLogToPath(Path newFile) { Log refered = get(); if (Files.isDirectory(newFile)) { Path actual = newFile.resolve(tempPath.getFileName()); FileIO.queueWriteTask(actual, refered.serialize()); } else { FileIO.queueWriteTask(newFile, refered.serialize()); } } public synchronized void pushToTempFileNow(Log same) throws IOException { assert (manages(same)); assert (theLog != null && theLog.get() == same); useTempPath(); updateInternal(same); FileIO.writeLogToPath(tempPath, same); } public synchronized void pushToLoadFileNow(Log same) throws IOException { assert (manages(same)); assert (theLog != null && theLog.get() == same); if (loadPath == null) { String em = "cannot pushToLoadFile if loadFile == null"; debug.error(em); throw new RuntimeException(em); } useTempPath(); pushToTempFileNow(same); FileIO.writeLogToPath(loadPath, same); } public synchronized void pushToTempFile(Log same) { assert (manages(same)); assert (theLog != null && theLog.get() == same); useTempPath(); updateInternal(same); FileIO.queueWriteTask(tempPath, same.serialize()); } public synchronized void pushToLoadFile(Log same) { assert (manages(same)); assert (theLog != null && theLog.get() == same); if (loadPath == null) { String em = "cannot pushToLoadFile if loadFile == null"; debug.error(em); throw new RuntimeException(em); } useTempPath(); pushToTempFile(same); FileIO.queueWriteTask(loadPath, same.serialize()); } public boolean temporary() { return loadPath == null; } public Path loadPath() { return loadPath; } public Path tempPath() { return tempPath; } public boolean manages(Log log) { return log.jvm_unique_id == this.savedID; } private LogReference(Log makeFrom) { this.savedID = makeFrom.jvm_unique_id; updateInternal(makeFrom); theLog = new WeakReference<>(makeFrom); } protected LogReference(long cw, String lc, String ht, String hn, String ha, String desc, Path copyFrom) throws IOException { this.createdWhen = cw; this.logClass = lc; this.host_type = ht; this.host_name = hn; this.host_addr = ha; this.description = desc; this.savedID = -1; this.theLog = null; String tempName = tempName(); loadPath = copyFrom; tempPath = TEMP_DIR.resolve(tempName); FileIO.addTaskToQueue(new CopyTask(tempPath, copyFrom, this)); } private static class InitTask extends FileIO.Task { private LogReference ref; private Log log; protected InitTask(Path path, LogReference ref, Log log) { super(path, null); this.log = log; this.ref = ref; } @Override public void executeWrite() throws IOException { Files.createFile(path); FileIO.writeLogToPath(path, log); this.log = null; debug.info("Init done..."); synchronized (this.ref) { debug.info("in synch..."); this.ref.tempWritten = true; this.ref.notify(); } } } private static class CopyTask extends FileIO.Task { private LogReference ref; private Path from; protected CopyTask(Path path, Path from, LogReference ref) { super(path, null); this.ref = ref; this.from = from; } @Override public void executeWrite() throws IOException { Files.copy(from, path); debug.info("Copy done..."); synchronized (this.ref) { debug.info("in synch..."); this.ref.tempWritten = true; this.ref.notify(); } } } private String tempName() { return String.format("%s_%s_v%d_id%d_u%s.nblog", this.host_name, this.logClass, ToolSettings.VERSION, this.thisID, Utility.getRandomHexString(10)); } private void initializeTemp() { String tempName = tempName(); tempPath = TEMP_DIR.resolve(tempName); assert (!Files.exists(tempPath)); Log ourLog = null; debug.info("inittemp() calling get()"); assert (theLog != null && (ourLog = theLog.get()) != null); FileIO.addTaskToQueue(new InitTask(tempPath, this, ourLog)); } public static LogReference quickReferenceFromFile(Path readFrom) throws IOException { assert (Files.exists(readFrom)); return LogInternal.quickParse(readFrom); } public static LogReference referenceFromFile(Path readFrom) throws IOException { assert (Files.exists(readFrom)); Log read = FileIO.readLogFromPath(readFrom); if (read == null) { debug.error("could not parse Log from %s", readFrom); throw new IOException("could not parse Log from " + readFrom); } LogReference ref = new LogReference(read); ref.initializeTemp(); ref.loadPath = readFrom; read.logReference = ref; return ref; } public static LogReference referenceFromLog(Log log) { assert (log.getReference() == null); LogReference ref = new LogReference(log); ref.initializeTemp(); ref.loadPath = null; log.logReference = ref; return ref; } private synchronized void updateInternal(Log from) { assert (manages(from)); this.createdWhen = from.createdWhen; this.logClass = from.logClass; this.host_type = from.host_type; this.host_name = from.host_name; this.host_addr = from.host_addr; this.description = from.getFullDescription(); } @Override public String toString() { return String.format("LogReference[r%d](of:%d, load@%s, temp@%s ())", thisID, savedID, loadPath, tempPath, temporary() ? "TEMP" : "PERM"); } public String guiString() { return String.format("log r%d: %s from %s@%s time %d %s", thisID, logClass, host_name, host_addr, createdWhen, temporary() ? "TEMP" : "LOAD"); } public static void _NBL_ADD_TESTS_() { _NBL_REQUIRED_START_(); Tests.add("data.LogReference", new TestBase("basic") { @Override public boolean testBody() throws Exception { Path loadPath = TestBase.resourcePathAtClass(this, "testLog2"); assert (Files.exists(loadPath)); debug.info("before read"); LogReference ref = FileIO.readRefFromPath(loadPath); debug.info("test() calling get()"); Log refMng = ref.get(); assert (refMng != null && refMng.logClass.equals(SharedConstants.LogClass_Tripoint())); assert (ref.manages(refMng)); assert (ref.theLog.get() == refMng); assert (!ref.temporary()); assert (ref.logClass.equals(refMng.logClass)); assert (Files.exists(ref.loadPath)); String other = "OTHERTYPE"; refMng.logClass = other; assert (ref.get().logClass.equals(other)); ref.pushToTempFileNow(refMng); assert (Files.exists(ref.tempPath)); ref.theLog = null; Log reload = ref.get(); assert (ref.manages(reload)); assert (!ref.manages(refMng)); assert (reload.logClass.equals(other)); return true; } }); } }