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.
This commit is contained in:
Andreas Troelsen 2018-04-22 22:20:20 +02:00
parent b1634f9460
commit e4d919cf3e

View File

@ -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<String, YamlConfiguration> 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<String> present = section.getKeys(true);
Set<String> 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) {