diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java index d821d43a49f22..439210c22e07b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.stream.Collectors; @@ -25,6 +26,7 @@ import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; import org.jboss.logging.Logger; +import org.objectweb.asm.ClassVisitor; import io.quarkus.bootstrap.logging.InitialConfigurator; import io.quarkus.bootstrap.logging.QuarkusDelayedHandler; @@ -40,6 +42,7 @@ import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; import io.quarkus.deployment.builditem.BytecodeRecorderConstantDefinitionBuildItem; import io.quarkus.deployment.builditem.BytecodeRecorderObjectLoaderBuildItem; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; @@ -65,6 +68,7 @@ import io.quarkus.gizmo.BytecodeCreator; import io.quarkus.gizmo.CatchBlockCreator; import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassTransformer; import io.quarkus.gizmo.FieldCreator; import io.quarkus.gizmo.FieldDescriptor; import io.quarkus.gizmo.MethodCreator; @@ -106,6 +110,7 @@ public class MainClassBuildStep { void.class); private static final DotName QUARKUS_APPLICATION = DotName.createSimple(QuarkusApplication.class.getName()); private static final DotName OBJECT = DotName.createSimple(Object.class.getName()); + private static final Type STRING_ARRAY = Type.create(DotName.createSimple(String[].class.getName()), Type.Kind.ARRAY); @BuildStep void build(List staticInitTasks, @@ -143,14 +148,14 @@ void build(List staticInitTasks, scField.setModifiers(Modifier.PUBLIC | Modifier.STATIC); MethodCreator ctor = file.getMethodCreator("", void.class); - ctor.invokeSpecialMethod(MethodDescriptor.ofMethod(Application.class, "", void.class, boolean.class), + ctor.invokeSpecialMethod(ofMethod(Application.class, "", void.class, boolean.class), ctor.getThis(), ctor.load(launchMode.isAuxiliaryApplication())); ctor.returnValue(null); MethodCreator mv = file.getMethodCreator("", void.class); mv.setModifiers(Modifier.PUBLIC | Modifier.STATIC); if (!namingConfig.enableJndi && allowJNDIBuildItems.isEmpty()) { - mv.invokeStaticMethod(MethodDescriptor.ofMethod(DisabledInitialContextManager.class, "register", void.class)); + mv.invokeStaticMethod(ofMethod(DisabledInitialContextManager.class, "register", void.class)); } //very first thing is to set system props (for build time) @@ -161,12 +166,12 @@ void build(List staticInitTasks, //set the launch mode ResultHandle lm = mv .readStaticField(FieldDescriptor.of(LaunchMode.class, launchMode.getLaunchMode().name(), LaunchMode.class)); - mv.invokeStaticMethod(MethodDescriptor.ofMethod(ProfileManager.class, "setLaunchMode", void.class, LaunchMode.class), + mv.invokeStaticMethod(ofMethod(ProfileManager.class, "setLaunchMode", void.class, LaunchMode.class), lm); mv.invokeStaticMethod(CONFIGURE_STEP_TIME_ENABLED); - mv.invokeStaticMethod(MethodDescriptor.ofMethod(Timing.class, "staticInitStarted", void.class, boolean.class), + mv.invokeStaticMethod(ofMethod(Timing.class, "staticInitStarted", void.class, boolean.class), mv.load(launchMode.isAuxiliaryApplication())); // ensure that the config class is initialized @@ -248,7 +253,7 @@ void build(List staticInitTasks, //now set the command line arguments mv.invokeVirtualMethod( - MethodDescriptor.ofMethod(StartupContext.class, "setCommandLineArguments", void.class, String[].class), + ofMethod(StartupContext.class, "setCommandLineArguments", void.class, String[].class), startupContext, mv.getMethodParam(0)); mv.invokeStaticMethod(CONFIGURE_STEP_TIME_ENABLED); @@ -333,6 +338,7 @@ void build(List staticInitTasks, @BuildStep public MainClassBuildItem mainClassBuildStep(BuildProducer generatedClass, + BuildProducer transformedClass, ApplicationArchivesBuildItem applicationArchivesBuildItem, CombinedIndexBuildItem combinedIndexBuildItem, Optional quarkusApplicationClass, @@ -357,6 +363,7 @@ public MainClassBuildItem mainClassBuildStep(BuildProducer impls = index .getAllKnownImplementors(QUARKUS_APPLICATION); ClassInfo classByName = index.getClassByName(DotName.createSimple(mainClassName)); - MethodInfo mainClassMethod = null; if (classByName != null) { mainClassMethod = classByName - .method("main", Type.create(DotName.createSimple(String[].class.getName()), Type.Kind.ARRAY)); + .method("main", STRING_ARRAY); } if (mainClassMethod == null) { boolean found = false; @@ -415,6 +421,10 @@ public MainClassBuildItem mainClassBuildStep(BuildProducerJEP 445. + * Note that we can support this regardless of the JDK version running the application. + */ + private static class MainMethodTransformer implements BiFunction { + + private final IndexView index; + + public MainMethodTransformer(IndexView index) { + this.index = index; + } + + @Override + public ClassVisitor apply(String mainClassName, ClassVisitor outputClassVisitor) { + ClassInfo mainClassInfo = index.getClassByName(mainClassName); + if (mainClassInfo == null) { + throw new IllegalStateException(mainClassName + " should have a corresponding ClassInfo at this point"); + } + ClassTransformer transformer = new ClassTransformer(mainClassName); + Result result = doApply(mainClassName, outputClassVisitor, transformer, mainClassInfo); + if (!result.isValid) { + throw new RuntimeException(errorMessage(mainClassName)); + } + if (result.classVisitor == null) { + throw new IllegalStateException("result.classvisitor should not be null at this point"); + } + return result.classVisitor; + } + + private Result doApply(String originalMainClassName, + ClassVisitor classVisitor, ClassTransformer transformer, + ClassInfo currentClassInfo) { + boolean isTopLevel = currentClassInfo.name().toString().equals(originalMainClassName); + boolean allowStatic = isTopLevel; + boolean hasStaticWithArgs = false; + boolean hasStaticWithoutArgs = false; + boolean hasInstanceWithArgs = false; + boolean hasInstanceWithoutArgs = false; + + MethodInfo withArgs = currentClassInfo.method("main", STRING_ARRAY); + MethodInfo withoutArgs = currentClassInfo.method("main"); + + if (withArgs != null) { + if (Modifier.isStatic(withArgs.flags())) { + if (allowStatic) { + hasStaticWithArgs = true; + } + } else { + hasInstanceWithArgs = true; + } + } + if (withoutArgs != null) { + if (Modifier.isStatic(withoutArgs.flags())) { + if (allowStatic) { + hasStaticWithoutArgs = true; + } + } else { + hasInstanceWithoutArgs = true; + } + } + + Result result; + + //impl NOTE: the sequence of boolean checks is very important as it follows what the JEP says is the proper sequence of method lookups + if (hasStaticWithArgs) { + if (Modifier.isPublic(withArgs.flags())) { + // nothing to do here + result = Result.valid(classVisitor); + } else if (Modifier.isPrivate(withArgs.flags())) { + // the launch protocol says we can't use this one, but we still need to rename it to avoid conflicts with the potentially generated main + transformer.modifyMethod(MethodDescriptor.of(withArgs)).rename("$originalMain$"); + result = Result.invalid(transformer.applyTo(classVisitor)); + } else { + // this is the simplest case where we just make the method public + transformer.modifyMethod(MethodDescriptor.of(withArgs)).removeModifiers(Modifier.PROTECTED) + .addModifiers(Modifier.PUBLIC); + result = Result.valid(transformer.applyTo(classVisitor)); + } + } else if (hasStaticWithoutArgs) { + if (Modifier.isPrivate(withoutArgs.flags())) { + // the launch protocol says we can't use this one + result = Result.invalid(); + ; + } else { + // we create a public static void(String[] args) method and all the target from it + MethodCreator standardMain = createStandardMain(transformer); + standardMain.invokeStaticMethod(MethodDescriptor.of(withoutArgs)); + standardMain.returnValue(null); + result = Result.valid(transformer.applyTo(classVisitor)); + } + } else if (hasInstanceWithArgs) { + if (Modifier.isPrivate(withArgs.flags())) { + // the launch protocol says we can't use this one, but we still need to rename it to avoid conflicts with the potentially generated main + transformer.modifyMethod(MethodDescriptor.of(withArgs)).rename("$originalMain$"); + result = Result.invalid(transformer.applyTo(classVisitor)); + } else { + // here we need to construct an instance and call the instance method with the args parameter + MethodCreator standardMain = createStandardMain(transformer); + ResultHandle instanceHandle = standardMain.newInstance(ofConstructor(originalMainClassName)); + ResultHandle argsParamHandle = standardMain.getMethodParam(0); + if (isTopLevel) { + // we need to rename the method in order to avoid having two main methods with the same name + standardMain.invokeVirtualMethod( + ofMethod(originalMainClassName, "$$main$$", void.class, String[].class), + instanceHandle, argsParamHandle); + + transformer.modifyMethod(MethodDescriptor.of(withArgs)).rename("$$main$$"); + } else { + // Invoke super + standardMain.invokeSpecialMethod(withArgs, instanceHandle, argsParamHandle); + } + standardMain.returnValue(null); + result = Result.valid(transformer.applyTo(classVisitor)); + } + } else if (hasInstanceWithoutArgs) { + if (Modifier.isPrivate(withoutArgs.flags())) { + // the launch protocol says we can't use this one + result = Result.invalid(); + } else { + // here we need to construct an instance and call the instance method without any parameters + MethodCreator standardMain = createStandardMain(transformer); + ResultHandle instanceHandle = standardMain.newInstance(ofConstructor(originalMainClassName)); + standardMain.invokeVirtualMethod(MethodDescriptor.of(withoutArgs), instanceHandle); + standardMain.returnValue(null); + result = Result.valid(transformer.applyTo(classVisitor)); + } + } else { + // this means that no main (with our without args was found) + result = resultFromSuper(originalMainClassName, classVisitor, transformer, currentClassInfo); + } + if (!result.isValid) { + // this means there were private main methods that we ignored + result = resultFromSuper(originalMainClassName, classVisitor, transformer, currentClassInfo); + } + + return result; + } + + private Result resultFromSuper(String originalMainClassName, ClassVisitor outputClassVisitor, + ClassTransformer transformer, ClassInfo currentClassInfo) { + DotName superName = currentClassInfo.superName(); + if (superName.equals(OBJECT)) { + // no valid main method was found + return Result.invalid(); + } + ClassInfo superClassInfo = index.getClassByName(superName); + if (superClassInfo == null) { + throw new IllegalStateException("Unable to find main method on class '" + originalMainClassName + + "' while it was also not possible to traverse the class hierarchy"); + } + + // check if the superclass has any valid candidates + return doApply(originalMainClassName, outputClassVisitor, transformer, superClassInfo); + } + + private static String errorMessage(String originalMainClassName) { + return "Unable to find a valid main method on class '" + originalMainClassName + + "'. See https://openjdk.org/jeps/445 for details of what constitutes a valid main method."; + } + + private static MethodCreator createStandardMain(ClassTransformer transformer) { + return transformer.addMethod("main", void.class, String[].class) + .setModifiers(Modifier.PUBLIC | Modifier.STATIC); + } + + private static class Result { + private final boolean isValid; + private final ClassVisitor classVisitor; + + private Result(boolean isValid, ClassVisitor classVisitor) { + this.isValid = isValid; + this.classVisitor = classVisitor; + } + + private static Result valid(ClassVisitor classVisitor) { + return new Result(true, classVisitor); + } + + private static Result invalid(ClassVisitor classVisitor) { + return new Result(false, classVisitor); + } + + private static Result invalid() { + return new Result(false, null); + } + } + } + } diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/IgnorePrivateMainCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/IgnorePrivateMainCommandModeTestCase.java new file mode 100644 index 0000000000000..7bc7f11168a71 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/IgnorePrivateMainCommandModeTestCase.java @@ -0,0 +1,48 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class IgnorePrivateMainCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldSuperSuper.class, HelloWorldSuper.class, HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain extends HelloWorldSuper { + + private static void main(String[] args) { + System.out.println("Private Static World"); + } + } + + public static class HelloWorldSuperSuper { + + protected void main() { + System.out.println("Hello World"); + } + } + + public static class HelloWorldSuper extends HelloWorldSuperSuper { + + private void main(String[] args) { + System.out.println("Private Instance World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainCommandModeTestCase.java new file mode 100644 index 0000000000000..bbe2358693c7d --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainCommandModeTestCase.java @@ -0,0 +1,34 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class InstanceMainCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + void main(String[] args) { + System.out.println("Hello World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassCommandModeTestCase.java new file mode 100644 index 0000000000000..de83882c43e2c --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassCommandModeTestCase.java @@ -0,0 +1,45 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class InstanceMainInSuperClassCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldSuperSuper.class, HelloWorldSuper.class, HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain extends HelloWorldSuper { + + } + + public static class HelloWorldSuperSuper { + + protected void main() { + System.out.println("Hello World"); + } + } + + public static class HelloWorldSuper extends HelloWorldSuperSuper { + + protected void main2() { + System.out.println("Hello"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassNoArgsCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassNoArgsCommandModeTestCase.java new file mode 100644 index 0000000000000..f61ffce1970c0 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainInSuperClassNoArgsCommandModeTestCase.java @@ -0,0 +1,46 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class InstanceMainInSuperClassNoArgsCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldSuper.class, HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain extends HelloWorldSuper { + + } + + public static class HelloWorldSuper { + + void main() { + System.out.println("Hello World"); + } + + void main2() { + System.out.println("Hello"); + } + + void main3(String[] args) { + System.out.println("Hello"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainNoArgsCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainNoArgsCommandModeTestCase.java new file mode 100644 index 0000000000000..d67ee8059d955 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/InstanceMainNoArgsCommandModeTestCase.java @@ -0,0 +1,34 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class InstanceMainNoArgsCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + void main() { + System.out.println("Hello World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MixWithAndWithoutArgsMainCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MixWithAndWithoutArgsMainCommandModeTestCase.java new file mode 100644 index 0000000000000..5dc1f311814d5 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MixWithAndWithoutArgsMainCommandModeTestCase.java @@ -0,0 +1,38 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class MixWithAndWithoutArgsMainCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + static void main(String[] args) { + System.out.println("Hello World"); + } + + void main() { + System.out.println("Hi World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MultipleInstanceMainInSuperClassCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MultipleInstanceMainInSuperClassCommandModeTestCase.java new file mode 100644 index 0000000000000..fbf74a64a533f --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/MultipleInstanceMainInSuperClassCommandModeTestCase.java @@ -0,0 +1,49 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class MultipleInstanceMainInSuperClassCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldSuperSuper.class, HelloWorldSuper.class, HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hi World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain extends HelloWorldSuper { + + } + + public static class HelloWorldSuperSuper { + + protected void main(String[] args) { + System.out.println("Hi World"); + } + + protected void main() { + System.out.println("Hello World"); + } + } + + public static class HelloWorldSuper extends HelloWorldSuperSuper { + + protected void main2() { + System.out.println("Hello"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/NonPublicStaticMainCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/NonPublicStaticMainCommandModeTestCase.java new file mode 100644 index 0000000000000..6fa21df673009 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/NonPublicStaticMainCommandModeTestCase.java @@ -0,0 +1,34 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class NonPublicStaticMainCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + public static void main(String[] args) { + System.out.println("Hello World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/PrivateStaticMainCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/PrivateStaticMainCommandModeTestCase.java new file mode 100644 index 0000000000000..c1fe15c918ba0 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/PrivateStaticMainCommandModeTestCase.java @@ -0,0 +1,33 @@ +package io.quarkus.commandmode.launch; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class PrivateStaticMainCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectedException(RuntimeException.class); + + @Test + public void shouldNeverRun() { + fail(); + } + + @QuarkusMain + public static class HelloWorldMain { + + private static void main(String[] args) { + System.out.println("Hello World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/ProtectedStaticMainNoArgsCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/ProtectedStaticMainNoArgsCommandModeTestCase.java new file mode 100644 index 0000000000000..9ac771f20d71e --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/ProtectedStaticMainNoArgsCommandModeTestCase.java @@ -0,0 +1,34 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class ProtectedStaticMainNoArgsCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + protected static void main() { + System.out.println("Hello World"); + } + } + +} diff --git a/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/StaticMainNoArgsCommandModeTestCase.java b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/StaticMainNoArgsCommandModeTestCase.java new file mode 100644 index 0000000000000..218e5e5493fd1 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/test/java/io/quarkus/commandmode/launch/StaticMainNoArgsCommandModeTestCase.java @@ -0,0 +1,34 @@ +package io.quarkus.commandmode.launch; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.test.QuarkusProdModeTest; + +public class StaticMainNoArgsCommandModeTestCase { + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloWorldMain.class)) + .setApplicationName("run-exit") + .setApplicationVersion("0.1-SNAPSHOT") + .setExpectExit(true) + .setRun(true); + + @Test + public void testRun() { + Assertions.assertThat(config.getStartupConsoleOutput()).contains("Hello World"); + Assertions.assertThat(config.getExitCode()).isEqualTo(0); + } + + @QuarkusMain + public static class HelloWorldMain { + + static void main() { + System.out.println("Hello World"); + } + } + +}