mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-05 15:01:46 +01:00
Working extensions + extension dependencies without MSRC
The code is pretty messy, and external dependencies do not work (i dont think). MSEC instances now have a parent of MinecraftServer's CL and use the regular Java classloader search path (parent first). If the class cannot be found (its in a dependency), then it searches through its child classloaders (of which the dependency is one).
This commit is contained in:
parent
18ecefd97a
commit
7130cc9dc9
@ -12,8 +12,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an extension from an `extension.json` that is capable of powering an Extension object.
|
* Represents an extension from an `extension.json` that is capable of powering an Extension object.
|
||||||
@ -21,6 +23,7 @@ import java.util.List;
|
|||||||
* This has no constructor as its properties are set via GSON.
|
* This has no constructor as its properties are set via GSON.
|
||||||
*/
|
*/
|
||||||
public final class DiscoveredExtension {
|
public final class DiscoveredExtension {
|
||||||
|
private static final ExtensionManager EXTENSION_MANAGER = MinecraftServer.getExtensionManager();
|
||||||
|
|
||||||
/** Static logger for this class. */
|
/** Static logger for this class. */
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class);
|
public static final Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class);
|
||||||
@ -205,30 +208,47 @@ public final class DiscoveredExtension {
|
|||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MinestomExtensionClassLoader makeClassLoader() {
|
public MinestomExtensionClassLoader makeClassLoader(List<DiscoveredExtension> discoveredExtensions) {
|
||||||
final URL[] urls = this.files.toArray(new URL[0]);
|
final URL[] urls = this.files.toArray(new URL[0]);
|
||||||
|
|
||||||
MinestomRootClassLoader root = MinestomRootClassLoader.getInstance();
|
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), this.getEntrypoint(), urls, MinecraftServer.class.getClassLoader());
|
||||||
|
|
||||||
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), this.getEntrypoint(), urls, root);
|
System.out.println("CREATED " + loader + " WITH " + Arrays.toString(urls));
|
||||||
|
|
||||||
if (this.getDependencies().length == 0 || MinecraftServer.getExtensionManager() == null) { // it also may invoked in early class loader
|
// add children to the dependencies
|
||||||
// orphaned extension, we can insert it directly
|
for (String dependencyName : this.getDependencies()) {
|
||||||
root.addChild(loader);
|
DiscoveredExtension dependency = discoveredExtensions.stream()
|
||||||
} else {
|
.filter(ext -> ext.getName().equalsIgnoreCase(dependencyName))
|
||||||
// add children to the dependencies
|
.findFirst().orElse(null);
|
||||||
for (String dependency : this.getDependencies()) {
|
|
||||||
if (MinecraftServer.getExtensionManager().hasExtension(dependency.toLowerCase())) {
|
|
||||||
MinestomExtensionClassLoader parentLoader = MinecraftServer.getExtensionManager().getExtension(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader();
|
|
||||||
|
|
||||||
// TODO should never happen but replace with better throws error.
|
if (dependency != null) {
|
||||||
assert parentLoader != null;
|
MinestomExtensionClassLoader dependencyLoader = dependency.getMinestomExtensionClassLoader();
|
||||||
|
assert dependencyLoader != null; //TODO: Better error handling
|
||||||
|
|
||||||
parentLoader.addChild(loader);
|
loader.addChild(dependencyLoader);
|
||||||
}
|
} else {
|
||||||
|
//TODO: Better error handling
|
||||||
|
throw new RuntimeException("Missing dependency " + dependencyName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (this.getDependencies().length == 0 || MinecraftServer.getExtensionManager() == null) { // it also may invoked in early class loader
|
||||||
|
// // orphaned extension, we can insert it directly
|
||||||
|
// root.addChild(loader);
|
||||||
|
// } else {
|
||||||
|
// // add children to the dependencies
|
||||||
|
// for (String dependency : this.getDependencies()) {
|
||||||
|
// if (MinecraftServer.getExtensionManager().hasExtension(dependency.toLowerCase())) {
|
||||||
|
// MinestomExtensionClassLoader parentLoader = MinecraftServer.getExtensionManager().getExtension(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader();
|
||||||
|
//
|
||||||
|
// // TODO should never happen but replace with better throws error.
|
||||||
|
// assert parentLoader != null;
|
||||||
|
//
|
||||||
|
// parentLoader.addChild(loader);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ import java.util.zip.ZipFile;
|
|||||||
|
|
||||||
public class ExtensionManager {
|
public class ExtensionManager {
|
||||||
|
|
||||||
public final static String DISABLE_EARLY_LOAD_SYSTEM_KEY = "minestom.extension.disable_early_load";
|
|
||||||
|
|
||||||
public final static Logger LOGGER = LoggerFactory.getLogger(ExtensionManager.class);
|
public final static Logger LOGGER = LoggerFactory.getLogger(ExtensionManager.class);
|
||||||
|
|
||||||
public final static String INDEV_CLASSES_FOLDER = "minestom.extension.indevfolder.classes";
|
public final static String INDEV_CLASSES_FOLDER = "minestom.extension.indevfolder.classes";
|
||||||
@ -164,7 +162,8 @@ public class ExtensionManager {
|
|||||||
// set class loaders for all extensions.
|
// set class loaders for all extensions.
|
||||||
for (DiscoveredExtension discoveredExtension : discoveredExtensions) {
|
for (DiscoveredExtension discoveredExtension : discoveredExtensions) {
|
||||||
try {
|
try {
|
||||||
discoveredExtension.setMinestomExtensionClassLoader(discoveredExtension.makeClassLoader());
|
discoveredExtension.setMinestomExtensionClassLoader(discoveredExtension.makeClassLoader(discoveredExtensions)); //TODO: This is a hack to pass the dependent DiscoveredExtensions to the classloader
|
||||||
|
System.out.println("SET " + discoveredExtension.getName() + " TO " + discoveredExtension.getMinestomExtensionClassLoader());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER;
|
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER;
|
||||||
MinecraftServer.getExceptionManager().handleException(e);
|
MinecraftServer.getExceptionManager().handleException(e);
|
||||||
@ -547,7 +546,7 @@ public class ExtensionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getter.addMavenResolver(repoList);
|
getter.addMavenResolver(repoList);
|
||||||
getter.addResolver(extensionDependencyResolver);
|
// getter.addResolver(extensionDependencyResolver);
|
||||||
|
|
||||||
for (String artifact : externalDependencies.artifacts) {
|
for (String artifact : externalDependencies.artifacts) {
|
||||||
var resolved = getter.get(artifact, dependenciesFolder);
|
var resolved = getter.get(artifact, dependenciesFolder);
|
||||||
@ -555,11 +554,11 @@ public class ExtensionManager {
|
|||||||
LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved);
|
LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String dependencyName : discoveredExtension.getDependencies()) {
|
// for (String dependencyName : discoveredExtension.getDependencies()) {
|
||||||
var resolved = getter.get(dependencyName, dependenciesFolder);
|
// var resolved = getter.get(dependencyName, dependenciesFolder);
|
||||||
addDependencyFile(resolved, discoveredExtension);
|
// addDependencyFile(resolved, discoveredExtension);
|
||||||
LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved);
|
// LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved);
|
||||||
}
|
// }
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES;
|
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES;
|
||||||
LOGGER.error("Failed to load dependencies for extension {}", discoveredExtension.getName());
|
LOGGER.error("Failed to load dependencies for extension {}", discoveredExtension.getName());
|
||||||
@ -570,6 +569,7 @@ public class ExtensionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addDependencyFile(@NotNull ResolvedDependency dependency, @NotNull DiscoveredExtension extension) {
|
private void addDependencyFile(@NotNull ResolvedDependency dependency, @NotNull DiscoveredExtension extension) {
|
||||||
|
System.out.println("ADDING DEPENDENCY " + dependency.getName() + " TO " + extension.getName());
|
||||||
URL location = dependency.getContentsLocation();
|
URL location = dependency.getContentsLocation();
|
||||||
extension.files.add(location);
|
extension.files.add(location);
|
||||||
LOGGER.trace("Added dependency {} to extension {} classpath", location.toExternalForm(), extension.getName());
|
LOGGER.trace("Added dependency {} to extension {} classpath", location.toExternalForm(), extension.getName());
|
||||||
@ -721,7 +721,7 @@ public class ExtensionManager {
|
|||||||
// setup new classloaders for the extensions to reload
|
// setup new classloaders for the extensions to reload
|
||||||
for (DiscoveredExtension toReload : extensionsToLoad) {
|
for (DiscoveredExtension toReload : extensionsToLoad) {
|
||||||
LOGGER.debug("Setting up classloader for extension {}", toReload.getName());
|
LOGGER.debug("Setting up classloader for extension {}", toReload.getName());
|
||||||
toReload.setMinestomExtensionClassLoader(toReload.makeClassLoader());
|
// toReload.setMinestomExtensionClassLoader(toReload.makeClassLoader()); //TODO: Fix this
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Extension> newExtensions = new LinkedList<>();
|
List<Extension> newExtensions = new LinkedList<>();
|
||||||
|
@ -11,7 +11,7 @@ public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
|||||||
/**
|
/**
|
||||||
* Root ClassLoader, everything goes through it before any attempt at loading is done inside this classloader
|
* Root ClassLoader, everything goes through it before any attempt at loading is done inside this classloader
|
||||||
*/
|
*/
|
||||||
private final MinestomRootClassLoader root;
|
private final ClassLoader root;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main of the main class of the extension linked to this classloader
|
* Main of the main class of the extension linked to this classloader
|
||||||
@ -20,7 +20,7 @@ public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
|||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(MinestomExtensionClassLoader.class);
|
private final Logger logger = LoggerFactory.getLogger(MinestomExtensionClassLoader.class);
|
||||||
|
|
||||||
public MinestomExtensionClassLoader(String extensionName, String mainClassName, URL[] urls, MinestomRootClassLoader root) {
|
public MinestomExtensionClassLoader(String extensionName, String mainClassName, URL[] urls, ClassLoader root) {
|
||||||
super(extensionName, urls, root);
|
super(extensionName, urls, root);
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.mainClassName = mainClassName;
|
this.mainClassName = mainClassName;
|
||||||
@ -47,12 +47,38 @@ public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||||
return root.loadClass(name);
|
try {
|
||||||
|
System.out.println("TRYING TO LOAD " + name + " IN " + getName());
|
||||||
|
return super.loadClass(name);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
System.out.println("COULD NOT LOAD, TRYING CHILDREN");
|
||||||
|
for (MinestomExtensionClassLoader child : children) {
|
||||||
|
try {
|
||||||
|
return child.loadClass(name);
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
System.out.println("NOT FOUND IN " + child.getName() + " EITHER");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
return root.loadClass(name, resolve);
|
try {
|
||||||
|
System.out.println("TRYING 2 LOAD " + name + " IN " + getName());
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
System.out.println("COULD NOT LOAD 2, TRYING CHILDREN");
|
||||||
|
for (MinestomExtensionClassLoader child : children) {
|
||||||
|
try {
|
||||||
|
return child.loadClass(name, resolve);
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
System.out.println("NOT FOUND IN " + child.getName() + " EITHER");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,44 +88,42 @@ public class MinestomExtensionClassLoader extends HierarchyClassLoader {
|
|||||||
* @throws ClassNotFoundException if the class is not found inside this classloader
|
* @throws ClassNotFoundException if the class is not found inside this classloader
|
||||||
*/
|
*/
|
||||||
public Class<?> loadClassAsChild(String name, boolean resolve) throws ClassNotFoundException {
|
public Class<?> loadClassAsChild(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
Class<?> loadedClass = findLoadedClass(name);
|
throw new RuntimeException("Cannot load " + name + " using old mechanism");
|
||||||
if(loadedClass != null) {
|
// logger.info("Loading class " + name + " as child of " + getName());
|
||||||
return loadedClass;
|
// Class<?> loadedClass = findLoadedClass(name);
|
||||||
}
|
// if(loadedClass != null) {
|
||||||
|
// logger.info("Found loaded class");
|
||||||
try {
|
// return loadedClass;
|
||||||
// not in children, attempt load in this classloader
|
// }
|
||||||
String path = name.replace(".", "/") + ".class";
|
//
|
||||||
InputStream in = getResourceAsStream(path);
|
// try {
|
||||||
if (in == null) {
|
// // not in children, attempt load in this classloader
|
||||||
throw new ClassNotFoundException("Could not load class " + name);
|
// String path = name.replace(".", "/") + ".class";
|
||||||
}
|
// InputStream in = getResourceAsStream(path);
|
||||||
try (in) {
|
// if (in == null) {
|
||||||
byte[] bytes = in.readAllBytes();
|
// throw new ClassNotFoundException("Could not load class " + name);
|
||||||
Class<?> clazz = defineClass(name, bytes, 0, bytes.length);
|
// }
|
||||||
if (resolve) {
|
// logger.info("Found in resources");
|
||||||
resolveClass(clazz);
|
// try (in) {
|
||||||
}
|
// byte[] bytes = in.readAllBytes();
|
||||||
return clazz;
|
// Class<?> clazz = defineClass(name, bytes, 0, bytes.length);
|
||||||
} catch (IOException e) {
|
// if (resolve) {
|
||||||
throw new ClassNotFoundException("Could not load class " + name, e);
|
// resolveClass(clazz);
|
||||||
}
|
// }
|
||||||
} catch (ClassNotFoundException e) {
|
// return clazz;
|
||||||
for(MinestomExtensionClassLoader child : children) {
|
// } catch (IOException e) {
|
||||||
try {
|
// throw new ClassNotFoundException("Could not load class " + name, e);
|
||||||
return child.loadClassAsChild(name, resolve);
|
// }
|
||||||
} catch (ClassNotFoundException e1) {
|
// } catch (ClassNotFoundException e) {
|
||||||
// move on to next child
|
// for(MinestomExtensionClassLoader child : children) {
|
||||||
}
|
// try {
|
||||||
}
|
// return child.loadClassAsChild(name, resolve);
|
||||||
throw e;
|
// } catch (ClassNotFoundException e1) {
|
||||||
}
|
// // move on to next child
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
@Override
|
// throw e;
|
||||||
protected void finalize() throws Throwable {
|
// }
|
||||||
super.finalize();
|
|
||||||
logger.info("Class loader " + getName() + " finalized.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +54,7 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
|
|||||||
add("kotlin");
|
add("kotlin");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to let ASM find out common super types, without actually commiting to loading them
|
* Used to let ASM find out common super types, without actually commiting to loading them
|
||||||
* Otherwise ASM would accidentally load classes we might want to modify
|
* Otherwise ASM would accidentally load classes we might want to modify
|
||||||
@ -122,18 +123,19 @@ public class MinestomRootClassLoader extends HierarchyClassLoader {
|
|||||||
try {
|
try {
|
||||||
// we do not load system classes by ourselves
|
// we do not load system classes by ourselves
|
||||||
Class<?> systemClass = ClassLoader.getPlatformClassLoader().loadClass(name);
|
Class<?> systemClass = ClassLoader.getPlatformClassLoader().loadClass(name);
|
||||||
LOGGER.trace("System class: {}", systemClass);
|
LOGGER.info("System class: {}", systemClass);
|
||||||
return systemClass;
|
return systemClass;
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
try {
|
try {
|
||||||
if (isProtected(name)) {
|
if (isProtected(name)) {
|
||||||
LOGGER.trace("Protected: {}", name);
|
LOGGER.info("Protected: {}", name);
|
||||||
return super.loadClass(name, resolve);
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Define: {}", name);
|
||||||
return define(name, resolve);
|
return define(name, resolve);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOGGER.trace("Fail to load class, resorting to parent loader: " + name, ex);
|
LOGGER.info("Fail to load class, resorting to parent loader: " + name, ex);
|
||||||
// fail to load class, let parent load
|
// fail to load class, let parent load
|
||||||
// this forbids code modification, but at least it will load
|
// this forbids code modification, but at least it will load
|
||||||
return super.loadClass(name, resolve);
|
return super.loadClass(name, resolve);
|
||||||
|
Loading…
Reference in New Issue
Block a user