From 19d415f5f4fe0c7eebb2a783172f3addc6322922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ZX=E5=A4=8F=E5=A4=9C=E4=B9=8B=E9=A3=8E?= Date: Sat, 27 Jul 2024 15:12:08 +0800 Subject: [PATCH] Add ability about loading libraries.json to this library See Minecrell/plugin-yml#54 --- .../byteflux/libby/BukkitLibraryManager.java | 30 ++++++ .../byteflux/libby/BungeeLibraryManager.java | 30 ++++++ core/pom.xml | 9 ++ .../net/byteflux/libby/LibrariesJson.java | 100 ++++++++++++++++++ .../net/byteflux/libby/LibraryManager.java | 23 ++++ .../byteflux/libby/NukkitLibraryManager.java | 30 ++++++ .../byteflux/libby/PaperLibraryManager.java | 30 ++++++ 7 files changed, 252 insertions(+) create mode 100644 core/src/main/java/net/byteflux/libby/LibrariesJson.java diff --git a/bukkit/src/main/java/net/byteflux/libby/BukkitLibraryManager.java b/bukkit/src/main/java/net/byteflux/libby/BukkitLibraryManager.java index 203e50e..221dbcf 100644 --- a/bukkit/src/main/java/net/byteflux/libby/BukkitLibraryManager.java +++ b/bukkit/src/main/java/net/byteflux/libby/BukkitLibraryManager.java @@ -4,6 +4,7 @@ import net.byteflux.libby.logging.adapters.JDKLogAdapter; import org.bukkit.plugin.Plugin; +import java.io.InputStream; import java.net.URLClassLoader; import java.nio.file.Path; @@ -13,10 +14,19 @@ * A runtime dependency manager for Bukkit plugins. */ public class BukkitLibraryManager extends LibraryManager { + /** + * Default libraries.json name (see Minecrell/plugin-yml repository at GitHub) + */ + private static final String DEFAULT_JSON = "bukkit-libraries.json"; + /** * Plugin classpath helper */ private final URLClassLoaderHelper classLoader; + /** + * The plugin + */ + private final Plugin plugin; /** * Creates a new Bukkit library manager. @@ -36,6 +46,7 @@ public BukkitLibraryManager(Plugin plugin) { public BukkitLibraryManager(Plugin plugin, String directoryName) { super(new JDKLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName); classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this); + this.plugin = plugin; } /** @@ -47,4 +58,23 @@ public BukkitLibraryManager(Plugin plugin, String directoryName) { protected void addToClasspath(Path file) { classLoader.addToClasspath(file); } + + /** + * Load the libraries by reading {@link #DEFAULT_JSON} file in the plugin. + */ + public void loadLibrariesInJsonFile() { + loadLibrariesInJsonFile(DEFAULT_JSON); + } + + /** + * Load the libraries by reading provided JSON file in the plugin. + * + * @param resourcePath The path to the JSON file + */ + public void loadLibrariesInJsonFile(String resourcePath) { + final InputStream stream = plugin.getResource(resourcePath); + if (stream != null) { + loadLibrariesInJsonFile(stream); + } + } } diff --git a/bungee/src/main/java/net/byteflux/libby/BungeeLibraryManager.java b/bungee/src/main/java/net/byteflux/libby/BungeeLibraryManager.java index a93db6c..c5c2b09 100644 --- a/bungee/src/main/java/net/byteflux/libby/BungeeLibraryManager.java +++ b/bungee/src/main/java/net/byteflux/libby/BungeeLibraryManager.java @@ -4,6 +4,7 @@ import net.byteflux.libby.logging.adapters.JDKLogAdapter; import net.md_5.bungee.api.plugin.Plugin; +import java.io.InputStream; import java.net.URLClassLoader; import java.nio.file.Path; @@ -13,10 +14,19 @@ * A runtime dependency manager for Bungee plugins. */ public class BungeeLibraryManager extends LibraryManager { + /** + * Default libraries.json name (see Minecrell/plugin-yml repository at GitHub) + */ + private static final String DEFAULT_JSON = "bungee-libraries.json"; + /** * Plugin classpath helper */ private final URLClassLoaderHelper classLoader; + /** + * The plugin + */ + private final Plugin plugin; /** * Creates a new Bungee library manager. @@ -36,6 +46,7 @@ public BungeeLibraryManager(Plugin plugin) { public BungeeLibraryManager(Plugin plugin, String directoryName) { super(new JDKLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName); classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this); + this.plugin = plugin; } /** @@ -47,4 +58,23 @@ public BungeeLibraryManager(Plugin plugin, String directoryName) { protected void addToClasspath(Path file) { classLoader.addToClasspath(file); } + + /** + * Load the libraries by reading {@link #DEFAULT_JSON} file in the plugin. + */ + public void loadLibrariesInJsonFile() { + loadLibrariesInJsonFile(DEFAULT_JSON); + } + + /** + * Load the libraries by reading provided JSON file in the plugin. + * + * @param resourcePath The path to the JSON file + */ + public void loadLibrariesInJsonFile(String resourcePath) { + final InputStream stream = plugin.getResourceAsStream(resourcePath); + if (stream != null) { + loadLibrariesInJsonFile(stream); + } + } } diff --git a/core/pom.xml b/core/pom.xml index 89e6008..f036ba2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -42,4 +42,13 @@ + + + + com.google.code.gson + gson + 2.10.1 + compile + + \ No newline at end of file diff --git a/core/src/main/java/net/byteflux/libby/LibrariesJson.java b/core/src/main/java/net/byteflux/libby/LibrariesJson.java new file mode 100644 index 0000000..8968dfe --- /dev/null +++ b/core/src/main/java/net/byteflux/libby/LibrariesJson.java @@ -0,0 +1,100 @@ +package net.byteflux.libby; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +/** + * A representation of the libraries.json generated by + * Minecrell's plugin-yml gradle plugin.
+ * The instance of this class could convert the loaded JSON into {@link Library} instances, + * and insert the parsed dependencies to the provided {@link LibraryManager}. + * + * @author SNWCreations + */ +public class LibrariesJson { + private static final Pattern QUOTED_DOT; + + static { + QUOTED_DOT = Pattern.compile(Pattern.quote(".")); + } + + private final Map repositories; + private final List libraries; + + public LibrariesJson(Map repositories, List libraries) { + this.repositories = repositories; + this.libraries = libraries; + } + + /** + * Parse the library notations with default settings defined by + * the {@link Library.Builder}. + * + * @return The parsed {@link Library} instances. + * @see #toLibraries(Consumer) + */ + public List toLibraries() { + return toLibraries(unused -> {}); + } + + /** + * Convert the library notations defined by this instance to {@link Library} instances. + * + * @param postBuild Called before building the final {@link Library} instance. + * We could do more modification there to customize the result. + * For example, we could determine if the library is loaded in an + * isolated class loader by calling + * {@link Library.Builder#isolatedLoad(boolean)} in the callback. + * @return The parsed {@link Library} instances. + */ + public List toLibraries(Consumer postBuild) { + final List result = new ArrayList<>(); + for (String notation : libraries) { + final Library.Builder builder = Library.builder(); + // Dependency notation format: https://stackoverflow.com/q/28713154 + final String[] split = QUOTED_DOT.split(notation); + final String group = split[0]; + final String artifact = split[1]; + final String version = split[2]; + builder.groupId(group); + builder.artifactId(artifact); + builder.version(version); + if (split.length > 3) { // we have classifier! + final String finalClassifier; + final String classifier = split[3]; + if (classifier.contains("@")) { // extension detected + final String[] splitClassifier = QUOTED_DOT.split(classifier); + finalClassifier = splitClassifier[0]; + // extension is dropped there as we don't need it, we just need JAR + // and this should never happen + } else { + finalClassifier = classifier; + } + builder.classifier(finalClassifier); + } + // insert repository urls to the built library object + for (String repoUrl : repositories.values()) { + builder.repository(repoUrl); + } + postBuild.accept(builder); // let callback run + final Library builtLibrary = builder.build(); + result.add(builtLibrary); + } + return result; + } + + /** + * Let the provided {@link LibraryManager} load the libraries defined in this instance. + * + * @param loader The {@link LibraryManager} which will be used to load libraries. + */ + public void load(LibraryManager loader) { + final List parsedLibraries = toLibraries(); + for (Library library : parsedLibraries) { + loader.loadLibrary(library); + } + } +} diff --git a/core/src/main/java/net/byteflux/libby/LibraryManager.java b/core/src/main/java/net/byteflux/libby/LibraryManager.java index 9892996..b63c366 100644 --- a/core/src/main/java/net/byteflux/libby/LibraryManager.java +++ b/core/src/main/java/net/byteflux/libby/LibraryManager.java @@ -1,5 +1,6 @@ package net.byteflux.libby; +import com.google.gson.Gson; import net.byteflux.libby.classloader.IsolatedClassLoader; import net.byteflux.libby.logging.LogLevel; import net.byteflux.libby.logging.Logger; @@ -15,16 +16,19 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -60,6 +64,15 @@ * @see Library */ public abstract class LibraryManager { + /** + * Gson object used to deserialize libraries.json + */ + private static final Gson GSON; + + static { + GSON = new Gson(); + } + /** * Wrapped plugin logger */ @@ -593,4 +606,14 @@ public void loadLibrary(Library library) { addToClasspath(file); } } + + protected void loadLibrariesInJsonFile(InputStream stream) { + final LibrariesJson librariesJson; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + librariesJson = GSON.fromJson(reader, LibrariesJson.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + librariesJson.load(this); + } } diff --git a/nukkit/src/main/java/net/byteflux/libby/NukkitLibraryManager.java b/nukkit/src/main/java/net/byteflux/libby/NukkitLibraryManager.java index e6b95bb..126c571 100644 --- a/nukkit/src/main/java/net/byteflux/libby/NukkitLibraryManager.java +++ b/nukkit/src/main/java/net/byteflux/libby/NukkitLibraryManager.java @@ -4,6 +4,7 @@ import net.byteflux.libby.classloader.URLClassLoaderHelper; import net.byteflux.libby.logging.adapters.NukkitLogAdapter; +import java.io.InputStream; import java.net.URLClassLoader; import java.nio.file.Path; @@ -13,10 +14,19 @@ * A runtime dependency manager for Nukkit plugins. */ public class NukkitLibraryManager extends LibraryManager { + /** + * Default libraries.json name (see Minecrell/plugin-yml repository at GitHub) + */ + private static final String DEFAULT_JSON = "nukkit-libraries.json"; + /** * Plugin classpath helper */ private final URLClassLoaderHelper classLoader; + /** + * The plugin + */ + private final Plugin plugin; /** * Creates a new Nukkit library manager. @@ -36,6 +46,7 @@ public NukkitLibraryManager(Plugin plugin) { public NukkitLibraryManager(Plugin plugin, String directoryName) { super(new NukkitLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName); classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this); + this.plugin = plugin; } /** @@ -47,4 +58,23 @@ public NukkitLibraryManager(Plugin plugin, String directoryName) { protected void addToClasspath(Path file) { classLoader.addToClasspath(file); } + + /** + * Load the libraries by reading {@link #DEFAULT_JSON} file in the plugin. + */ + public void loadLibrariesInJsonFile() { + loadLibrariesInJsonFile(DEFAULT_JSON); + } + + /** + * Load the libraries by reading provided JSON file in the plugin. + * + * @param resourcePath The path to the JSON file + */ + public void loadLibrariesInJsonFile(String resourcePath) { + final InputStream stream = plugin.getResource(resourcePath); + if (stream != null) { + loadLibrariesInJsonFile(stream); + } + } } diff --git a/paper/src/main/java/net/byteflux/libby/PaperLibraryManager.java b/paper/src/main/java/net/byteflux/libby/PaperLibraryManager.java index af44cac..980526d 100644 --- a/paper/src/main/java/net/byteflux/libby/PaperLibraryManager.java +++ b/paper/src/main/java/net/byteflux/libby/PaperLibraryManager.java @@ -4,6 +4,7 @@ import net.byteflux.libby.logging.adapters.JDKLogAdapter; import org.bukkit.plugin.Plugin; +import java.io.InputStream; import java.lang.reflect.Field; import java.net.URLClassLoader; import java.nio.file.Path; @@ -15,10 +16,19 @@ * See: Paper docs */ public class PaperLibraryManager extends LibraryManager { + /** + * Default libraries.json name (see Minecrell/plugin-yml repository at GitHub) + */ + private static final String DEFAULT_JSON = "paper-libraries.json"; + /** * Plugin classpath helper */ private final URLClassLoaderHelper classLoader; + /** + * The plugin + */ + private final Plugin plugin; /** * Creates a new Paper library manager. @@ -71,6 +81,7 @@ public PaperLibraryManager(Plugin plugin, String directoryName) { } classLoader = new URLClassLoaderHelper(libraryLoader, this); + this.plugin = plugin; } /** @@ -82,4 +93,23 @@ public PaperLibraryManager(Plugin plugin, String directoryName) { protected void addToClasspath(Path file) { classLoader.addToClasspath(file); } + + /** + * Load the libraries by reading {@link #DEFAULT_JSON} file in the plugin. + */ + public void loadLibrariesInJsonFile() { + loadLibrariesInJsonFile(DEFAULT_JSON); + } + + /** + * Load the libraries by reading provided JSON file in the plugin. + * + * @param resourcePath The path to the JSON file + */ + public void loadLibrariesInJsonFile(String resourcePath) { + final InputStream stream = plugin.getResource(resourcePath); + if (stream != null) { + loadLibrariesInJsonFile(stream); + } + } }