Feature - plugin watcher, detect file changes & reload

This commit is contained in:
Frank van der Heijden 2020-07-20 20:19:07 +02:00
parent 719163c579
commit 07719cc1cc
No known key found for this signature in database
GPG key ID: 26DA56488D314D11
24 changed files with 387 additions and 15 deletions

View file

@ -0,0 +1,13 @@
package net.frankheijden.serverutils.common.entities;
public interface AbstractResult {
/**
* Retrieves the associated message of the result
* and sends it to a CommandSender.
* @param sender The receiver.
* @param action The action which let to the result.
* @param what An associated variable.
*/
void sendTo(ServerCommandSender sender, String action, String what);
}

View file

@ -0,0 +1,6 @@
package net.frankheijden.serverutils.common.entities;
public abstract class AbstractTask implements Runnable {
public abstract void cancel();
}

View file

@ -5,7 +5,7 @@ import net.frankheijden.serverutils.common.config.Messenger;
/**
* An enum containing possible results.
*/
public enum Result {
public enum Result implements AbstractResult {
NOT_EXISTS,
NOT_ENABLED,
ALREADY_LOADED,
@ -44,6 +44,7 @@ public enum Result {
* @param action The action which let to the result.
* @param what An associated variable.
*/
@Override
public void sendTo(ServerCommandSender sender, String action, String what) {
Messenger.sendMessage(sender, "serverutils." + this.name().toLowerCase(),
"%action%", action,

View file

@ -17,4 +17,10 @@ public interface ServerCommandSender {
* @return Whether or not they have the permission.
*/
boolean hasPermission(String permission);
/**
* Whether or not the given instance is a player.
* @return Boolean true or false.
*/
boolean isPlayer();
}

View file

@ -15,7 +15,7 @@ public abstract class ServerUtilsPlugin {
public abstract <T> AbstractPluginManager<T> getPluginManager();
public abstract AbstractTaskManager getTaskManager();
public abstract <T> AbstractTaskManager<T> getTaskManager();
public abstract ResourceProvider getResourceProvider();
@ -52,4 +52,12 @@ public abstract class ServerUtilsPlugin {
}
return file;
}
public void enable() {
}
public void disable() {
getTaskManager().cancelAllTasks();
}
}

View file

@ -0,0 +1,24 @@
package net.frankheijden.serverutils.common.entities;
import net.frankheijden.serverutils.common.config.Messenger;
public enum WatchResult implements AbstractResult {
START,
CHANGE,
NOT_WATCHING,
STOPPED;
/**
* Retrieves the associated message of the result
* and sends it to a CommandSender.
* @param sender The receiver.
* @param action The action which let to the result.
* @param what An associated variable.
*/
@Override
public void sendTo(ServerCommandSender sender, String action, String what) {
Messenger.sendMessage(sender, "serverutils.watcher." + this.name().toLowerCase(),
"%what%", what);
}
}

View file

@ -2,10 +2,15 @@ package net.frankheijden.serverutils.common.managers;
import java.io.File;
import net.frankheijden.serverutils.common.ServerUtilsApp;
import net.frankheijden.serverutils.common.entities.AbstractResult;
import net.frankheijden.serverutils.common.entities.CloseableResult;
import net.frankheijden.serverutils.common.entities.LoadResult;
import net.frankheijden.serverutils.common.entities.Result;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.entities.WatchResult;
import net.frankheijden.serverutils.common.providers.PluginProvider;
import net.frankheijden.serverutils.common.tasks.PluginWatcherTask;
public abstract class AbstractPluginManager<T> extends PluginProvider<T> {
@ -22,4 +27,27 @@ public abstract class AbstractPluginManager<T> extends PluginProvider<T> {
public abstract CloseableResult unloadPlugin(String pluginName);
public abstract CloseableResult unloadPlugin(T plugin);
/**
* Starts watching the specified plugin for changes.
* Reloads the plugin if a change is detected.
* @param pluginName The plugin to watch.
* @return The result of the action.
*/
public AbstractResult watchPlugin(ServerCommandSender sender, String pluginName) {
if (getPlugin(pluginName) == null) return Result.NOT_EXISTS;
ServerUtilsApp.getPlugin().getTaskManager()
.runTaskAsynchronously(pluginName, new PluginWatcherTask(sender, pluginName));
return WatchResult.START;
}
/**
* Stops watching the plugin for changes.
* @param pluginName The plugin to stop watching.
* @return The result of the action.
*/
public AbstractResult unwatchPlugin(String pluginName) {
if (ServerUtilsApp.getPlugin().getTaskManager().cancelTask(pluginName)) return WatchResult.STOPPED;
return WatchResult.NOT_WATCHING;
}
}

View file

@ -1,8 +1,71 @@
package net.frankheijden.serverutils.common.managers;
public abstract class AbstractTaskManager {
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import net.frankheijden.serverutils.common.entities.AbstractTask;
public abstract class AbstractTaskManager<T> {
private final List<T> serverTasks;
private final Consumer<T> taskCloser;
private final Map<String, AbstractTask> tasks;
/**
* Constructs a new TaskManager with a consumer which closes a task.
* @param taskCloser The consumer which will close tasks.
*/
public AbstractTaskManager(Consumer<T> taskCloser) {
this.taskCloser = taskCloser;
this.serverTasks = new ArrayList<>();
this.tasks = new HashMap<>();
}
public abstract void runTask(Runnable runnable);
public void runTask(String key, AbstractTask task) {
tasks.put(key, task);
runTask(task);
}
public abstract void runTaskAsynchronously(Runnable runnable);
public void runTaskAsynchronously(String key, AbstractTask task) {
tasks.put(key, task);
runTaskAsynchronously(task);
}
public void addTask(T task) {
serverTasks.add(task);
}
/**
* Cancels a single task by key.
* @param key The key of the task.
* @return Whether or not the task existed.
*/
public boolean cancelTask(String key) {
AbstractTask task = tasks.remove(key);
if (task == null) return false;
task.cancel();
return true;
}
/**
* Cancels all tasks.
*/
public void cancelAllTasks() {
for (AbstractTask task : tasks.values()) {
task.cancel();
}
tasks.clear();
for (T task : serverTasks) {
taskCloser.accept(task);
}
serverTasks.clear();
}
}

View file

@ -17,6 +17,10 @@ public abstract class PluginProvider<T> {
public abstract File getPluginFile(T plugin);
public abstract File getPluginFile(String pluginName);
public abstract T getPlugin(String pluginName);
public abstract Set<String> getCommands();
/**

View file

@ -0,0 +1,97 @@
package net.frankheijden.serverutils.common.tasks;
import com.sun.nio.file.SensitivityWatchEventModifier;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.atomic.AtomicBoolean;
import net.frankheijden.serverutils.common.ServerUtilsApp;
import net.frankheijden.serverutils.common.entities.AbstractTask;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin;
import net.frankheijden.serverutils.common.entities.WatchResult;
import net.frankheijden.serverutils.common.managers.AbstractPluginManager;
import net.frankheijden.serverutils.common.managers.AbstractTaskManager;
import net.frankheijden.serverutils.common.providers.ChatProvider;
public class PluginWatcherTask extends AbstractTask {
private static final WatchEvent.Kind<?>[] EVENTS = new WatchEvent.Kind[] {
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
};
private final ServerUtilsPlugin plugin = ServerUtilsApp.getPlugin();
@SuppressWarnings("rawtypes")
private final AbstractPluginManager pluginManager = plugin.getPluginManager();
private final ChatProvider chatProvider = plugin.getChatProvider();
@SuppressWarnings("rawtypes")
private final AbstractTaskManager taskManager = plugin.getTaskManager();
private final ServerCommandSender sender;
private final String pluginName;
private File file;
private final AtomicBoolean run;
/**
* Constructs a new PluginWatcherTask for the specified plugin.
* @param pluginName The name of the plugin.
*/
public PluginWatcherTask(ServerCommandSender sender, String pluginName) {
this.sender = sender;
this.pluginName = pluginName;
this.file = pluginManager.getPluginFile(pluginName);
this.run = new AtomicBoolean(true);
}
@Override
public void run() {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
File folder = pluginManager.getPluginsFolder();
folder.toPath().register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH);
while (run.get()) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
if (file.getName().equals(event.context().toString())) {
send(WatchResult.CHANGE);
taskManager.runTask(() -> {
pluginManager.reloadPlugin(pluginName);
file = pluginManager.getPluginFile(pluginName);
});
}
}
if (file == null || !key.reset()) {
send(WatchResult.STOPPED);
break;
}
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (InterruptedException ignored) {
//
}
}
private void send(WatchResult result) {
result.sendTo(sender, null, pluginName);
if (sender.isPlayer()) {
result.sendTo(chatProvider.getConsoleSender(), null, pluginName);
}
}
@Override
public void cancel() {
run.set(false);
}
}