mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 08:31:07 +03:00
[REFACTOR] Using Java 17
This commit is contained in:
parent
0855fc589d
commit
dfbb6e507a
10 changed files with 324 additions and 634 deletions
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
dependencies {
|
||||
api project(':LauncherCore')
|
||||
|
@ -14,29 +14,10 @@ api project(':LauncherCore')
|
|||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
}
|
||||
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
|
||||
tasks.register('sourcesJar', Jar) {
|
||||
from sourceSets.main.allJava
|
||||
archiveClassifier.set('sources')
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
url "https://repo.spring.io/plugins-release/"
|
||||
}
|
||||
}
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||
|
@ -21,28 +21,10 @@
|
|||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation group: 'com.google.code.gson', name: 'gson', version: rootProject['verGson']
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Multi-Release": "true")
|
||||
}
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
|
||||
tasks.register('sourcesJar', Jar) {
|
||||
from sourceSets.main.allJava
|
||||
|
|
|
@ -53,35 +53,12 @@ public static ARCH getArch(String arch) {
|
|||
}
|
||||
|
||||
public static int getVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
if (version.startsWith("1.")) {
|
||||
version = version.substring(2, 3);
|
||||
} else {
|
||||
int dot = version.indexOf(".");
|
||||
if (dot != -1) {
|
||||
version = version.substring(0, dot);
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(version);
|
||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||
return Runtime.version().feature();
|
||||
}
|
||||
|
||||
public static int getBuild() {
|
||||
String version = System.getProperty("java.version");
|
||||
int dot;
|
||||
if (version.startsWith("1.")) {
|
||||
dot = version.indexOf("_");
|
||||
} else {
|
||||
dot = version.lastIndexOf(".");
|
||||
}
|
||||
if (dot != -1) {
|
||||
version = version.substring(dot + 1);
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(version);
|
||||
} catch (NumberFormatException exception) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Runtime.version().update();
|
||||
}
|
||||
|
||||
public static String getNativeExtension(JVMHelper.OS OS_TYPE) {
|
||||
|
|
|
@ -1,17 +1,323 @@
|
|||
package pro.gravit.utils.launch;
|
||||
|
||||
import pro.gravit.utils.helper.HackHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ModuleLaunch implements Launch {
|
||||
private ModuleClassLoader moduleClassLoader;
|
||||
private Configuration configuration;
|
||||
private ModuleLayer.Controller controller;
|
||||
private ModuleFinder moduleFinder;
|
||||
private ModuleLayer layer;
|
||||
private MethodHandles.Lookup hackLookup;
|
||||
private boolean disablePackageDelegateSupport;
|
||||
@Override
|
||||
public ClassLoaderControl init(List<Path> files, String nativePath, LaunchOptions options) {
|
||||
throw new UnsupportedOperationException("Please use Multi-Release JAR");
|
||||
this.disablePackageDelegateSupport = options.disablePackageDelegateSupport;
|
||||
moduleClassLoader = new ModuleClassLoader(files.stream().map((e) -> {
|
||||
try {
|
||||
return e.toUri().toURL();
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}).toArray(URL[]::new), ClassLoader.getPlatformClassLoader());
|
||||
moduleClassLoader.nativePath = nativePath;
|
||||
{
|
||||
if(options.enableHacks) {
|
||||
hackLookup = HackHelper.createHackLookup(ModuleLaunch.class);
|
||||
}
|
||||
if(options.moduleConf != null) {
|
||||
// Create Module Layer
|
||||
moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).map(Path::toAbsolutePath).toArray(Path[]::new));
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
if(options.moduleConf.modules.contains("ALL-MODULE-PATH")) {
|
||||
var set = moduleFinder.findAll();
|
||||
if(LogHelper.isDevEnabled()) {
|
||||
for(var m : set) {
|
||||
LogHelper.dev("Found module %s in %s", m.descriptor().name(), m.location().map(URI::toString).orElse("unknown"));
|
||||
}
|
||||
LogHelper.dev("Found %d modules", set.size());
|
||||
}
|
||||
for(var m : set) {
|
||||
options.moduleConf.modules.add(m.descriptor().name());
|
||||
}
|
||||
options.moduleConf.modules.remove("ALL-MODULE-PATH");
|
||||
}
|
||||
configuration = bootLayer.configuration()
|
||||
.resolveAndBind(moduleFinder, ModuleFinder.of(), options.moduleConf.modules);
|
||||
controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), moduleClassLoader);
|
||||
layer = controller.layer();
|
||||
// Configure exports / opens
|
||||
for(var e : options.moduleConf.exports.entrySet()) {
|
||||
String[] split = e.getKey().split("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
LogHelper.dev("Export module: %s package: %s to %s", moduleName, pkg, e.getValue());
|
||||
Module source = layer.findModule(split[0]).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", moduleName));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addExports(source, pkg, target);
|
||||
} else {
|
||||
controller.addExports(source, pkg, target);
|
||||
}
|
||||
}
|
||||
for(var e : options.moduleConf.opens.entrySet()) {
|
||||
String[] split = e.getKey().split("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
LogHelper.dev("Open module: %s package: %s to %s", moduleName, pkg, e.getValue());
|
||||
Module source = layer.findModule(split[0]).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", moduleName));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addOpens(source, pkg, target);
|
||||
} else {
|
||||
controller.addOpens(source, pkg, target);
|
||||
}
|
||||
}
|
||||
for(var e : options.moduleConf.reads.entrySet()) {
|
||||
LogHelper.dev("Read module %s to %s", e.getKey(), e.getValue());
|
||||
Module source = layer.findModule(e.getKey()).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getKey()));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addReads(source, target);
|
||||
} else {
|
||||
controller.addReads(source, target);
|
||||
}
|
||||
}
|
||||
moduleClassLoader.initializeWithLayer(layer);
|
||||
}
|
||||
}
|
||||
return moduleClassLoader.makeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModule, Collection<String> args) throws Throwable {
|
||||
throw new UnsupportedOperationException("Please use Multi-Release JAR");
|
||||
public void launch(String mainClass, String mainModuleName, Collection<String> args) throws Throwable {
|
||||
Thread.currentThread().setContextClassLoader(moduleClassLoader);
|
||||
if(mainModuleName == null) {
|
||||
Class<?> mainClazz = Class.forName(mainClass, true, moduleClassLoader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity();
|
||||
JVMHelper.fullGC();
|
||||
mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0]));
|
||||
return;
|
||||
}
|
||||
Module mainModule = layer.findModule(mainModuleName).orElseThrow();
|
||||
Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule();
|
||||
if(unnamed != null) {
|
||||
controller.addOpens(mainModule, getPackageFromClass(mainClass), unnamed);
|
||||
}
|
||||
// Start main class
|
||||
ClassLoader loader = mainModule.getClassLoader();
|
||||
Class<?> mainClazz = Class.forName(mainClass, true, loader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class));
|
||||
mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private static String getPackageFromClass(String clazz) {
|
||||
int index = clazz.lastIndexOf(".");
|
||||
if(index >= 0) {
|
||||
return clazz.substring(0, index);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private class ModuleClassLoader extends URLClassLoader {
|
||||
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
||||
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
||||
private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, Module> packageToModule = new HashMap<>();
|
||||
private String nativePath;
|
||||
|
||||
private final List<String> packages = new ArrayList<>();
|
||||
public ModuleClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super("LAUNCHER", urls, parent);
|
||||
packages.add("pro.gravit.launcher.");
|
||||
packages.add("pro.gravit.utils.");
|
||||
}
|
||||
|
||||
private void initializeWithLayer(ModuleLayer layer) {
|
||||
for(var m : layer.modules()) {
|
||||
for(var p : m.getPackages()) {
|
||||
packageToModule.put(p, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if(name != null && !disablePackageDelegateSupport) {
|
||||
for(String pkg : packages) {
|
||||
if(name.startsWith(pkg)) {
|
||||
return SYSTEM_CLASS_LOADER.loadClass(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
var clazz = findClass(null, name);
|
||||
if(clazz == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String moduleName, String name) {
|
||||
Class<?> clazz;
|
||||
{
|
||||
clazz = classMap.get(name);
|
||||
if(clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
if(name != null && !transformers.isEmpty()) {
|
||||
boolean needTransform = false;
|
||||
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
||||
if(t.filter(moduleName, name)) {
|
||||
needTransform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(needTransform) {
|
||||
String rawClassName = name.replace(".", "/").concat(".class");
|
||||
try(InputStream input = getResourceAsStream(rawClassName)) {
|
||||
byte[] bytes = IOHelper.read(input);
|
||||
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
||||
bytes = t.transform(moduleName, name, null, bytes);
|
||||
}
|
||||
clazz = defineClass(name, bytes, 0, bytes.length);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(clazz == null && layer != null && name != null) {
|
||||
var pkg = getPackageFromClass(name);
|
||||
var module = packageToModule.get(pkg);
|
||||
if(module != null) {
|
||||
try {
|
||||
clazz = module.getClassLoader().loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(clazz == null) {
|
||||
try {
|
||||
clazz = super.findClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if(clazz != null) {
|
||||
classMap.put(name, clazz);
|
||||
return clazz;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findLibrary(String name) {
|
||||
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
|
||||
}
|
||||
|
||||
public void addAllowedPackage(String pkg) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
|
||||
private ModuleClassLoaderControl makeControl() {
|
||||
return new ModuleClassLoaderControl();
|
||||
}
|
||||
|
||||
private class ModuleClassLoaderControl implements ClassLoaderControl {
|
||||
|
||||
@Override
|
||||
public void addLauncherPackage(String prefix) {
|
||||
addAllowedPackage(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransformer(ClassTransformer transformer) {
|
||||
transformers.add(transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
ModuleClassLoader.this.addURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJar(Path path) {
|
||||
try {
|
||||
ModuleClassLoader.this.addURL(path.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL[] getURLs() {
|
||||
return ModuleClassLoader.this.getURLs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getClass(String name) throws ClassNotFoundException {
|
||||
return Class.forName(name, false, ModuleClassLoader.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return ModuleClassLoader.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getJava9ModuleController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup getHackLookup() {
|
||||
return hackLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
package pro.gravit.utils.helper;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public final class JVMHelper {
|
||||
|
||||
// MXBeans exports
|
||||
public static final RuntimeMXBean RUNTIME_MXBEAN = ManagementFactory.getRuntimeMXBean();
|
||||
public static final OperatingSystemMXBean OPERATING_SYSTEM_MXBEAN =
|
||||
ManagementFactory.getOperatingSystemMXBean();
|
||||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
// System properties
|
||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||
public static final String NATIVE_EXTENSION = getNativeExtension(OS_TYPE);
|
||||
public static final String NATIVE_PREFIX = getNativePrefix(OS_TYPE);
|
||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||
// Public static fields
|
||||
public static final Runtime RUNTIME = Runtime.getRuntime();
|
||||
public static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
||||
public static final int JVM_VERSION = getVersion();
|
||||
public static final int JVM_BUILD = getBuild();
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.publicLookup(); // Just to initialize class
|
||||
} catch (Throwable exc) {
|
||||
throw new InternalError(exc);
|
||||
}
|
||||
}
|
||||
|
||||
private JVMHelper() {
|
||||
}
|
||||
|
||||
public static ARCH getArch(String arch) {
|
||||
if (arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if (arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if (arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if (arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||
return Runtime.version().feature();
|
||||
}
|
||||
|
||||
public static int getBuild() {
|
||||
return Runtime.version().update();
|
||||
}
|
||||
|
||||
public static String getNativeExtension(JVMHelper.OS OS_TYPE) {
|
||||
switch (OS_TYPE) {
|
||||
case MUSTDIE:
|
||||
return ".dll";
|
||||
case LINUX:
|
||||
return ".so";
|
||||
case MACOSX:
|
||||
return ".dylib";
|
||||
default:
|
||||
throw new InternalError(String.format("Unsupported OS TYPE '%s'", OS_TYPE));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getNativePrefix(JVMHelper.OS OS_TYPE) {
|
||||
switch (OS_TYPE) {
|
||||
case LINUX:
|
||||
case MACOSX:
|
||||
return "lib";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void appendVars(ProcessBuilder builder, Map<String, String> vars) {
|
||||
builder.environment().putAll(vars);
|
||||
}
|
||||
|
||||
public static Class<?> firstClass(String... names) throws ClassNotFoundException {
|
||||
for (String name : names)
|
||||
try {
|
||||
return Class.forName(name, false, LOADER);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// Expected
|
||||
}
|
||||
throw new ClassNotFoundException(Arrays.toString(names));
|
||||
}
|
||||
|
||||
public static void fullGC() {
|
||||
RUNTIME.gc();
|
||||
LogHelper.debug("Used heap: %d MiB", RUNTIME.totalMemory() - RUNTIME.freeMemory() >> 20);
|
||||
}
|
||||
|
||||
public static String[] getClassPath() {
|
||||
return System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
}
|
||||
|
||||
public static URL[] getClassPathURL() {
|
||||
String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
URL[] list = new URL[cp.length];
|
||||
|
||||
for (int i = 0; i < cp.length; i++) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(cp[i]);
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
list[i] = url;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static X509Certificate[] getCertificates(Class<?> clazz) {
|
||||
Object[] signers = clazz.getSigners();
|
||||
if (signers == null) return null;
|
||||
return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new);
|
||||
}
|
||||
|
||||
public static void checkStackTrace(Class<?> mainClass) {
|
||||
LogHelper.debug("Testing stacktrace");
|
||||
Exception e = new Exception("Testing stacktrace");
|
||||
StackTraceElement[] list = e.getStackTrace();
|
||||
if (!list[list.length - 1].getClassName().equals(mainClass.getName())) {
|
||||
throw new SecurityException(String.format("Invalid StackTraceElement: %s", list[list.length - 1].getClassName()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCorrectOSArch() {
|
||||
// As always, mustdie must die
|
||||
if (OS_TYPE == OS.MUSTDIE)
|
||||
return System.getenv("ProgramFiles(x86)") == null ? 32 : 64;
|
||||
|
||||
// Or trust system property (maybe incorrect)
|
||||
return System.getProperty("os.arch").contains("64") ? 64 : 32;
|
||||
}
|
||||
|
||||
public static String getEnvPropertyCaseSensitive(String name) {
|
||||
return System.getenv().get(name);
|
||||
}
|
||||
|
||||
public static boolean isJVMMatchesSystemArch() {
|
||||
return JVM_BITS == OS_BITS;
|
||||
}
|
||||
|
||||
public static String jvmProperty(String name, String value) {
|
||||
return String.format("-D%s=%s", name, value);
|
||||
}
|
||||
|
||||
public static String systemToJvmProperty(String name) {
|
||||
return String.format("-D%s=%s", name, System.getProperties().getProperty(name));
|
||||
}
|
||||
|
||||
public static void addSystemPropertyToArgs(Collection<String> args, String name) {
|
||||
String property = System.getProperty(name);
|
||||
if (property != null)
|
||||
args.add(String.format("-D%s=%s", name, property));
|
||||
}
|
||||
|
||||
public static void verifySystemProperties(Class<?> mainClass, boolean requireSystem) {
|
||||
Locale.setDefault(Locale.US);
|
||||
// Verify class loader
|
||||
LogHelper.debug("Verifying class loader");
|
||||
if (requireSystem && !mainClass.getClassLoader().equals(LOADER))
|
||||
throw new SecurityException("ClassLoader should be system");
|
||||
|
||||
// Verify system and java architecture
|
||||
LogHelper.debug("Verifying JVM architecture");
|
||||
}
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum OS {
|
||||
MUSTDIE("mustdie"), LINUX("linux"), MACOSX("macosx");
|
||||
|
||||
public final String name;
|
||||
|
||||
OS(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static OS byName(String name) {
|
||||
if (name.startsWith("Windows"))
|
||||
return MUSTDIE;
|
||||
if (name.startsWith("Linux"))
|
||||
return LINUX;
|
||||
if (name.startsWith("Mac OS X"))
|
||||
return MACOSX;
|
||||
throw new RuntimeException(String.format("This shit is not yet supported: '%s'", name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,323 +0,0 @@
|
|||
package pro.gravit.utils.launch;
|
||||
|
||||
import pro.gravit.utils.helper.HackHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ModuleLaunch implements Launch {
|
||||
private ModuleClassLoader moduleClassLoader;
|
||||
private Configuration configuration;
|
||||
private ModuleLayer.Controller controller;
|
||||
private ModuleFinder moduleFinder;
|
||||
private ModuleLayer layer;
|
||||
private MethodHandles.Lookup hackLookup;
|
||||
private boolean disablePackageDelegateSupport;
|
||||
@Override
|
||||
public ClassLoaderControl init(List<Path> files, String nativePath, LaunchOptions options) {
|
||||
this.disablePackageDelegateSupport = options.disablePackageDelegateSupport;
|
||||
moduleClassLoader = new ModuleClassLoader(files.stream().map((e) -> {
|
||||
try {
|
||||
return e.toUri().toURL();
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}).toArray(URL[]::new), ClassLoader.getPlatformClassLoader());
|
||||
moduleClassLoader.nativePath = nativePath;
|
||||
{
|
||||
if(options.enableHacks) {
|
||||
hackLookup = HackHelper.createHackLookup(ModuleLaunch.class);
|
||||
}
|
||||
if(options.moduleConf != null) {
|
||||
// Create Module Layer
|
||||
moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).map(Path::toAbsolutePath).toArray(Path[]::new));
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
if(options.moduleConf.modules.contains("ALL-MODULE-PATH")) {
|
||||
var set = moduleFinder.findAll();
|
||||
if(LogHelper.isDevEnabled()) {
|
||||
for(var m : set) {
|
||||
LogHelper.dev("Found module %s in %s", m.descriptor().name(), m.location().map(URI::toString).orElse("unknown"));
|
||||
}
|
||||
LogHelper.dev("Found %d modules", set.size());
|
||||
}
|
||||
for(var m : set) {
|
||||
options.moduleConf.modules.add(m.descriptor().name());
|
||||
}
|
||||
options.moduleConf.modules.remove("ALL-MODULE-PATH");
|
||||
}
|
||||
configuration = bootLayer.configuration()
|
||||
.resolveAndBind(moduleFinder, ModuleFinder.of(), options.moduleConf.modules);
|
||||
controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), moduleClassLoader);
|
||||
layer = controller.layer();
|
||||
// Configure exports / opens
|
||||
for(var e : options.moduleConf.exports.entrySet()) {
|
||||
String[] split = e.getKey().split("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
LogHelper.dev("Export module: %s package: %s to %s", moduleName, pkg, e.getValue());
|
||||
Module source = layer.findModule(split[0]).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", moduleName));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addExports(source, pkg, target);
|
||||
} else {
|
||||
controller.addExports(source, pkg, target);
|
||||
}
|
||||
}
|
||||
for(var e : options.moduleConf.opens.entrySet()) {
|
||||
String[] split = e.getKey().split("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
LogHelper.dev("Open module: %s package: %s to %s", moduleName, pkg, e.getValue());
|
||||
Module source = layer.findModule(split[0]).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", moduleName));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addOpens(source, pkg, target);
|
||||
} else {
|
||||
controller.addOpens(source, pkg, target);
|
||||
}
|
||||
}
|
||||
for(var e : options.moduleConf.reads.entrySet()) {
|
||||
LogHelper.dev("Read module %s to %s", e.getKey(), e.getValue());
|
||||
Module source = layer.findModule(e.getKey()).orElse(null);
|
||||
if(source == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getKey()));
|
||||
}
|
||||
Module target = layer.findModule(e.getValue()).orElse(null);
|
||||
if(target == null) {
|
||||
throw new RuntimeException(String.format("Module %s not found", e.getValue()));
|
||||
}
|
||||
if(options.enableHacks && source.getLayer() != layer) {
|
||||
ModuleHacks.createController(hackLookup, source.getLayer()).addReads(source, target);
|
||||
} else {
|
||||
controller.addReads(source, target);
|
||||
}
|
||||
}
|
||||
moduleClassLoader.initializeWithLayer(layer);
|
||||
}
|
||||
}
|
||||
return moduleClassLoader.makeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModuleName, Collection<String> args) throws Throwable {
|
||||
Thread.currentThread().setContextClassLoader(moduleClassLoader);
|
||||
if(mainModuleName == null) {
|
||||
Class<?> mainClazz = Class.forName(mainClass, true, moduleClassLoader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity();
|
||||
JVMHelper.fullGC();
|
||||
mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0]));
|
||||
return;
|
||||
}
|
||||
Module mainModule = layer.findModule(mainModuleName).orElseThrow();
|
||||
Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule();
|
||||
if(unnamed != null) {
|
||||
controller.addOpens(mainModule, getPackageFromClass(mainClass), unnamed);
|
||||
}
|
||||
// Start main class
|
||||
ClassLoader loader = mainModule.getClassLoader();
|
||||
Class<?> mainClazz = Class.forName(mainClass, true, loader);
|
||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class));
|
||||
mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private static String getPackageFromClass(String clazz) {
|
||||
int index = clazz.lastIndexOf(".");
|
||||
if(index >= 0) {
|
||||
return clazz.substring(0, index);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private class ModuleClassLoader extends URLClassLoader {
|
||||
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
||||
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
||||
private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, Module> packageToModule = new HashMap<>();
|
||||
private String nativePath;
|
||||
|
||||
private final List<String> packages = new ArrayList<>();
|
||||
public ModuleClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super("LAUNCHER", urls, parent);
|
||||
packages.add("pro.gravit.launcher.");
|
||||
packages.add("pro.gravit.utils.");
|
||||
}
|
||||
|
||||
private void initializeWithLayer(ModuleLayer layer) {
|
||||
for(var m : layer.modules()) {
|
||||
for(var p : m.getPackages()) {
|
||||
packageToModule.put(p, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if(name != null && !disablePackageDelegateSupport) {
|
||||
for(String pkg : packages) {
|
||||
if(name.startsWith(pkg)) {
|
||||
return SYSTEM_CLASS_LOADER.loadClass(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
var clazz = findClass(null, name);
|
||||
if(clazz == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String moduleName, String name) {
|
||||
Class<?> clazz;
|
||||
{
|
||||
clazz = classMap.get(name);
|
||||
if(clazz != null) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
if(name != null && !transformers.isEmpty()) {
|
||||
boolean needTransform = false;
|
||||
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
||||
if(t.filter(moduleName, name)) {
|
||||
needTransform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(needTransform) {
|
||||
String rawClassName = name.replace(".", "/").concat(".class");
|
||||
try(InputStream input = getResourceAsStream(rawClassName)) {
|
||||
byte[] bytes = IOHelper.read(input);
|
||||
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
||||
bytes = t.transform(moduleName, name, null, bytes);
|
||||
}
|
||||
clazz = defineClass(name, bytes, 0, bytes.length);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(clazz == null && layer != null && name != null) {
|
||||
var pkg = getPackageFromClass(name);
|
||||
var module = packageToModule.get(pkg);
|
||||
if(module != null) {
|
||||
try {
|
||||
clazz = module.getClassLoader().loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(clazz == null) {
|
||||
try {
|
||||
clazz = super.findClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if(clazz != null) {
|
||||
classMap.put(name, clazz);
|
||||
return clazz;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findLibrary(String name) {
|
||||
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
|
||||
}
|
||||
|
||||
public void addAllowedPackage(String pkg) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
|
||||
private ModuleClassLoaderControl makeControl() {
|
||||
return new ModuleClassLoaderControl();
|
||||
}
|
||||
|
||||
private class ModuleClassLoaderControl implements ClassLoaderControl {
|
||||
|
||||
@Override
|
||||
public void addLauncherPackage(String prefix) {
|
||||
addAllowedPackage(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTransformer(ClassTransformer transformer) {
|
||||
transformers.add(transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
ModuleClassLoader.this.addURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJar(Path path) {
|
||||
try {
|
||||
ModuleClassLoader.this.addURL(path.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL[] getURLs() {
|
||||
return ModuleClassLoader.this.getURLs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getClass(String name) throws ClassNotFoundException {
|
||||
return Class.forName(name, false, ModuleClassLoader.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return ModuleClassLoader.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getJava9ModuleController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup getHackLookup() {
|
||||
return hackLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,8 +8,8 @@
|
|||
url "https://repo.spring.io/plugins-release/"
|
||||
}
|
||||
}
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
|
|
|
@ -14,30 +14,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
java11 {
|
||||
java {
|
||||
srcDirs = ['src/main/java11']
|
||||
}
|
||||
dependencies {
|
||||
java11Implementation project(':LauncherAPI')
|
||||
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
compileJava11Java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
}
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
jar {
|
||||
into('META-INF/versions/11') {
|
||||
from sourceSets.java11.output
|
||||
}
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Main-Class": mainClassName,
|
||||
"Premain-Class": mainAgentName,
|
||||
|
|
Loading…
Reference in a new issue