From e3b00d45ec752006b4025043954b0fcf211ae963 Mon Sep 17 00:00:00 2001 From: dfsek Date: Tue, 16 Nov 2021 10:11:44 -0700 Subject: [PATCH] manifest addon loading --- .../manifest-addon-loader/build.gradle.kts | 2 + .../impl/ManifestAddonClassLoader.java | 15 +++++ .../manifest/impl/ManifestAddonLoader.java | 65 ++++++++++++++++++- .../impl/exception/AddonException.java | 11 ++++ .../impl/exception/ManifestException.java | 11 ++++ .../ManifestNotPresentException.java | 11 ++++ 6 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonClassLoader.java create mode 100644 common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/AddonException.java create mode 100644 common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestException.java create mode 100644 common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestNotPresentException.java diff --git a/common/addons/manifest-addon-loader/build.gradle.kts b/common/addons/manifest-addon-loader/build.gradle.kts index 7d82dc72f..2d02cabbf 100644 --- a/common/addons/manifest-addon-loader/build.gradle.kts +++ b/common/addons/manifest-addon-loader/build.gradle.kts @@ -1,2 +1,4 @@ dependencies { + "shadedApi"("commons-io:commons-io:2.6") + "shadedImplementation"("com.dfsek.tectonic:yaml:2.1.2") } diff --git a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonClassLoader.java b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonClassLoader.java new file mode 100644 index 000000000..bfc8fa01c --- /dev/null +++ b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonClassLoader.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.addons.manifest.impl; + +import java.net.URL; +import java.net.URLClassLoader; + + +public class ManifestAddonClassLoader extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + + public ManifestAddonClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } +} diff --git a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonLoader.java b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonLoader.java index fd12f4ea8..3d8e9faa1 100644 --- a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonLoader.java +++ b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/ManifestAddonLoader.java @@ -2,14 +2,27 @@ package com.dfsek.terra.addons.manifest.impl; import ca.solostudios.strata.version.Version; import ca.solostudios.strata.version.VersionRange; +import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.loading.ConfigLoader; +import com.dfsek.tectonic.yaml.YamlConfiguration; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import com.dfsek.terra.addons.manifest.impl.config.AddonManifest; import com.dfsek.terra.addons.manifest.impl.config.WebsiteConfig; import com.dfsek.terra.addons.manifest.impl.config.loaders.VersionLoader; import com.dfsek.terra.addons.manifest.impl.config.loaders.VersionRangeLoader; +import com.dfsek.terra.addons.manifest.impl.exception.AddonException; +import com.dfsek.terra.addons.manifest.impl.exception.ManifestException; +import com.dfsek.terra.addons.manifest.impl.exception.ManifestNotPresentException; import com.dfsek.terra.api.addon.BaseAddon; import com.dfsek.terra.api.addon.bootstrap.BootstrapBaseAddon; @@ -20,8 +33,54 @@ public class ManifestAddonLoader implements BootstrapBaseAddon { ConfigLoader manifestLoader = new ConfigLoader(); manifestLoader.registerLoader(Version.class, new VersionLoader()) .registerLoader(VersionRange.class, new VersionRangeLoader()) - .registerLoader(WebsiteConfig.class, WebsiteConfig::new); - return Collections.emptySet(); + .registerLoader(WebsiteConfig.class, WebsiteConfig::new); + + try { + return Files.walk(addonsFolder, 1) + .filter(path -> path.toFile().isFile() && path.getFileName().endsWith(".jar")) + .flatMap(path -> { + try { + JarFile jar = new JarFile(path.toFile()); + + JarEntry manifestEntry = jar.getJarEntry("terra.addon.yml"); + if(manifestEntry == null) { + throw new ManifestNotPresentException("Addon " + path + " does not contain addon manifest."); + } + + try { + AddonManifest manifest = manifestLoader.load(new AddonManifest(), + new YamlConfiguration(jar.getInputStream(manifestEntry), + "terra.addon.yml")); + + ManifestAddonClassLoader loader = new ManifestAddonClassLoader(new URL[]{ path.toUri().toURL() }, + parent); + + return manifest.getEntryPoints().stream().map(entryPoint -> { + try { + Object in = loader.loadClass(entryPoint).getConstructor().newInstance(); + if(!(in instanceof BaseAddon)) { + throw new AddonException(in.getClass() + " does not extend " + BaseAddon.class); + } + return (BaseAddon) in; + } catch(InvocationTargetException e) { + throw new AddonException("Exception occurred while instantiating addon: ", e); + } catch(NoSuchMethodException | IllegalAccessException | InstantiationException e) { + throw new AddonException("No valid default constructor found in entry point " + entryPoint); + } catch(ClassNotFoundException e) { + throw new AddonException("Entry point " + entryPoint + " not found in JAR."); + } + }); + + } catch(LoadException e) { + throw new ManifestException("Failed to load addon manifest", e); + } + } catch(IOException e) { + throw new UncheckedIOException(e); + } + }).collect(Collectors.toList()); + } catch(IOException e) { + throw new UncheckedIOException(e); + } } @Override diff --git a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/AddonException.java b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/AddonException.java new file mode 100644 index 000000000..03ccbd3a6 --- /dev/null +++ b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/AddonException.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.addons.manifest.impl.exception; + +public class AddonException extends RuntimeException { + public AddonException(String message) { + super(message); + } + + public AddonException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestException.java b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestException.java new file mode 100644 index 000000000..70430a373 --- /dev/null +++ b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestException.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.addons.manifest.impl.exception; + +public class ManifestException extends AddonException{ + public ManifestException(String message) { + super(message); + } + + public ManifestException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestNotPresentException.java b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestNotPresentException.java new file mode 100644 index 000000000..ae54d3dba --- /dev/null +++ b/common/addons/manifest-addon-loader/src/main/java/com/dfsek/terra/addons/manifest/impl/exception/ManifestNotPresentException.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.addons.manifest.impl.exception; + +public class ManifestNotPresentException extends ManifestException{ + public ManifestNotPresentException(String message) { + super(message); + } + + public ManifestNotPresentException(String message, Throwable cause) { + super(message, cause); + } +}