Implemented config save and reload

Always caching config value if it hasn't been cached
This commit is contained in:
Norbi Peti 2019-03-13 13:47:49 +01:00
parent 7289385a33
commit 25b9caa903
8 changed files with 77 additions and 20 deletions

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" /> <output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" /> <output-test url="file://$MODULE_DIR$/target/test-classes" />

View file

@ -1,6 +1,7 @@
package buttondevteam.core; package buttondevteam.core;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.Command2.Subcommand; import buttondevteam.lib.chat.Command2.Subcommand;
@ -24,7 +25,10 @@ public class ComponentCommand extends ICommand2MC {
@Subcommand @Subcommand
public boolean enable(CommandSender sender, Plugin plugin, String component) { public boolean enable(CommandSender sender, Plugin plugin, String component) {
plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable if (plugin instanceof ButtonPlugin)
((ButtonPlugin) plugin).justReload();
else
plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable
return enable_disable(sender, plugin, component, true); return enable_disable(sender, plugin, component, true);
} }

View file

@ -34,9 +34,10 @@ public final class ComponentManager {
/** /**
* Unregister all components of a plugin that are enabled - called on {@link ButtonPlugin} disable * Unregister all components of a plugin that are enabled - called on {@link ButtonPlugin} disable
*/ */
public static void unregComponents(ButtonPlugin plugin) { @SuppressWarnings("unchecked")
public static <T extends ButtonPlugin> void unregComponents(T plugin) {
while (!plugin.getComponentStack().empty()) //Unregister in reverse order while (!plugin.getComponentStack().empty()) //Unregister in reverse order
Component.unregisterComponent(plugin, plugin.getComponentStack().pop()); //Components are pushed on register Component.unregisterComponent(plugin, (Component<T>) plugin.getComponentStack().pop()); //Components are pushed on register
componentsEnabled = false; componentsEnabled = false;
} }

View file

@ -0,0 +1,14 @@
package buttondevteam.core;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import org.bukkit.command.CommandSender;
@CommandClass
public class ThorpeCommand extends ICommand2MC {
@Command2.Subcommand //TODO: Main permissions (groups) like 'mod'
public void reload(CommandSender sender) {
MainPlugin.Instance.reloadConfig();
}
}

View file

@ -20,10 +20,10 @@ public abstract class ButtonPlugin extends JavaPlugin {
@Getter(AccessLevel.PROTECTED) @Getter(AccessLevel.PROTECTED)
private IHaveConfig data; //TODO private IHaveConfig data; //TODO
/** /**
* Used to unregister components in the right order * Used to unregister components in the right order - and to reload configs
*/ */
@Getter @Getter
private Stack<Component> componentStack = new Stack<>(); private Stack<Component<?>> componentStack = new Stack<>();
protected abstract void pluginEnable(); protected abstract void pluginEnable();
@ -40,9 +40,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
@Override @Override
public final void onEnable() { public final void onEnable() {
var section = super.getConfig().getConfigurationSection("global"); loadConfig();
if (section == null) section = super.getConfig().createSection("global");
iConfig = new IHaveConfig(section, this::saveConfig);
try { try {
pluginEnable(); pluginEnable();
} catch (Exception e) { } catch (Exception e) {
@ -50,6 +48,12 @@ public abstract class ButtonPlugin extends JavaPlugin {
} }
} }
private void loadConfig() {
var section = super.getConfig().getConfigurationSection("global");
if (section == null) section = super.getConfig().createSection("global");
iConfig = new IHaveConfig(section, this::saveConfig);
}
@Override @Override
public final void onDisable() { public final void onDisable() {
try { try {
@ -63,4 +67,19 @@ public abstract class ButtonPlugin extends JavaPlugin {
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e); TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
} }
} }
@Override
public void reloadConfig() {
justReload();
loadConfig();
componentStack.forEach(c -> Component.updateConfig(this, c));
}
public void justReload() {
if (ConfigData.saveNow(getConfig())) {
getLogger().warning("Saved pending configuration changes to the file, didn't reload (try again).");
return;
}
super.reloadConfig();
}
} }

View file

@ -140,7 +140,7 @@ public abstract class Component<TP extends JavaPlugin> {
} }
} }
private static void updateConfig(JavaPlugin plugin, Component component) { public static void updateConfig(JavaPlugin plugin, Component component) {
if (plugin.getConfig() != null) { //Production if (plugin.getConfig() != null) { //Production
var compconf = plugin.getConfig().getConfigurationSection("components"); var compconf = plugin.getConfig().getConfigurationSection("components");
if (compconf == null) compconf = plugin.getConfig().createSection("components"); if (compconf == null) compconf = plugin.getConfig().createSection("components");
@ -237,10 +237,10 @@ public abstract class Component<TP extends JavaPlugin> {
var cs=c.getConfigurationSection(key); var cs=c.getConfigurationSection(key);
if(cs==null) cs=c.createSection(key); if(cs==null) cs=c.createSection(key);
val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection) val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection)
.collect(Collectors.toMap(Map.Entry::getKey, kv -> new IHaveConfig((ConfigurationSection) kv.getValue()))); .collect(Collectors.toMap(Map.Entry::getKey, kv -> new IHaveConfig((ConfigurationSection) kv.getValue(), getPlugin()::saveConfig)));
if (res.size() == 0) { if (res.size() == 0) {
for (val entry : defaultProvider.entrySet()) { for (val entry : defaultProvider.entrySet()) {
val conf = new IHaveConfig(cs.createSection(entry.getKey())); val conf = new IHaveConfig(cs.createSection(entry.getKey()), getPlugin()::saveConfig);
entry.getValue().accept(conf); entry.getValue().accept(conf);
res.put(entry.getKey(), conf); res.put(entry.getKey(), conf);
} }

View file

@ -2,12 +2,12 @@ package buttondevteam.lib.architecture;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.Configuration; import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import java.lang.reflect.Array; import java.lang.reflect.Array;
@ -23,7 +23,7 @@ import java.util.function.Function;
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
//@AllArgsConstructor(access = AccessLevel.PACKAGE) //@AllArgsConstructor(access = AccessLevel.PACKAGE)
public class ConfigData<T> { public class ConfigData<T> {
private static final HashMap<Configuration, BukkitTask> saveTasks= new HashMap<>(); private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
/** /**
* May be null for testing * May be null for testing
*/ */
@ -45,8 +45,12 @@ public class ConfigData<T> {
* The config value should not change outside this instance * The config value should not change outside this instance
*/ */
private T value; private T value;
/**
* Whether the default value is saved in the yaml
*/
private boolean saved = false; private boolean saved = false;
//This constructor is needed because it sets the getter and setter
public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) { public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
this.config = config; this.config = config;
this.path = path; this.path = path;
@ -88,9 +92,9 @@ public class ConfigData<T> {
else if (def instanceof Double) else if (def instanceof Double)
val = ((Number) val).doubleValue(); val = ((Number) val).doubleValue();
} }
if (val instanceof List && def.getClass().isArray()) if (val instanceof List && def != null && def.getClass().isArray())
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0)); val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
return (T) val; return value = (T) val; //Always cache, if not cached yet
} }
public void set(T value) { public void set(T value) {
@ -102,15 +106,32 @@ public class ConfigData<T> {
config.set(path, val); config.set(path, val);
if(!saveTasks.containsKey(config.getRoot())) { if(!saveTasks.containsKey(config.getRoot())) {
synchronized (saveTasks) { synchronized (saveTasks) {
saveTasks.put(config.getRoot(), Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> { saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
synchronized (saveTasks) { synchronized (saveTasks) {
saveTasks.remove(config.getRoot()); saveTasks.remove(config.getRoot());
saveAction.run(); saveAction.run();
} }
}, 100)); }, 100), saveAction));
} }
} }
} }
this.value = value; this.value = value;
} }
@AllArgsConstructor
private static class SaveTask {
BukkitTask task;
Runnable saveAction;
}
public static boolean saveNow(Configuration config) {
SaveTask st = saveTasks.get(config);
if (st != null) {
st.task.cancel();
saveTasks.remove(config);
st.saveAction.run();
return true;
}
return false;
}
} }

View file

@ -13,8 +13,6 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" /> <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" />
<orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" /> <orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" /> <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" /> <orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />