diff --git a/src/core/Config.java b/src/core/Config.java index 1e50223..c76c9c2 100644 --- a/src/core/Config.java +++ b/src/core/Config.java @@ -1,10 +1,12 @@ package core; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Vector; import utils.GlobalLog; +import utils.LogFilter; import utils.io.FileMonitor; public class Config @@ -21,30 +23,57 @@ public class Config { if(instance == null) { - sections = new Vector(); - - // Add all sections - sections.add(new LocCommands()); - sections.add(new LocStrings()); - sections.add(new CommandEnabler()); - - // Build the config file, then start monitoring it. - buildConfigFile(filepath); - monitoredConfigFile = new FileMonitor(filepath); - - instance = this; + try + { + sections = new Vector(); + + // Add all sections + sections.add(new LocCommands()); + sections.add(new LocStrings()); + sections.add(new CommandEnabler()); + + // Build the config file, then start monitoring it. + buildConfigFile(filepath); + monitoredConfigFile = new FileMonitor(filepath); + + instance = this; + } + catch(Exception e) + { + String logText = "Failure during config startup. The config must start correctly, or the bot cannot run - aborting. Error: " + e.getMessage(); + GlobalLog.error(LogFilter.Core, logText); + System.exit(-1); + } } else { - System.out.println("Only one config class is allowed, period. Aborting."); - System.out.flush(); + GlobalLog.error(LogFilter.Core, "Only one config class is allowed, period. Aborting."); System.exit(-1); } } + + // This has more overhead but safely builds the file. + // It returns if it succeeded or not. + private boolean safelyBuildConfigFile(String path) + { + try + { + buildConfigFile(path); + } + catch (Exception e) + { + GlobalLog.error("Failure during config file updating, aborting update immediately and attempting to salvage operation! The error was: " + e); + return false; + } + + return true; + } - public void buildConfigFile(String path) + // Builds the config file and will throw an exception if it fails + public void buildConfigFile(String path) throws IOException { configCSV = new ConfigCSV(sections, path); + Map> groupedSections = ConfigCSV.groupByColumn(configCSV.getItems(), 0); if(groupedSections != null) @@ -58,19 +87,23 @@ public class Config configCSV.writeFile(); } - + + // Call very frequently, but lazily. This will update the file content. public void upkeep() { monitoredConfigFile.update((monitoredFile) -> { GlobalLog.log("File updated at " + monitoredFile.path); - buildConfigFile(monitoredFile.path.toString()); - // Re-register all commands now that we've updated the config. - // Before we do that tho, sync off anything that needs syncing. - // TODO: This is not thread safe. - DatabaseManager.instance.upkeep(); - CommandManager.instance.registerAllCommands(); + // Attempt to rebuild. If we do it safely, then force-sync the DB and re-register. + if(safelyBuildConfigFile(monitoredFile.path.toString())) + { + // Re-register all commands now that we've updated the config. + // Before we do that tho, sync off anything that needs syncing. + // TODO: This is not thread safe. + DatabaseManager.instance.upkeep(); + CommandManager.instance.registerAllCommands(); + } }); } } diff --git a/src/core/ConfigCSV.java b/src/core/ConfigCSV.java index b42d042..7b644d0 100644 --- a/src/core/ConfigCSV.java +++ b/src/core/ConfigCSV.java @@ -25,7 +25,7 @@ public class ConfigCSV private List sections; // Constructor - public ConfigCSV(List sections, String path) + public ConfigCSV(List sections, String path) throws IOException { this.path = path; this.sections = sections; @@ -46,49 +46,42 @@ public class ConfigCSV } // Read the file. This is internal only. - private void read(String filepath) + private void read(String filepath) throws IOException { - try + File configFile = new File(filepath); + + // If we had to create a new file, write the default header into it. + if(configFile.createNewFile()) { - File configFile = new File(filepath); - - // If we had to create a new file, write the default header into it. - if(configFile.createNewFile()) + Writer writer = Files.newBufferedWriter(Paths.get(path)); + CSVWriter csvWriter = makeWriter(writer); + csvWriter.writeNext(ConfigItem.header); + csvWriter.close(); + } + + Reader reader = Files.newBufferedReader(Paths.get(filepath)); + CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).build(); + fileContents.clear(); + + // Read contents. As for the line number, we not only start counting at 1, we also skip the header. + int line = 2; + String[] record = null; + while ((record = csvReader.readNext()) != null) + { + if(record.length < ConfigItem.items) { - Writer writer = Files.newBufferedWriter(Paths.get(path)); - CSVWriter csvWriter = makeWriter(writer); - csvWriter.writeNext(ConfigItem.header); - csvWriter.close(); + GlobalLog.warn("Incorrect number of items on line " + line + " in " + path); + continue; } - Reader reader = Files.newBufferedReader(Paths.get(filepath)); - CSVReader csvReader = new CSVReaderBuilder(reader).withSkipLines(1).build(); - fileContents.clear(); - - // Read contents. As for the line number, we not only start counting at 1, we also skip the header. - int line = 2; - String[] record = null; - while ((record = csvReader.readNext()) != null) - { - if(record.length < ConfigItem.items) - { - GlobalLog.warn("Incorrect number of items on line " + line + " in " + path); - continue; - } - - ConfigItem parsedLine = new ConfigItem(record[0], record[1], record[2]); - fileContents.add(parsedLine); - ++line; - } - - // Close down - csvReader.close(); - reader.close(); - } - catch (Exception e) - { - e.printStackTrace(); + ConfigItem parsedLine = new ConfigItem(record[0], record[1], record[2]); + fileContents.add(parsedLine); + ++line; } + + // Close down + csvReader.close(); + reader.close(); } // Get a copy of the internal items diff --git a/src/utils/GlobalLog.java b/src/utils/GlobalLog.java index e0652ba..e1c43aa 100644 --- a/src/utils/GlobalLog.java +++ b/src/utils/GlobalLog.java @@ -36,13 +36,14 @@ public class GlobalLog System.out.println(logLine); } - public static void log(String msg) { write(log, LogFilter.Debug, msg); } - public static void warn(String msg) { write(warn, LogFilter.Debug, msg); } - public static void error(String msg) { write(error, LogFilter.Debug, msg); } - public static void fatal(String msg) throws Exception { write(fatal, LogFilter.Debug, msg); throw new Exception(msg); } - public static void log(LogFilter filter, String msg) { write(log, filter, msg); } public static void warn(LogFilter filter, String msg) { write(warn, filter, msg); } - public static void error(LogFilter filter, String msg) { write(error, filter, msg); } + public static void error(LogFilter filter, String msg) { write(error, filter, msg); System.err.println(msg); System.err.flush(); } public static void fatal(LogFilter filter, String msg) throws Exception { write(fatal, filter, msg); throw new Exception(msg); } + + public static void log(String msg) { log(LogFilter.Debug, msg); } + public static void warn(String msg) { warn(LogFilter.Debug, msg); } + public static void error(String msg) { error(LogFilter.Debug, msg); } + public static void fatal(String msg) throws Exception { fatal(LogFilter.Debug, msg); } + }