Skip to content

Commit

Permalink
Builtin JVMPluginResource must be implemented by the platform
Browse files Browse the repository at this point in the history
  • Loading branch information
Yeregorix committed Aug 28, 2024
1 parent 56ce009 commit 0d64d1a
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 395 deletions.
8 changes: 6 additions & 2 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
module org.spongepowered.plugin.spi {
requires transitive org.spongepowered.plugin.metadata;
requires transitive org.apache.logging.log4j;

exports org.spongepowered.plugin;
exports org.spongepowered.plugin.blackboard;
exports org.spongepowered.plugin.builtin;
exports org.spongepowered.plugin.builtin.jvm;
exports org.spongepowered.plugin.builtin.jvm.locator;

requires transitive org.spongepowered.plugin.metadata;
requires transitive org.apache.logging.log4j;
provides org.spongepowered.plugin.PluginResourceLocatorService with
org.spongepowered.plugin.builtin.jvm.locator.DirectoryPluginResourceLocatorService,
org.spongepowered.plugin.builtin.jvm.locator.EnvironmentPluginResourceLocatorService;
}
15 changes: 7 additions & 8 deletions src/main/java/org/spongepowered/plugin/ResourceQueryable.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;

/**
Expand All @@ -36,21 +35,21 @@
public interface ResourceQueryable {

/**
* Resolves the location of a bundled resource, given a relative {@link URI}.
* Resolves the location of a bundled resource, given a relative path.
*
* @param relative The relative URI
* @param path The relative path
* @return The resolved resource location, if available
*/
Optional<URI> locateResource(URI relative);
Optional<URI> locateResource(final String path);

