1
0
Fork 0
mirror of https://github.com/xHyroM/lighteco.git synced 2024-12-22 04:21:06 +01:00

feat: make jar even more small, dependency relocation

This commit is contained in:
Jozef Steinhübl 2023-08-30 09:53:50 +02:00
parent 3802dba410
commit 1ae9a2e5bc
11 changed files with 224 additions and 36 deletions

View file

@ -37,5 +37,8 @@ tasks.shadowJar {
relocate("org.yaml.snakeyaml", "dev.xhyrom.lighteco.libraries.org.yaml.snakeyaml")
relocate("com.zaxxer.hikari", "dev.xhyrom.lighteco.libraries.com.zaxxer.hikari")
relocate("org.mariadb.jdbc", "dev.xhyrom.lighteco.libraries.mariadb")
relocate("com.mysql", "dev.xhyrom.lighteco.libraries.mysql")
relocate("org.postgresql", "dev.xhyrom.lighteco.libraries.postgresql")
relocate("com.zaxxer.hikari", "dev.xhyrom.lighteco.libraries.hikari")
}

View file

@ -52,7 +52,7 @@ public class BukkitLightEcoPlugin extends AbstractLightEcoPlugin {
@Override
protected void registerPlatformHooks() {
Hooks.register();
Hooks.register(this);
}
@Override

View file

@ -2,18 +2,28 @@ package dev.xhyrom.lighteco.bukkit;
import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerAdapter;
import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerTask;
import lombok.Getter;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class BukkitSchedulerAdapter implements SchedulerAdapter {
private final Executor async;
private final BukkitLightEcoBootstrap bootstrap;
private final BukkitScheduler scheduler;
public BukkitSchedulerAdapter(BukkitLightEcoBootstrap bootstrap) {
this.bootstrap = bootstrap;
this.scheduler = bootstrap.getServer().getScheduler();
this.async = runnable -> this.scheduler.runTaskAsynchronously(this.bootstrap.getLoader(), runnable);
}
public Executor async() {
return this.async;
}
@Override

View file

@ -1,13 +1,16 @@
package dev.xhyrom.lighteco.bukkit.hooks;
import dev.xhyrom.lighteco.bukkit.BukkitLightEcoPlugin;
import org.bukkit.Bukkit;
public class Hooks {
private static PlaceholderAPIExpansion placeholderAPIExpansion;
public static void register() {
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null)
public static void register(BukkitLightEcoPlugin plugin) {
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
placeholderAPIExpansion = new PlaceholderAPIExpansion(plugin);
placeholderAPIExpansion.register();
}
}
public static void unregister() {

View file

@ -9,13 +9,16 @@ dependencies {
exclude(module = "checker-qual")
exclude(module = "annotations")
}
api("net.kyori:adventure-text-minimessage:4.14.0")
api("net.kyori:adventure-text-minimessage:4.14.0") {
exclude(module = "adventure-bom")
exclude(module = "adventure-api")
}
api("com.google.guava:guava:32.1.2-jre")
implementation("eu.okaeri:okaeri-configs-yaml-snakeyaml:5.0.0-beta.5")
implementation("eu.okaeri:okaeri-configs-validator-okaeri:5.0.0-beta.5")
implementation("com.zaxxer:HikariCP:5.0.1")
compileOnly("com.zaxxer:HikariCP:5.0.1")
compileOnly("org.projectlombok:lombok:1.18.28")
annotationProcessor("org.projectlombok:lombok:1.18.28")

View file

@ -1,55 +1,104 @@
package dev.xhyrom.lighteco.common.dependencies;
import com.google.common.collect.ImmutableList;
import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation;
import lombok.Getter;
import java.util.List;
@Getter
public enum Dependency {
/**
* Somewhere we use brackets instad of dots, so we need to rewrite them
* This is because gradle's shadow plugin relocates using replacing full paths (dots)
*/
ASM(
"org.ow2.asm",
"asm",
"9.1"
),
ASM_COMMONS(
"org.ow2.asm",
"asm-commons",
"9.1"
),
JAR_RELOCATOR(
"me.lucko",
"jar-relocator",
"1.7"
),
HIKARI(
"com{}zaxxer",
"HikariCP",
"5.0.1",
Relocation.of("hikari", "com{}zaxxer{}hikari")
),
H2_DRIVER(
"com.h2database",
"h2",
"2.1.214"
"com.h2database",
"h2",
"2.1.214"
),
SQLITE_DRIVER(
"org.xerial",
"sqlite-jdbc",
"3.28.0"
"org.xerial",
"sqlite-jdbc",
"3.28.0"
),
MARIADB_DRIVER(
"org.mariadb.jdbc",
"mariadb-java-client",
"3.1.3"
"org{}mariadb{}jdbc",
"mariadb-java-client",
"3.1.3",
Relocation.of("mariadb", "org{}mariadb{}jdbc")
),
MYSQL_DRIVER(
"mysql",
"mysql-connector-java",
"8.0.23"
"mysql",
"mysql-connector-java",
"8.0.23",
Relocation.of("mysql", "com{}mysql")
),
POSTGRESQL_DRIVER(
"org.postgresql",
"postgresql",
"42.6.0"
"org{}postgresql",
"postgresql",
"42.6.0",
Relocation.of("postgresql", "org{}postgresql")
);
private final String fullPath;
private final String version;
@Getter
private final List<Relocation> relocations;
private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar";
Dependency(String groupId, String artifactId, String version) {
this(groupId, artifactId, version, new Relocation[0]);
}
Dependency(String groupId, String artifactId, String version, Relocation... relocations) {
this.fullPath = String.format(MAVEN_FORMAT,
groupId.replace('.', '/'),
artifactId,
rewriteEscape(groupId).replace('.', '/'),
rewriteEscape(artifactId),
version,
artifactId,
rewriteEscape(artifactId),
version
);
this.version = version;
this.relocations = ImmutableList.copyOf(relocations);
}
private static String rewriteEscape(String path) {
return path.replace("{}", ".");
}
public String getFileName() {
String name = name().toLowerCase().replace('_', '-');
return getFileName(null);
}
return String.format("%s-%s.jar", name, this.version);
public String getFileName(String classifier) {
String name = name().toLowerCase().replace('_', '-');
String extra = classifier == null ? "" : "-" + classifier;
return String.format("%s-%s.jar", name, this.version + extra);
}
}

View file

@ -2,22 +2,23 @@ package dev.xhyrom.lighteco.common.dependencies;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.MoreFiles;
import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation;
import dev.xhyrom.lighteco.common.dependencies.relocation.RelocationHandler;
import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger;
import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess;
import dev.xhyrom.lighteco.common.storage.StorageType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
public class DependencyManagerImpl implements DependencyManager {
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class);
@ -27,6 +28,7 @@ public class DependencyManagerImpl implements DependencyManager {
private final DependencyRegistry registry;
private final Path cacheDirectory;
private final URLClassLoaderAccess classLoader;
private @MonotonicNonNull RelocationHandler relocationHandler;
public DependencyManagerImpl(LightEcoPlugin plugin) {
this.logger = plugin.getBootstrap().getLogger();
@ -35,9 +37,18 @@ public class DependencyManagerImpl implements DependencyManager {
this.classLoader = URLClassLoaderAccess.create((URLClassLoader) plugin.getBootstrap().getClass().getClassLoader());
}
private synchronized RelocationHandler getRelocationHandler() {
if (this.relocationHandler == null) {
this.relocationHandler = new RelocationHandler(this);
}
return this.relocationHandler;
}
@Override
public void loadDependencies(Set<Dependency> dependencies) {
CountDownLatch latch = new CountDownLatch(dependencies.size());
this.logger.info("Loading dependencies: " + dependencies);
for (Dependency dependency : dependencies) {
if (this.loaded.containsKey(dependency)) {
@ -46,18 +57,22 @@ public class DependencyManagerImpl implements DependencyManager {
}
CompletableFuture.runAsync(() -> {
System.out.println("Loading dependency " + dependency);
try {
loadDependency(dependency);
} catch (Exception e) {
throw new RuntimeException("Failed to load dependency " + dependency, e);
} finally {
latch.countDown();
System.out.println("Loaded dependency " + dependency);
}
});
}
try {
latch.await();
this.logger.info("Loaded dependencies: " + dependencies);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
@ -68,7 +83,7 @@ public class DependencyManagerImpl implements DependencyManager {
return;
}
Path file = downloadDependency(dependency);
Path file = remapDependency(dependency, downloadDependency(dependency));
this.loaded.put(dependency, file);
@ -95,6 +110,24 @@ public class DependencyManagerImpl implements DependencyManager {
return file;
}
private Path remapDependency(Dependency dependency, Path normalFile) throws Exception {
List<Relocation> rules = new ArrayList<>(dependency.getRelocations());
if (rules.isEmpty()) {
return normalFile;
}
Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName("remapped"));
// if the remapped source exists already, just use that.
if (Files.exists(remappedFile)) {
return remappedFile;
}
this.getRelocationHandler().remap(normalFile, remappedFile, rules);
return remappedFile;
}
@Override
public void loadStorageDependencies(Set<StorageType> types) {
loadDependencies(this.registry.resolveStorageDependencies(types));

View file

@ -9,11 +9,11 @@ import java.util.Set;
public class DependencyRegistry {
private static final SetMultimap<StorageType, Dependency> STORAGE_DEPENDENCIES = ImmutableSetMultimap.<StorageType, Dependency>builder()
.putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER)
.putAll(StorageType.H2, Dependency.H2_DRIVER)
.putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER)
.putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER)
.putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER)
.putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER)
.putAll(StorageType.H2, Dependency.H2_DRIVER)
.putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER, Dependency.HIKARI)
.putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER, Dependency.HIKARI)
.putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI)
.build();
public Set<Dependency> resolveStorageDependencies(Set<StorageType> types) {

View file

@ -0,0 +1,21 @@
package dev.xhyrom.lighteco.common.dependencies.relocation;
import lombok.Getter;
@Getter
public class Relocation {
private static final String RELOCATION_PREFIX = "dev.xhyrom.lighteco.libraries.";
public static Relocation of(String id, String pattern) {
return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id);
}
private final String pattern;
private final String relocatedPattern;
private Relocation(String pattern, String relocatedPattern) {
this.pattern = pattern;
this.relocatedPattern = relocatedPattern;
}
}

View file

@ -0,0 +1,63 @@
package dev.xhyrom.lighteco.common.dependencies.relocation;
import dev.xhyrom.lighteco.common.dependencies.Dependency;
import dev.xhyrom.lighteco.common.dependencies.DependencyManager;
import dev.xhyrom.lighteco.common.dependencies.IsolatedClassLoader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;
public class RelocationHandler {
public static final Set<Dependency> DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR);
private static final String JAR_RELOCATOR_CLASS = "me.lucko.jarrelocator.JarRelocator";
private static final String JAR_RELOCATOR_RUN_METHOD = "run";
private final Constructor<?> jarRelocatorConstructor;
private final Method jarRelocatorRunMethod;
public RelocationHandler(DependencyManager dependencyManager) {
ClassLoader classLoader = null;
try {
// download the required dependencies for remapping
dependencyManager.loadDependencies(DEPENDENCIES);
// get a classloader containing the required dependencies as sources
classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES);
// load the relocator class
Class<?> jarRelocatorClass = classLoader.loadClass(JAR_RELOCATOR_CLASS);
// prepare the reflected constructor & method instances
this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class);
this.jarRelocatorConstructor.setAccessible(true);
this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod(JAR_RELOCATOR_RUN_METHOD);
this.jarRelocatorRunMethod.setAccessible(true);
} catch (Exception e) {
try {
if (classLoader instanceof IsolatedClassLoader) {
((IsolatedClassLoader) classLoader).close();
}
} catch (IOException ex) {
e.addSuppressed(ex);
}
throw new RuntimeException(e);
}
}
public void remap(Path input, Path output, List<Relocation> relocations) throws Exception {
Map<String, String> mappings = new HashMap<>();
for (Relocation relocation : relocations) {
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
}
// create and invoke a new relocator
Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings);
this.jarRelocatorRunMethod.invoke(relocator);
}
}

View file

@ -1,8 +1,11 @@
package dev.xhyrom.lighteco.common.plugin.scheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public interface SchedulerAdapter {
Executor async();
SchedulerTask asyncLater(Runnable runnable, long delay, TimeUnit unit);
SchedulerTask asyncRepeating(Runnable runnable, long interval, TimeUnit unit);