From 380fdbc605ed3bf6016ca76467ce23754a5c6ccf Mon Sep 17 00:00:00 2001 From: Matthew Cech Date: Sun, 5 May 2019 23:20:38 -0700 Subject: [PATCH] + Added FileMonitor feature for localization files to allow for active reloading --- locStrings.config | 4 +- src/core/LocBase.java | 21 ++++++++ src/core/LocCommands.java | 8 +++ src/core/LocStrings.java | 8 +++ src/core/benchmark/BenchmarkManager.java | 4 +- src/main/Main.java | 26 +++++++-- src/utils/FileUtils.java | 17 +++++- .../DirectoryMonitor.java | 34 +++--------- src/utils/io/FileMonitor.java | 53 +++++++++++++++++++ src/utils/io/IOLog.java | 12 +++++ .../MonitoredFile.java | 2 +- 11 files changed, 152 insertions(+), 37 deletions(-) rename src/utils/{directoryMonitor => io}/DirectoryMonitor.java (80%) create mode 100644 src/utils/io/FileMonitor.java create mode 100644 src/utils/io/IOLog.java rename src/utils/{directoryMonitor => io}/MonitoredFile.java (95%) diff --git a/locStrings.config b/locStrings.config index c077078..f2757d8 100644 --- a/locStrings.config +++ b/locStrings.config @@ -1,6 +1,6 @@ [CommandBenchmark] -BenchmarkInfo= -BenchmarkInvalid= +BenchmarkInfo=Allows you to lookup PC component benchmarks from UserBenchmark! You can use `bench find ` to see what's in the database, `bench info ` to look at a specific component, or the experimental `bench compare` commands! +BenchmarkInvalid=The benchmark operation you were trying to perform was invalid! [CommandPollManage] PollManageInfo='start' will start a new poll with the query of the line you put after, 'choice' will add a choice to the poll, 'stop' will end the poll diff --git a/src/core/LocBase.java b/src/core/LocBase.java index 8106779..15f6c8f 100644 --- a/src/core/LocBase.java +++ b/src/core/LocBase.java @@ -12,6 +12,8 @@ import dataStructures.TaggedPairStore; import utils.FileUtils; import utils.GlobalLog; import utils.LogFilter; +import utils.io.FileMonitor; +import utils.io.MonitoredFile; // A quick-and-dirty localization tool that scrapes the project for calls to itself, then // generates/updates a file externally with all the stub values as keys that are localized. @@ -32,6 +34,9 @@ public abstract class LocBase private void Warn(String str) { GlobalLog.Warn(LogFilter.Strings, str); } private void Error(String str) { GlobalLog.Error(LogFilter.Strings, str); } + // File monitoring + protected FileMonitor fileMonitor; + // Ok... so this is an array because if it's not an array, the parser will parse the string public LocBase(String filename, String functionName) { @@ -39,6 +44,22 @@ public abstract class LocBase this.functionName = functionName; } + // Checks the file specified for updates + public void Update() + { + synchronized(stringStore) + { + fileMonitor.Update(this::OnFileChange); + } + } + + // When the file is changed + protected void OnFileChange(MonitoredFile file) + { + Log("Loc file was modified at path " + file.path); + UpdateLocFromDisk(); + } + // Structure used for holding a pair of strings and any other info we need // about localized information that is being looked up. private class LocInfo diff --git a/src/core/LocCommands.java b/src/core/LocCommands.java index 04c6ed5..7d75228 100644 --- a/src/core/LocCommands.java +++ b/src/core/LocCommands.java @@ -3,6 +3,7 @@ package core; import java.util.ArrayList; import utils.GlobalLog; import utils.LogFilter; +import utils.io.FileMonitor; import dataStructures.Pair; // Performs the same localization for the strings associated with command names as @@ -27,6 +28,8 @@ public class LocCommands extends LocBase UpdateLocFromDisk(); ScrapeAll(); SaveLocToDisk(); + + fileMonitor = new FileMonitor(filename); } else { @@ -47,4 +50,9 @@ public class LocCommands extends LocBase instance.stringStore.ForEach((pair) -> raw.add((String)((Pair)pair).First )); return raw; } + + public static void Upkeep() + { + instance.Update(); + } } diff --git a/src/core/LocStrings.java b/src/core/LocStrings.java index ad7ed5f..d4a0bd0 100644 --- a/src/core/LocStrings.java +++ b/src/core/LocStrings.java @@ -2,6 +2,7 @@ package core; import utils.GlobalLog; import utils.LogFilter; +import utils.io.FileMonitor; // A quick-and-dirty localization tool that scrapes the project for calls to itself, then // generates/updates a file externally (phrases.config) with all the stub values as keys that @@ -26,6 +27,8 @@ public class LocStrings extends LocBase UpdateLocFromDisk(); ScrapeAll(); SaveLocToDisk(); + + fileMonitor = new FileMonitor(filename); } else { @@ -43,4 +46,9 @@ public class LocStrings extends LocBase { return instance.GetKey(stubbedPreviously); } + + public static void Upkeep() + { + instance.Update(); + } } \ No newline at end of file diff --git a/src/core/benchmark/BenchmarkManager.java b/src/core/benchmark/BenchmarkManager.java index 5404dd3..166cab4 100644 --- a/src/core/benchmark/BenchmarkManager.java +++ b/src/core/benchmark/BenchmarkManager.java @@ -7,8 +7,8 @@ import java.util.List; import dataStructures.Pair; import utils.FileUtils; -import utils.directoryMonitor.DirectoryMonitor; -import utils.directoryMonitor.MonitoredFile; +import utils.io.DirectoryMonitor; +import utils.io.MonitoredFile; // All things considered, this doesn't need to be particularly efficient since anything // less than 10ms of search time won't be noticable to an end user really, not for diff --git a/src/main/Main.java b/src/main/Main.java index db63422..d18a13d 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -78,6 +78,9 @@ public class Main extends ListenerAdapter // RP logging system RPManager.instance.addLine(channel, user, input); + // Run any pre-emptive upkeep we need to + PerCommandUpkeepPre(); + // Attempt to spin up a command. If the command doesn't exist. // Run plugins right before invoking the commands but after all other setup. if(commandManager.InvokeOnNewThread(guild, channel, user, input, response) == false) @@ -88,8 +91,8 @@ public class Main extends ListenerAdapter response.Call(pluginOutput); } - // Run any upkeep we need to - PerCommandUpkeep(); + // Run any upkeep in post we need to + PerCommandUpkeepPost(); } // This is run on the JDA GuildMessageReceivedEvent before anything else happens. @@ -143,12 +146,27 @@ public class Main extends ListenerAdapter } // This is for stuff that we need to do on a regular basis, but don't - // necessarily want running at all points in time. - private static boolean PerCommandUpkeep() + // necessarily want running at all points in time. + // Happens just after the command / plugin runs. + private static boolean PerCommandUpkeepPost() { + // Upkeep database databaseManager.Upkeep(); + + // Update command-specific RPManager.Upkeep(kitty); return true; } + + // Only called once per command. Good for lazily updating. + // Happens just before the command / plugin runs. + private static boolean PerCommandUpkeepPre() + { + // Upkeep localization system's file monitoring + LocStrings.Upkeep(); + LocCommands.Upkeep(); + + return true; + } } \ No newline at end of file diff --git a/src/utils/FileUtils.java b/src/utils/FileUtils.java index 716fae3..97fe636 100644 --- a/src/utils/FileUtils.java +++ b/src/utils/FileUtils.java @@ -41,7 +41,6 @@ public class FileUtils if(!tmpDir.exists()) return new ArrayList(); - try { Files.find(Paths.get(startingDir), 999, (path, attributes) -> attributes.isRegularFile()).forEach(items::add); @@ -53,4 +52,20 @@ public class FileUtils return items; } + + // Check when a file was last modified by path + public static Long LastModified(Path path) + { + File file = new File(path.toString()); + + if(file.exists()) + { + return file.lastModified(); + } + else + { + GlobalLog.Error(LogFilter.Util, "File doesn't exist at path: " + path); + return null; + } + } } diff --git a/src/utils/directoryMonitor/DirectoryMonitor.java b/src/utils/io/DirectoryMonitor.java similarity index 80% rename from src/utils/directoryMonitor/DirectoryMonitor.java rename to src/utils/io/DirectoryMonitor.java index 144665a..59cf1a5 100644 --- a/src/utils/directoryMonitor/DirectoryMonitor.java +++ b/src/utils/io/DirectoryMonitor.java @@ -1,4 +1,4 @@ -package utils.directoryMonitor; +package utils.io; import java.io.File; import java.nio.file.Files; @@ -10,6 +10,7 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; +import utils.FileUtils; import utils.GlobalLog; import utils.LogFilter; @@ -17,11 +18,6 @@ import utils.LogFilter; // for external stability and support public class DirectoryMonitor { - // Logging - private void Log(String s) { GlobalLog.Log(LogFilter.Util, s); } - private void Warn(String s) { GlobalLog.Warn(LogFilter.Util, s); } - private void Error(String s) { GlobalLog.Error(LogFilter.Util, s); } - // Variables private String directory; private ArrayList last; @@ -29,7 +25,7 @@ public class DirectoryMonitor // Constructs a new file monitor and logs initialization public DirectoryMonitor(String directory) { - Log("Reading directory files for montioring in: " + directory.toString()); + IOLog.Log("Reading directory files for montioring in: " + directory.toString()); this.directory = directory; this.last = Scrape(); @@ -37,22 +33,6 @@ public class DirectoryMonitor Print(last); } - // Check when a file was last modified by path - private Long LastModified(Path path) - { - File file = new File(path.toString()); - - if(file.exists()) - { - return file.lastModified(); - } - else - { - Error("File looking for doesn't exist for directory monitor at path: " + path); - return null; - } - } - // Scrape the target directory for all files and store them as custom objects in the list private ArrayList Scrape() { @@ -64,13 +44,13 @@ public class DirectoryMonitor { paths.filter(Files::isRegularFile).forEach((path)-> { - files.add(new MonitoredFile(path, LastModified(path))); + files.add(new MonitoredFile(path, FileUtils.LastModified(path))); }); } } catch(Exception e) { - Error(e.getMessage()); + IOLog.Error(e.getMessage()); } return files; @@ -150,7 +130,7 @@ public class DirectoryMonitor Collections.sort(last); if(last.size() == 0) - Warn("There's nothing at all in a FileMonitor's target folder! Folder: " + directory); + IOLog.Warn("There's nothing at all in a FileMonitor's target folder! Folder: " + directory); } // Returns the current list of files the monitor is tracking in the directory. @@ -163,6 +143,6 @@ public class DirectoryMonitor private void Print(ArrayList toPrint) { for(int i = 0; i < toPrint.size(); ++i) - Log(toPrint.get(i).path.toString()); + IOLog.Log(toPrint.get(i).path.toString()); } } diff --git a/src/utils/io/FileMonitor.java b/src/utils/io/FileMonitor.java new file mode 100644 index 0000000..283052a --- /dev/null +++ b/src/utils/io/FileMonitor.java @@ -0,0 +1,53 @@ +package utils.io; + +import java.nio.file.Paths; +import java.util.function.Consumer; + +import utils.FileUtils; + +// Monitors a single file for changes - the file must exist and is expected to continue to exist. +public class FileMonitor +{ + MonitoredFile file; + + // Constructs file monitor for a given file at a specific path. + public FileMonitor(String path) + { + Long last = FileUtils.LastModified(Paths.get(path)); + + if(last == null) + { + file = null; + IOLog.Error("Couldn't find a readable file at " + path + "!"); + } + else + { + file = new MonitoredFile(Paths.get(path), last); + } + } + + // Looks at the file and sees if it has undergone any changes. + // If so, the fileChanged function provided is called! + public void Update(Consumer fileChanged) + { + if(file == null) + { + IOLog.Error("Attempted to update a file that doesn't exist"); + return; + } + + Long last = FileUtils.LastModified(file.path); + if(last == null) + { + IOLog.Error("The file previously at " + file.path + " couldn't be read!"); + return; + } + + if(last.longValue() != file.lastModified.longValue()) + { + IOLog.Log("Most recently modified at " + last + ", stored version at " + file.lastModified); + file = new MonitoredFile(file.path, last); + fileChanged.accept(file); + } + } +} diff --git a/src/utils/io/IOLog.java b/src/utils/io/IOLog.java new file mode 100644 index 0000000..5e994c3 --- /dev/null +++ b/src/utils/io/IOLog.java @@ -0,0 +1,12 @@ +package utils.io; + +import utils.GlobalLog; +import utils.LogFilter; + +// Logging shim +public class IOLog +{ + public static void Log(String s) { GlobalLog.Log(LogFilter.Util, s); } + public static void Warn(String s) { GlobalLog.Warn(LogFilter.Util, s); } + public static void Error(String s) { GlobalLog.Error(LogFilter.Util, s); } +} diff --git a/src/utils/directoryMonitor/MonitoredFile.java b/src/utils/io/MonitoredFile.java similarity index 95% rename from src/utils/directoryMonitor/MonitoredFile.java rename to src/utils/io/MonitoredFile.java index f77e58f..4ceb6c3 100644 --- a/src/utils/directoryMonitor/MonitoredFile.java +++ b/src/utils/io/MonitoredFile.java @@ -1,4 +1,4 @@ -package utils.directoryMonitor; +package utils.io; import java.nio.file.Path;