From fdca9af8822fc55a1f6fc4fc9db6321e1a47e8d0 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 00:31:43 +0200 Subject: [PATCH 1/5] Refactor services - Support recursive locators so that Sponge locators can find plugin services. Before that the SPI was unusable by plugins. - Split language service into reader and loader services. - Allow multiple metadata files (e.g. extra file for tests). --- src/main/java/module-info.java | 20 +++- .../plugin/PluginLanguageService.java | 68 ----------- .../spongepowered/plugin/PluginLoader.java | 16 +-- ...LocatorService.java => PluginService.java} | 11 +- .../spongepowered/plugin/blackboard/Keys.java | 11 +- .../plugin/builtin/StandardEnvironment.java | 5 - .../builtin/StandardPluginContainer.java | 27 +++-- .../StandardPluginLanguageService.java | 113 ------------------ .../builtin/StandardPluginMetadataReader.java | 65 ++++++++++ .../plugin/builtin/jvm/JVMPluginResource.java | 25 +--- ...va => ClasspathPluginResourceLocator.java} | 34 ++---- ...va => DirectoryPluginResourceLocator.java} | 45 ++----- ... => EnvironmentPluginResourceLocator.java} | 32 ++--- .../PluginMetadataReader.java} | 31 +++-- .../{ => discovery}/PluginResource.java | 11 +- .../PluginResourceLocator.java} | 37 +++--- .../ResourceLoading.java} | 32 ++++- .../discovery/UnknownResourceStrategy.java | 63 ++++++++++ 18 files changed, 286 insertions(+), 360 deletions(-) delete mode 100644 src/main/java/org/spongepowered/plugin/PluginLanguageService.java rename src/main/java/org/spongepowered/plugin/{builtin/jvm/locator/JVMPluginResourceLocatorService.java => PluginService.java} (81%) delete mode 100644 src/main/java/org/spongepowered/plugin/builtin/StandardPluginLanguageService.java create mode 100644 src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java rename src/main/java/org/spongepowered/plugin/builtin/jvm/locator/{ClasspathPluginResourceLocatorService.java => ClasspathPluginResourceLocator.java} (58%) rename src/main/java/org/spongepowered/plugin/builtin/jvm/locator/{DirectoryPluginResourceLocatorService.java => DirectoryPluginResourceLocator.java} (58%) rename src/main/java/org/spongepowered/plugin/builtin/jvm/locator/{EnvironmentPluginResourceLocatorService.java => EnvironmentPluginResourceLocator.java} (71%) rename src/main/java/org/spongepowered/plugin/{PluginCandidate.java => discovery/PluginMetadataReader.java} (56%) rename src/main/java/org/spongepowered/plugin/{ => discovery}/PluginResource.java (90%) rename src/main/java/org/spongepowered/plugin/{PluginResourceLocatorService.java => discovery/PluginResourceLocator.java} (57%) rename src/main/java/org/spongepowered/plugin/{builtin/StandardPluginCandidate.java => discovery/ResourceLoading.java} (54%) create mode 100644 src/main/java/org/spongepowered/plugin/discovery/UnknownResourceStrategy.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 1e57894..88b3d00 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,3 +1,10 @@ +import org.spongepowered.plugin.builtin.StandardPluginMetadataReader; +import org.spongepowered.plugin.discovery.PluginMetadataReader; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.builtin.jvm.locator.ClasspathPluginResourceLocator; +import org.spongepowered.plugin.builtin.jvm.locator.DirectoryPluginResourceLocator; +import org.spongepowered.plugin.builtin.jvm.locator.EnvironmentPluginResourceLocator; + module org.spongepowered.plugin.spi { requires transitive org.spongepowered.plugin.metadata; requires transitive org.apache.logging.log4j; @@ -7,9 +14,12 @@ exports org.spongepowered.plugin.builtin; exports org.spongepowered.plugin.builtin.jvm; exports org.spongepowered.plugin.builtin.jvm.locator; + exports org.spongepowered.plugin.discovery; + + provides PluginResourceLocator with + ClasspathPluginResourceLocator, + DirectoryPluginResourceLocator, + EnvironmentPluginResourceLocator; - provides org.spongepowered.plugin.PluginResourceLocatorService with - org.spongepowered.plugin.builtin.jvm.locator.ClasspathPluginResourceLocatorService, - org.spongepowered.plugin.builtin.jvm.locator.DirectoryPluginResourceLocatorService, - org.spongepowered.plugin.builtin.jvm.locator.EnvironmentPluginResourceLocatorService; -} \ No newline at end of file + provides PluginMetadataReader with StandardPluginMetadataReader; +} diff --git a/src/main/java/org/spongepowered/plugin/PluginLanguageService.java b/src/main/java/org/spongepowered/plugin/PluginLanguageService.java deleted file mode 100644 index c271f7a..0000000 --- a/src/main/java/org/spongepowered/plugin/PluginLanguageService.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of plugin-spi, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.plugin; - -import java.util.List; - -/** - * A service that processes {@link PluginResource resources} and triggers loading of - * {@link PluginContainer containers} via a specified {@link PluginLoader loader}. - *

- * No class loading should occur at this time - *

- * Implementors are required to have a no-args constructor - */ -public interface PluginLanguageService { - - /** - * @return The {@link String name} - */ - String name(); - - /** - * @return The fully qualified path to the {@link PluginLoader loader} - */ - String pluginLoader(); - - /** - * Callback so that implementors can perform any necessary initialization of the service. - * - * @param environment The environment the service is running under - */ - void initialize(Environment environment); - - /** - * Asks the service if the provided {@link PluginResource resource} can become {@link PluginCandidate candidates}. - *

- * It is left up to implementors on when to throw exceptions (typically from parsing the resource) or to simply return - * {@link List lists} that are empty. Therefore it should be known that an empty list returned is not an error and should - * be discarded by callers of this method. - * - * @param environment The environment - * @param resource The resource - * @return The {@link List candidates} - */ - List createPluginCandidates(Environment environment, PluginResource resource) throws Exception; -} diff --git a/src/main/java/org/spongepowered/plugin/PluginLoader.java b/src/main/java/org/spongepowered/plugin/PluginLoader.java index af9a5f6..f604050 100644 --- a/src/main/java/org/spongepowered/plugin/PluginLoader.java +++ b/src/main/java/org/spongepowered/plugin/PluginLoader.java @@ -25,14 +25,15 @@ package org.spongepowered.plugin; import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.metadata.PluginMetadata; /** - * A loader used to create and load {@link PluginContainer plugins}. + * A service used to create and load {@link PluginContainer plugins}. *

* Implementors of this class are required to have a no-args constructor - * @param

The container type */ -public interface PluginLoader

