[REFACTOR] Using Java 17

This commit is contained in:
Gravita 2023-12-23 12:18:10 +07:00
parent 0855fc589d
commit dfbb6e507a
10 changed files with 324 additions and 634 deletions

View File

@ -1,5 +1,5 @@
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '17'
targetCompatibility = '17'
dependencies {
api project(':LauncherCore')
@ -14,29 +14,10 @@ test {
}
}
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')

View File

@ -8,8 +8,8 @@ repositories {
url "https://repo.spring.io/plugins-release/"
}
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '17'
targetCompatibility = '17'
jar {
archiveClassifier.set('clean')

View File

@ -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 @@ test {
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

View File

@ -53,35 +53,12 @@ public final class JVMHelper {
}
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) {

View File

@ -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;
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -8,8 +8,8 @@ repositories {
url "https://repo.spring.io/plugins-release/"
}
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '17'
targetCompatibility = '17'
jar {
archiveClassifier.set('clean')

View File

@ -14,30 +14,10 @@ repositories {
}
}
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,