+ Added FileMonitor feature for localization files to allow for active reloading

This commit is contained in:
Matthew Cech
2019-05-05 23:20:38 -07:00
parent e7506b124a
commit 380fdbc605
11 changed files with 152 additions and 37 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
[CommandBenchmark] [CommandBenchmark]
BenchmarkInfo= BenchmarkInfo=Allows you to lookup PC component benchmarks from UserBenchmark! You can use `bench find <name-snippet>` to see what's in the database, `bench info <name-snippet>` to look at a specific component, or the experimental `bench compare` commands!
BenchmarkInvalid= BenchmarkInvalid=The benchmark operation you were trying to perform was invalid!
[CommandPollManage] [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 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
+21
View File
@@ -12,6 +12,8 @@ import dataStructures.TaggedPairStore;
import utils.FileUtils; import utils.FileUtils;
import utils.GlobalLog; import utils.GlobalLog;
import utils.LogFilter; 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 // 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. // 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 Warn(String str) { GlobalLog.Warn(LogFilter.Strings, str); }
private void Error(String str) { GlobalLog.Error(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 // 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) public LocBase(String filename, String functionName)
{ {
@@ -39,6 +44,22 @@ public abstract class LocBase
this.functionName = functionName; 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 // Structure used for holding a pair of strings and any other info we need
// about localized information that is being looked up. // about localized information that is being looked up.
private class LocInfo private class LocInfo
+8
View File
@@ -3,6 +3,7 @@ package core;
import java.util.ArrayList; import java.util.ArrayList;
import utils.GlobalLog; import utils.GlobalLog;
import utils.LogFilter; import utils.LogFilter;
import utils.io.FileMonitor;
import dataStructures.Pair; import dataStructures.Pair;
// Performs the same localization for the strings associated with command names as // Performs the same localization for the strings associated with command names as
@@ -27,6 +28,8 @@ public class LocCommands extends LocBase
UpdateLocFromDisk(); UpdateLocFromDisk();
ScrapeAll(); ScrapeAll();
SaveLocToDisk(); SaveLocToDisk();
fileMonitor = new FileMonitor(filename);
} }
else else
{ {
@@ -47,4 +50,9 @@ public class LocCommands extends LocBase
instance.stringStore.ForEach((pair) -> raw.add((String)((Pair<?, ?>)pair).First )); instance.stringStore.ForEach((pair) -> raw.add((String)((Pair<?, ?>)pair).First ));
return raw; return raw;
} }
public static void Upkeep()
{
instance.Update();
}
} }
+8
View File
@@ -2,6 +2,7 @@ package core;
import utils.GlobalLog; import utils.GlobalLog;
import utils.LogFilter; import utils.LogFilter;
import utils.io.FileMonitor;
// A quick-and-dirty localization tool that scrapes the project for calls to itself, then // 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 // 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(); UpdateLocFromDisk();
ScrapeAll(); ScrapeAll();
SaveLocToDisk(); SaveLocToDisk();
fileMonitor = new FileMonitor(filename);
} }
else else
{ {
@@ -43,4 +46,9 @@ public class LocStrings extends LocBase
{ {
return instance.GetKey(stubbedPreviously); return instance.GetKey(stubbedPreviously);
} }
public static void Upkeep()
{
instance.Update();
}
} }
+2 -2
View File
@@ -7,8 +7,8 @@ import java.util.List;
import dataStructures.Pair; import dataStructures.Pair;
import utils.FileUtils; import utils.FileUtils;
import utils.directoryMonitor.DirectoryMonitor; import utils.io.DirectoryMonitor;
import utils.directoryMonitor.MonitoredFile; import utils.io.MonitoredFile;
// All things considered, this doesn't need to be particularly efficient since anything // 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 // less than 10ms of search time won't be noticable to an end user really, not for
+21 -3
View File
@@ -78,6 +78,9 @@ public class Main extends ListenerAdapter
// RP logging system // RP logging system
RPManager.instance.addLine(channel, user, input); 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. // Attempt to spin up a command. If the command doesn't exist.
// Run plugins right before invoking the commands but after all other setup. // Run plugins right before invoking the commands but after all other setup.
if(commandManager.InvokeOnNewThread(guild, channel, user, input, response) == false) if(commandManager.InvokeOnNewThread(guild, channel, user, input, response) == false)
@@ -88,8 +91,8 @@ public class Main extends ListenerAdapter
response.Call(pluginOutput); response.Call(pluginOutput);
} }
// Run any upkeep we need to // Run any upkeep in post we need to
PerCommandUpkeep(); PerCommandUpkeepPost();
} }
// This is run on the JDA GuildMessageReceivedEvent before anything else happens. // This is run on the JDA GuildMessageReceivedEvent before anything else happens.
@@ -144,11 +147,26 @@ public class Main extends ListenerAdapter
// This is for stuff that we need to do on a regular basis, but don't // This is for stuff that we need to do on a regular basis, but don't
// necessarily want running at all points in time. // necessarily want running at all points in time.
private static boolean PerCommandUpkeep() // Happens just after the command / plugin runs.
private static boolean PerCommandUpkeepPost()
{ {
// Upkeep database
databaseManager.Upkeep(); databaseManager.Upkeep();
// Update command-specific
RPManager.Upkeep(kitty); RPManager.Upkeep(kitty);
return true; 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;
}
} }
+16 -1
View File
@@ -41,7 +41,6 @@ public class FileUtils
if(!tmpDir.exists()) if(!tmpDir.exists())
return new ArrayList<Path>(); return new ArrayList<Path>();
try try
{ {
Files.find(Paths.get(startingDir), 999, (path, attributes) -> attributes.isRegularFile()).forEach(items::add); Files.find(Paths.get(startingDir), 999, (path, attributes) -> attributes.isRegularFile()).forEach(items::add);
@@ -53,4 +52,20 @@ public class FileUtils
return items; 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;
}
}
} }
@@ -1,4 +1,4 @@
package utils.directoryMonitor; package utils.io;
import java.io.File; import java.io.File;
import java.nio.file.Files; import java.nio.file.Files;
@@ -10,6 +10,7 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Stream; import java.util.stream.Stream;
import utils.FileUtils;
import utils.GlobalLog; import utils.GlobalLog;
import utils.LogFilter; import utils.LogFilter;
@@ -17,11 +18,6 @@ import utils.LogFilter;
// for external stability and support // for external stability and support
public class DirectoryMonitor 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 // Variables
private String directory; private String directory;
private ArrayList<MonitoredFile> last; private ArrayList<MonitoredFile> last;
@@ -29,7 +25,7 @@ public class DirectoryMonitor
// Constructs a new file monitor and logs initialization // Constructs a new file monitor and logs initialization
public DirectoryMonitor(String directory) 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.directory = directory;
this.last = Scrape(); this.last = Scrape();
@@ -37,22 +33,6 @@ public class DirectoryMonitor
Print(last); 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 // Scrape the target directory for all files and store them as custom objects in the list
private ArrayList<MonitoredFile> Scrape() private ArrayList<MonitoredFile> Scrape()
{ {
@@ -64,13 +44,13 @@ public class DirectoryMonitor
{ {
paths.filter(Files::isRegularFile).forEach((path)-> paths.filter(Files::isRegularFile).forEach((path)->
{ {
files.add(new MonitoredFile(path, LastModified(path))); files.add(new MonitoredFile(path, FileUtils.LastModified(path)));
}); });
} }
} }
catch(Exception e) catch(Exception e)
{ {
Error(e.getMessage()); IOLog.Error(e.getMessage());
} }
return files; return files;
@@ -150,7 +130,7 @@ public class DirectoryMonitor
Collections.sort(last); Collections.sort(last);
if(last.size() == 0) 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. // Returns the current list of files the monitor is tracking in the directory.
@@ -163,6 +143,6 @@ public class DirectoryMonitor
private void Print(ArrayList<MonitoredFile> toPrint) private void Print(ArrayList<MonitoredFile> toPrint)
{ {
for(int i = 0; i < toPrint.size(); ++i) for(int i = 0; i < toPrint.size(); ++i)
Log(toPrint.get(i).path.toString()); IOLog.Log(toPrint.get(i).path.toString());
} }
} }
+53
View File
@@ -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<? super MonitoredFile> 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);
}
}
}
+12
View File
@@ -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); }
}
@@ -1,4 +1,4 @@
package utils.directoryMonitor; package utils.io;
import java.nio.file.Path; import java.nio.file.Path;