Cleanup and improve extension changes.

This commit is contained in:
Articdive 2020-10-25 10:41:51 +01:00
parent 535e8946b6
commit 5217964259
No known key found for this signature in database
GPG Key ID: B069585F0F7D90DE
3 changed files with 161 additions and 138 deletions

View File

@ -2,13 +2,114 @@ package net.minestom.server.extensions;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
@Slf4j
class DiscoveredExtension {
private static String NAME_REGEX = "[A-Za-z][_A-Za-z0-9]+";
@Slf4j(topic = "minestom-extensions")
final class DiscoveredExtension {
public static final String NAME_REGEX = "[A-Za-z][_A-Za-z0-9]+";
private String name;
private String entrypoint;
private String version;
private String mixinConfig;
private String[] authors;
private String[] codeModifiers;
private Dependencies dependencies;
transient File[] files = new File[0];
transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS;
@NotNull
public String getName() {
return name;
}
@NotNull
public String[] getCodeModifiers() {
if (codeModifiers == null) {
codeModifiers = new String[0];
}
return codeModifiers;
}
@NotNull
public String getMixinConfig() {
return mixinConfig;
}
@NotNull
public String[] getAuthors() {
return authors;
}
@NotNull
public String getVersion() {
return version;
}
@NotNull
public String getEntrypoint() {
return entrypoint;
}
@NotNull
public Dependencies getDependencies() {
return dependencies;
}
static void verifyIntegrity(@NotNull DiscoveredExtension extension) {
if (extension.name == null) {
StringBuilder fileList = new StringBuilder();
for (File f : extension.files) {
fileList.append(f.getAbsolutePath()).append(", ");
}
log.error("Extension with no name. (at {}})", fileList);
log.error("Extension at ({}) will not be loaded.", fileList);
extension.loadStatus = DiscoveredExtension.LoadStatus.INVALID_NAME;
// To ensure @NotNull: name = INVALID_NAME
extension.name = extension.loadStatus.name();
return;
}
if (!extension.name.matches(NAME_REGEX)) {
log.error("Extension '{}' specified an invalid name.", extension.name);
log.error("Extension '{}' will not be loaded.", extension.name);
extension.loadStatus = DiscoveredExtension.LoadStatus.INVALID_NAME;
// To ensure @NotNull: name = INVALID_NAME
extension.name = extension.loadStatus.name();
return;
}
if (extension.entrypoint == null) {
log.error("Extension '{}' did not specify an entry point (via 'entrypoint').", extension.name);
log.error("Extension '{}' will not be loaded.", extension.name);
extension.loadStatus = DiscoveredExtension.LoadStatus.NO_ENTRYPOINT;
// To ensure @NotNull: entrypoint = NO_ENTRYPOINT
extension.entrypoint = extension.loadStatus.name();
return;
}
// Handle defaults
// If we reach this code, then the extension will most likely be loaded:
if (extension.version == null) {
log.warn("Extension '{}' did not specify a version.", extension.name);
log.warn("Extension '{}' will continue to load but should specify a plugin version.", extension.name);
extension.version = "Unspecified";
}
if (extension.mixinConfig == null) {
extension.mixinConfig = "";
}
if (extension.authors == null) {
extension.authors = new String[0];
}
if (extension.codeModifiers == null) {
extension.codeModifiers = new String[0];
}
// No dependencies were specified
if (extension.dependencies == null) {
extension.dependencies = new Dependencies();
}
}
enum LoadStatus {
LOAD_SUCCESS("Actually, it did not fail. This message should not have been printed."),
@ -19,102 +120,23 @@ class DiscoveredExtension {
private final String message;
LoadStatus(String message) {
LoadStatus(@NotNull String message) {
this.message = message;
}
@NotNull
public String getMessage() {
return message;
}
}
static class Dependencies {
static final class Dependencies {
Repository[] repositories = new Repository[0];
String[] artifacts = new String[0];
static class Repository {
String name;
String url;
String name = "";
String url = "";
}
Repository[] repositories;
String[] artifacts;
}
transient File[] files = new File[0];
transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS;
private String[] codeModifiers;
private String[] authors;
private String mixinConfig;
private String name;
private String version;
private String entrypoint;
private Dependencies dependencies;
void checkIntegrity() {
if(name == null) {
StringBuilder fileList = new StringBuilder();
for(File f : files) {
fileList.append(f.getAbsolutePath()).append(", ");
}
log.error("Extension with no name. (at {}})", fileList);
log.error("Extension at ({}) will not be loaded.", fileList);
loadStatus = LoadStatus.INVALID_NAME;
return;
}
if(!name.matches(NAME_REGEX)) {
log.error("Extension '{}' specified an invalid name.", name);
log.error("Extension '{}' will not be loaded.", name);
loadStatus = LoadStatus.INVALID_NAME;
return;
}
if(entrypoint == null) {
log.error("Extension '{}' did not specify an entry point (via 'entrypoint').", name);
log.error("Extension '{}' will not be loaded.", name);
loadStatus = LoadStatus.NO_ENTRYPOINT;
return;
}
if(codeModifiers == null) {
codeModifiers = new String[0];
}
}
@NotNull
public String getName() {
if(name == null) {
throw new IllegalStateException("Missing extension name");
}
return name;
}
@NotNull
public String[] getCodeModifiers() {
if(codeModifiers == null) {
codeModifiers = new String[0];
}
return codeModifiers;
}
@Nullable
public String getMixinConfig() {
return mixinConfig;
}
@Nullable
public String[] getAuthors() {
return authors;
}
@Nullable
public String getVersion() {
return version;
}
@NotNull
public String getEntrypoint() {
return entrypoint;
}
@Nullable
public Dependencies getDependencies() {
return dependencies;
}
}

View File

@ -1,14 +1,16 @@
package net.minestom.server.extensions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
public abstract class Extension {
// Set by reflection
@SuppressWarnings("unused")
private ExtensionDescription description;
// Set by reflection
@SuppressWarnings("unused")
private Logger logger;
protected Extension() {
@ -35,23 +37,25 @@ public abstract class Extension {
}
@NotNull
public ExtensionDescription getDescription() {
return description;
}
@NotNull
protected Logger getLogger() {
return logger;
}
protected static class ExtensionDescription {
public static class ExtensionDescription {
private final String name;
private final String version;
private final List<String> authors;
protected ExtensionDescription(@NotNull String name, @NotNull String version, @Nullable List<String> authors) {
ExtensionDescription(@NotNull String name, @NotNull String version, @NotNull List<String> authors) {
this.name = name;
this.version = version;
this.authors = authors != null ? authors : new ArrayList<>();
this.authors = authors;
}
@NotNull

View File

@ -22,10 +22,15 @@ import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;
@Slf4j
@Slf4j(topic = "Minestom-Extensions")
public class ExtensionManager {
private final static String INDEV_CLASSES_FOLDER = "minestom.extension.indevfolder.classes";
@ -78,28 +83,14 @@ public class ExtensionManager {
continue;
}
// Get ExtensionDescription (authors, version etc.)
// Create ExtensionDescription (authors, version etc.)
String extensionName = discoveredExtension.getName();
String mainClass = discoveredExtension.getEntrypoint();
Extension.ExtensionDescription extensionDescription;
{
String version;
if (discoveredExtension.getVersion() == null) {
log.warn("Extension '{}' did not specify a version.", extensionName);
log.warn("Extension '{}' will continue to load but should specify a plugin version.", extensionName);
version = "Not Specified";
} else {
version = discoveredExtension.getVersion();
}
List<String> authors;
if (discoveredExtension.getAuthors() == null) {
authors = new ArrayList<>();
} else {
authors = Arrays.asList(discoveredExtension.getAuthors());
}
extensionDescription = new Extension.ExtensionDescription(extensionName, version, authors);
}
Extension.ExtensionDescription extensionDescription = new Extension.ExtensionDescription(
extensionName,
discoveredExtension.getVersion(),
Arrays.asList(discoveredExtension.getAuthors())
);
extensionLoaders.put(extensionName.toLowerCase(), loader);
@ -165,9 +156,9 @@ public class ExtensionManager {
// Set logger
try {
Field descriptionField = extensionClass.getSuperclass().getDeclaredField("logger");
descriptionField.setAccessible(true);
descriptionField.set(extension, LoggerFactory.getLogger(extensionClass));
Field loggerField = extensionClass.getSuperclass().getDeclaredField("logger");
loggerField.setAccessible(true);
loggerField.set(extension, LoggerFactory.getLogger(extensionClass));
} catch (IllegalAccessException e) {
// We made it accessible, should not occur
e.printStackTrace();
@ -181,29 +172,29 @@ public class ExtensionManager {
}
private void loadDependencies(List<DiscoveredExtension> extensions) {
for(DiscoveredExtension ext : extensions) {
for (DiscoveredExtension ext : extensions) {
try {
DependencyGetter getter = new DependencyGetter();
DiscoveredExtension.Dependencies dependencies = ext.getDependencies();
if(dependencies.repositories == null) {
throw new IllegalStateException("Missing 'repositories' array.");
}
if(dependencies.artifacts == null) {
throw new IllegalStateException("Missing 'artifacts' array.");
}
List<MavenRepository> repoList = new LinkedList<>();
for(var repository : dependencies.repositories) {
if(repository.name == null) {
for (var repository : dependencies.repositories) {
if (repository.name == null) {
throw new IllegalStateException("Missing 'name' element in repository object.");
}
if(repository.url == null) {
if (repository.name.isEmpty()) {
throw new IllegalStateException("Invalid 'name' element in repository object.");
}
if (repository.url == null) {
throw new IllegalStateException("Missing 'url' element in repository object.");
}
if (repository.url.isEmpty()) {
throw new IllegalStateException("Invalid 'url' element in repository object.");
}
repoList.add(new MavenRepository(repository.name, repository.url));
}
getter.addMavenResolver(repoList);
for(var artifact : dependencies.artifacts) {
for (var artifact : dependencies.artifacts) {
var resolved = getter.get(artifact, dependenciesFolder);
injectIntoClasspath(resolved.getContentsLocation(), ext);
log.trace("Dependency of extension {}: {}", ext.getName(), resolved);
@ -227,7 +218,7 @@ public class ExtensionManager {
addURL.setAccessible(true);
addURL.invoke(cl, dependency);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("Failed to inject URL "+dependency+" into classpath. From extension "+extension.getName(), e);
throw new RuntimeException("Failed to inject URL " + dependency + " into classpath. From extension " + extension.getName(), e);
}
}
@ -246,8 +237,11 @@ public class ExtensionManager {
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
extension.files = new File[]{file};
extension.checkIntegrity();
if(extension.loadStatus == DiscoveredExtension.LoadStatus.LOAD_SUCCESS) {
// Verify integrity and ensure defaults
DiscoveredExtension.verifyIntegrity(extension);
if (extension.loadStatus == DiscoveredExtension.LoadStatus.LOAD_SUCCESS) {
extensions.add(extension);
}
} catch (IOException e) {
@ -263,8 +257,11 @@ public class ExtensionManager {
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(new File(extensionResources, "extension.json")))) {
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
extension.files = new File[]{new File(extensionClasses), new File(extensionResources)};
extension.checkIntegrity();
if(extension.loadStatus == DiscoveredExtension.LoadStatus.LOAD_SUCCESS) {
// Verify integrity and ensure defaults
DiscoveredExtension.verifyIntegrity(extension);
if (extension.loadStatus == DiscoveredExtension.LoadStatus.LOAD_SUCCESS) {
extensions.add(extension);
}
} catch (IOException e) {
@ -320,7 +317,7 @@ public class ExtensionManager {
for (String codeModifierClass : extension.getCodeModifiers()) {
modifiableClassLoader.loadModifier(extension.files, codeModifierClass);
}
if (extension.getMixinConfig() != null) {
if (!extension.getMixinConfig().isEmpty()) {
final String mixinConfigFile = extension.getMixinConfig();
Mixins.addConfiguration(mixinConfigFile);
log.info("Found mixin in extension " + extension.getName() + ": " + mixinConfigFile);