1
0
Fork 0
mirror of https://github.com/xHyroM/lighteco.git synced 2024-09-19 21:03:18 +02: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("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 @Override
protected void registerPlatformHooks() { protected void registerPlatformHooks() {
Hooks.register(); Hooks.register(this);
} }
@Override @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.SchedulerAdapter;
import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerTask; import dev.xhyrom.lighteco.common.plugin.scheduler.SchedulerTask;
import lombok.Getter;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class BukkitSchedulerAdapter implements SchedulerAdapter { public class BukkitSchedulerAdapter implements SchedulerAdapter {
private final Executor async;
private final BukkitLightEcoBootstrap bootstrap; private final BukkitLightEcoBootstrap bootstrap;
private final BukkitScheduler scheduler; private final BukkitScheduler scheduler;
public BukkitSchedulerAdapter(BukkitLightEcoBootstrap bootstrap) { public BukkitSchedulerAdapter(BukkitLightEcoBootstrap bootstrap) {
this.bootstrap = bootstrap; this.bootstrap = bootstrap;
this.scheduler = bootstrap.getServer().getScheduler(); this.scheduler = bootstrap.getServer().getScheduler();
this.async = runnable -> this.scheduler.runTaskAsynchronously(this.bootstrap.getLoader(), runnable);
}
public Executor async() {
return this.async;
} }
@Override @Override

View file

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

View file

@ -9,13 +9,16 @@ dependencies {
exclude(module = "checker-qual") exclude(module = "checker-qual")
exclude(module = "annotations") 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") 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-yaml-snakeyaml:5.0.0-beta.5")
implementation("eu.okaeri:okaeri-configs-validator-okaeri: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") compileOnly("org.projectlombok:lombok:1.18.28")
annotationProcessor("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; package dev.xhyrom.lighteco.common.dependencies;
import com.google.common.collect.ImmutableList;
import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation;
import lombok.Getter; import lombok.Getter;
import java.util.List;
@Getter @Getter
public enum Dependency { 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( H2_DRIVER(
"com.h2database", "com.h2database",
"h2", "h2",
"2.1.214" "2.1.214"
), ),
SQLITE_DRIVER( SQLITE_DRIVER(
"org.xerial", "org.xerial",
"sqlite-jdbc", "sqlite-jdbc",
"3.28.0" "3.28.0"
), ),
MARIADB_DRIVER( MARIADB_DRIVER(
"org.mariadb.jdbc", "org{}mariadb{}jdbc",
"mariadb-java-client", "mariadb-java-client",
"3.1.3" "3.1.3",
Relocation.of("mariadb", "org{}mariadb{}jdbc")
), ),
MYSQL_DRIVER( MYSQL_DRIVER(
"mysql", "mysql",
"mysql-connector-java", "mysql-connector-java",
"8.0.23" "8.0.23",
Relocation.of("mysql", "com{}mysql")
), ),
POSTGRESQL_DRIVER( POSTGRESQL_DRIVER(
"org.postgresql", "org{}postgresql",
"postgresql", "postgresql",
"42.6.0" "42.6.0",
Relocation.of("postgresql", "org{}postgresql")
); );
private final String fullPath; private final String fullPath;
private final String version; private final String version;
@Getter
private final List<Relocation> relocations;
private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar";
Dependency(String groupId, String artifactId, String version) { 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, this.fullPath = String.format(MAVEN_FORMAT,
groupId.replace('.', '/'), rewriteEscape(groupId).replace('.', '/'),
artifactId, rewriteEscape(artifactId),
version, version,
artifactId, rewriteEscape(artifactId),
version version
); );
this.version = version; this.version = version;
this.relocations = ImmutableList.copyOf(relocations);
}
private static String rewriteEscape(String path) {
return path.replace("{}", ".");
} }
public String getFileName() { 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.collect.ImmutableSet;
import com.google.common.io.MoreFiles; 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.LightEcoPlugin;
import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger; import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger;
import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess; import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess;
import dev.xhyrom.lighteco.common.storage.StorageType; import dev.xhyrom.lighteco.common.storage.StorageType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.EnumMap; import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
public class DependencyManagerImpl implements DependencyManager { public class DependencyManagerImpl implements DependencyManager {
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class); 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 DependencyRegistry registry;
private final Path cacheDirectory; private final Path cacheDirectory;
private final URLClassLoaderAccess classLoader; private final URLClassLoaderAccess classLoader;
private @MonotonicNonNull RelocationHandler relocationHandler;
public DependencyManagerImpl(LightEcoPlugin plugin) { public DependencyManagerImpl(LightEcoPlugin plugin) {
this.logger = plugin.getBootstrap().getLogger(); this.logger = plugin.getBootstrap().getLogger();
@ -35,9 +37,18 @@ public class DependencyManagerImpl implements DependencyManager {
this.classLoader = URLClassLoaderAccess.create((URLClassLoader) plugin.getBootstrap().getClass().getClassLoader()); 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 @Override
public void loadDependencies(Set<Dependency> dependencies) { public void loadDependencies(Set<Dependency> dependencies) {
CountDownLatch latch = new CountDownLatch(dependencies.size()); CountDownLatch latch = new CountDownLatch(dependencies.size());
this.logger.info("Loading dependencies: " + dependencies);
for (Dependency dependency : dependencies) { for (Dependency dependency : dependencies) {
if (this.loaded.containsKey(dependency)) { if (this.loaded.containsKey(dependency)) {
@ -46,18 +57,22 @@ public class DependencyManagerImpl implements DependencyManager {
} }
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
System.out.println("Loading dependency " + dependency);
try { try {
loadDependency(dependency); loadDependency(dependency);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to load dependency " + dependency, e); throw new RuntimeException("Failed to load dependency " + dependency, e);
} finally { } finally {
latch.countDown(); latch.countDown();
System.out.println("Loaded dependency " + dependency);
} }
}); });
} }
try { try {
latch.await(); latch.await();
this.logger.info("Loaded dependencies: " + dependencies);
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -68,7 +83,7 @@ public class DependencyManagerImpl implements DependencyManager {
return; return;
} }
Path file = downloadDependency(dependency); Path file = remapDependency(dependency, downloadDependency(dependency));
this.loaded.put(dependency, file); this.loaded.put(dependency, file);
@ -95,6 +110,24 @@ public class DependencyManagerImpl implements DependencyManager {
return file; 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 @Override
public void loadStorageDependencies(Set<StorageType> types) { public void loadStorageDependencies(Set<StorageType> types) {
loadDependencies(this.registry.resolveStorageDependencies(types)); loadDependencies(this.registry.resolveStorageDependencies(types));

View file

@ -9,11 +9,11 @@ import java.util.Set;
public class DependencyRegistry { public class DependencyRegistry {
private static final SetMultimap<StorageType, Dependency> STORAGE_DEPENDENCIES = ImmutableSetMultimap.<StorageType, Dependency>builder() private static final SetMultimap<StorageType, Dependency> STORAGE_DEPENDENCIES = ImmutableSetMultimap.<StorageType, Dependency>builder()
.putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER) .putAll(StorageType.SQLITE, Dependency.SQLITE_DRIVER)
.putAll(StorageType.H2, Dependency.H2_DRIVER) .putAll(StorageType.H2, Dependency.H2_DRIVER)
.putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER) .putAll(StorageType.MYSQL, Dependency.MYSQL_DRIVER, Dependency.HIKARI)
.putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER) .putAll(StorageType.MARIADB, Dependency.MARIADB_DRIVER, Dependency.HIKARI)
.putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER) .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI)
.build(); .build();
public Set<Dependency> resolveStorageDependencies(Set<StorageType> types) { 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; package dev.xhyrom.lighteco.common.plugin.scheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public interface SchedulerAdapter { public interface SchedulerAdapter {
Executor async();
SchedulerTask asyncLater(Runnable runnable, long delay, TimeUnit unit); SchedulerTask asyncLater(Runnable runnable, long delay, TimeUnit unit);
SchedulerTask asyncRepeating(Runnable runnable, long interval, TimeUnit unit); SchedulerTask asyncRepeating(Runnable runnable, long interval, TimeUnit unit);