From 4bf7d5f82fc8fb6103b4b0f0093f3caa6dafeb07 Mon Sep 17 00:00:00 2001 From: Harrison Deng Date: Mon, 11 Nov 2024 08:08:12 +0000 Subject: [PATCH] Basic functioning plugin completed. --- .devcontainer/devcontainer.json | 35 +++++ .gitignore | 130 ++++++++++++++++++ .vscode/settings.json | 4 + .vscode/tasks.json | 15 ++ buildtools/install.sh | 6 + devserver/loadplugin.sh | 3 + devserver/start.sh | 3 + pom.xml | 58 ++++++++ .../spigotresourcesync/ConfigManager.java | 121 ++++++++++++++++ .../SpigotResourceSync.java | 54 ++++++++ .../spigotresourcesync/SyncListManager.java | 47 +++++++ .../data/Configuration.java | 42 ++++++ .../spigotresourcesync/data/SyncList.java | 67 +++++++++ .../spigotresourcesync/data/SyncPair.java | 55 ++++++++ .../events/ObjectLoadListener.java | 7 + .../serialisation/JacksonYamlSerialiser.java | 68 +++++++++ .../serialisation/Serialiser.java | 14 ++ .../ApacheCommonsIOSynchroniser.java | 49 +++++++ .../synchronisation/Synchroniser.java | 11 ++ src/main/resources/plugin.yml | 3 + .../spigotresourcesync/TestConfigManager.java | 32 +++++ .../TestSyncListManager.java | 10 ++ .../data/TestSynchronisationPair.java | 14 ++ .../TestJacksonYamlSerialiser.java | 78 +++++++++++ .../TestApacheCommonsIOSynchroniser.java | 44 ++++++ 25 files changed, 970 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 buildtools/install.sh create mode 100644 devserver/loadplugin.sh create mode 100755 devserver/start.sh create mode 100644 pom.xml create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/ConfigManager.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/SpigotResourceSync.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/SyncListManager.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/Configuration.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncList.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncPair.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/events/ObjectLoadListener.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/JacksonYamlSerialiser.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/Serialiser.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/ApacheCommonsIOSynchroniser.java create mode 100644 src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/Synchroniser.java create mode 100644 src/main/resources/plugin.yml create mode 100644 src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestConfigManager.java create mode 100644 src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestSyncListManager.java create mode 100644 src/test/java/solutions/reslate/entertainment/spigotresourcesync/data/TestSynchronisationPair.java create mode 100644 src/test/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/TestJacksonYamlSerialiser.java create mode 100644 src/test/java/solutions/reslate/entertainment/spigotresourcesync/synchroniser/TestApacheCommonsIOSynchroniser.java diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b7848ee --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/java +{ + "name": "Java", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye", + + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "none", + "installMaven": "true", + "installGradle": "false" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "redhat.java", + "redhat.vscode-xml" + ] + } + } + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "java -version", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf9b9f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,130 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,gradle,java,kotlin,maven +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,gradle,java,kotlin,maven + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Kotlin ### +# Compiled class file + +# Log file + +# BlueJ files + +# Mobile Tools for Java (J2ME) + +# Package Files # + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +# JDT-specific (Eclipse Java Development Tools) + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,gradle,java,kotlin,maven + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + +buildtools/* +!buildtools/install.sh +devserver/* +!devserver/start.sh +!devserver/loadplugin.sh \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0be1c0c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..eab3cbe --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "java (buildArtifact)", + "targetPath": "${workspaceFolder}/devserver/plugins/${workspaceFolderBasename}.jar", + "elements": [ + "${compileOutput}", + "${dependencies}" + ], + "problemMatcher": [], + "label": "Build SpigotResourceSync to plugins" + } + ] +} \ No newline at end of file diff --git a/buildtools/install.sh b/buildtools/install.sh new file mode 100644 index 0000000..e2f7dad --- /dev/null +++ b/buildtools/install.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +curl -o BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar +java -jar BuildTools.jar --rev 1.20.4 --final-name spigot.jar +cp spigot.jar ../devserver/. +echo "Moved spigot.jar to ../devserver/." \ No newline at end of file diff --git a/devserver/loadplugin.sh b/devserver/loadplugin.sh new file mode 100644 index 0000000..3b28d72 --- /dev/null +++ b/devserver/loadplugin.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cp ../target/*.jar ./plugins/. \ No newline at end of file diff --git a/devserver/start.sh b/devserver/start.sh new file mode 100755 index 0000000..4bddb68 --- /dev/null +++ b/devserver/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +java -Xmx2G -XX:+UseG1GC -jar spigot.jar nogui \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..140d40d --- /dev/null +++ b/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + systems.reslate.entertainment.spigotresourcesync + spigotresourcesync + 1.0-SNAPSHOT + + + 21 + 21 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.20.4-R0.1-SNAPSHOT + provided + + + commons-io + commons-io + 2.17.0 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.18.0 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.18.0 + + + com.fasterxml.jackson.core + jackson-core + 2.18.0 + + + org.junit.jupiter + junit-jupiter-engine + 5.11.0-M2 + test + + + + \ No newline at end of file diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/ConfigManager.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/ConfigManager.java new file mode 100644 index 0000000..aee0bf5 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/ConfigManager.java @@ -0,0 +1,121 @@ +package solutions.reslate.entertainment.spigotresourcesync; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Collection; +import java.util.HashSet; +import java.util.logging.Logger; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; + +import solutions.reslate.entertainment.spigotresourcesync.data.Configuration; +import solutions.reslate.entertainment.spigotresourcesync.events.ObjectLoadListener; +import solutions.reslate.entertainment.spigotresourcesync.serialisation.Serialiser; + +public class ConfigManager extends FileConfiguration { + private Serialiser serialiser; + private Configuration config; + private File file; + private Logger logger; + private Collection> configLoadListener; + + + public ConfigManager(File file, Serialiser serialiser, Logger logger) { + super(); + this.serialiser = serialiser; + this.config = new Configuration(); + this.file = file; + this.logger = logger; + this.configLoadListener = new HashSet<>(); + } + + public void flush() { + try { + save(this.file); + } catch (IOException e) { + logger.severe(String.format("Unable to save configuration to \"%s\". %s", this.file.getAbsolutePath(), e.getMessage())); + } + } + + @Override + public void save(String file) throws IOException { + this.save(new File(file)); + } + + @Override + public void save(File file) throws IOException { + this.serialiser.flushTo(file, this.config); + logger.info(String.format("Saved configuration to \"%s\".", this.file.getAbsolutePath())); + } + + public void load() { + try { + this.load(file); + } catch (IOException | InvalidConfigurationException e) { + logger.severe(String.format("Unable to load configuration from \"%s\".", file.getAbsolutePath(), e.getMessage())); + if (file.exists()) { + logger.severe(String.format("Found pre-existing file. No overwriting will occur. Delete \"%s\" and restart to generate new configuration.", this.file.getAbsolutePath())); + } else { + flush(); + } + } + } + + + @Override + public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException { + this.load(new File(file)); + } + + @Override + public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException { + try (FileReader reader = new FileReader(file)) { + this.load(reader); + } + } + + @Override + public void load(Reader reader) throws IOException, InvalidConfigurationException { + this.config = this.serialiser.load(reader, Configuration.class); + logger.info(String.format("Successfully loaded configuration from \"%s\".", this.file.getAbsolutePath())); + onLoadConfig(); + } + + public Configuration getConfiguration() { + return config; + } + + private void onLoadConfig() { + for (ObjectLoadListener objectUpdateListener : configLoadListener) { + objectUpdateListener.objectLoaded(config); + } + } + + @Override + public String saveToString() { + return serialiser.serialize(this.config); + } + + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + this.config = serialiser.deserialize(contents, Configuration.class); + onLoadConfig(); + } + + public void addConfigLoadListener(ObjectLoadListener listener) { + this.configLoadListener.add(listener); + } + + public void removeConfigLoadListener(ObjectLoadListener listener) { + this.configLoadListener.remove(listener); + } + + public void resetConfiguration() { + this.config = new Configuration(); + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SpigotResourceSync.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SpigotResourceSync.java new file mode 100644 index 0000000..a2d04be --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SpigotResourceSync.java @@ -0,0 +1,54 @@ +package solutions.reslate.entertainment.spigotresourcesync; + +import java.io.File; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import solutions.reslate.entertainment.spigotresourcesync.serialisation.JacksonYamlSerialiser; +import solutions.reslate.entertainment.spigotresourcesync.synchronisation.ApacheCommonsIOSynchroniser; + +public class SpigotResourceSync extends JavaPlugin { + private ConfigManager configManager; + private SyncListManager syncListManager; + + @Override + public void onDisable() { + configManager.flush(); + super.onDisable(); + } + + @Override + public void onLoad() { + configManager = new ConfigManager(new File("plugins", getName() + ".yml"), new JacksonYamlSerialiser<>(), getLogger()); + configManager.load(); + syncListManager = new SyncListManager(configManager.getConfiguration(), new ApacheCommonsIOSynchroniser(), getLogger()); + configManager.addConfigLoadListener(syncListManager); + if (configManager.getConfiguration().getSyncOnLoad()) { + syncListManager.synchroniseAllSyncList(); + } + super.onLoad(); + } + + @Override + public void onEnable() { + super.onEnable(); + } + + @Override + public FileConfiguration getConfig() { + return configManager; + } + + @Override + public void saveConfig() { + configManager.flush(); + } + + @Override + public void saveDefaultConfig() { + configManager.resetConfiguration(); + configManager.flush(); + super.saveDefaultConfig(); + } +} \ No newline at end of file diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SyncListManager.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SyncListManager.java new file mode 100644 index 0000000..b60ec28 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/SyncListManager.java @@ -0,0 +1,47 @@ +package solutions.reslate.entertainment.spigotresourcesync; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Logger; + +import solutions.reslate.entertainment.spigotresourcesync.data.Configuration; +import solutions.reslate.entertainment.spigotresourcesync.data.SyncPair; +import solutions.reslate.entertainment.spigotresourcesync.events.ObjectLoadListener; +import solutions.reslate.entertainment.spigotresourcesync.synchronisation.Synchroniser; + +public class SyncListManager implements ObjectLoadListener { + private Configuration configuration; + private Synchroniser synchroniser; + private Logger logger; + + public SyncListManager(Configuration configuration, Synchroniser synchroniser, Logger logger) { + super(); + this.logger = logger; + this.configuration = configuration; + this.synchroniser = synchroniser; + } + + public void synchroniseAllSyncList() { + logger.info("Synchronising all sync pairs..."); + for (SyncPair syncPair : this.configuration.getSyncList().gatherAllSyncPairs()) { + File source = new File(syncPair.getSource()); + File dest = new File(syncPair.getDestination()); + try { + synchroniser.sync(source, dest); + logger.info(String.format("Synchronised \"%s\" to \"%s\"!", source.getName(), dest.getName())); + } catch (IOException e) { + logger.warning(String.format("Failed to synchronise \"%s\" to \"%s\". %s", source.getAbsolutePath(), dest.getAbsolutePath(), e.getMessage())); + } + } + logger.info("Done synchronising."); + } + + @Override + public void objectLoaded(Configuration obj) { + logger.info("Updating synchronisation list due to recently loading configuration..."); + this.configuration = obj; + synchroniseAllSyncList(); + logger.info("Done."); + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/Configuration.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/Configuration.java new file mode 100644 index 0000000..1c1f159 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/Configuration.java @@ -0,0 +1,42 @@ +package solutions.reslate.entertainment.spigotresourcesync.data; + +import java.io.Serializable; + +public class Configuration implements Serializable { + private boolean enabled; + private boolean syncOnLoad; + private SyncList syncList; + + public Configuration() { + super(); + enabled = true; + syncOnLoad = true; + syncList = new SyncList(); + syncList.addSynchronisationPair("exampleA", "exampleB", "general"); + } + + public boolean getEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean getSyncOnLoad() { + return syncOnLoad; + } + + public void setSyncOnLoad(boolean syncOnLoad) { + this.syncOnLoad = syncOnLoad; + } + + public SyncList getSyncList() { + return syncList; + } + + public void setSyncList(SyncList syncList) { + this.syncList = syncList; + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncList.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncList.java new file mode 100644 index 0000000..7bbbaea --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncList.java @@ -0,0 +1,67 @@ +package solutions.reslate.entertainment.spigotresourcesync.data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.SequencedCollection; +import java.util.SequencedMap; + + +public class SyncList implements Serializable { + private SequencedMap> syncPairs; + + public SyncList() { + super(); + this.syncPairs = new LinkedHashMap<>(); + } + + public List gatherAllSyncPairs() { + List allSyncPairs = new ArrayList<>(); + for (SequencedCollection syncPairs : this.syncPairs.values()) { + allSyncPairs.addAll(syncPairs); + } + return allSyncPairs; + } + + public void addSynchronisationPair(String source, String dest, String group) { + this.addSynchronisationPair(new SyncPair(source, dest), group); + } + + public void addSynchronisationPair(SyncPair syncPair, String group) { + addSynchronisationGroup(group); + this.syncPairs.get(group).add(syncPair); + } + + public void removeSynchronisationSet(String source, String dest, String group) { + if (this.syncPairs.containsKey(group)) { + this.syncPairs.get(group).remove(new SyncPair(source, dest)); + } + } + + public void addSynchronisationGroup(String group) { + if (!this.syncPairs.containsKey(group)) { + this.syncPairs.put(group, new LinkedHashSet<>()); + } + } + + public void removeSynchronisationGroup(String group) { + if (this.syncPairs.containsKey(group)) { + this.syncPairs.remove(group); + } + } + + public List gatherSynchronisationGroups() { + return new ArrayList<>(this.syncPairs.keySet()); + } + + public SequencedMap> getSyncPairs() { + return syncPairs; + } + + public void setSyncPairs(SequencedMap> syncPairs) { + this.syncPairs = syncPairs; + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncPair.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncPair.java new file mode 100644 index 0000000..9269e34 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/data/SyncPair.java @@ -0,0 +1,55 @@ +package solutions.reslate.entertainment.spigotresourcesync.data; + +import java.io.Serializable; +import java.util.Objects; + +public class SyncPair implements Serializable { + private String source; + private String destination; + + public SyncPair(String source, String destination) { + super(); + this.source = source; + this.destination = destination; + } + + public SyncPair() { + super(); + } + + public void setSource(String source) { + this.source = source; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public String getSource() { + return source; + } + + public String getDestination() { + return destination; + } + + @Override + public int hashCode() { + return Objects.hash(this.source, this.destination); + } + + @Override + public boolean equals(Object obj) { + if (getClass() != obj.getClass()) { + return false; + } + SyncPair syncPair = (SyncPair) obj; + return this.source.equals(syncPair.source) + && this.destination.equals(syncPair.destination); + } + + @Override + public String toString() { + return String.format("(%s, %s)", this.source, this.destination); + } +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/events/ObjectLoadListener.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/events/ObjectLoadListener.java new file mode 100644 index 0000000..4f2bd70 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/events/ObjectLoadListener.java @@ -0,0 +1,7 @@ +package solutions.reslate.entertainment.spigotresourcesync.events; + +import java.util.EventListener; + +public interface ObjectLoadListener extends EventListener { + void objectLoaded(T obj); +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/JacksonYamlSerialiser.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/JacksonYamlSerialiser.java new file mode 100644 index 0000000..e72653f --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/JacksonYamlSerialiser.java @@ -0,0 +1,68 @@ +package solutions.reslate.entertainment.spigotresourcesync.serialisation; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature; + +public class JacksonYamlSerialiser implements Serialiser { + + private ObjectMapper mapper; + + public JacksonYamlSerialiser() { + super(); + YAMLFactory yamlFactory = new YAMLFactory() + .disable(Feature.WRITE_DOC_START_MARKER); + mapper = new ObjectMapper(yamlFactory) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + @Override + public void flushTo(File file, T serialisable) throws IOException { + try (FileWriter writer = new FileWriter(file)) { + mapper.writeValue(writer, serialisable); + } + } + + @Override + public T load(File file, Class type) throws IOException { + try (FileReader reader = new FileReader(file)) { + return this.load(reader, type); + } + } + + @Override + public T load(Reader reader, Class type) throws IOException { + try { + return mapper.readValue(reader, type); + } catch (IOException e) { + throw e; + } + } + + @Override + public String serialize(T serialisable) { + try { + return mapper.writeValueAsString(serialisable); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Given type cannot be serialised.", e); + } + } + + @Override + public T deserialize(String data, Class type) { + try { + return mapper.readValue(data, type); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Given string cannot be deserialised.", e); + } + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/Serialiser.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/Serialiser.java new file mode 100644 index 0000000..1309102 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/Serialiser.java @@ -0,0 +1,14 @@ +package solutions.reslate.entertainment.spigotresourcesync.serialisation; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; + +public interface Serialiser { + void flushTo(File file, T serialisable) throws IOException; + String serialize(T serialisable); + T load(File file, Class type) throws IOException; + T load(Reader reader, Class type) throws IOException; + T deserialize(String data, Class type); + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/ApacheCommonsIOSynchroniser.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/ApacheCommonsIOSynchroniser.java new file mode 100644 index 0000000..36f0d62 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/ApacheCommonsIOSynchroniser.java @@ -0,0 +1,49 @@ +package solutions.reslate.entertainment.spigotresourcesync.synchronisation; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import org.apache.commons.io.FileUtils; + +public class ApacheCommonsIOSynchroniser implements Synchroniser { + + @Override + public void sync(File source, File dest, File root) throws IOException { + if (root != null) { + source = Path.of(root.getPath(), source.getPath()).toFile(); + source = Path.of(root.getPath(), dest.getPath()).toFile(); + } + + if (source.isDirectory()) { + if (dest.isDirectory()) { + FileUtils.copyDirectory(source, dest); + } else { + throw new IOException("Cannot synchronise directy to non-directory!"); + // TODO add more detail to error message. + } + } else { + if (dest.isDirectory()) { + FileUtils.copyFileToDirectory(source, dest); + } else { + FileUtils.copyFile(source, dest); + } + } + } + + @Override + public void sync(String source, String dest, String root) throws IOException { + this.sync(new File(source), new File(dest), root != null ? new File(root) : null); + } + + @Override + public void sync(File source, File dest) throws IOException { + sync(source, dest, null); + } + + @Override + public void sync(String source, String dest) throws IOException { + sync(source, dest, null); + } + +} diff --git a/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/Synchroniser.java b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/Synchroniser.java new file mode 100644 index 0000000..119ab74 --- /dev/null +++ b/src/main/java/solutions/reslate/entertainment/spigotresourcesync/synchronisation/Synchroniser.java @@ -0,0 +1,11 @@ +package solutions.reslate.entertainment.spigotresourcesync.synchronisation; + +import java.io.File; +import java.io.IOException; + +public interface Synchroniser { + void sync(File source, File dest, File root) throws IOException; + void sync(File source, File dest) throws IOException; + void sync(String source, String dest, String root) throws IOException; + void sync(String source, String dest) throws IOException; +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..5f182e2 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,3 @@ +name: SpigotResourceSync +version: 1.0.0 +main: solutions.reslate.entertainment.spigotresourcesync.SpigotResourceSync \ No newline at end of file diff --git a/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestConfigManager.java b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestConfigManager.java new file mode 100644 index 0000000..8ff6163 --- /dev/null +++ b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestConfigManager.java @@ -0,0 +1,32 @@ +package solutions.reslate.entertainment.spigotresourcesync; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Logger; + +import org.junit.jupiter.api.Test; + +import solutions.reslate.entertainment.spigotresourcesync.data.Configuration; +import solutions.reslate.entertainment.spigotresourcesync.data.SyncPair; +import solutions.reslate.entertainment.spigotresourcesync.serialisation.JacksonYamlSerialiser; +import solutions.reslate.entertainment.spigotresourcesync.serialisation.Serialiser; + +public class TestConfigManager { + @Test + void persistConfiguration() throws IOException { + File dummyConfig = File.createTempFile("dummy_config", ".yml"); + Serialiser dummySerialiser = new JacksonYamlSerialiser<>(); + Logger logger = Logger.getLogger(this.getClass().getName()); + ConfigManager initialConfigManager = new ConfigManager(dummyConfig, dummySerialiser, logger); + SyncPair dummySyncPair = new SyncPair("abc", "def"); + initialConfigManager.getConfiguration().getSyncList().addSynchronisationPair(dummySyncPair, "a"); + initialConfigManager.flush(); + assumeTrue(dummyConfig.exists()); + ConfigManager finalConfigManager = new ConfigManager(dummyConfig, dummySerialiser, logger); + finalConfigManager.load(); + assertEquals(dummySyncPair, finalConfigManager.getConfiguration().getSyncList().gatherAllSyncPairs().get(0)); + } +} diff --git a/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestSyncListManager.java b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestSyncListManager.java new file mode 100644 index 0000000..17d3b8c --- /dev/null +++ b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/TestSyncListManager.java @@ -0,0 +1,10 @@ +package solutions.reslate.entertainment.spigotresourcesync; + +import org.junit.jupiter.api.Test; + +public class TestSyncListManager { + @Test + void testDirectorySync() { + // TODO Write directory sync test. + } +} diff --git a/src/test/java/solutions/reslate/entertainment/spigotresourcesync/data/TestSynchronisationPair.java b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/data/TestSynchronisationPair.java new file mode 100644 index 0000000..aa544ab --- /dev/null +++ b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/data/TestSynchronisationPair.java @@ -0,0 +1,14 @@ +package solutions.reslate.entertainment.spigotresourcesync.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class TestSynchronisationPair { + @Test + void dataEqualsThereforeObjectEquals() { + SyncPair firstPair = new SyncPair("abc", "def"); + SyncPair secondPair = new SyncPair("abc", "def"); + assertEquals(firstPair, secondPair); + } +} diff --git a/src/test/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/TestJacksonYamlSerialiser.java b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/TestJacksonYamlSerialiser.java new file mode 100644 index 0000000..b05b9b4 --- /dev/null +++ b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/serialisation/TestJacksonYamlSerialiser.java @@ -0,0 +1,78 @@ +package solutions.reslate.entertainment.spigotresourcesync.serialisation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.SequencedCollection; +import java.util.SequencedMap; + +import org.junit.jupiter.api.Test; + +import solutions.reslate.entertainment.spigotresourcesync.data.SyncPair; + +public class TestJacksonYamlSerialiser { + @Test + void testListSerialisation() throws IOException { + ArrayList dummyList = new ArrayList<>(); + dummyList.add("abc"); + dummyList.add("def"); + File dummyFile = File.createTempFile("dummy_file", ".yml"); + JacksonYamlSerialiser> dummyJacksonYamlSerialiser = new JacksonYamlSerialiser<>(); + dummyJacksonYamlSerialiser.flushTo(dummyFile, dummyList); + assumeTrue(dummyFile.exists()); + List writtenLines = Files.readAllLines(dummyFile.toPath()); + assertEquals(2, writtenLines.size()); + assertEquals("- \"abc\"", writtenLines.get(0)); + assertEquals("- \"def\"", writtenLines.get(1)); + } + + @Test + void testSyncPairSerialisation() throws IOException { + File dummyFile = File.createTempFile("dummy_file", ".yml"); + SyncPair dummySyncPair = new SyncPair("abc", "def"); + JacksonYamlSerialiser dummyJacksonYamlSerialiser = new JacksonYamlSerialiser<>(); + dummyJacksonYamlSerialiser.flushTo(dummyFile, dummySyncPair); + assumeTrue(dummyFile.exists()); + List writtenLines = Files.readAllLines(dummyFile.toPath()); + assertEquals(2, writtenLines.size()); + assertEquals("source: \"abc\"", writtenLines.get(0)); + assertEquals("destination: \"def\"", writtenLines.get(1)); + } + + @Test + void testSequencedCollectionSerialisation() throws IOException { + File dummyFile = File.createTempFile("dummy_file", ".yml"); + SequencedCollection collection = new LinkedHashSet<>(); + collection.add("abc"); + collection.add("def"); + JacksonYamlSerialiser> dummyJacksonYamlSerialiser = new JacksonYamlSerialiser<>(); + dummyJacksonYamlSerialiser.flushTo(dummyFile, collection); + assumeTrue(dummyFile.exists()); + List writtenLines = Files.readAllLines(dummyFile.toPath()); + assertEquals(2, writtenLines.size()); + assertEquals("- \"abc\"", writtenLines.get(0)); + assertEquals("- \"def\"", writtenLines.get(1)); + } + + @Test + void testSequencedMapSerialisation() throws IOException { + File dummyFile = File.createTempFile("dummy_file", ".yml"); + SequencedMap collection = new LinkedHashMap<>(); + collection.put("abc", "def"); + collection.put("123", "456"); + JacksonYamlSerialiser> dummyJacksonYamlSerialiser = new JacksonYamlSerialiser<>(); + dummyJacksonYamlSerialiser.flushTo(dummyFile, collection); + assumeTrue(dummyFile.exists()); + List writtenLines = Files.readAllLines(dummyFile.toPath()); + assertEquals(2, writtenLines.size()); + assertEquals("abc: \"def\"", writtenLines.get(0)); + assertEquals("\"123\": \"456\"", writtenLines.get(1)); + } +} diff --git a/src/test/java/solutions/reslate/entertainment/spigotresourcesync/synchroniser/TestApacheCommonsIOSynchroniser.java b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/synchroniser/TestApacheCommonsIOSynchroniser.java new file mode 100644 index 0000000..59f0e73 --- /dev/null +++ b/src/test/java/solutions/reslate/entertainment/spigotresourcesync/synchroniser/TestApacheCommonsIOSynchroniser.java @@ -0,0 +1,44 @@ +package solutions.reslate.entertainment.spigotresourcesync.synchroniser; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.Test; + +import solutions.reslate.entertainment.spigotresourcesync.synchronisation.ApacheCommonsIOSynchroniser; + +public class TestApacheCommonsIOSynchroniser { + @Test + void testDir2DirSync() throws IOException { + ApacheCommonsIOSynchroniser dummySynchroniser = new ApacheCommonsIOSynchroniser(); + File dummyDirA = Files.createTempDirectory("dir_A").toFile(); + File dummyDirB = Files.createTempDirectory("dir_B").toFile(); + File dummyFileA = File.createTempFile("file_A", ".txt", dummyDirA); + dummySynchroniser.sync(dummyDirA, dummyDirB); + assertTrue(new File(dummyDirB, dummyFileA.getName()).exists()); + } + + @Test + void testFile2DirSync() throws IOException { + ApacheCommonsIOSynchroniser dummySynchroniser = new ApacheCommonsIOSynchroniser(); + File dummyDirA = Files.createTempDirectory("dir_A").toFile(); + File dummyDirB = Files.createTempDirectory("dir_B").toFile(); + File dummyFileA = File.createTempFile("file_A", ".txt", dummyDirA); + dummySynchroniser.sync(dummyFileA, dummyDirB); + assertTrue(new File(dummyDirB, dummyFileA.getName()).exists()); + } + + @Test + void testFile2FileSync() throws IOException { + ApacheCommonsIOSynchroniser dummySynchroniser = new ApacheCommonsIOSynchroniser(); + File dummyDirA = Files.createTempDirectory("dir_A").toFile(); + File dummyDirB = Files.createTempDirectory("dir_B").toFile(); + File dummyFileA = File.createTempFile("file_A", ".txt", dummyDirA); + File dummyFileB = new File(dummyDirB, "file_B"); + dummySynchroniser.sync(dummyFileA, dummyFileB); + assertTrue(dummyFileB.exists()); + } +}