From 22bf157f42d27e0daa3cede3bf31950ccccc9348 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 26 Sep 2023 13:19:24 +0300 Subject: [PATCH] Support Java 21 main launch protocol in prod and dev mode Relates to: #36154 --- .../runner/bootstrap/StartupActionImpl.java | 4 +- .../bootstrap/runner/MainMethodInvoker.java | 73 +++++++++++++++++++ .../bootstrap/runner/QuarkusEntryPoint.java | 10 ++- 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/MainMethodInvoker.java diff --git a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java index b67e9d4b1852f..718a559d26a49 100644 --- a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java +++ b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/StartupActionImpl.java @@ -26,6 +26,7 @@ import io.quarkus.bootstrap.app.RunningQuarkusApplication; import io.quarkus.bootstrap.app.StartupAction; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.bootstrap.runner.MainMethodInvoker; import io.quarkus.builder.BuildResult; import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem; import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem; @@ -104,13 +105,12 @@ public RunningQuarkusApplication runMainClass(String... args) throws Exception { try { // force init here Class appClass = Class.forName(className, true, runtimeClassLoader); - Method start = appClass.getMethod("main", String[].class); Thread t = new Thread(new Runnable() { @Override public void run() { Thread.currentThread().setContextClassLoader(runtimeClassLoader); try { - start.invoke(null, (Object) (args == null ? new String[0] : args)); + MainMethodInvoker.invoke(appClass, args); } catch (Throwable e) { log.error("Error running Quarkus", e); //this can happen if we did not make it to application init diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/MainMethodInvoker.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/MainMethodInvoker.java new file mode 100644 index 0000000000000..ab2841b0fba58 --- /dev/null +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/MainMethodInvoker.java @@ -0,0 +1,73 @@ +package io.quarkus.bootstrap.runner; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public final class MainMethodInvoker { + + // follow the rules outlines in https://openjdk.org/jeps/445 section 'Selecting a main method' + public static void invoke(Class mainClass, Object args) + throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException { + + // public static void main(String[] args) + Method mainWithArgs = null; + try { + mainWithArgs = mainClass.getDeclaredMethod("main", String[].class); + int modifiers = mainWithArgs.getModifiers(); + if (Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { + if (!Modifier.isPublic(modifiers)) { + mainWithArgs.setAccessible(true); + } + mainWithArgs.invoke(null, args); + return; + } + } catch (NoSuchMethodException ignored) { + + } + + // public static void main() + Method mainWithoutArgs = null; + try { + mainWithoutArgs = mainClass.getDeclaredMethod("main"); + int modifiers = mainWithoutArgs.getModifiers(); + if (Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { + if (!Modifier.isPublic(modifiers)) { + mainWithoutArgs.setAccessible(true); + } + mainWithoutArgs.invoke(null); + return; + } + } catch (NoSuchMethodException ignored) { + + } + + var instance = mainClass.getConstructor().newInstance(); + + // public void main(String[] args) + if (mainWithArgs != null) { + int modifiers = mainWithArgs.getModifiers(); + if (!Modifier.isPrivate(modifiers)) { + if (!Modifier.isPublic(modifiers)) { + mainWithArgs.setAccessible(true); + } + mainWithArgs.invoke(instance, args); + return; + } + } + + // public void main() + if (mainWithoutArgs != null) { + int modifiers = mainWithoutArgs.getModifiers(); + if (!Modifier.isPrivate(modifiers)) { + if (!Modifier.isPublic(modifiers)) { + mainWithoutArgs.setAccessible(true); + } + mainWithoutArgs.invoke(instance); + return; + } + } + + throw new NoSuchMethodException("Unable to find main method"); + } +} diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java index 0afc6b6ec1de0..05dabd95f97af 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java @@ -1,5 +1,7 @@ package io.quarkus.bootstrap.runner; +import static io.quarkus.bootstrap.runner.MainMethodInvoker.invoke; + import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; @@ -36,7 +38,7 @@ public static void main(String... args) throws Throwable { } } - private static void doRun(Object args) throws IOException, ClassNotFoundException, IllegalAccessException, + private static void doRun(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { String path = QuarkusEntryPoint.class.getProtectionDomain().getCodeSource().getLocation().getPath(); String decodedPath = URLDecoder.decode(path, "UTF-8"); @@ -57,8 +59,12 @@ private static void doRun(Object args) throws IOException, ClassNotFoundExceptio try { Thread.currentThread().setContextClassLoader(appRunnerClassLoader); QuarkusForkJoinWorkerThread.setQuarkusAppClassloader(appRunnerClassLoader); + Class mainClass = appRunnerClassLoader.loadClass(app.getMainClass()); - mainClass.getMethod("main", String[].class).invoke(null, args); + + invoke(mainClass, args); + } catch (InstantiationException e) { + throw new RuntimeException(e); } finally { QuarkusForkJoinWorkerThread.setQuarkusAppClassloader(null); appRunnerClassLoader.close();