From 03a93b118670d6a521d2a7206fdc50caf212d436 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 19 Jan 2018 19:27:08 +0000 Subject: [PATCH] Apply dependency remapping using an isolated classloader * Fixes the issue which prevented LP from loading on Java 9 * Should also fix #697 --- bukkit/pom.xml | 10 +- bungee/pom.xml | 10 +- common/pom.xml | 22 +--- .../common/dependencies/Dependency.java | 58 +++++---- .../dependencies/DependencyManager.java | 110 ++++++++--------- .../dependencies/DependencyRegistry.java | 1 + .../{Relocations.java => Relocation.java} | 27 +++-- .../relocation/RelocationHandler.java | 112 ++++++++++++++++++ .../messaging/RedisMessagingService.java | 8 +- sponge/pom.xml | 10 +- 10 files changed, 241 insertions(+), 127 deletions(-) rename common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/{Relocations.java => Relocation.java} (71%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/RelocationHandler.java diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 578929283..400642c5b 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -50,10 +50,6 @@ net.kyori.text me.lucko.luckperms.lib.text - - me.lucko.jarrelocator - me.lucko.luckperms.lib.jarrelocator - @@ -93,9 +89,13 @@ me.lucko.luckperms.lib.bson - redis.clients.jedis.shaded + redis.clients.jedis me.lucko.luckperms.lib.jedis + + org.apache.commons.pool2 + me.lucko.luckperms.lib.commonspool2 + ninja.leaping.configurate me.lucko.luckperms.lib.configurate diff --git a/bungee/pom.xml b/bungee/pom.xml index 23d93dce4..197e8e820 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -50,10 +50,6 @@ net.kyori.text me.lucko.luckperms.lib.text - - me.lucko.jarrelocator - me.lucko.luckperms.lib.jarrelocator - @@ -93,9 +89,13 @@ me.lucko.luckperms.lib.bson - redis.clients.jedis.shaded + redis.clients.jedis me.lucko.luckperms.lib.jedis + + org.apache.commons.pool2 + me.lucko.luckperms.lib.commonspool2 + ninja.leaping.configurate me.lucko.luckperms.lib.configurate diff --git a/common/pom.xml b/common/pom.xml index 929ad5f45..754853517 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -44,24 +44,6 @@ provided - - - me.lucko - jar-relocator - 1.2 - compile - - - org.ow2.asm - asm - - - org.ow2.asm - asm-commons - - - - net.kyori @@ -101,7 +83,7 @@ com.google.guava guava 19.0 - provided + compile @@ -169,7 +151,7 @@ redis.clients jedis - 2.9.1-shaded + 2.9.0 provided diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index c89299a09..3705963da 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -28,8 +28,7 @@ package me.lucko.luckperms.common.dependencies; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; -import me.lucko.jarrelocator.Relocation; -import me.lucko.luckperms.common.dependencies.relocation.Relocations; +import me.lucko.luckperms.common.dependencies.relocation.Relocation; import java.io.InputStream; import java.net.URL; @@ -53,55 +52,61 @@ public enum Dependency { "5.2", "zBMYiX4sdxy3l6aNX06mQcI6UfBDfKUXq+z5ZN2yZAs=" ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "1.2", + "ECR0wrAMwmM0dpmuY1ifCG+2rpObOIlSI127jBbSrbI=" + ), CAFFEINE( "com{}github{}ben-manes{}caffeine", "caffeine", "2.6.0", "JmT16VQnCnVBAjRJCQkkkjmSVx2jajpzeBuKwpbzDA8=", - Relocations.of("caffeine", "com{}github{}benmanes{}caffeine") + Relocation.of("caffeine", "com{}github{}benmanes{}caffeine") ), MARIADB_DRIVER( "org{}mariadb{}jdbc", "mariadb-java-client", "2.2.0", "/q0LPGHrp3L9rvKr7TuA6urbtXBqvXis92mP4KhxzUw=", - Relocations.of("mariadb", "org{}mariadb{}jdbc") + Relocation.of("mariadb", "org{}mariadb{}jdbc") ), MYSQL_DRIVER( "mysql", "mysql-connector-java", "5.1.44", "d4RZVzTeWpoHBPB/tQP3mSafNy7L9MDUSOt4Ku9LGCc=", - Relocations.of("mysql", "com{}mysql") + Relocation.of("mysql", "com{}mysql") ), POSTGRESQL_DRIVER( "org{}postgresql", "postgresql", "9.4.1212", "DLKhWL4xrPIY4KThjI89usaKO8NIBkaHc/xECUsMNl0=", - Relocations.of("postgresql", "org{}postgresql") + Relocation.of("postgresql", "org{}postgresql") ), H2_DRIVER( "com.h2database", "h2", "1.4.196", "CgX0oNW4WEAUiq3OY6QjtdPDbvRHVjibT6rQjScz+vU=", - Relocations.of("h2", "org{}h2") + Relocation.of("h2", "org{}h2") ), SQLITE_DRIVER( "org.xerial", "sqlite-jdbc", "3.21.0", "bglRaH4Y+vQFZV7TfOdsVLO3rJpauJ+IwjuRULAb45Y=", - Relocations.of("sqlite", "org{}sqlite") + Relocation.of("sqlite", "org{}sqlite") ), HIKARI( "com{}zaxxer", "HikariCP", "2.7.4", "y9JE6/VmbydCqlV1z468+oqdkBswBk6aw+ECT178AT4=", - Relocations.of("hikari", "com{}zaxxer{}hikari") + Relocation.of("hikari", "com{}zaxxer{}hikari") ), SLF4J_SIMPLE( "org.slf4j", @@ -121,36 +126,47 @@ public enum Dependency { "3.5.0", "gxrbKVSI/xM6r+6uL7g7I0DzNV+hlNTtfw4UL13XdK8=", ImmutableList.builder() - .addAll(Relocations.of("mongodb", "com{}mongodb")) - .addAll(Relocations.of("bson", "org{}bson")) + .addAll(Relocation.of("mongodb", "com{}mongodb")) + .addAll(Relocation.of("bson", "org{}bson")) .build() ), JEDIS( - "https://github.com/lucko/jedis/releases/download/jedis-2.9.1-shaded/jedis-2.9.1-shaded.jar", - "2.9.1-shaded", - "mM19X6LyD97KP4RSbcCR5BTRAwQ0x9y02voX7ePOSjE=", - Relocations.of("jedis", "redis{}clients{}jedis{}shaded") + "redis.clients", + "jedis", + "2.9.0", + "HqqWy45QVeTVF0Z/DzsrPLvGKn2dHotqI8YX7GDThvo=", + ImmutableList.builder() + .addAll(Relocation.of("jedis", "redis{}clients{}jedis")) + .addAll(Relocation.of("commonspool2", "org{}apache{}commons{}pool2")) + .build() + ), + COMMONS_POOL_2( + "org.apache.commons", + "commons-pool2", + "2.4.2", + "IREqpnNzPfzQRTVN33WzHh1GS5nI5RWXQ0myUyJUzFM=", + Relocation.of("commonspool2", "org{}apache{}commons{}pool2") ), CONFIGURATE_CORE( "ninja{}leaping{}configurate", "configurate-core", "3.3", "4leBJEqj1kVszaifZeKNl4hgHxG5M+Nk5TJKkPW2s4Y=", - Relocations.of("configurate", "ninja{}leaping{}configurate") + Relocation.of("configurate", "ninja{}leaping{}configurate") ), CONFIGURATE_GSON( "ninja{}leaping{}configurate", "configurate-gson", "3.3", "4HxrW3/ZKdn095x/W4gylQMNskdmteXYVxVv0UKGJA4=", - Relocations.of("configurate", "ninja{}leaping{}configurate") + Relocation.of("configurate", "ninja{}leaping{}configurate") ), CONFIGURATE_YAML( "ninja{}leaping{}configurate", "configurate-yaml", "3.3", "hgADp3g+xHHPD34bAuxMWtB+OQ718Tlw69jVp2KPJNk=", - Relocations.of("configurate", "ninja{}leaping{}configurate") + Relocation.of("configurate", "ninja{}leaping{}configurate") ), CONFIGURATE_HOCON( "ninja{}leaping{}configurate", @@ -158,8 +174,8 @@ public enum Dependency { "3.3", "UIy5FVmsBUG6+Z1mpIEE2EXgtOI1ZL0p/eEW+BbtGLU=", ImmutableList.builder() - .addAll(Relocations.of("configurate", "ninja{}leaping{}configurate")) - .addAll(Relocations.of("hocon", "com{}typesafe{}config")) + .addAll(Relocation.of("configurate", "ninja{}leaping{}configurate")) + .addAll(Relocation.of("hocon", "com{}typesafe{}config")) .build() ), HOCON_CONFIG( @@ -167,7 +183,7 @@ public enum Dependency { "config", "1.3.1", "5vrfxhCCINOmuGqn5OFsnnu4V7pYlViGMIuxOXImSvA=", - Relocations.of("hocon", "com{}typesafe{}config") + Relocation.of("hocon", "com{}typesafe{}config") ); private final String url; diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java index 02a3a74fb..ed9cf7b60 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java @@ -27,8 +27,8 @@ package me.lucko.luckperms.common.dependencies; import com.google.common.io.ByteStreams; -import me.lucko.jarrelocator.JarRelocator; -import me.lucko.jarrelocator.Relocation; +import me.lucko.luckperms.common.dependencies.relocation.Relocation; +import me.lucko.luckperms.common.dependencies.relocation.RelocationHandler; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.storage.StorageType; @@ -53,6 +53,7 @@ public class DependencyManager { private final MessageDigest digest; private final DependencyRegistry registry; private final EnumSet alreadyLoaded = EnumSet.noneOf(Dependency.class); + private RelocationHandler relocationHandler = null; public DependencyManager(LuckPermsPlugin plugin) { this.plugin = plugin; @@ -64,23 +65,29 @@ public class DependencyManager { this.registry = new DependencyRegistry(plugin); } + private synchronized RelocationHandler getRelocationHandler() { + if (this.relocationHandler == null) { + this.relocationHandler = new RelocationHandler(this); + } + return this.relocationHandler; + } + + public File getSaveDirectory() { + File saveDirectory = new File(this.plugin.getDataDirectory(), "lib"); + if (!(saveDirectory.exists() || saveDirectory.mkdirs())) { + throw new RuntimeException("Unable to create lib dir - " + saveDirectory.getPath()); + } + + return saveDirectory; + } + public void loadStorageDependencies(Set storageTypes) { loadDependencies(this.registry.resolveStorageDependencies(storageTypes)); } public void loadDependencies(Set dependencies) { - loadDependencies(dependencies, true); - } - - public void loadDependencies(Set dependencies, boolean applyRemapping) { - if (applyRemapping) { - this.plugin.getLog().info("Identified the following dependencies: " + dependencies.toString()); - } - - File saveDirectory = new File(this.plugin.getDataDirectory(), "lib"); - if (!(saveDirectory.exists() || saveDirectory.mkdirs())) { - throw new RuntimeException("Unable to create lib dir - " + saveDirectory.getPath()); - } + this.plugin.getLog().info("Identified the following dependencies: " + dependencies.toString()); + File saveDirectory = getSaveDirectory(); // create a list of file sources List sources = new ArrayList<>(); @@ -102,53 +109,36 @@ public class DependencyManager { // apply any remapping rules to the files List remappedJars = new ArrayList<>(sources.size()); - if (applyRemapping) { - for (Source source : sources) { - try { - // apply remap rules - List relocations = source.dependency.getRelocations(); + for (Source source : sources) { + try { + // apply remap rules + List relocations = source.dependency.getRelocations(); - if (relocations.isEmpty()) { - remappedJars.add(source.file); - continue; - } - - File input = source.file; - File output = new File(input.getParentFile(), "remap-" + input.getName()); - - // if the remapped file exists already, just use that. - if (output.exists()) { - remappedJars.add(output); - continue; - } - - // make sure ASM is loaded - Set asmDepends = EnumSet.noneOf(Dependency.class); - if (!DependencyRegistry.asmPresent()) { - asmDepends.add(Dependency.ASM); - } - if (!DependencyRegistry.asmCommonsPresent()) { - asmDepends.add(Dependency.ASM_COMMONS); - } - if (!asmDepends.isEmpty()) { - // load asm before calling the jar relocator - loadDependencies(asmDepends, false); - } - - // attempt to remap the jar. - this.plugin.getLog().info("Attempting to remap " + input.getName() + "..."); - JarRelocator relocator = new JarRelocator(input, output, relocations); - relocator.run(); - - remappedJars.add(output); - } catch (Throwable e) { - this.plugin.getLog().severe("Unable to remap the source file '" + source.dependency.name() + "'."); - e.printStackTrace(); + if (relocations.isEmpty()) { + remappedJars.add(source.file); + continue; } - } - } else { - for (Source source : sources) { - remappedJars.add(source.file); + + File input = source.file; + File output = new File(input.getParentFile(), "remap-" + input.getName()); + + // if the remapped file exists already, just use that. + if (output.exists()) { + remappedJars.add(output); + continue; + } + + // init the relocation handler + RelocationHandler relocationHandler = getRelocationHandler(); + + // attempt to remap the jar. + this.plugin.getLog().info("Attempting to apply relocations to " + input.getName() + "..."); + relocationHandler.remap(input, output, relocations); + + remappedJars.add(output); + } catch (Throwable e) { + this.plugin.getLog().severe("Unable to remap the source file '" + source.dependency.name() + "'."); + e.printStackTrace(); } } @@ -163,7 +153,7 @@ public class DependencyManager { } } - private File downloadDependency(File saveDirectory, Dependency dependency) throws Exception { + public File downloadDependency(File saveDirectory, Dependency dependency) throws Exception { String fileName = dependency.name().toLowerCase() + "-" + dependency.getVersion() + ".jar"; File file = new File(saveDirectory, fileName); diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java index 4a2724438..fb56b6d3d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRegistry.java @@ -65,6 +65,7 @@ public class DependencyRegistry { } if (this.plugin.getConfiguration().get(ConfigKeys.REDIS_ENABLED)) { + dependencies.add(Dependency.COMMONS_POOL_2); dependencies.add(Dependency.JEDIS); } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocations.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java similarity index 71% rename from common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocations.java rename to common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java index bf95754e9..e9ec5abbb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocations.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/Relocation.java @@ -25,20 +25,33 @@ package me.lucko.luckperms.common.dependencies.relocation; -import me.lucko.jarrelocator.Relocation; - import java.util.ArrayList; import java.util.List; -public final class Relocations { +public final class Relocation { public static List of(String name, String... packages) { - List relocations = new ArrayList<>(); + List ret = new ArrayList<>(); for (String p : packages) { - relocations.add(new Relocation(p.replace("{}", "."), "me.lucko.luckperms.lib." + name)); + ret.add(new Relocation(p.replace("{}", "."), "me.lucko.luckperms.lib." + name)); } - return relocations; + return ret; + } + + private final String pattern; + private final String relocatedPattern; + + public Relocation(String pattern, String relocatedPattern) { + this.pattern = pattern; + this.relocatedPattern = relocatedPattern; + } + + public String getPattern() { + return this.pattern; + } + + public String getRelocatedPattern() { + return this.relocatedPattern; } - private Relocations() {} } diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/RelocationHandler.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/RelocationHandler.java new file mode 100644 index 000000000..26b21b0af --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/relocation/RelocationHandler.java @@ -0,0 +1,112 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.dependencies.relocation; + +import me.lucko.luckperms.common.dependencies.Dependency; +import me.lucko.luckperms.common.dependencies.DependencyManager; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Handles class runtime relocation of packages in downloaded dependencies + */ +public class RelocationHandler implements AutoCloseable { + private final DependencyManager dependencyManager; + + private URLClassLoader classLoader; + private Constructor relocatorConstructor; + private Method relocatorRunMethod; + + public RelocationHandler(DependencyManager dependencyManager) { + this.dependencyManager = dependencyManager; + try { + setup(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void setup() throws Exception { + File saveDirectory = this.dependencyManager.getSaveDirectory(); + + // download the required dependencies for the remapping. + File asm = this.dependencyManager.downloadDependency(saveDirectory, Dependency.ASM); + File asmCommons = this.dependencyManager.downloadDependency(saveDirectory, Dependency.ASM_COMMONS); + File jarRelocator = this.dependencyManager.downloadDependency(saveDirectory, Dependency.JAR_RELOCATOR); + + URL[] urls = new URL[]{ + asm.toURI().toURL(), + asmCommons.toURI().toURL(), + jarRelocator.toURI().toURL() + }; + + // construct an isolated classloader instance containing the dependencies needed + this.classLoader = new URLClassLoader(urls, null); + + // load the relocator class + Class relocatorClass = this.classLoader.loadClass("me.lucko.jarrelocator.JarRelocator"); + + // prepare the the reflected constructor & method instances + this.relocatorConstructor = relocatorClass.getDeclaredConstructor(File.class, File.class, Map.class); + this.relocatorConstructor.setAccessible(true); + + this.relocatorRunMethod = relocatorClass.getDeclaredMethod("run"); + this.relocatorRunMethod.setAccessible(true); + } + + public void remap(File input, File output, List relocations) throws Exception { + if (this.classLoader == null) { + throw new IllegalStateException("ClassLoader is closed"); + } + + Map mappings = new HashMap<>(); + for (Relocation relocation : relocations) { + mappings.put(relocation.getPattern(), relocation.getRelocatedPattern()); + } + + // create and invoke a new relocator + Object relocator = this.relocatorConstructor.newInstance(input, output, mappings); + this.relocatorRunMethod.invoke(relocator); + } + + @Override + public void close() throws IOException { + if (this.classLoader == null) { + return; + } + + this.classLoader.close(); + this.classLoader = null; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessagingService.java b/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessagingService.java index 2c28d6b55..4a7f7bcbc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessagingService.java +++ b/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessagingService.java @@ -27,10 +27,10 @@ package me.lucko.luckperms.common.messaging; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import redis.clients.jedis.shaded.Jedis; -import redis.clients.jedis.shaded.JedisPool; -import redis.clients.jedis.shaded.JedisPoolConfig; -import redis.clients.jedis.shaded.JedisPubSub; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.JedisPubSub; /** * An implementation of {@link me.lucko.luckperms.api.MessagingService} using Redis. diff --git a/sponge/pom.xml b/sponge/pom.xml index c2535b4d3..bae019216 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -50,10 +50,6 @@ net.kyori.text me.lucko.luckperms.lib.text - - me.lucko.jarrelocator - me.lucko.luckperms.lib.jarrelocator - @@ -89,9 +85,13 @@ me.lucko.luckperms.lib.bson - redis.clients.jedis.shaded + redis.clients.jedis me.lucko.luckperms.lib.jedis + + org.apache.commons.pool2 + me.lucko.luckperms.lib.commonspool2 +