From e4d919cf3e4fb77e31849d94a2c67b89df44a531 Mon Sep 17 00:00:00 2001 From: Andreas Troelsen Date: Sun, 22 Apr 2018 22:20:20 +0200 Subject: [PATCH] Add caching and conditional saving to ConfigUtils. This commit changes the naive behavior of the ConfigUtils methods addIfEmpty() and addMissingRemoveObsolete(). Instead of an almost guaranteed config-file write on every invocation of either method, writes only happen if the loaded configuration changes. The excessive writes result in long config reload times, and this change fixes that, effectively fixing the second part of #435. To further the performance boost, the resource reads are cached in a map - this turns out to not be an issue for server plugin reloads, as a new ClassLoader instance is used to load the new set of plugins. --- .../MobArena/util/config/ConfigUtils.java | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/garbagemule/MobArena/util/config/ConfigUtils.java b/src/main/java/com/garbagemule/MobArena/util/config/ConfigUtils.java index 183eb30..776183a 100644 --- a/src/main/java/com/garbagemule/MobArena/util/config/ConfigUtils.java +++ b/src/main/java/com/garbagemule/MobArena/util/config/ConfigUtils.java @@ -4,19 +4,26 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import java.io.File; +import java.io.InputStream; import java.io.InputStreamReader; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; +import java.util.Scanner; import java.util.Set; public class ConfigUtils { + private static Map resourceCache = new HashMap<>(); + public static void addIfEmpty(Plugin plugin, String resource, ConfigurationSection section) { process(plugin, resource, section, true, false); } @@ -35,32 +42,50 @@ public class ConfigUtils } private static void process(Plugin plugin, String resource, ConfigurationSection section, boolean addOnlyIfEmpty, boolean removeObsolete) { - try { - YamlConfiguration defaults = new YamlConfiguration(); - defaults.load(new InputStreamReader(plugin.getResource("res/" + resource))); + YamlConfiguration defaults = resourceCache.computeIfAbsent(resource, res -> { + InputStream is = plugin.getResource("res/" + res); + if (is == null) { + throw new IllegalStateException("Couldn't read " + res + " from jar, please re-install MobArena"); + } + Scanner scanner = new Scanner(is).useDelimiter("\\A"); + if (!scanner.hasNext()) { + throw new IllegalStateException("No content in " + res + " in jar, please re-install MobArena"); + } + String contents = scanner.next(); + YamlConfiguration yaml = new YamlConfiguration(); + try { + yaml.loadFromString(contents); + return yaml; + } catch (InvalidConfigurationException e) { + throw new IllegalStateException("Invalid contents in " + res + " in jar, please re-install MobArena", e); + } + }); - process(defaults, section, addOnlyIfEmpty, removeObsolete); + boolean modified = process(defaults, section, addOnlyIfEmpty, removeObsolete); + if (modified) { plugin.saveConfig(); - } catch (Exception e) { - e.printStackTrace(); } } - private static void process(YamlConfiguration defaults, ConfigurationSection section, boolean addOnlyIfEmpty, boolean removeObsolete) { + private static boolean process(YamlConfiguration defaults, ConfigurationSection section, boolean addOnlyIfEmpty, boolean removeObsolete) { + boolean modified = false; Set present = section.getKeys(true); Set required = defaults.getKeys(true); if (!addOnlyIfEmpty || present.isEmpty()) { for (String req : required) { if (!present.remove(req)) { section.set(req, defaults.get(req)); + modified = true; } } } if (removeObsolete) { for (String obs : present) { section.set(obs, null); + modified = true; } } + return modified; } public static ConfigurationSection makeSection(ConfigurationSection config, String section) {