{ +public interface PluginLoader extends PluginService { /** * @return The {@link ArtifactVersion version}, as a maven artifact @@ -40,13 +41,12 @@ public interface PluginLoader

{ ArtifactVersion version(); /** - * Instructs the loader to load the {@link PluginCandidate candidate} as a {@link PluginContainer container} into the - * target {@link ClassLoader classloader}. + * Loads the plugin candidate as a {@link PluginContainer container}. * * @param environment The environment - * @param candidate The candidate - * @param targetClassLoader The classloader + * @param resource The candidate resource + * @param metadata The candidate metadata * @throws InvalidPluginException If the candidate is invalid */ - P loadPlugin(Environment environment, PluginCandidate candidate, ClassLoader targetClassLoader) throws InvalidPluginException; + PluginContainer loadPlugin(Environment environment, PluginResource resource, PluginMetadata metadata) throws InvalidPluginException; } diff --git a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/JVMPluginResourceLocatorService.java b/src/main/java/org/spongepowered/plugin/PluginService.java similarity index 81% rename from src/main/java/org/spongepowered/plugin/builtin/jvm/locator/JVMPluginResourceLocatorService.java rename to src/main/java/org/spongepowered/plugin/PluginService.java index 420dbd0..8dea61d 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/JVMPluginResourceLocatorService.java +++ b/src/main/java/org/spongepowered/plugin/PluginService.java @@ -22,11 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin.builtin.jvm.locator; +package org.spongepowered.plugin; -import org.spongepowered.plugin.PluginResourceLocatorService; -import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; - -public interface JVMPluginResourceLocatorService extends PluginResourceLocatorService { +public interface PluginService { + /** + * @return The {@link String name} of this service. + */ + String name(); } diff --git a/src/main/java/org/spongepowered/plugin/blackboard/Keys.java b/src/main/java/org/spongepowered/plugin/blackboard/Keys.java index bdc5780..3a1ef25 100644 --- a/src/main/java/org/spongepowered/plugin/blackboard/Keys.java +++ b/src/main/java/org/spongepowered/plugin/blackboard/Keys.java @@ -29,22 +29,13 @@ public final class Keys { - /** - * Indicates whether the target environment is a development environment. - *

- * The implementation may choose to interpret this flag in any number of ways. - * For example, it may disable certain behaviour in a development environment; or - * even change the way it handles some behaviour entirely. - */ - public static final Key DEVELOPMENT = Key.of("development", Boolean.class); - public static final Key VERSION = Key.of("version", String.class); public static final Key BASE_DIRECTORY = Key.of("base_directory", Path.class); public static final Key> PLUGIN_DIRECTORIES = Key.of("plugin_directories", List.class); - public static final Key METADATA_FILE_PATH = Key.of("metadata_file_path", String.class); + public static final Key> METADATA_FILE_PATHS = Key.of("metadata_file_paths", List.class); private Keys() { } diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardEnvironment.java b/src/main/java/org/spongepowered/plugin/builtin/StandardEnvironment.java index 3e56adf..edcd591 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardEnvironment.java +++ b/src/main/java/org/spongepowered/plugin/builtin/StandardEnvironment.java @@ -24,7 +24,6 @@ */ package org.spongepowered.plugin.builtin; -import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.spongepowered.plugin.Environment; import org.spongepowered.plugin.blackboard.Blackboard; @@ -36,10 +35,6 @@ public final class StandardEnvironment implements Environment { private final Logger logger; private final Blackboard blackboard; - public StandardEnvironment() { - this(LogManager.getLogger("plugin")); - } - public StandardEnvironment(final Logger logger) { this.logger = Objects.requireNonNull(logger, "logger"); this.blackboard = new StandardBlackboard(); diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java index 29474de..01d9902 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java +++ b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java @@ -26,8 +26,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.spongepowered.plugin.PluginCandidate; import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.discovery.PluginResource; import org.spongepowered.plugin.metadata.PluginMetadata; import java.net.URI; @@ -37,22 +37,24 @@ public class StandardPluginContainer implements PluginContainer { - private final PluginCandidate candidate; + private final PluginResource resource; + private final PluginMetadata metadata; private final Logger logger; private Object instance; - public StandardPluginContainer(final PluginCandidate candidate) { - this(Objects.requireNonNull(candidate, "candidate"), LogManager.getLogger(candidate.metadata().id())); + public StandardPluginContainer(final PluginResource resource, final PluginMetadata metadata) { + this(resource, metadata, LogManager.getLogger(metadata.id())); } - public StandardPluginContainer(final PluginCandidate candidate, final Logger logger) { - this.candidate = Objects.requireNonNull(candidate, "candidate"); + public StandardPluginContainer(final PluginResource resource, final PluginMetadata metadata, final Logger logger) { + this.resource = Objects.requireNonNull(resource, "resource"); + this.metadata = Objects.requireNonNull(metadata, "metadata"); this.logger = Objects.requireNonNull(logger, "logger"); } @Override public final PluginMetadata metadata() { - return this.candidate.metadata(); + return this.metadata; } @Override @@ -67,20 +69,19 @@ public final Object instance() { protected void initializeInstance(final Object instance) { if (this.instance != null) { - throw new RuntimeException(String.format("Attempt made to set the plugin within container '%s' twice!", - this.candidate.metadata().id())); + throw new RuntimeException(String.format("Attempt made to set the plugin within container '%s' twice!", this.metadata.id())); } this.instance = Objects.requireNonNull(instance, "instance"); } @Override public Optional locateResource(final String path) { - return this.candidate.resource().locateResource(path); + return this.resource.locateResource(path); } @Override public int hashCode() { - return Objects.hashCode(this.candidate.metadata().id()); + return Objects.hashCode(this.metadata.id()); } @Override @@ -93,12 +94,12 @@ public boolean equals(final Object that) { return false; } - return this.candidate.metadata().id().equals(((PluginContainer) that).metadata().id()); + return this.metadata.id().equals(((PluginContainer) that).metadata().id()); } protected StringJoiner toStringJoiner() { return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]") - .add("metadata=" + this.candidate.metadata()); + .add("metadata=" + this.metadata); } @Override diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginLanguageService.java b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginLanguageService.java deleted file mode 100644 index affb840..0000000 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginLanguageService.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of plugin-spi, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.plugin.builtin; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginLanguageService; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.blackboard.Keys; -import org.spongepowered.plugin.metadata.Container; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.MetadataParser; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -public abstract class StandardPluginLanguageService implements PluginLanguageService { - protected final Logger logger = LogManager.getLogger(this.name()); - - @Override - public void initialize(final Environment environment) { - } - - @Override - public List createPluginCandidates(final Environment environment, final PluginResource resource) throws Exception { - Objects.requireNonNull(environment, "environment"); - Objects.requireNonNull(resource, "resource"); - - final String metadataPath = environment.blackboard().get(Keys.METADATA_FILE_PATH); - - final List candidates = new LinkedList<>(); - - final Optional optStream = resource.openResource(metadataPath); - if (optStream.isEmpty()) { - this.logger.debug("Container in path '{}' doesn't have a metadata file, skipping...", resource.path()); - return candidates; - } - - try (final InputStream stream = optStream.get()) { - final Container container = this.loadMetadataContainer(environment, stream); - if (!container.loader().name().equals(this.name())) { - throw new IOException(String.format("Attempt made to load Container in path '%s' with loader '%s' yet it requires '%s'!", - resource.path(), this.name(), container.loader().name())); - } - - if (!this.isValidContainer(environment, container)) { - this.logger.debug("Container in path '{}' with loader '{}' is not valid, skipping...", resource.path(), container.loader().name()); - } else { - boolean containerHasMetadata = false; - for (final PluginMetadata metadata : container.metadata()) { - if (!this.isValidMetadata(environment, metadata)) { - this.logger.debug("PluginMetadata '{}' within Container in path '{}' with loader '{}' is not valid, skipping...", - metadata.id(), resource.path(), container.loader().name()); - continue; - } - containerHasMetadata = true; - candidates.add(new StandardPluginCandidate(metadata, resource)); - } - - if (!containerHasMetadata) { - this.logger.debug("Container in path '{}' with loader '{}' has no plugin metadata, skipping...", resource.path(), - container.loader().name()); - } - } - } - - return candidates; - } - - protected Container loadMetadataContainer(final Environment environment, final InputStream stream) throws Exception { - final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); - return MetadataParser.read(reader); - } - - protected boolean isValidContainer(final Environment environment, final Container container) { - return true; - } - - protected boolean isValidMetadata(final Environment environment, final PluginMetadata metadata) { - return true; - } -} diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java new file mode 100644 index 0000000..ef92a76 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java @@ -0,0 +1,65 @@ +/* + * This file is part of plugin-spi, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.plugin.builtin; + +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.discovery.PluginMetadataReader; +import org.spongepowered.plugin.discovery.PluginResource; +import org.spongepowered.plugin.blackboard.Keys; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.MetadataParser; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public final class StandardPluginMetadataReader implements PluginMetadataReader { + + @Override + public String name() { + return "standard"; + } + + @Override + public Collection readPluginMetadata(final Environment environment, final PluginResource resource, final List locators) throws Exception { + final Set result = new LinkedHashSet<>(); + for (final String metadataPath : environment.blackboard().get(Keys.METADATA_FILE_PATHS)) { + final Optional stream = resource.openResource(metadataPath); + if (stream.isPresent()) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream.get(), StandardCharsets.UTF_8))) { + result.addAll(MetadataParser.read(reader).metadata()); + } + } + } + return result; + } +} diff --git a/src/main/java/org/spongepowered/plugin/builtin/jvm/JVMPluginResource.java b/src/main/java/org/spongepowered/plugin/builtin/jvm/JVMPluginResource.java index 208cbb7..533a6cb 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/jvm/JVMPluginResource.java +++ b/src/main/java/org/spongepowered/plugin/builtin/jvm/JVMPluginResource.java @@ -25,10 +25,8 @@ package org.spongepowered.plugin.builtin.jvm; import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.PluginResource; +import org.spongepowered.plugin.discovery.PluginResource; -import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; @@ -38,40 +36,25 @@ public interface JVMPluginResource extends PluginResource { Manifest manifest(); - /** - * Retrieves the root path containing all the resources. - * This may or may not be the root of the file system containing the resources. - * - * @return The root path containing all the resources. - */ - Path resourcesRoot(); - @Override default Optional property(final String key) { return Optional.ofNullable(this.manifest().getMainAttributes().getValue(Objects.requireNonNull(key, "key"))); } - @Override - default Optional locateResource(final String path) { - final Path p = this.resourcesRoot().resolve(path); - return Files.exists(p) ? Optional.of(p.toUri()) : Optional.empty(); - } - /** * Creates a {@link JVMPluginResource} from an array of paths. * Each path can point to a JAR file or a directory. * When multiple paths are given, the resulting resource is a virtual union of all files contained by these paths. * * @param environment The environment - * @param locator The locator that created this resource * @param paths The paths * @return The {@link JVMPluginResource}. */ - static JVMPluginResource create(final Environment environment, final String locator, final Path... paths) { - return environment.blackboard().get(JVMKeys.JVM_PLUGIN_RESOURCE_FACTORY).create(locator, paths); + static JVMPluginResource create(final Environment environment, final Path... paths) { + return environment.blackboard().get(JVMKeys.JVM_PLUGIN_RESOURCE_FACTORY).create(paths); } interface Factory { - JVMPluginResource create(final String locator, final Path[] paths); + JVMPluginResource create(final Path[] paths); } } diff --git a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocatorService.java b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocator.java similarity index 58% rename from src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocatorService.java rename to src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocator.java index cb00ac9..daa8b9a 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocatorService.java +++ b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/ClasspathPluginResourceLocator.java @@ -25,19 +25,17 @@ package org.spongepowered.plugin.builtin.jvm.locator; import org.spongepowered.plugin.Environment; -import org.spongepowered.plugin.blackboard.Keys; import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.Set; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; -public final class ClasspathPluginResourceLocatorService implements JVMPluginResourceLocatorService { +public final class ClasspathPluginResourceLocator implements PluginResourceLocator { @Override public String name() { @@ -45,34 +43,16 @@ public String name() { } @Override - public Set locatePluginResources(final Environment environment) { - environment.logger().info("Locating '{}' resources...", this.name()); - - final String metadataPath = environment.blackboard().get(Keys.METADATA_FILE_PATH); + public Set locatePluginResources(final Environment environment) { final String[] cp = System.getProperty("java.class.path").split(File.pathSeparator); - final Set resources = new HashSet<>(); - + final Set results = new HashSet<>(); for (final String str : cp) { if (str.endsWith(".jar")) { final Path path = Paths.get(str); - - try (final JarFile jar = new JarFile(path.toFile())) { - final ZipEntry metadataEntry = jar.getEntry(metadataPath); - if (metadataEntry == null) { - environment.logger().debug("'{}' does not contain any plugin metadata so it is not a plugin. Skipping...", str); - continue; - } - - resources.add(JVMPluginResource.create(environment, this.name(), path)); - } catch (final IOException e) { - environment.logger().error("Error reading '{}' as a Jar file. Skipping...", path, e); - } + results.add(new Result(JVMPluginResource.create(environment, path), UnknownResourceStrategy.IGNORE)); } } - - environment.logger().info("Located [{}] resource(s) for '{}'...", resources.size(), this.name()); - - return resources; + return results; } } diff --git a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocatorService.java b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocator.java similarity index 58% rename from src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocatorService.java rename to src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocator.java index 32264ef..0c4d09d 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocatorService.java +++ b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/DirectoryPluginResourceLocator.java @@ -27,19 +27,21 @@ import org.spongepowered.plugin.Environment; import org.spongepowered.plugin.blackboard.Keys; import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.util.stream.Stream; -public final class DirectoryPluginResourceLocatorService implements JVMPluginResourceLocatorService { +public final class DirectoryPluginResourceLocator implements PluginResourceLocator { @Override public String name() { @@ -47,49 +49,28 @@ public String name() { } @Override - public Set locatePluginResources(final Environment environment) { + public Collection locatePluginResources(final Environment environment) { final Optional> dirs = environment.blackboard().find(Keys.PLUGIN_DIRECTORIES); if (dirs.isEmpty()) { environment.logger().debug("Locator '{}' is disabled.", this.name()); return Collections.emptySet(); } - environment.logger().info("Locating '{}' resources...", this.name()); - - final Set resources = new HashSet<>(); - final String metadataPath = environment.blackboard().get(Keys.METADATA_FILE_PATH); - + final Set results = new HashSet<>(); for (final Path pluginsDir : dirs.get()) { if (Files.notExists(pluginsDir)) { - environment.logger().debug("Plugin directory '{}' does not exist for locator '{}'. Skipping...", pluginsDir, this.name()); + environment.logger().debug("Plugin directory '{}' does not exist. Skipping...", pluginsDir); continue; } - try { - for (final Path path : Files.walk(pluginsDir).toList()) { - if (!Files.isRegularFile(path) || !path.getFileName().toString().endsWith(".jar")) { - continue; - } - - try (final JarFile jf = new JarFile(path.toFile())) { - final JarEntry pluginMetadataJarEntry = jf.getJarEntry(metadataPath); - if (pluginMetadataJarEntry == null) { - environment.logger().debug("'{}' does not contain any plugin metadata so it is not a plugin. Skipping...", path); - continue; - } - - resources.add(JVMPluginResource.create(environment, this.name(), path)); - } catch (final IOException e) { - environment.logger().error("Error reading '{}' as a Jar file when traversing directory resources for plugin discovery! Skipping...", path, e); - } - } + try (final Stream st = Files.walk(pluginsDir)) { + st.filter(path -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")) + .map(path -> new Result(JVMPluginResource.create(environment, path), UnknownResourceStrategy.WARN)) + .forEach(results::add); } catch (final IOException ex) { environment.logger().error("Error walking plugins directory {}", pluginsDir, ex); } } - - environment.logger().info("Located [{}] resource(s) for '{}'...", resources.size(), this.name()); - - return resources; + return results; } } diff --git a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocatorService.java b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocator.java similarity index 71% rename from src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocatorService.java rename to src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocator.java index 7e5cacf..9afbe72 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocatorService.java +++ b/src/main/java/org/spongepowered/plugin/builtin/jvm/locator/EnvironmentPluginResourceLocator.java @@ -27,15 +27,19 @@ import org.spongepowered.plugin.Environment; import org.spongepowered.plugin.builtin.jvm.JVMKeys; import org.spongepowered.plugin.builtin.jvm.JVMPluginResource; +import org.spongepowered.plugin.discovery.PluginResourceLocator; +import org.spongepowered.plugin.discovery.UnknownResourceStrategy; +import java.io.File; import java.nio.file.Path; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -public final class EnvironmentPluginResourceLocatorService implements JVMPluginResourceLocatorService { +public final class EnvironmentPluginResourceLocator implements PluginResourceLocator { @Override public String name() { @@ -43,30 +47,26 @@ public String name() { } @Override - public Set locatePluginResources(final Environment environment) { + public Collection locatePluginResources(final Environment environment) { final Optional envName = environment.blackboard().find(JVMKeys.ENVIRONMENT_LOCATOR_VARIABLE_NAME); if (envName.isEmpty()) { environment.logger().debug("Locator '{}' is disabled.", this.name()); return Collections.emptySet(); } - environment.logger().info("Locating '{}' resources...", this.name()); - - final Set resources = new HashSet<>(); final String env = System.getenv(envName.get()); - if (env != null) { - for (final String entry : env.split(";")) { - if (entry.isBlank()) { - continue; - } + if (env == null) { + return Collections.emptySet(); + } - final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); - resources.add(JVMPluginResource.create(environment, this.name(), paths)); + final Set results = new HashSet<>(); + for (final String entry : env.split(File.pathSeparator)) { + if (entry.isBlank()) { + continue; } + final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new); + results.add(new Result(JVMPluginResource.create(environment, paths), UnknownResourceStrategy.WARN)); } - - environment.logger().info("Located [{}] resource(s) for '{}'...", resources.size(), this.name()); - - return resources; + return results; } } diff --git a/src/main/java/org/spongepowered/plugin/PluginCandidate.java b/src/main/java/org/spongepowered/plugin/discovery/PluginMetadataReader.java similarity index 56% rename from src/main/java/org/spongepowered/plugin/PluginCandidate.java rename to src/main/java/org/spongepowered/plugin/discovery/PluginMetadataReader.java index 2b54247..1c7ad18 100644 --- a/src/main/java/org/spongepowered/plugin/PluginCandidate.java +++ b/src/main/java/org/spongepowered/plugin/discovery/PluginMetadataReader.java @@ -22,23 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin; +package org.spongepowered.plugin.discovery; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginService; import org.spongepowered.plugin.metadata.PluginMetadata; +import java.util.Collection; +import java.util.List; + /** - * Represents the combination of a {@link PluginMetadata metadata} and {@link PluginResource resource} - * that will be a candidate for a {@link PluginContainer container}. + * A service used to read {@link PluginMetadata metadata} from {@link PluginResource resources}. + *

+ * Implementors are required to have a no-args constructor + *

+ * Jars declaring this service are loaded in an isolated classloader. + * They can only access the plugin-spi and cannot be accessed by plugins. */ -public interface PluginCandidate { - - /** - * @return The {@link PluginMetadata metadata} - */ - PluginMetadata metadata(); +public interface PluginMetadataReader extends PluginService { /** - * @return The {@link PluginResource resource} + * Reads a list of {@link PluginMetadata plugin metadata} from the given {@link PluginResource resource}. + * + * @param environment The environment + * @param resource The resource + * @param locators The services that located the resource + * @return The {@link List candidates} */ - PluginResource resource(); + Collection readPluginMetadata(Environment environment, PluginResource resource, List locators) throws Exception; } diff --git a/src/main/java/org/spongepowered/plugin/PluginResource.java b/src/main/java/org/spongepowered/plugin/discovery/PluginResource.java similarity index 90% rename from src/main/java/org/spongepowered/plugin/PluginResource.java rename to src/main/java/org/spongepowered/plugin/discovery/PluginResource.java index cb763ca..edbb30d 100644 --- a/src/main/java/org/spongepowered/plugin/PluginResource.java +++ b/src/main/java/org/spongepowered/plugin/discovery/PluginResource.java @@ -22,21 +22,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin; +package org.spongepowered.plugin.discovery; + +import org.spongepowered.plugin.ResourceQueryable; import java.nio.file.Path; import java.util.Optional; /** - * Represents a resource provided by a {@link PluginResourceLocatorService locator}. + * Represents a resource provided by a {@link PluginResourceLocator locator}. */ public interface PluginResource extends ResourceQueryable { - /** - * @return The name of the {@link PluginResourceLocatorService service} that located this resource - */ - String locator(); - /** * @return The path where this resource originates from */ diff --git a/src/main/java/org/spongepowered/plugin/PluginResourceLocatorService.java b/src/main/java/org/spongepowered/plugin/discovery/PluginResourceLocator.java similarity index 57% rename from src/main/java/org/spongepowered/plugin/PluginResourceLocatorService.java rename to src/main/java/org/spongepowered/plugin/discovery/PluginResourceLocator.java index 5f980c7..c2328c1 100644 --- a/src/main/java/org/spongepowered/plugin/PluginResourceLocatorService.java +++ b/src/main/java/org/spongepowered/plugin/discovery/PluginResourceLocator.java @@ -22,29 +22,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin; +package org.spongepowered.plugin.discovery; -import java.util.Set; +import org.spongepowered.plugin.Environment; +import org.spongepowered.plugin.PluginService; + +import java.util.Collection; +import java.util.Objects; /** - * A service used to find {@link PluginResource resources} to be processed by - * {@link PluginLanguageService language services} or other implementation constructs. + * A service used to find {@link PluginResource resources} to be processed by {@link PluginMetadataReader readers}. *

- * No class loading should occur at this time. + * The service may also return resources that contain no metadata but provide other {@link PluginResourceLocator locators} or {@link PluginMetadataReader readers}. *

* Implementors of this class are required to have a no-args constructor. - * @param

The resource type + *

+ * Jars declaring this service are loaded in an isolated classloader. + * They can only access the plugin-spi and cannot be accessed by plugins. */ -public interface PluginResourceLocatorService

{ - - /** - * @return The {@link String name} - */ - String name(); +public interface PluginResourceLocator extends PluginService { /** + * Locates plugin resources. + * * @param environment The environment - * @return The {@link PluginResource resources} as an unmodifiable {@link Set} + * @return The {@link PluginResource resources} */ - Set

locatePluginResources(Environment environment); + Collection locatePluginResources(Environment environment) throws Exception; + + record Result(PluginResource resource, UnknownResourceStrategy unknownResourceStrategy) { + public Result { + Objects.requireNonNull(resource, "resource"); + Objects.requireNonNull(unknownResourceStrategy, "unknownResourceStrategy"); + } + } } diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginCandidate.java b/src/main/java/org/spongepowered/plugin/discovery/ResourceLoading.java similarity index 54% rename from src/main/java/org/spongepowered/plugin/builtin/StandardPluginCandidate.java rename to src/main/java/org/spongepowered/plugin/discovery/ResourceLoading.java index 81447d9..a47bd7b 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginCandidate.java +++ b/src/main/java/org/spongepowered/plugin/discovery/ResourceLoading.java @@ -22,11 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin.builtin; +package org.spongepowered.plugin.discovery; -import org.spongepowered.plugin.PluginCandidate; -import org.spongepowered.plugin.PluginResource; -import org.spongepowered.plugin.metadata.PluginMetadata; +public enum ResourceLoading { + /** + * The resource doesn't need to be loaded. + * The resource will not be loaded unless another locator provided a different loading strategy. + */ + IGNORED, + /** + * Assuming the resource is a jar, it will be added to a classloader and accessible by plugins. + * The library doesn't need to access the game or to be class-transformable. + */ + LIBRARY, + /** + * Assuming the resource is a jar, it will be added to a classloader and accessible by plugins. + * The library needs access to the game or to be class-transformable. + */ + GAME_LIBRARY; -public record StandardPluginCandidate(PluginMetadata metadata, PluginResource resource) implements PluginCandidate { + /** + * Assuming two locators returned different loading strategies, + * this will return a strategy that can satisfy both needs. + * + * @param other The other strategy + * @return The resulting strategy + */ + public ResourceLoading merge(final ResourceLoading other) { + return this.ordinal() > other.ordinal() ? this : other; + } } diff --git a/src/main/java/org/spongepowered/plugin/discovery/UnknownResourceStrategy.java b/src/main/java/org/spongepowered/plugin/discovery/UnknownResourceStrategy.java new file mode 100644 index 0000000..92acd9f --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/discovery/UnknownResourceStrategy.java @@ -0,0 +1,63 @@ +/* + * This file is part of plugin-spi, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.plugin.discovery; + +import java.util.Objects; + +/** + * Defines what happen to a resource that has not been recognized by any {@link PluginMetadataReader readers} + * and does not contain {@link PluginResourceLocator locators} or {@link PluginMetadataReader readers}. + * + * @param warn Whether the platform should warn when the associated resource is not recognized. + * @param loading The loading strategy to apply when the associated resource is not recognized. + */ +public record UnknownResourceStrategy(boolean warn, ResourceLoading loading) { + + public UnknownResourceStrategy { + Objects.requireNonNull(loading, "loading"); + } + + /** + * The unknown resource will be ignored and a warning logged. + * The default strategy most locators should stick to. + */ + public static final UnknownResourceStrategy WARN = new UnknownResourceStrategy(true, ResourceLoading.IGNORED); + + /** + * The unknown resource will be ignored silently. + */ + public static final UnknownResourceStrategy IGNORE = new UnknownResourceStrategy(false, ResourceLoading.IGNORED); + + /** + * Assuming two locators returned different strategies, + * this will return a strategy that can satisfy both needs. + * + * @param other The other strategy + * @return The resulting strategy + */ + public UnknownResourceStrategy merge(final UnknownResourceStrategy other) { + return new UnknownResourceStrategy(this.warn || other.warn, this.loading.merge(other.loading)); + } +} From e1564434a0430e406aead152d80908cd2a639cd6 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sat, 6 Jun 2026 09:19:45 +0200 Subject: [PATCH 2/5] Update workflows Java version to 21 --- .github/workflows/check-spotless.yaml | 4 ++-- .github/workflows/ci.yaml | 2 +- .github/workflows/codeql-analysis.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-spotless.yaml b/.github/workflows/check-spotless.yaml index 314367a..8ff8b99 100644 --- a/.github/workflows/check-spotless.yaml +++ b/.github/workflows/check-spotless.yaml @@ -1,4 +1,4 @@ -name: License Check +name: Spotless Check on: push: @@ -10,5 +10,5 @@ jobs: call-check: uses: SpongePowered/.github/.github/workflows/shared-check-spotless.yaml@master with: - runtime_version: 17 + runtime_version: 21 secrets: inherit diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4cdac87..ce0b4ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,6 @@ jobs: call-build: uses: SpongePowered/.github/.github/workflows/shared-ci.yaml@master with: - runtime_version: 17 + runtime_version: 21 extra_gradle_publish_params: closeAndReleaseSonatypeStagingRepository secrets: inherit diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 647c7b4..8f2341e 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -10,5 +10,5 @@ jobs: call-analyze: uses: SpongePowered/.github/.github/workflows/shared-codeql-analysis.yaml@master with: - runtime_version: 17 + runtime_version: 21 secrets: inherit From 875f66c0cc3640bbda91ce83be8a5c1f1d8d18b6 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 7 Jun 2026 08:37:32 +0200 Subject: [PATCH 3/5] Remove InvalidPluginException --- .../plugin/InvalidPluginException.java | 45 ------------------- .../spongepowered/plugin/PluginLoader.java | 4 +- 2 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 src/main/java/org/spongepowered/plugin/InvalidPluginException.java diff --git a/src/main/java/org/spongepowered/plugin/InvalidPluginException.java b/src/main/java/org/spongepowered/plugin/InvalidPluginException.java deleted file mode 100644 index f6cb1eb..0000000 --- a/src/main/java/org/spongepowered/plugin/InvalidPluginException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of plugin-spi, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.plugin; - -/** - * Thrown when a plugin cannot be constructed and should be considered invalid. - */ -public final class InvalidPluginException extends Exception { - - private static final long serialVersionUID = -9198957064244669388L; - - public InvalidPluginException(final String message) { - super(message); - } - - public InvalidPluginException(final String message, final Throwable cause) { - super(message, cause); - } - - public InvalidPluginException(final Throwable cause) { - super(cause); - } -} diff --git a/src/main/java/org/spongepowered/plugin/PluginLoader.java b/src/main/java/org/spongepowered/plugin/PluginLoader.java index f604050..da7af8a 100644 --- a/src/main/java/org/spongepowered/plugin/PluginLoader.java +++ b/src/main/java/org/spongepowered/plugin/PluginLoader.java @@ -46,7 +46,7 @@ public interface PluginLoader extends PluginService { * @param environment The environment * @param resource The candidate resource * @param metadata The candidate metadata - * @throws InvalidPluginException If the candidate is invalid + * @return The {@link PluginContainer container} */ - PluginContainer loadPlugin(Environment environment, PluginResource resource, PluginMetadata metadata) throws InvalidPluginException; + PluginContainer loadPlugin(Environment environment, PluginResource resource, PluginMetadata metadata) throws Exception; } From 5a38ec7508fe0ee41cc801966957c72028692d4d Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sat, 20 Jun 2026 17:34:38 +0200 Subject: [PATCH 4/5] Bump plugin-meta to 0.9.0-SNAPSHOT --- build.gradle.kts | 5 ++--- .../plugin/builtin/StandardPluginMetadataReader.java | 12 +++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2a6ec63..8ffab55 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,7 +39,7 @@ tasks { links( "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", "https://checkerframework.org/api/", - "https://maven.apache.org/ref/3.8.6/maven-artifact/apidocs", + "https://maven.apache.org/ref/3.9.16/maven-artifact/apidocs", "https://jd.spongepowered.org/plugin-meta/0.8.2/" ) } @@ -75,8 +75,7 @@ indraCrossdoc { } dependencies { - api("org.spongepowered:plugin-meta:0.8.2") - api("org.apache.maven:maven-artifact:3.8.6") + api("org.spongepowered:plugin-meta:0.9.0-SNAPSHOT") api("org.apache.logging.log4j:log4j-api:2.17.0") compileOnlyApi("org.checkerframework:checker-qual:3.26.0") } diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java index ef92a76..61336ad 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java +++ b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginMetadataReader.java @@ -37,10 +37,10 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Collection; -import java.util.LinkedHashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; -import java.util.Set; public final class StandardPluginMetadataReader implements PluginMetadataReader { @@ -51,15 +51,17 @@ public String name() { @Override public Collection readPluginMetadata(final Environment environment, final PluginResource resource, final List locators) throws Exception { - final Set result = new LinkedHashSet<>(); + final Map result = new LinkedHashMap<>(); for (final String metadataPath : environment.blackboard().get(Keys.METADATA_FILE_PATHS)) { final Optional stream = resource.openResource(metadataPath); if (stream.isPresent()) { try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream.get(), StandardCharsets.UTF_8))) { - result.addAll(MetadataParser.read(reader).metadata()); + for (final PluginMetadata metadata : MetadataParser.read(reader).plugins()) { + result.put(metadata.id(), metadata); + } } } } - return result; + return result.values(); } } From 1bf891868394157836c55ed7be41855b3cb6c096 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 28 Jun 2026 22:38:32 +0200 Subject: [PATCH 5/5] Deprecate PluginContainer#instance --- .../org/spongepowered/plugin/PluginContainer.java | 5 ++++- .../plugin/builtin/StandardPluginContainer.java | 13 ------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/PluginContainer.java b/src/main/java/org/spongepowered/plugin/PluginContainer.java index 91a7594..9a79d17 100644 --- a/src/main/java/org/spongepowered/plugin/PluginContainer.java +++ b/src/main/java/org/spongepowered/plugin/PluginContainer.java @@ -48,5 +48,8 @@ public interface PluginContainer extends ResourceQueryable { /** * @return The instance */ - Object instance(); + @Deprecated(forRemoval = true, since = "0.5.0") + default Object instance() { + return this; + } } diff --git a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java index 01d9902..1bbe20b 100644 --- a/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java +++ b/src/main/java/org/spongepowered/plugin/builtin/StandardPluginContainer.java @@ -40,7 +40,6 @@ public class StandardPluginContainer implements PluginContainer { private final PluginResource resource; private final PluginMetadata metadata; private final Logger logger; - private Object instance; public StandardPluginContainer(final PluginResource resource, final PluginMetadata metadata) { this(resource, metadata, LogManager.getLogger(metadata.id())); @@ -62,18 +61,6 @@ public final Logger logger() { return this.logger; } - @Override - public final Object instance() { - return this.instance; - } - - protected void initializeInstance(final Object instance) { - if (this.instance != null) { - throw new RuntimeException(String.format("Attempt made to set the plugin within container '%s' twice!", this.metadata.id())); - } - this.instance = Objects.requireNonNull(instance, "instance"); - } - @Override public Optional locateResource(final String path) { return this.resource.locateResource(path);