mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-08-16 16:26:12 +00:00
inject into the library loader to make sure libraries can always be loaded
This commit is contained in:
parent
fc54fcb7eb
commit
3949468a60
@ -1,20 +1,35 @@
|
|||||||
package com.volmit.iris.util.misc;
|
package com.volmit.iris.util.misc;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
import io.github.slimjar.app.builder.ApplicationBuilder;
|
import io.github.slimjar.app.builder.ApplicationBuilder;
|
||||||
|
import io.github.slimjar.exceptions.InjectorException;
|
||||||
|
import io.github.slimjar.injector.loader.Injectable;
|
||||||
|
import io.github.slimjar.injector.loader.InjectableFactory;
|
||||||
|
import io.github.slimjar.injector.loader.IsolatedInjectableClassLoader;
|
||||||
import io.github.slimjar.logging.ProcessLogger;
|
import io.github.slimjar.logging.ProcessLogger;
|
||||||
|
import io.github.slimjar.resolver.data.Repository;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class SlimJar {
|
public class SlimJar {
|
||||||
private static final Logger LOGGER = Logger.getLogger("Iris");
|
private static final String NAME = "Iris";
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(NAME);
|
||||||
private static final ReentrantLock lock = new ReentrantLock();
|
private static final ReentrantLock lock = new ReentrantLock();
|
||||||
private static final AtomicBoolean loaded = new AtomicBoolean();
|
private static final AtomicBoolean loaded = new AtomicBoolean();
|
||||||
|
private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("iris.disable-remapper");
|
||||||
|
|
||||||
public static void debug(boolean debug) {
|
public static void debug(boolean debug) {
|
||||||
LOGGER.setLevel(debug ? Level.FINE : Level.INFO);
|
LOGGER.setLevel(debug ? Level.FINE : Level.INFO);
|
||||||
@ -31,28 +46,99 @@ public class SlimJar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.info("Loading libraries...");
|
LOGGER.info("Loading libraries...");
|
||||||
ApplicationBuilder.appending("Iris")
|
load(localRepository.toPath(), new ProcessLogger() {
|
||||||
.downloadDirectoryPath(localRepository.toPath())
|
@Override
|
||||||
.logger(new ProcessLogger() {
|
public void info(@NotNull String message, @Nullable Object... args) {
|
||||||
@Override
|
LOGGER.fine(message.formatted(args));
|
||||||
public void info(@NotNull String message, @Nullable Object... args) {
|
}
|
||||||
LOGGER.fine(message.formatted(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(@NotNull String message, @Nullable Object... args) {
|
public void error(@NotNull String message, @Nullable Object... args) {
|
||||||
LOGGER.severe(message.formatted(args));
|
LOGGER.severe(message.formatted(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(@NotNull String message, @Nullable Object... args) {
|
public void debug(@NotNull String message, @Nullable Object... args) {
|
||||||
LOGGER.fine(message.formatted(args));
|
LOGGER.fine(message.formatted(args));
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.build();
|
|
||||||
LOGGER.info("Libraries loaded successfully!");
|
LOGGER.info("Libraries loaded successfully!");
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void load(Path downloadPath, ProcessLogger logger) {
|
||||||
|
try {
|
||||||
|
loadSpigot(downloadPath, logger);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.warn("Failed to inject the library loader, falling back to application builder");
|
||||||
|
ApplicationBuilder.appending(NAME)
|
||||||
|
.downloadDirectoryPath(downloadPath)
|
||||||
|
.logger(logger)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadSpigot(Path downloadPath, ProcessLogger logger) throws Throwable {
|
||||||
|
var current = SlimJar.class.getClassLoader();
|
||||||
|
var libraryLoader = current.getClass().getDeclaredField("libraryLoader");
|
||||||
|
libraryLoader.setAccessible(true);
|
||||||
|
if (!ClassLoader.class.isAssignableFrom(libraryLoader.getType())) throw new IllegalStateException("Failed to find library loader");
|
||||||
|
|
||||||
|
final var pair = findRemapper();
|
||||||
|
final var remapper = pair.getA();
|
||||||
|
final var factory = pair.getB();
|
||||||
|
|
||||||
|
final var libraries = factory.apply(new URL[0], current.getParent());
|
||||||
|
final var injecting = InjectableFactory.create(downloadPath, List.of(Repository.central()), libraries);
|
||||||
|
|
||||||
|
ApplicationBuilder.injecting(NAME, new Injectable() {
|
||||||
|
@Override
|
||||||
|
public void inject(@NotNull URL url) throws InjectorException {
|
||||||
|
try {
|
||||||
|
final List<Path> mapped;
|
||||||
|
synchronized (remapper) {
|
||||||
|
mapped = remapper.apply(List.of(Path.of(url.toURI())));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Path path : mapped) {
|
||||||
|
injecting.inject(path.toUri().toURL());
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new InjectorException("Failed to inject " + url, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isThreadSafe() {
|
||||||
|
return injecting.isThreadSafe();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.downloadDirectoryPath(downloadPath)
|
||||||
|
.logger(logger)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
libraryLoader.set(current, libraries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<Function<List<Path>, List<Path>>, BiFunction<URL[], ClassLoader, URLClassLoader>> findRemapper() {
|
||||||
|
Function<List<Path>, List<Path>> mapper = null;
|
||||||
|
BiFunction<URL[], ClassLoader, URLClassLoader> factory = null;
|
||||||
|
if (!DISABLE_REMAPPER) {
|
||||||
|
try {
|
||||||
|
var libraryLoader = Class.forName("org.bukkit.plugin.java.LibraryLoader");
|
||||||
|
var mapperField = libraryLoader.getDeclaredField("REMAPPER");
|
||||||
|
var factoryField = libraryLoader.getDeclaredField("LIBRARY_LOADER_FACTORY");
|
||||||
|
mapperField.setAccessible(true);
|
||||||
|
factoryField.setAccessible(true);
|
||||||
|
mapper = (Function<List<Path>, List<Path>>) mapperField.get(null);
|
||||||
|
factory = (BiFunction<URL[], ClassLoader, URLClassLoader>) factoryField.get(null);
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapper == null) mapper = Function.identity();
|
||||||
|
if (factory == null) factory = (urls, parent) -> new IsolatedInjectableClassLoader(urls, List.of(), parent);
|
||||||
|
return new Pair<>(mapper, factory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user