/**
* Opens an {@link InputStream} of the location of a bundled resource, given a relative {@link URI}.
* Opens an {@link InputStream} of the location of a bundled resource, given a relative path.
*
* @param relative The relative URI
* @param path The relative path
* @return The opened resource, if available
*/
default Optional<InputStream> openResource(final URI relative) {
return this.locateResource(Objects.requireNonNull(relative, "relative")).flatMap(url -> {
default Optional<InputStream> openResource(final String path) {
return this.locateResource(path).flatMap(url -> {
try {
return Optional.of(url.toURL().openStream());
} catch (final IOException ignored) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/spongepowered/plugin/blackboard/Keys.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public final class Keys {

public static final Key<List<Path>> PLUGIN_DIRECTORIES = Key.of("plugin_directories", List.class);

public static final Key<String> METADATA_FILE_PATH = Key.of("metadata_file_path", String.class);

private Keys() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,31 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.plugin.builtin.jvm;
package org.spongepowered.plugin.builtin;

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.builtin.jvm.locator.JVMPluginResource;
import org.spongepowered.plugin.PluginResource;
import org.spongepowered.plugin.metadata.PluginMetadata;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

public class JVMPluginContainer implements PluginContainer {
public class StandardPluginContainer implements PluginContainer {

private final PluginCandidate<JVMPluginResource> candidate;
private final PluginCandidate<? extends PluginResource> candidate;
private final Logger logger;
private Object instance;

public JVMPluginContainer(final PluginCandidate<JVMPluginResource> candidate) {
public StandardPluginContainer(final PluginCandidate<? extends PluginResource> candidate) {
this(Objects.requireNonNull(candidate, "candidate"), LogManager.getLogger(candidate.metadata().id()));
}

public JVMPluginContainer(final PluginCandidate<JVMPluginResource> candidate, final Logger logger) {
public StandardPluginContainer(final PluginCandidate<? extends PluginResource> candidate, final Logger logger) {
this.candidate = Objects.requireNonNull(candidate, "candidate");
this.logger = Objects.requireNonNull(logger, "logger");
}
Expand Down Expand Up @@ -77,19 +75,8 @@ protected void initializeInstance(final Object instance) {
}

@Override
public Optional<URI> locateResource(final URI relative) {
Objects.requireNonNull(relative, "relative");

final ClassLoader classLoader = this.instance().getClass().getClassLoader();
final URL resolved = classLoader.getResource(relative.getPath());
try {
if (resolved == null) {
return Optional.empty();
}
return Optional.of(resolved.toURI());
} catch (final URISyntaxException ignored) {
return Optional.empty();
}
public Optional<URI> locateResource(final String path) {
return this.candidate.resource().locateResource(path);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,52 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.plugin.builtin.jvm;
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.builtin.StandardPluginCandidate;
import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource;
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.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.Optional;

public abstract class JVMPluginLanguageService implements PluginLanguageService<JVMPluginResource> {

private final Logger logger = LogManager.getLogger(this.name());
public abstract class StandardPluginLanguageService implements PluginLanguageService<PluginResource> {
protected final Logger logger = LogManager.getLogger(this.name());

@Override
public void initialize(final Environment environment) {
}

@Override
public final List<PluginCandidate<JVMPluginResource>> createPluginCandidates(final Environment environment,
final JVMPluginResource resource) throws Exception {
public List<PluginCandidate<PluginResource>> createPluginCandidates(final Environment environment, final PluginResource resource) throws Exception {
Objects.requireNonNull(environment, "environment");
Objects.requireNonNull(resource, "resource");

final String metadataPath = environment.blackboard().get(JVMKeys.METADATA_FILE_PATH);
final String metadataPath = environment.blackboard().get(Keys.METADATA_FILE_PATH);

final List<PluginCandidate<PluginResource>> candidates = new LinkedList<>();

final List<PluginCandidate<JVMPluginResource>> candidates = new LinkedList<>();
final Optional<InputStream> optStream = resource.openResource(metadataPath);
if (!optStream.isPresent()) {
this.logger.debug("Container in path '{}' doesn't have a metadata file, skipping...", resource.path());
return candidates;
}

try (final InputStream stream = this.getFileAsStream(resource.path(), metadataPath)) {
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'!",
Expand Down Expand Up @@ -96,31 +98,16 @@ public final List<PluginCandidate<JVMPluginResource>> createPluginCandidates(fin
return candidates;
}

public abstract Container loadMetadataContainer(final Environment environment, final InputStream stream) throws Exception;
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);
}

private boolean isValidContainer(final Environment environment, final Container container) {
protected boolean isValidContainer(final Environment environment, final Container container) {
return true;
}

protected boolean isValidMetadata(final Environment environment, final PluginMetadata metadata) {
return true;
}

private InputStream getFileAsStream(final Path rootDirectory, final String relativePath) throws URISyntaxException, IOException {
final URI uri = rootDirectory.toUri();
Path jarFile = null;
if (uri.getRawSchemeSpecificPart().contains("!")) {
jarFile = Paths.get(new URI(uri.getRawSchemeSpecificPart().split("!")[0]));
} else if (rootDirectory.toString().endsWith(".jar")) {
jarFile = rootDirectory;
}

if (jarFile != null) {
final JarFile jf = new JarFile(jarFile.toFile());
final JarEntry entry = jf.getJarEntry(relativePath);
return jf.getInputStream(entry);
} else {
return Files.newInputStream(rootDirectory.resolve(relativePath));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@

public final class JVMKeys {

public static final Key<String> METADATA_FILE_PATH = Key.of("metadata_file_path", String.class);
public static final Key<String> ENVIRONMENT_LOCATOR_VARIABLE_NAME = Key.of("environment_locator_variable_name", String.class);

public static final Key<JVMPluginResource.Factory> JVM_PLUGIN_RESOURCE_FACTORY = Key.of("jvm_plugin_resource_factory", JVMPluginResource.Factory.class);

private JVMKeys() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
*/
package org.spongepowered.plugin.builtin.jvm;

import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.PluginLoader;
import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource;

public abstract class JVMPluginLoader<T extends JVMPluginResource, U extends JVMPluginContainer> implements PluginLoader<T, U> {
public interface JVMPluginLoader<T extends JVMPluginResource, U extends PluginContainer> extends PluginLoader<T, U> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* This file is part of plugin-spi, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* 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.jvm;

import org.spongepowered.plugin.Environment;
import org.spongepowered.plugin.PluginResource;

import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.jar.Manifest;

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<String> property(final String key) {
return Optional.ofNullable(this.manifest().getMainAttributes().getValue(Objects.requireNonNull(key, "key")));
}

@Override
default Optional<URI> 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);
}

interface Factory {
JVMPluginResource create(final String locator, final Path[] paths);
}
}
Loading

0 comments on commit 0d64d1a

Please sign in to comment.