mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-12-29 12:37:40 +01:00
Don't relocate H2 or SQLite depends in favour of loading into isolated classloaders (fixes #704)
This commit is contained in:
parent
1f70ad978f
commit
4858e59b70
@ -68,14 +68,6 @@
|
|||||||
<pattern>org.postgresql</pattern>
|
<pattern>org.postgresql</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
<relocation>
|
|
||||||
<pattern>org.h2</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
|
||||||
<pattern>org.sqlite</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
<relocation>
|
||||||
<pattern>com.zaxxer.hikari</pattern>
|
<pattern>com.zaxxer.hikari</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
||||||
|
@ -68,14 +68,6 @@
|
|||||||
<pattern>org.postgresql</pattern>
|
<pattern>org.postgresql</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
<relocation>
|
|
||||||
<pattern>org.h2</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
|
||||||
<pattern>org.sqlite</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
<relocation>
|
||||||
<pattern>com.zaxxer.hikari</pattern>
|
<pattern>com.zaxxer.hikari</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
||||||
|
@ -147,20 +147,6 @@
|
|||||||
<version>2.7.3</version>
|
<version>2.7.3</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- h2 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.h2database</groupId>
|
|
||||||
<artifactId>h2</artifactId>
|
|
||||||
<version>1.4.196</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- sqlite -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.xerial</groupId>
|
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
|
||||||
<version>3.21.0</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- Jedis -->
|
<!-- Jedis -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.dependencies;
|
package me.lucko.luckperms.common.dependencies;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
import me.lucko.luckperms.common.dependencies.relocation.Relocation;
|
import me.lucko.luckperms.common.dependencies.relocation.Relocation;
|
||||||
@ -91,15 +90,17 @@ public enum Dependency {
|
|||||||
"com.h2database",
|
"com.h2database",
|
||||||
"h2",
|
"h2",
|
||||||
"1.4.196",
|
"1.4.196",
|
||||||
"CgX0oNW4WEAUiq3OY6QjtdPDbvRHVjibT6rQjScz+vU=",
|
"CgX0oNW4WEAUiq3OY6QjtdPDbvRHVjibT6rQjScz+vU="
|
||||||
Relocation.of("h2", "org{}h2")
|
// we don't apply relocations to h2 - it gets loaded via
|
||||||
|
// an isolated classloader
|
||||||
),
|
),
|
||||||
SQLITE_DRIVER(
|
SQLITE_DRIVER(
|
||||||
"org.xerial",
|
"org.xerial",
|
||||||
"sqlite-jdbc",
|
"sqlite-jdbc",
|
||||||
"3.21.0",
|
"3.21.0",
|
||||||
"bglRaH4Y+vQFZV7TfOdsVLO3rJpauJ+IwjuRULAb45Y=",
|
"bglRaH4Y+vQFZV7TfOdsVLO3rJpauJ+IwjuRULAb45Y="
|
||||||
Relocation.of("sqlite", "org{}sqlite")
|
// we don't apply relocations to sqlite - it gets loaded via
|
||||||
|
// an isolated classloader
|
||||||
),
|
),
|
||||||
HIKARI(
|
HIKARI(
|
||||||
"com{}zaxxer",
|
"com{}zaxxer",
|
||||||
@ -125,20 +126,20 @@ public enum Dependency {
|
|||||||
"mongo-java-driver",
|
"mongo-java-driver",
|
||||||
"3.5.0",
|
"3.5.0",
|
||||||
"gxrbKVSI/xM6r+6uL7g7I0DzNV+hlNTtfw4UL13XdK8=",
|
"gxrbKVSI/xM6r+6uL7g7I0DzNV+hlNTtfw4UL13XdK8=",
|
||||||
ImmutableList.<Relocation>builder()
|
Relocation.allOf(
|
||||||
.addAll(Relocation.of("mongodb", "com{}mongodb"))
|
Relocation.of("mongodb", "com{}mongodb"),
|
||||||
.addAll(Relocation.of("bson", "org{}bson"))
|
Relocation.of("bson", "org{}bson")
|
||||||
.build()
|
)
|
||||||
),
|
),
|
||||||
JEDIS(
|
JEDIS(
|
||||||
"redis.clients",
|
"redis.clients",
|
||||||
"jedis",
|
"jedis",
|
||||||
"2.9.0",
|
"2.9.0",
|
||||||
"HqqWy45QVeTVF0Z/DzsrPLvGKn2dHotqI8YX7GDThvo=",
|
"HqqWy45QVeTVF0Z/DzsrPLvGKn2dHotqI8YX7GDThvo=",
|
||||||
ImmutableList.<Relocation>builder()
|
Relocation.allOf(
|
||||||
.addAll(Relocation.of("jedis", "redis{}clients{}jedis"))
|
Relocation.of("jedis", "redis{}clients{}jedis"),
|
||||||
.addAll(Relocation.of("commonspool2", "org{}apache{}commons{}pool2"))
|
Relocation.of("commonspool2", "org{}apache{}commons{}pool2")
|
||||||
.build()
|
)
|
||||||
),
|
),
|
||||||
COMMONS_POOL_2(
|
COMMONS_POOL_2(
|
||||||
"org.apache.commons",
|
"org.apache.commons",
|
||||||
@ -173,10 +174,10 @@ public enum Dependency {
|
|||||||
"configurate-hocon",
|
"configurate-hocon",
|
||||||
"3.3",
|
"3.3",
|
||||||
"UIy5FVmsBUG6+Z1mpIEE2EXgtOI1ZL0p/eEW+BbtGLU=",
|
"UIy5FVmsBUG6+Z1mpIEE2EXgtOI1ZL0p/eEW+BbtGLU=",
|
||||||
ImmutableList.<Relocation>builder()
|
Relocation.allOf(
|
||||||
.addAll(Relocation.of("configurate", "ninja{}leaping{}configurate"))
|
Relocation.of("configurate", "ninja{}leaping{}configurate"),
|
||||||
.addAll(Relocation.of("hocon", "com{}typesafe{}config"))
|
Relocation.of("hocon", "com{}typesafe{}config")
|
||||||
.build()
|
)
|
||||||
),
|
),
|
||||||
HOCON_CONFIG(
|
HOCON_CONFIG(
|
||||||
"com{}typesafe",
|
"com{}typesafe",
|
||||||
@ -197,6 +198,10 @@ public enum Dependency {
|
|||||||
this(groupId, artifactId, version, checksum, Collections.emptyList());
|
this(groupId, artifactId, version, checksum, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dependency(String groupId, String artifactId, String version, String checksum, Relocation relocation) {
|
||||||
|
this(groupId, artifactId, version, checksum, Collections.singletonList(relocation));
|
||||||
|
}
|
||||||
|
|
||||||
Dependency(String groupId, String artifactId, String version, String checksum, List<Relocation> relocations) {
|
Dependency(String groupId, String artifactId, String version, String checksum, List<Relocation> relocations) {
|
||||||
this(
|
this(
|
||||||
String.format(MAVEN_CENTRAL_FORMAT,
|
String.format(MAVEN_CENTRAL_FORMAT,
|
||||||
@ -210,6 +215,14 @@ public enum Dependency {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dependency(String url, String version, String checksum) {
|
||||||
|
this(url, version, checksum, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Dependency(String url, String version, String checksum, Relocation relocation) {
|
||||||
|
this(url, version, checksum, Collections.singletonList(relocation));
|
||||||
|
}
|
||||||
|
|
||||||
Dependency(String url, String version, String checksum, List<Relocation> relocations) {
|
Dependency(String url, String version, String checksum, List<Relocation> relocations) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.dependencies;
|
package me.lucko.luckperms.common.dependencies;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
|
||||||
import me.lucko.luckperms.common.dependencies.relocation.Relocation;
|
import me.lucko.luckperms.common.dependencies.relocation.Relocation;
|
||||||
import me.lucko.luckperms.common.dependencies.relocation.RelocationHandler;
|
import me.lucko.luckperms.common.dependencies.relocation.RelocationHandler;
|
||||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
@ -34,6 +36,7 @@ import me.lucko.luckperms.common.storage.StorageType;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
@ -41,8 +44,10 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +57,8 @@ public class DependencyManager {
|
|||||||
private final LuckPermsPlugin plugin;
|
private final LuckPermsPlugin plugin;
|
||||||
private final MessageDigest digest;
|
private final MessageDigest digest;
|
||||||
private final DependencyRegistry registry;
|
private final DependencyRegistry registry;
|
||||||
private final EnumSet<Dependency> alreadyLoaded = EnumSet.noneOf(Dependency.class);
|
private final EnumMap<Dependency, File> loaded = new EnumMap<>(Dependency.class);
|
||||||
|
private final Map<ImmutableSet<Dependency>, IsolatedClassLoader> loaders = new HashMap<>();
|
||||||
private RelocationHandler relocationHandler = null;
|
private RelocationHandler relocationHandler = null;
|
||||||
|
|
||||||
public DependencyManager(LuckPermsPlugin plugin) {
|
public DependencyManager(LuckPermsPlugin plugin) {
|
||||||
@ -72,7 +78,7 @@ public class DependencyManager {
|
|||||||
return this.relocationHandler;
|
return this.relocationHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getSaveDirectory() {
|
private File getSaveDirectory() {
|
||||||
File saveDirectory = new File(this.plugin.getDataDirectory(), "lib");
|
File saveDirectory = new File(this.plugin.getDataDirectory(), "lib");
|
||||||
if (!(saveDirectory.exists() || saveDirectory.mkdirs())) {
|
if (!(saveDirectory.exists() || saveDirectory.mkdirs())) {
|
||||||
throw new RuntimeException("Unable to create lib dir - " + saveDirectory.getPath());
|
throw new RuntimeException("Unable to create lib dir - " + saveDirectory.getPath());
|
||||||
@ -81,12 +87,43 @@ public class DependencyManager {
|
|||||||
return saveDirectory;
|
return saveDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IsolatedClassLoader obtainClassLoaderWith(Set<Dependency> dependencies) {
|
||||||
|
ImmutableSet<Dependency> set = ImmutableSet.copyOf(dependencies);
|
||||||
|
|
||||||
|
for (Dependency dependency : dependencies) {
|
||||||
|
if (!this.loaded.containsKey(dependency)) {
|
||||||
|
throw new IllegalStateException("Dependency " + dependency + " is not loaded.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this.loaders) {
|
||||||
|
IsolatedClassLoader classLoader = this.loaders.get(set);
|
||||||
|
if (classLoader != null) {
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL[] urls = set.stream()
|
||||||
|
.map(this.loaded::get)
|
||||||
|
.map(file -> {
|
||||||
|
try {
|
||||||
|
return file.toURI().toURL();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toArray(URL[]::new);
|
||||||
|
|
||||||
|
classLoader = new IsolatedClassLoader(urls);
|
||||||
|
this.loaders.put(set, classLoader);
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadStorageDependencies(Set<StorageType> storageTypes) {
|
public void loadStorageDependencies(Set<StorageType> storageTypes) {
|
||||||
loadDependencies(this.registry.resolveStorageDependencies(storageTypes));
|
loadDependencies(this.registry.resolveStorageDependencies(storageTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadDependencies(Set<Dependency> dependencies) {
|
public void loadDependencies(Set<Dependency> dependencies) {
|
||||||
this.plugin.getLog().info("Identified the following dependencies: " + dependencies.toString());
|
|
||||||
File saveDirectory = getSaveDirectory();
|
File saveDirectory = getSaveDirectory();
|
||||||
|
|
||||||
// create a list of file sources
|
// create a list of file sources
|
||||||
@ -94,7 +131,7 @@ public class DependencyManager {
|
|||||||
|
|
||||||
// obtain a file for each of the dependencies
|
// obtain a file for each of the dependencies
|
||||||
for (Dependency dependency : dependencies) {
|
for (Dependency dependency : dependencies) {
|
||||||
if (!this.alreadyLoaded.add(dependency)) {
|
if (this.loaded.containsKey(dependency)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,23 +145,23 @@ public class DependencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply any remapping rules to the files
|
// apply any remapping rules to the files
|
||||||
List<File> remappedJars = new ArrayList<>(sources.size());
|
List<Source> remappedJars = new ArrayList<>(sources.size());
|
||||||
for (Source source : sources) {
|
for (Source source : sources) {
|
||||||
try {
|
try {
|
||||||
// apply remap rules
|
// apply remap rules
|
||||||
List<Relocation> relocations = source.dependency.getRelocations();
|
List<Relocation> relocations = source.dependency.getRelocations();
|
||||||
|
|
||||||
if (relocations.isEmpty()) {
|
if (relocations.isEmpty()) {
|
||||||
remappedJars.add(source.file);
|
remappedJars.add(source);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
File input = source.file;
|
File input = source.file;
|
||||||
File output = new File(input.getParentFile(), "remap-" + input.getName());
|
File output = new File(input.getParentFile(), "remapped-" + input.getName());
|
||||||
|
|
||||||
// if the remapped file exists already, just use that.
|
// if the remapped file exists already, just use that.
|
||||||
if (output.exists()) {
|
if (output.exists()) {
|
||||||
remappedJars.add(output);
|
remappedJars.add(new Source(source.dependency, output));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +172,7 @@ public class DependencyManager {
|
|||||||
this.plugin.getLog().info("Attempting to apply relocations to " + input.getName() + "...");
|
this.plugin.getLog().info("Attempting to apply relocations to " + input.getName() + "...");
|
||||||
relocationHandler.remap(input, output, relocations);
|
relocationHandler.remap(input, output, relocations);
|
||||||
|
|
||||||
remappedJars.add(output);
|
remappedJars.add(new Source(source.dependency, output));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
this.plugin.getLog().severe("Unable to remap the source file '" + source.dependency.name() + "'.");
|
this.plugin.getLog().severe("Unable to remap the source file '" + source.dependency.name() + "'.");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -143,17 +180,23 @@ public class DependencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load each of the jars
|
// load each of the jars
|
||||||
for (File file : remappedJars) {
|
for (Source source : remappedJars) {
|
||||||
|
if (!DependencyRegistry.shouldAutoLoad(source.dependency)) {
|
||||||
|
this.loaded.put(source.dependency, source.file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.plugin.getPluginClassLoader().loadJar(file);
|
this.plugin.getPluginClassLoader().loadJar(source.file);
|
||||||
|
this.loaded.put(source.dependency, source.file);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
this.plugin.getLog().severe("Failed to load dependency jar '" + file.getName() + "'.");
|
this.plugin.getLog().severe("Failed to load dependency jar '" + source.file.getName() + "'.");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public File downloadDependency(File saveDirectory, Dependency dependency) throws Exception {
|
private File downloadDependency(File saveDirectory, Dependency dependency) throws Exception {
|
||||||
String fileName = dependency.name().toLowerCase() + "-" + dependency.getVersion() + ".jar";
|
String fileName = dependency.name().toLowerCase() + "-" + dependency.getVersion() + ".jar";
|
||||||
File file = new File(saveDirectory, fileName);
|
File file = new File(saveDirectory, fileName);
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ public class DependencyRegistry {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean classExists(String className) {
|
private static boolean classExists(String className) {
|
||||||
try {
|
try {
|
||||||
Class.forName(className);
|
Class.forName(className);
|
||||||
return true;
|
return true;
|
||||||
@ -100,17 +100,19 @@ public class DependencyRegistry {
|
|||||||
return classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory");
|
return classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to remap dependencies - we check to see if they're present to avoid loading unnecessarily.
|
public static boolean shouldAutoLoad(Dependency dependency) {
|
||||||
|
switch (dependency) {
|
||||||
public static boolean asmPresent() {
|
// all used within 'isolated' classloaders, and are therefore not
|
||||||
return classExists("org.objectweb.asm.ClassReader") &&
|
// relocated.
|
||||||
classExists("org.objectweb.asm.ClassVisitor") &&
|
case ASM:
|
||||||
classExists("org.objectweb.asm.ClassWriter");
|
case ASM_COMMONS:
|
||||||
|
case JAR_RELOCATOR:
|
||||||
|
case H2_DRIVER:
|
||||||
|
case SQLITE_DRIVER:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean asmCommonsPresent() {
|
|
||||||
return classExists("org.objectweb.asm.commons.ClassRemapper") &&
|
|
||||||
classExists("org.objectweb.asm.commons.Remapper");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* 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.classloader;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A classloader "isolated" from the rest of the Minecraft server.
|
||||||
|
*
|
||||||
|
* <p>Used to load specific LuckPerms dependencies without causing conflicts
|
||||||
|
* with other plugins, or libraries provided by the server implementation.</p>
|
||||||
|
*/
|
||||||
|
public class IsolatedClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
public IsolatedClassLoader(URL[] urls) {
|
||||||
|
super(urls, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,23 +25,23 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.dependencies.relocation;
|
package me.lucko.luckperms.common.dependencies.relocation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class Relocation {
|
public final class Relocation {
|
||||||
|
|
||||||
public static List<Relocation> of(String name, String... packages) {
|
public static Relocation of(String id, String pattern) {
|
||||||
List<Relocation> ret = new ArrayList<>();
|
return new Relocation(pattern.replace("{}", "."), "me.lucko.luckperms.lib." + id);
|
||||||
for (String p : packages) {
|
|
||||||
ret.add(new Relocation(p.replace("{}", "."), "me.lucko.luckperms.lib." + name));
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
public static List<Relocation> allOf(Relocation... relocations) {
|
||||||
|
return Arrays.asList(relocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String pattern;
|
private final String pattern;
|
||||||
private final String relocatedPattern;
|
private final String relocatedPattern;
|
||||||
|
|
||||||
public Relocation(String pattern, String relocatedPattern) {
|
private Relocation(String pattern, String relocatedPattern) {
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.relocatedPattern = relocatedPattern;
|
this.relocatedPattern = relocatedPattern;
|
||||||
}
|
}
|
||||||
|
@ -27,86 +27,55 @@ package me.lucko.luckperms.common.dependencies.relocation;
|
|||||||
|
|
||||||
import me.lucko.luckperms.common.dependencies.Dependency;
|
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||||
|
import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.util.EnumSet;
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles class runtime relocation of packages in downloaded dependencies
|
* Handles class runtime relocation of packages in downloaded dependencies
|
||||||
*/
|
*/
|
||||||
public class RelocationHandler implements AutoCloseable {
|
public class RelocationHandler {
|
||||||
private final DependencyManager dependencyManager;
|
private static final Set<Dependency> DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR);
|
||||||
|
|
||||||
private URLClassLoader classLoader;
|
private final Constructor<?> jarRelocatorConstructor;
|
||||||
private Constructor<?> relocatorConstructor;
|
private final Method jarRelocatorRunMethod;
|
||||||
private Method relocatorRunMethod;
|
|
||||||
|
|
||||||
public RelocationHandler(DependencyManager dependencyManager) {
|
public RelocationHandler(DependencyManager dependencyManager) {
|
||||||
this.dependencyManager = dependencyManager;
|
|
||||||
try {
|
try {
|
||||||
setup();
|
// download the required dependencies for remapping
|
||||||
|
dependencyManager.loadDependencies(DEPENDENCIES);
|
||||||
|
// get a classloader containing the required dependencies as sources
|
||||||
|
IsolatedClassLoader classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES);
|
||||||
|
|
||||||
|
// load the relocator class
|
||||||
|
Class<?> jarRelocatorClass = classLoader.loadClass("me.lucko.jarrelocator.JarRelocator");
|
||||||
|
|
||||||
|
// prepare the the reflected constructor & method instances
|
||||||
|
this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class);
|
||||||
|
this.jarRelocatorConstructor.setAccessible(true);
|
||||||
|
|
||||||
|
this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod("run");
|
||||||
|
this.jarRelocatorRunMethod.setAccessible(true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(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<Relocation> relocations) throws Exception {
|
public void remap(File input, File output, List<Relocation> relocations) throws Exception {
|
||||||
if (this.classLoader == null) {
|
|
||||||
throw new IllegalStateException("ClassLoader is closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> mappings = new HashMap<>();
|
Map<String, String> mappings = new HashMap<>();
|
||||||
for (Relocation relocation : relocations) {
|
for (Relocation relocation : relocations) {
|
||||||
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
|
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
// create and invoke a new relocator
|
// create and invoke a new relocator
|
||||||
Object relocator = this.relocatorConstructor.newInstance(input, output, mappings);
|
Object relocator = this.jarRelocatorConstructor.newInstance(input, output, mappings);
|
||||||
this.relocatorRunMethod.invoke(relocator);
|
this.jarRelocatorRunMethod.invoke(relocator);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (this.classLoader == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.classLoader.close();
|
|
||||||
this.classLoader = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,28 +122,33 @@ public class StorageFactory {
|
|||||||
private AbstractDao makeDao(StorageType method) {
|
private AbstractDao makeDao(StorageType method) {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case MARIADB:
|
case MARIADB:
|
||||||
return new SqlDao(this.plugin, new MariaDbConnectionFactory(
|
return new SqlDao(
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
this.plugin,
|
||||||
|
new MariaDbConnectionFactory(this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||||
);
|
);
|
||||||
case MYSQL:
|
case MYSQL:
|
||||||
return new SqlDao(this.plugin, new MySqlConnectionFactory(
|
return new SqlDao(
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
this.plugin,
|
||||||
|
new MySqlConnectionFactory(this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||||
);
|
);
|
||||||
case SQLITE:
|
case SQLITE:
|
||||||
return new SqlDao(this.plugin, new SQLiteConnectionFactory(
|
return new SqlDao(
|
||||||
new File(this.plugin.getDataDirectory(), "luckperms-sqlite.db")),
|
this.plugin,
|
||||||
|
new SQLiteConnectionFactory(this.plugin, new File(this.plugin.getDataDirectory(), "luckperms-sqlite.db")),
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||||
);
|
);
|
||||||
case H2:
|
case H2:
|
||||||
return new SqlDao(this.plugin, new H2ConnectionFactory(
|
return new SqlDao(
|
||||||
new File(this.plugin.getDataDirectory(), "luckperms-h2")),
|
this.plugin,
|
||||||
|
new H2ConnectionFactory(this.plugin, new File(this.plugin.getDataDirectory(), "luckperms-h2")),
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||||
);
|
);
|
||||||
case POSTGRESQL:
|
case POSTGRESQL:
|
||||||
return new SqlDao(this.plugin, new PostgreConnectionFactory(
|
return new SqlDao(
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
this.plugin,
|
||||||
|
new PostgreConnectionFactory(this.plugin.getConfiguration().get(ConfigKeys.DATABASE_VALUES)),
|
||||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||||
);
|
);
|
||||||
case MONGODB:
|
case MONGODB:
|
||||||
|
@ -25,19 +25,27 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
||||||
|
|
||||||
import org.h2.Driver;
|
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||||
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.Driver;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
|
// the driver used to obtain connections
|
||||||
|
private final Driver driver;
|
||||||
|
// the active connection
|
||||||
private NonClosableConnection connection;
|
private NonClosableConnection connection;
|
||||||
|
|
||||||
public H2ConnectionFactory(File file) {
|
public H2ConnectionFactory(LuckPermsPlugin plugin, File file) {
|
||||||
super("H2", file);
|
super("H2", file);
|
||||||
|
|
||||||
// backwards compat
|
// backwards compat
|
||||||
@ -45,21 +53,26 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
|||||||
if (data.exists()) {
|
if (data.exists()) {
|
||||||
data.renameTo(new File(file.getParent(), file.getName() + ".mv.db"));
|
data.renameTo(new File(file.getParent(), file.getName() + ".mv.db"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup the classloader
|
||||||
|
URLClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER));
|
||||||
|
try {
|
||||||
|
Class<?> driverClass = classLoader.loadClass("org.h2.Driver");
|
||||||
|
Method loadMethod = driverClass.getMethod("load");
|
||||||
|
this.driver = (Driver) loadMethod.invoke(null);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection getConnection() throws SQLException {
|
public synchronized Connection getConnection() throws SQLException {
|
||||||
this.lock.lock();
|
|
||||||
try {
|
|
||||||
if (this.connection == null || this.connection.isClosed()) {
|
if (this.connection == null || this.connection.isClosed()) {
|
||||||
Connection connection = Driver.load().connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
Connection connection = this.driver.connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
this.connection = new NonClosableConnection(connection);
|
this.connection = new NonClosableConnection(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
this.lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.connection == null) {
|
if (this.connection == null) {
|
||||||
throw new SQLException("Unable to get a connection.");
|
throw new SQLException("Unable to get a connection.");
|
||||||
|
@ -25,19 +25,26 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
||||||
|
|
||||||
import org.sqlite.JDBC;
|
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||||
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
|
// the method invoked to obtain new connection instances
|
||||||
|
private final Method createConnectionMethod;
|
||||||
|
// the active connection
|
||||||
private NonClosableConnection connection;
|
private NonClosableConnection connection;
|
||||||
|
|
||||||
public SQLiteConnectionFactory(File file) {
|
public SQLiteConnectionFactory(LuckPermsPlugin plugin, File file) {
|
||||||
super("SQLite", file);
|
super("SQLite", file);
|
||||||
|
|
||||||
// backwards compat
|
// backwards compat
|
||||||
@ -45,23 +52,39 @@ public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
|||||||
if (data.exists()) {
|
if (data.exists()) {
|
||||||
data.renameTo(file);
|
data.renameTo(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup the classloader
|
||||||
|
URLClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER));
|
||||||
|
try {
|
||||||
|
Class<?> jdcbClass = classLoader.loadClass("org.sqlite.JDBC");
|
||||||
|
this.createConnectionMethod = jdcbClass.getMethod("createConnection", String.class, Properties.class);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Connection createConnection(String url) throws SQLException {
|
||||||
|
try {
|
||||||
|
return (Connection) this.createConnectionMethod.invoke(null, url, new Properties());
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
if (e.getCause() instanceof SQLException) {
|
||||||
|
throw ((SQLException) e.getCause());
|
||||||
|
}
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection getConnection() throws SQLException {
|
public synchronized Connection getConnection() throws SQLException {
|
||||||
this.lock.lock();
|
|
||||||
try {
|
|
||||||
if (this.connection == null || this.connection.isClosed()) {
|
if (this.connection == null || this.connection.isClosed()) {
|
||||||
Connection connection = JDBC.createConnection("jdbc:sqlite:" + this.file.getAbsolutePath(), new Properties());
|
Connection connection = createConnection("jdbc:sqlite:" + this.file.getAbsolutePath());
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
this.connection = new NonClosableConnection(connection);
|
this.connection = new NonClosableConnection(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
|
||||||
this.lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.connection == null) {
|
if (this.connection == null) {
|
||||||
throw new SQLException("Unable to get a connection.");
|
throw new SQLException("Unable to get a connection.");
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,6 @@
|
|||||||
<pattern>org.postgresql</pattern>
|
<pattern>org.postgresql</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
<relocation>
|
|
||||||
<pattern>org.h2</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
|
||||||
<pattern>org.sqlite</pattern>
|
|
||||||
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
<relocation>
|
||||||
<pattern>com.zaxxer.hikari</pattern>
|
<pattern>com.zaxxer.hikari</pattern>
|
||||||
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
||||||
|
Loading…
Reference in New Issue
Block a user