From 0acf54eed5f437f47ef0deb59950db2974ea395a Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 23 Dec 2018 22:52:26 +0200 Subject: [PATCH] [#777] Added NetworkSettingManager - This manager updates config in database if a server config file is updated on proxy server, and updates the config in database if the file is modified. - Added a setting for the update check delay --- .../network/NetworkSettingManager.java | 177 ++++++++++++++++++ .../system/settings/paths/TimeSettings.java | 1 + .../plan/system/tasks/TaskSystem.java | 2 +- .../plan/utilities/file/FileWatcher.java | 11 +- .../plan/utilities/file/WatchedFile.java | 14 ++ .../src/main/resources/bungeeconfig.yml | 2 + Plan/common/src/main/resources/config.yml | 2 + 7 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java new file mode 100644 index 000000000..cadfae0b8 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java @@ -0,0 +1,177 @@ +package com.djrapitops.plan.system.settings.network; + +import com.djrapitops.plan.api.exceptions.EnableException; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.database.databases.Database; +import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.settings.config.Config; +import com.djrapitops.plan.system.settings.config.ConfigReader; +import com.djrapitops.plan.system.settings.config.ConfigWriter; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.system.tasks.TaskSystem; +import com.djrapitops.plan.utilities.file.FileWatcher; +import com.djrapitops.plan.utilities.file.WatchedFile; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.logging.error.ErrorHandler; +import com.djrapitops.plugin.task.AbsRunnable; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * In charge of all configs on the network. + *

+ * Performs the following tasks related to network configs: + * - File modification watching related to server configs + * - Database update operations related to server configs + * - File update operations from database related to server configs + * + * @author Rsl1122 + */ +@Singleton +public class NetworkSettingManager { + + private final PlanFiles files; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + private final TaskSystem taskSystem; + private PlanConfig config; + private ErrorHandler errorHandler; + + private File serverSettingsFolder; + + private FileWatcher watcher; + + @Inject + public NetworkSettingManager( + PlanFiles files, + PlanConfig config, + DBSystem dbSystem, + ServerInfo serverInfo, + TaskSystem taskSystem, + ErrorHandler errorHandler + ) { + this.files = files; + this.config = config; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.taskSystem = taskSystem; + + this.errorHandler = errorHandler; + } + + public void enable() throws EnableException { + serverSettingsFolder = createServerSettingsFolder(); + + watcher = prepareFileWatcher(); + watcher.start(); + + scheduleDBCheckTask(); + } + + public void disable() { + if (watcher != null) { + watcher.interrupt(); + } + } + + private FileWatcher prepareFileWatcher() { + FileWatcher fileWatcher = new FileWatcher(serverSettingsFolder, errorHandler); + + File[] files = serverSettingsFolder.listFiles((dir, name) -> name.endsWith(".yml")); + if (files != null) { + for (File file : files) { + addFileToWatchList(fileWatcher, file); + } + } + + return fileWatcher; + } + + private void addFileToWatchList(FileWatcher fileWatcher, File file) { + try { + String fileName = file.getName(); + String uuidString = fileName.substring(0, fileName.length() - 4); + UUID serverUUID = UUID.fromString(uuidString); + + fileWatcher.addToWatchlist(new WatchedFile(file, () -> updateConfigInDB(file, serverUUID))); + } catch (IndexOutOfBoundsException | IllegalArgumentException ignore) { + /* Invalid file-name, ignored */ + } + } + + private void scheduleDBCheckTask() { + long checkPeriod = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MINUTES); + taskSystem.registerTask("Config Update DB Checker", new AbsRunnable() { + @Override + public void run() { + checkDBForNewConfigSettings(); + } + }).runTaskTimerAsynchronously(checkPeriod, checkPeriod); + } + + private File createServerSettingsFolder() throws EnableException { + try { + File serverConfigFolder = files.getFileFromPluginFolder("serverConfiguration"); + Files.createDirectories(serverConfigFolder.toPath()); + return serverConfigFolder; + } catch (IOException e) { + throw new EnableException("Could not initialize NetworkSettingManager: " + e.getMessage(), e); + } + } + + private File getServerConfigFile(UUID serverUUID) { + return new File(serverSettingsFolder, serverUUID + ".yml"); + } + + private void checkDBForNewConfigSettings() { + Database database = dbSystem.getDatabase(); + List serverUUIDs = database.fetch().getServerUUIDs(); + serverUUIDs.remove(serverInfo.getServerUUID()); + + for (UUID serverUUID : serverUUIDs) { + checkDBForNewConfigSettings(database, serverUUID); + } + } + + private void checkDBForNewConfigSettings(Database database, UUID serverUUID) { + File configFile = getServerConfigFile(serverUUID); + long lastModified = configFile.exists() ? configFile.lastModified() : -1; + + Optional foundConfig = database.fetch().getNewConfig(lastModified, serverUUID); + if (foundConfig.isPresent()) { + try { + new ConfigWriter(configFile.toPath()).write(foundConfig.get()); + addFileToWatchList(watcher, configFile); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + private void updateConfigInDB(File file, UUID serverUUID) { + if (!file.exists()) { + return; + } + + Database database = dbSystem.getDatabase(); + + try { + Config config = new ConfigReader(file.toPath()).read(); + database.save().saveConfig(serverUUID, config); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java index 6ddd34dc7..1c4179939 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java @@ -22,6 +22,7 @@ public class TimeSettings { public static final Setting ANALYSIS_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Analysis_refresh_every"); public static final Setting CLEAN_CACHE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_caches_every"); public static final Setting CLEAN_DATABASE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_Database_every"); + public static final Setting CONFIG_UPDATE_INTERVAL = new TimeSetting("Time.Periodic_tasks.Check_DB_for_server_config_files_every"); private TimeSettings() { /* static variable class */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java index c255de838..4f25386ed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java @@ -43,7 +43,7 @@ public abstract class TaskSystem implements SubSystem { return registerTask(taskName, runnable); } - protected PluginRunnable registerTask(String name, AbsRunnable runnable) { + public PluginRunnable registerTask(String name, AbsRunnable runnable) { return runnableFactory.create(name, runnable); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java index b791d6447..18e99619a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java @@ -23,8 +23,8 @@ import com.djrapitops.plugin.utilities.Verify; import java.io.File; import java.io.IOException; import java.nio.file.*; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; @@ -40,16 +40,16 @@ public class FileWatcher extends Thread { private volatile boolean running; private Path watchedPath; - private List watchedFiles; + private Set watchedFiles; public FileWatcher(File folder, ErrorHandler errorHandler) { - this(folder, errorHandler, new ArrayList<>()); + this(folder, errorHandler, new HashSet<>()); } public FileWatcher( File folder, ErrorHandler errorHandler, - List watchedFiles + Set watchedFiles ) { this.errorHandler = errorHandler; this.running = false; @@ -61,6 +61,7 @@ public class FileWatcher extends Thread { } public void addToWatchlist(WatchedFile watchedFile) { + watchedFiles.remove(watchedFile); watchedFiles.add(watchedFile); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java index a758a7ae9..2df09b56b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java @@ -20,6 +20,7 @@ import com.djrapitops.plan.utilities.java.VoidFunction; import java.io.File; import java.nio.file.Path; +import java.util.Objects; /** * File with a consumer that is called if the file is modified. @@ -41,4 +42,17 @@ public class WatchedFile { onChange.apply(); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WatchedFile that = (WatchedFile) o; + return Objects.equals(file, that.file); + } + + @Override + public int hashCode() { + return Objects.hash(file); + } } diff --git a/Plan/common/src/main/resources/bungeeconfig.yml b/Plan/common/src/main/resources/bungeeconfig.yml index 2257472a5..6e14e70fb 100644 --- a/Plan/common/src/main/resources/bungeeconfig.yml +++ b/Plan/common/src/main/resources/bungeeconfig.yml @@ -84,6 +84,8 @@ Time: Remove_inactive_player_data_after: 180 Unit: DAYS Periodic_tasks: + Check_DB_for_server_config_files_every: 1 + Unit: MINUTES Clean_caches_every: 10 Unit: MINUTES Clean_Database_every: 1 diff --git a/Plan/common/src/main/resources/config.yml b/Plan/common/src/main/resources/config.yml index 10a275122..f6fc8d97b 100644 --- a/Plan/common/src/main/resources/config.yml +++ b/Plan/common/src/main/resources/config.yml @@ -91,6 +91,8 @@ Time: Periodic_tasks: Analysis_refresh_every: 60 Unit: MINUTES + Check_DB_for_server_config_files_every: 1 + Unit: MINUTES Clean_caches_every: 10 Unit: MINUTES Clean_Database_every: 1