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:
@@ -4,19 +4,26 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ConfigUtils
|
public class ConfigUtils
|
||||||
{
|
{
|
||||||
|
private static Map<String, YamlConfiguration> resourceCache = new HashMap<>();
|
||||||
|
|
||||||
public static void addIfEmpty(Plugin plugin, String resource, ConfigurationSection section) {
|
public static void addIfEmpty(Plugin plugin, String resource, ConfigurationSection section) {
|
||||||
process(plugin, resource, section, true, false);
|
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) {
|
private static void process(Plugin plugin, String resource, ConfigurationSection section, boolean addOnlyIfEmpty, boolean removeObsolete) {
|
||||||
try {
|
YamlConfiguration defaults = resourceCache.computeIfAbsent(resource, res -> {
|
||||||
YamlConfiguration defaults = new YamlConfiguration();
|
InputStream is = plugin.getResource("res/" + res);
|
||||||
defaults.load(new InputStreamReader(plugin.getResource("res/" + resource)));
|
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();
|
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> present = section.getKeys(true);
|
||||||
Set<String> required = defaults.getKeys(true);
|
Set<String> required = defaults.getKeys(true);
|
||||||
if (!addOnlyIfEmpty || present.isEmpty()) {
|
if (!addOnlyIfEmpty || present.isEmpty()) {
|
||||||
for (String req : required) {
|
for (String req : required) {
|
||||||
if (!present.remove(req)) {
|
if (!present.remove(req)) {
|
||||||
section.set(req, defaults.get(req));
|
section.set(req, defaults.get(req));
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (removeObsolete) {
|
if (removeObsolete) {
|
||||||
for (String obs : present) {
|
for (String obs : present) {
|
||||||
section.set(obs, null);
|
section.set(obs, null);
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigurationSection makeSection(ConfigurationSection config, String section) {
|
public static ConfigurationSection makeSection(ConfigurationSection config, String section) {
|
||||||
|
|||||||
Reference in New Issue
Block a user