+ 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
+21
View File
@@ -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
+8
View File
@@ -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();
}
}
+8
View File
@@ -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();
}
}
+2 -2
View File
@@ -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
+22 -4
View File
@@ -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;
}
}
+16 -1
View File
@@ -41,7 +41,6 @@ public class FileUtils
if(!tmpDir.exists())
return new ArrayList<Path>();
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;
}
}
}
@@ -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<MonitoredFile> 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<MonitoredFile> 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<MonitoredFile> toPrint)
{
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;