From 01547275b5583bd4198e10ae141f6f99db501aa8 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 5 Mar 2019 09:41:22 +0200 Subject: [PATCH 1/8] Add very basic support for command line only applications Relates to: #284 --- bom/pom.xml | 6 + build-parent/pom.xml | 10 ++ .../quarkus/deployment/ExtensionLoader.java | 24 +++- .../quarkus/deployment/QuarkusAugmentor.java | 3 + .../deployment/annotations/ExecutionTime.java | 7 +- ...AfterStartupBytecodeRecorderBuildItem.java | 18 +++ .../builditem/MainArgsBuildItem.java | 23 ++++ .../builditem/ShutdownBuildItem.java | 6 + .../deployment/steps/MainClassBuildStep.java | 31 +++++ .../java/io/quarkus/runtime/Application.java | 9 ++ .../io/quarkus/runtime/MainArgsSupplier.java | 6 + .../io/quarkus/runtime/StartupContext.java | 9 ++ .../command-line-runner/deployment/pom.xml | 61 ++++++++++ .../CommandLineRunnerBuildItem.java | 16 +++ .../CommandLineRunnerProcessor.java | 68 +++++++++++ extensions/command-line-runner/pom.xml | 37 ++++++ .../command-line-runner/runtime/pom.xml | 60 +++++++++ .../quarkus/clrunner/CommandLineRunner.java | 6 + .../runtime/CommandLineRunnerTemplate.java | 25 ++++ extensions/pom.xml | 2 + integration-tests/command-line-runner/pom.xml | 103 ++++++++++++++++ .../clrunner/tests/CommandLineRunnerIT.java | 70 +++++++++++ .../support/MavenProcessInvocationResult.java | 45 +++++++ .../tests/support/MavenProcessInvoker.java | 115 ++++++++++++++++++ .../clrunner/tests/support/MojoTestBase.java | 86 +++++++++++++ .../tests/support/RunningInvoker.java | 74 +++++++++++ .../test/resources/projects/classic/pom.xml | 62 ++++++++++ .../src/main/java/org/acme/Capitalizer.java | 13 ++ .../src/main/java/org/acme/ExampleRunner.java | 40 ++++++ .../META-INF/microprofile-config.properties | 3 + integration-tests/pom.xml | 2 + 31 files changed, 1034 insertions(+), 6 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/ShutdownBuildItem.java create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java create mode 100644 extensions/command-line-runner/deployment/pom.xml create mode 100644 extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java create mode 100644 extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java create mode 100644 extensions/command-line-runner/pom.xml create mode 100644 extensions/command-line-runner/runtime/pom.xml create mode 100644 extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/CommandLineRunner.java create mode 100644 extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java create mode 100644 integration-tests/command-line-runner/pom.xml create mode 100644 integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java create mode 100644 integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java create mode 100644 integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java create mode 100644 integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java create mode 100644 integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java create mode 100644 integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml create mode 100644 integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java create mode 100644 integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java create mode 100644 integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties diff --git a/bom/pom.xml b/bom/pom.xml index 8c17bb94f3c89..864ed13d74bb0 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -419,6 +419,12 @@ ${project.version} provided + + io.quarkus + quarkus-command-line-runner + ${project.version} + provided + diff --git a/build-parent/pom.xml b/build-parent/pom.xml index c592236aa0c64..67a858b648432 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -541,6 +541,16 @@ quarkus-legacy-launcher ${project.version} + + io.quarkus + quarkus-command-line-runner + ${project.version} + + + io.quarkus + quarkus-command-line-runner-runtime + ${project.version} + diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java index 41d7f6d2aba9a..7c2f3c61a623f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java @@ -51,6 +51,7 @@ import io.quarkus.deployment.builditem.AdditionalApplicationArchiveMarkerBuildItem; import io.quarkus.deployment.builditem.CapabilityBuildItem; import io.quarkus.deployment.builditem.ConfigurationBuildItem; +import io.quarkus.deployment.builditem.MainAfterStartupBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem; import io.quarkus.deployment.recording.BytecodeRecorderImpl; @@ -283,10 +284,21 @@ public static Consumer loadStepsFrom(Class clazz) { assert recordAnnotation != null; final ExecutionTime executionTime = recordAnnotation.value(); final boolean optional = recordAnnotation.optional(); - methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces( - executionTime == ExecutionTime.STATIC_INIT ? StaticBytecodeRecorderBuildItem.class - : MainBytecodeRecorderBuildItem.class, - optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE)); + methodStepConfig = methodStepConfig.andThen(bsb -> { + Class buildItem = executionTime == ExecutionTime.STATIC_INIT + ? StaticBytecodeRecorderBuildItem.class + : MainBytecodeRecorderBuildItem.class; + if (executionTime == ExecutionTime.STATIC_INIT) { + buildItem = StaticBytecodeRecorderBuildItem.class; + } else if (executionTime == ExecutionTime.RUNTIME_INIT) { + buildItem = MainBytecodeRecorderBuildItem.class; + } else if (executionTime == ExecutionTime.AFTER_STARTUP) { + buildItem = MainAfterStartupBytecodeRecorderBuildItem.class; + } + bsb.produces( + buildItem, + optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE); + }); } boolean methodConsumingConfig = consumingConfig; if (methodParameters.length == 0) { @@ -449,8 +461,10 @@ public void execute(final BuildContext bc) { // commit recorded data if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) { bc.produce(new StaticBytecodeRecorderBuildItem(bri)); - } else { + } else if (recordAnnotation.value() == ExecutionTime.RUNTIME_INIT) { bc.produce(new MainBytecodeRecorderBuildItem(bri)); + } else if (recordAnnotation.value() == ExecutionTime.AFTER_STARTUP) { + bc.produce(new MainAfterStartupBytecodeRecorderBuildItem(bri)); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java index 7bc6f6efedd03..b34a67347d6b9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java @@ -38,6 +38,7 @@ import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.builditem.MainArgsBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.substrate.SubstrateResourceBuildItem; import io.quarkus.runtime.LaunchMode; @@ -81,6 +82,7 @@ public BuildResult run() throws Exception { .addInitial(SubstrateResourceBuildItem.class) .addInitial(ArchiveRootBuildItem.class) .addInitial(ShutdownContextBuildItem.class) + .addInitial(MainArgsBuildItem.class) .addInitial(ClassOutputBuildItem.class) .addInitial(LaunchModeBuildItem.class) .addInitial(AdditionalApplicationArchiveBuildItem.class) @@ -104,6 +106,7 @@ public BuildResult run() throws Exception { .produce(new ArchiveRootBuildItem(root)) .produce(new ClassOutputBuildItem(output)) .produce(new ShutdownContextBuildItem()) + .produce(new MainArgsBuildItem()) .produce(new LaunchModeBuildItem(launchMode)) .produce(new ExtensionClassLoaderBuildItem(classLoader)); for (Path i : additionalApplicationArchives) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java b/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java index dcaf9981159a7..c37a3544fe669 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java @@ -27,5 +27,10 @@ public enum ExecutionTime { /** * The bytecode is run from a main method */ - RUNTIME_INIT + RUNTIME_INIT, + + /** + * The bytecode is run from a main method after all RUNTIME_INIT + */ + AFTER_STARTUP } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java new file mode 100644 index 0000000000000..741fddb458459 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.deployment.builditem; + +import org.jboss.builder.item.MultiBuildItem; + +import io.quarkus.deployment.recording.BytecodeRecorderImpl; + +public final class MainAfterStartupBytecodeRecorderBuildItem extends MultiBuildItem { + + private final BytecodeRecorderImpl bytecodeRecorder; + + public MainAfterStartupBytecodeRecorderBuildItem(BytecodeRecorderImpl bytecodeRecorder) { + this.bytecodeRecorder = bytecodeRecorder; + } + + public BytecodeRecorderImpl getBytecodeRecorder() { + return bytecodeRecorder; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java new file mode 100644 index 0000000000000..d111954bfbe73 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.deployment.builditem; + +import org.jboss.builder.item.SimpleBuildItem; + +import io.quarkus.deployment.recording.BytecodeRecorderImpl; +import io.quarkus.runtime.MainArgsSupplier; + +public final class MainArgsBuildItem extends SimpleBuildItem implements BytecodeRecorderImpl.ReturnedProxy, MainArgsSupplier { + @Override + public String __returned$proxy$key() { + return MainArgsSupplier.class.getName(); + } + + @Override + public boolean __static$$init() { + return true; + } + + @Override + public String[] getArgs() { + throw new IllegalStateException(); + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ShutdownBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ShutdownBuildItem.java new file mode 100644 index 0000000000000..ba6b10bc1b84e --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ShutdownBuildItem.java @@ -0,0 +1,6 @@ +package io.quarkus.deployment.builditem; + +import org.jboss.builder.item.SimpleBuildItem; + +public final class ShutdownBuildItem extends SimpleBuildItem { +} 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 59be7ea0fccdc..c9785dcc023ca 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 @@ -36,9 +36,11 @@ import io.quarkus.deployment.builditem.ClassOutputBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.JavaLibraryPathAdditionalPathBuildItem; +import io.quarkus.deployment.builditem.MainAfterStartupBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.MainClassBuildItem; import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem; +import io.quarkus.deployment.builditem.ShutdownBuildItem; import io.quarkus.deployment.builditem.SslTrustStoreSystemPropertyBuildItem; import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; @@ -70,12 +72,14 @@ class MainClassBuildStep { MainClassBuildItem build(List staticInitTasks, List substitutions, List mainMethod, + List mainMethodAfterStartup, List properties, List javaLibraryPathAdditionalPaths, Optional sslTrustStoreSystemProperty, List features, BuildProducer appClassNameProducer, List loaders, + Optional shutdownBuildItem, ClassOutputBuildItem classOutput) { String appClassName = APP_CLASS + COUNT.incrementAndGet(); @@ -131,6 +135,7 @@ MainClassBuildItem build(List staticInitTasks, mv = file.getMethodCreator("doStart", void.class, String[].class); mv.setModifiers(Modifier.PROTECTED | Modifier.FINAL); + ResultHandle mainMethodArgs = mv.getMethodParam(0); // very first thing is to set system props (for run time, which use substitutions for a different // storage from build-time) @@ -187,6 +192,9 @@ MainClassBuildItem build(List staticInitTasks, mv.invokeStaticMethod(ofMethod(Timing.class, "mainStarted", void.class)); startupContext = mv.readStaticField(scField.getFieldDescriptor()); + mv.invokeVirtualMethod(ofMethod(StartupContext.class, "setMainArgs", void.class, String[].class), + startupContext, mainMethodArgs); + tryBlock = mv.tryBlock(); for (MainBytecodeRecorderBuildItem holder : mainMethod) { final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder(); @@ -214,8 +222,31 @@ MainClassBuildItem build(List staticInitTasks, cb.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cb.getCaughtException()); cb.invokeVirtualMethod(ofMethod(StartupContext.class, "close", void.class), startupContext); cb.throwException(RuntimeException.class, "Failed to start quarkus", cb.getCaughtException()); + + // Application.class: start method after startup + for (MainAfterStartupBytecodeRecorderBuildItem holder : mainMethodAfterStartup) { + final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder(); + if (!recorder.isEmpty()) { + for (BytecodeRecorderObjectLoaderBuildItem item : loaders) { + recorder.registerObjectLoader(item.getObjectLoader()); + } + recorder.writeBytecode(classOutput.getClassOutput()); + ResultHandle dup = mv.newInstance(ofConstructor(recorder.getClassName())); + mv.invokeInterfaceMethod(ofMethod(StartupTask.class, "deploy", void.class, StartupContext.class), dup, + startupContext); + } + } + mv.returnValue(null); + // Application class: doPostStart method + if (shutdownBuildItem.isPresent()) { + mv = file.getMethodCreator("doPostStart", void.class); + mv.setModifiers(Modifier.PROTECTED); + mv.invokeSpecialMethod(ofMethod(Application.class, "requestShutdown", void.class), mv.getThis()); + mv.returnValue(null); + } + // Application class: stop method mv = file.getMethodCreator("doStop", void.class); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/Application.java b/core/runtime/src/main/java/io/quarkus/runtime/Application.java index 877ccade2678f..f3444a1eacb32 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/Application.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/Application.java @@ -108,10 +108,15 @@ public final void start(@SuppressWarnings("unused") String[] args) { } finally { stateLock.unlock(); } + doPostStart(); } protected abstract void doStart(String[] args); + protected void doPostStart() { + + } + /** * Stop the application. If another thread is also trying to stop the application, this method waits for that * thread to finish. Returns immediately if the application is already stopped. If an exception is thrown during @@ -222,6 +227,10 @@ private void exit() { } } + protected void requestShutdown() { + shutdownRequested = true; + } + private static IllegalStateException interruptedOnAwaitStart() { return new IllegalStateException("Interrupted while waiting for another thread to start the application"); } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java b/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java new file mode 100644 index 0000000000000..9cd724c713222 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java @@ -0,0 +1,6 @@ +package io.quarkus.runtime; + +public interface MainArgsSupplier { + + String[] getArgs(); +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java b/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java index c5a152746514f..da4814d4c5260 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java @@ -42,6 +42,15 @@ public StartupContext() { values.put(ShutdownContext.class.getName(), shutdownContext); } + public void setMainArgs(final String[] args) { + values.put(MainArgsSupplier.class.getName(), new MainArgsSupplier() { + @Override + public String[] getArgs() { + return args; + } + }); + } + public void putValue(String name, Object value) { values.put(name, value); } diff --git a/extensions/command-line-runner/deployment/pom.xml b/extensions/command-line-runner/deployment/pom.xml new file mode 100644 index 0000000000000..a6c8658849f12 --- /dev/null +++ b/extensions/command-line-runner/deployment/pom.xml @@ -0,0 +1,61 @@ + + + + + quarkus-command-line-runner-parent + io.quarkus + 999-SNAPSHOT + ../ + + + 4.0.0 + + quarkus-command-line-runner + Quarkus - Command Line Runner - Deployment + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-command-line-runner-runtime + + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java new file mode 100644 index 0000000000000..aa9355814995e --- /dev/null +++ b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java @@ -0,0 +1,16 @@ +package io.quarkus.clrunner.deployment; + +import org.jboss.builder.item.MultiBuildItem; + +public final class CommandLineRunnerBuildItem extends MultiBuildItem { + + private final String className; + + public CommandLineRunnerBuildItem(String className) { + this.className = className; + } + + public String getClassName() { + return className; + } +} diff --git a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java new file mode 100644 index 0000000000000..ee3f2f53f7803 --- /dev/null +++ b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java @@ -0,0 +1,68 @@ +package io.quarkus.clrunner.deployment; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; + +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.clrunner.CommandLineRunner; +import io.quarkus.clrunner.runtime.CommandLineRunnerTemplate; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.MainArgsBuildItem; +import io.quarkus.deployment.builditem.ShutdownBuildItem; +import io.quarkus.deployment.recording.RecorderContext; + +public class CommandLineRunnerProcessor { + + private static final DotName COMMAND_LINER_RUNNER = DotName.createSimple(CommandLineRunner.class.getName()); + + @BuildStep + List discover(CombinedIndexBuildItem combinedIndexBuildItem) { + final List result = new ArrayList<>(); + + for (ClassInfo info : combinedIndexBuildItem.getIndex().getAllKnownImplementors(COMMAND_LINER_RUNNER)) { + final DotName name = info.name(); + + result.add(new CommandLineRunnerBuildItem(name.toString())); + } + return result; + } + + @BuildStep + List beans(List runners) { + final List result = new ArrayList<>(); + for (CommandLineRunnerBuildItem runner : runners) { + result.add(new AdditionalBeanBuildItem(false, runner.getClassName())); + } + return result; + } + + @BuildStep + ShutdownBuildItem shutdown() { + // TODO: this has to be conditional only set when various things are not present + return new ShutdownBuildItem(); + } + + @BuildStep + @Record(ExecutionTime.AFTER_STARTUP) + public void runners(List runners, + BeanContainerBuildItem beanContainerBuildItem, + MainArgsBuildItem mainArgsBuildItem, + CommandLineRunnerTemplate template, + RecorderContext context) { + + Set> runnerClasses = new HashSet<>(); + for (CommandLineRunnerBuildItem runner : runners) { + runnerClasses.add((Class) context.classProxy(runner.getClassName())); + } + template.run(runnerClasses, beanContainerBuildItem.getValue(), mainArgsBuildItem); + } +} diff --git a/extensions/command-line-runner/pom.xml b/extensions/command-line-runner/pom.xml new file mode 100644 index 0000000000000..ea88743a770c4 --- /dev/null +++ b/extensions/command-line-runner/pom.xml @@ -0,0 +1,37 @@ + + + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-command-line-runner-parent + Quarkus - Command Line Runner + pom + + deployment + runtime + + + diff --git a/extensions/command-line-runner/runtime/pom.xml b/extensions/command-line-runner/runtime/pom.xml new file mode 100644 index 0000000000000..1459f21ada6a0 --- /dev/null +++ b/extensions/command-line-runner/runtime/pom.xml @@ -0,0 +1,60 @@ + + + + + + quarkus-command-line-runner-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-command-line-runner-runtime + Quarkus - Command Line Runner - Runtime + + + + + io.quarkus + quarkus-arc-runtime + + + + + + + + maven-dependency-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/CommandLineRunner.java b/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/CommandLineRunner.java new file mode 100644 index 0000000000000..88a0607c179d5 --- /dev/null +++ b/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/CommandLineRunner.java @@ -0,0 +1,6 @@ +package io.quarkus.clrunner; + +public interface CommandLineRunner { + + void run(String[] args); +} diff --git a/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java b/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java new file mode 100644 index 0000000000000..49d5fbd894268 --- /dev/null +++ b/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java @@ -0,0 +1,25 @@ +package io.quarkus.clrunner.runtime; + +import java.util.Set; + +import io.quarkus.arc.runtime.BeanContainer; +import io.quarkus.clrunner.CommandLineRunner; +import io.quarkus.runtime.MainArgsSupplier; +import io.quarkus.runtime.annotations.Template; + +@Template +public class CommandLineRunnerTemplate { + + public void run(Set> handlerClasses, BeanContainer beanContainer, + MainArgsSupplier mainArgsSupplier) { + for (Class handlerClass : handlerClasses) { + final BeanContainer.Factory factory = beanContainer.instanceFactory(handlerClass); + final BeanContainer.Instance instance = factory.create(); + final CommandLineRunner commandLineRunner = instance.get(); + + commandLineRunner.run(mainArgsSupplier.getArgs()); + + instance.close(); + } + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index 14eacc9a26c88..a1ac1bc20b35c 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -87,6 +87,8 @@ camel kotlin + + command-line-runner diff --git a/integration-tests/command-line-runner/pom.xml b/integration-tests/command-line-runner/pom.xml new file mode 100644 index 0000000000000..8296ebe31e921 --- /dev/null +++ b/integration-tests/command-line-runner/pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + + quarkus-integration-test-command-line-runner + Quarkus - Integration Tests - CLIR + + + + io.quarkus + quarkus-command-line-runner + provided + + + + + io.quarkus + quarkus-devtools-common + test + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-params + + + org.junit.jupiter + junit-jupiter-engine + + + org.assertj + assertj-core + + + org.apache.maven.shared + maven-invoker + test + + + org.awaitility + awaitility + test + + + org.jprocesses + jProcesses + test + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + ${maven.home} + ${settings.localRepository} + ${project.version} + + + + + + + diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java new file mode 100644 index 0000000000000..2f5440e7d6cd6 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java @@ -0,0 +1,70 @@ +package io.quarkus.clrunner.tests; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.junit.jupiter.api.Test; + +import io.quarkus.clrunner.tests.support.MavenProcessInvocationResult; +import io.quarkus.clrunner.tests.support.MojoTestBase; +import io.quarkus.clrunner.tests.support.RunningInvoker; + +public class CommandLineRunnerIT extends MojoTestBase { + + private RunningInvoker running; + private File testDir; + + @Test + public void testCommandLineRunnerIsBuiltProperlyAndExecutesAsExpected() + throws MavenInvocationException, IOException, InterruptedException { + // copy the template + testDir = initProject("projects/classic", "projects/project-classic-clir"); + + // invoke the build + running = new RunningInvoker(testDir, false); + final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"), + Collections.emptyMap()); + + // ensure the build completed successfully + assertThat(result.getProcess().waitFor()).isEqualTo(0); + + // ensure the runner jar was created + final File targetDir = getTargetDir(); + final List runnerJarFiles = getFilesEndingWith(targetDir, "-runner.jar"); + assertThat(runnerJarFiles).hasSize(1); + + final File runnerJarFile = runnerJarFiles.get(0); + final String expectedOutputFile = runnerJarFile.getParentFile().getAbsolutePath() + "/dummy.txt"; + + // launch the jar we just created by passing the file we expect it write and the some input we expect it to capitalize and place in the output + final Process process = new ProcessBuilder() + .directory(runnerJarFile.getParentFile()) + .command("java", "-jar", runnerJarFile.getAbsolutePath(), expectedOutputFile, "a", "b", "c") + .start(); + + // ensure the application completed successfully + assertThat(process.waitFor()).isEqualTo(0); + + // ensure it created the expected output + final String lineSep = System.lineSeparator(); + assertThat(new File(expectedOutputFile)) + .exists() + .hasContent("A" + lineSep + "B" + lineSep + "C" + lineSep); + } + + private File getTargetDir() { + return new File(testDir.getAbsoluteFile() + "/target"); + } + + private List getFilesEndingWith(File dir, String suffix) { + final File[] files = dir.listFiles((d, name) -> name.endsWith(suffix)); + return files != null ? Arrays.asList(files) : new ArrayList<>(); + } +} diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java new file mode 100644 index 0000000000000..2feb27e82dc58 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java @@ -0,0 +1,45 @@ +package io.quarkus.clrunner.tests.support; + +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.utils.cli.CommandLineException; + +/* + * Copied and adapted from devtools-maven + */ +public class MavenProcessInvocationResult implements InvocationResult { + + private Process process; + private CommandLineException exception; + + MavenProcessInvocationResult setProcess(Process process) { + this.process = process; + return this; + } + + public MavenProcessInvocationResult setException(CommandLineException exception) { + // Print the stack trace immediately to give some feedback early + // In intellij, the used `mvn` executable is not "executable" by default on Mac and probably linux. + // You need to chmod +x the file. + exception.printStackTrace(); + this.exception = exception; + return this; + } + + @Override + public CommandLineException getExecutionException() { + return exception; + } + + @Override + public int getExitCode() { + if (process == null) { + throw new IllegalStateException("No process"); + } else { + return process.exitValue(); + } + } + + public Process getProcess() { + return process; + } +} diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java new file mode 100644 index 0000000000000..172180c3ad46e --- /dev/null +++ b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java @@ -0,0 +1,115 @@ +package io.quarkus.clrunner.tests.support; + +import java.io.File; + +import org.apache.maven.shared.invoker.CommandLineConfigurationException; +import org.apache.maven.shared.invoker.DefaultInvoker; +import org.apache.maven.shared.invoker.InvocationOutputHandler; +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.invoker.InvokerLogger; +import org.apache.maven.shared.invoker.MavenCommandLineBuilder; +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.apache.maven.shared.invoker.SystemOutHandler; +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.Commandline; +import org.apache.maven.shared.utils.cli.StreamConsumer; +import org.apache.maven.shared.utils.cli.StreamPumper; + +/* + * Copied from devtools-maven + */ +public class MavenProcessInvoker extends DefaultInvoker { + + private static final InvocationOutputHandler DEFAULT_OUTPUT_HANDLER = new SystemOutHandler(); + + private InvocationOutputHandler outputHandler = DEFAULT_OUTPUT_HANDLER; + + private InvocationOutputHandler errorHandler = DEFAULT_OUTPUT_HANDLER; + + @Override + public InvocationResult execute(InvocationRequest request) throws MavenInvocationException { + MavenCommandLineBuilder cliBuilder = new MavenCommandLineBuilder(); + + InvokerLogger logger = getLogger(); + if (logger != null) { + cliBuilder.setLogger(getLogger()); + } + + File localRepo = getLocalRepositoryDirectory(); + if (localRepo != null) { + cliBuilder.setLocalRepositoryDirectory(getLocalRepositoryDirectory()); + } + + File mavenHome = getMavenHome(); + if (mavenHome != null) { + cliBuilder.setMavenHome(getMavenHome()); + } + + File mavenExecutable = getMavenExecutable(); + if (mavenExecutable != null) { + cliBuilder.setMavenExecutable(mavenExecutable); + } + + File workingDirectory = getWorkingDirectory(); + if (workingDirectory != null) { + cliBuilder.setWorkingDirectory(getWorkingDirectory()); + } + + request.setBatchMode(true); + + Commandline cli; + try { + cli = cliBuilder.build(request); + } catch (CommandLineConfigurationException e) { + throw new MavenInvocationException("Error configuring command-line. Reason: " + e.getMessage(), e); + } + + MavenProcessInvocationResult result = new MavenProcessInvocationResult(); + + try { + Process process = executeCommandLine(cli, request); + result.setProcess(process); + } catch (CommandLineException e) { + result.setException(e); + } + + return result; + } + + private Process executeCommandLine(Commandline cli, InvocationRequest request) + throws CommandLineException { + InvocationOutputHandler outputHandler = request.getOutputHandler(this.outputHandler); + InvocationOutputHandler errorHandler = request.getErrorHandler(this.errorHandler); + return executeCommandLine(cli, outputHandler, errorHandler); + } + + private static Process executeCommandLine(Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr) + throws CommandLineException { + if (cl == null) { + throw new IllegalArgumentException("the command line cannot be null."); + } else { + final Process p = cl.execute(); + final StreamPumper outputPumper = new StreamPumper(p.getInputStream(), systemOut); + final StreamPumper errorPumper = new StreamPumper(p.getErrorStream(), systemErr); + + outputPumper.start(); + errorPumper.start(); + + new Thread(() -> { + try { + // Wait for termination + p.waitFor(); + outputPumper.waitUntilDone(); + errorPumper.waitUntilDone(); + } catch (Exception e) { + outputPumper.disable(); + errorPumper.disable(); + e.printStackTrace(); + } + }).start(); + + return p; + } + } +} diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java new file mode 100644 index 0000000000000..2a1d1383ac170 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java @@ -0,0 +1,86 @@ +package io.quarkus.clrunner.tests.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.shared.utils.StringUtils; +import org.junit.jupiter.api.BeforeAll; + +import com.google.common.collect.ImmutableMap; + +import io.quarkus.maven.utilities.MojoUtils; + +/* + * Copied and adapted from devtools-maven + */ +public class MojoTestBase { + private static ImmutableMap VARIABLES; + + @BeforeAll + public static void init() { + VARIABLES = ImmutableMap.of( + "@project.groupId@", MojoUtils.getPluginGroupId(), + "@project.artifactId@", MojoUtils.getPluginArtifactId(), + "@project.version@", MojoUtils.getPluginVersion()); + } + + public static File initProject(String name, String output) { + File tc = new File("target/test-classes"); + if (!tc.isDirectory()) { + boolean mkdirs = tc.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, "test-classes created? " + mkdirs); + } + + File in = new File("src/test/resources", name); + if (!in.isDirectory()) { + throw new RuntimeException("Cannot find directory: " + in.getAbsolutePath()); + } + + File out = new File(tc, output); + if (out.isDirectory()) { + FileUtils.deleteQuietly(out); + } + boolean mkdirs = out.mkdirs(); + Logger.getLogger(MojoTestBase.class.getName()) + .log(Level.FINE, out.getAbsolutePath() + " created? " + mkdirs); + try { + org.codehaus.plexus.util.FileUtils.copyDirectoryStructure(in, out); + } catch (IOException e) { + throw new RuntimeException("Cannot copy project resources", e); + } + + File pom = new File(out, "pom.xml"); + try { + filter(pom, VARIABLES); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + + return out; + } + + static void filter(File input, Map variables) throws IOException { + assertThat(input).isFile(); + String data = FileUtils.readFileToString(input, "UTF-8"); + + for (Map.Entry token : variables.entrySet()) { + String value = String.valueOf(token.getValue()); + data = StringUtils.replace(data, token.getKey(), value); + } + FileUtils.write(input, data, "UTF-8"); + } + +} diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java new file mode 100644 index 0000000000000..e709bb111c1a2 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java @@ -0,0 +1,74 @@ +package io.quarkus.clrunner.tests.support; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; + +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.invoker.InvokerLogger; +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.apache.maven.shared.invoker.PrintStreamHandler; +import org.apache.maven.shared.invoker.PrintStreamLogger; + +/* + * Copied and adapted from devtools-maven + */ +public class RunningInvoker extends MavenProcessInvoker { + + private final boolean debug; + private MavenProcessInvocationResult result; + private final File log; + private final PrintStreamHandler logHandler; + + public RunningInvoker(File basedir, boolean debug) throws FileNotFoundException { + this.debug = debug; + setWorkingDirectory(basedir); + String repo = System.getProperty("maven.repo"); + if (repo == null) { + repo = new File(System.getProperty("user.home"), ".m2/repository").getAbsolutePath(); + } + log = new File(basedir, "build-" + basedir.getName() + ".log"); + PrintStream stream = null; + try { + stream = new PrintStream(log, "UTF-8"); + } catch (UnsupportedEncodingException e) { + stream = new PrintStream(log); + } + logHandler = new PrintStreamHandler(stream, true); + setErrorHandler(logHandler); + setOutputHandler(logHandler); + setLogger(new PrintStreamLogger(stream, InvokerLogger.DEBUG)); + } + + public MavenProcessInvocationResult execute(List goals, Map envVars) + throws MavenInvocationException { + DefaultInvocationRequest request = new DefaultInvocationRequest(); + request.setGoals(goals); + request.setDebug(debug); + request.setLocalRepositoryDirectory(getLocalRepositoryDirectory()); + request.setBaseDirectory(getWorkingDirectory()); + request.setPomFile(new File(getWorkingDirectory(), "pom.xml")); + + if (System.getProperty("mavenOpts") != null) { + request.setMavenOpts(System.getProperty("mavenOpts")); + } + + request.setShellEnvironmentInherited(true); + envVars.forEach(request::addShellEnvironment); + request.setOutputHandler(logHandler); + request.setErrorHandler(logHandler); + this.result = (MavenProcessInvocationResult) execute(request); + return result; + } + + @Override + public InvocationResult execute(InvocationRequest request) throws MavenInvocationException { + return super.execute(request); + } + +} diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml b/integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml new file mode 100644 index 0000000000000..18a18a6da4184 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + org.acme + acme + 1.0-SNAPSHOT + + @project.version@ + 1.8 + UTF-8 + 1.8 + + + + @project.groupId@ + quarkus-command-line-runner + ${quarkus.version} + provided + + + + + + @project.groupId@ + @project.artifactId@ + ${quarkus.version} + + + + build + + + + + + + + + native + + + + @project.groupId@ + @project.artifactId@ + ${quarkus.version} + + + + native-image + + + true + + + + + + + + + diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java b/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java new file mode 100644 index 0000000000000..81da4a5537ee2 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java @@ -0,0 +1,13 @@ +package org.acme; + +import javax.inject.Singleton; + + +@Singleton +public class Capitalizer { + + public String perform(String input) { + return input.toUpperCase(); + } + +} diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java b/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java new file mode 100644 index 0000000000000..abe000aca08b7 --- /dev/null +++ b/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java @@ -0,0 +1,40 @@ +package org.acme; + +import io.quarkus.clrunner.CommandLineRunner; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.charset.Charset; + +import javax.inject.Inject; + + + +public class ExampleRunner implements CommandLineRunner { + + @Inject + Capitalizer capitalizer; + + @Override + public void run(String[] args) { + final List lines = new ArrayList<>(); + for (int i=1; ielytron-security camel-core camel-salesforce + --> + command-line-runner From 2edec4218b17003ad6150b453258a061d05117e3 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 8 Mar 2019 23:29:26 +0200 Subject: [PATCH 2/8] Make dev mode fail for command line applications --- .../main/java/io/quarkus/runner/RuntimeRunner.java | 12 ++++++++++++ .../java/io/quarkus/gradle/tasks/QuarkusDev.java | 11 ++++++++++- .../src/main/java/io/quarkus/maven/DevMojo.java | 11 ++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/runner/RuntimeRunner.java b/core/deployment/src/main/java/io/quarkus/runner/RuntimeRunner.java index 9dd58fed4b9f7..e4f14660198e2 100644 --- a/core/deployment/src/main/java/io/quarkus/runner/RuntimeRunner.java +++ b/core/deployment/src/main/java/io/quarkus/runner/RuntimeRunner.java @@ -35,6 +35,7 @@ import io.quarkus.deployment.QuarkusAugmentor; import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.ShutdownBuildItem; import io.quarkus.runtime.Application; import io.quarkus.runtime.LaunchMode; @@ -108,6 +109,17 @@ public void run() { transformerTarget.setTransformers(functions); } + try { + // dev mode does not make sense when we a ShutdownBuildItem exists + // because such applications dont stay up + final ShutdownBuildItem shutdownBuildItem = result.consume(ShutdownBuildItem.class); + if ((shutdownBuildItem != null) && (launchMode == LaunchMode.DEVELOPMENT)) { + System.exit(99); + } + } catch (IllegalArgumentException e) { + // there were no ShutdownBuildItem so we can proceed + } + final Application application; Class appClass = loader .loadClass(result.consume(ApplicationClassNameBuildItem.class).getClassName()) diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java index 8ace0592c9927..9bf62a33806de 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java @@ -282,13 +282,22 @@ public void run() { p.destroy(); } }, "Development Mode Shutdown Hook")); + + int exitCode; try { - p.waitFor(); + exitCode = p.waitFor(); } catch (Exception e) { p.destroy(); throw e; } + if (exitCode != 0) { + String message = exitCode == 99 ? //TODO fix: this is pretty lame + "quarkus-dev cannot be used with command line applications" + : "The application did not terminate properly"; + throw new RuntimeException(message); + } + } catch (Exception e) { throw new GradleException("Failed to run", e); } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 27578b8067df7..efc2a8b29a664 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -288,13 +288,22 @@ public void run() { p.destroy(); } }, "Development Mode Shutdown Hook")); + + int exitCode = 0; try { - p.waitFor(); + exitCode = p.waitFor(); } catch (Exception e) { p.destroy(); throw e; } + if (exitCode != 0) { + String message = exitCode == 99 ? //TODO fix: this is pretty lame + "quarkus:dev cannot be used with command line applications" + : "The application did not terminate properly"; + throw new RuntimeException(message); + } + } catch (Exception e) { throw new MojoFailureException("Failed to run", e); } From b7a9c2d3a4f83e47285664ddbe07f70c30f5266c Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Sat, 9 Mar 2019 15:37:40 +0200 Subject: [PATCH 3/8] Remove the need for a new BytecodeRecorder --- .../quarkus/deployment/ExtensionLoader.java | 24 +---- .../quarkus/deployment/QuarkusAugmentor.java | 3 - .../deployment/annotations/ExecutionTime.java | 7 +- .../builditem/MainAfterStartupBuildItem.java | 39 ++++++++ ...AfterStartupBytecodeRecorderBuildItem.java | 18 ---- .../builditem/MainArgsBuildItem.java | 23 ----- .../deployment/steps/MainClassBuildStep.java | 21 +---- .../io/quarkus/runtime/MainArgsSupplier.java | 6 -- .../io/quarkus/runtime/StartupContext.java | 9 -- .../CommandLineRunnerBuildItem.java | 4 +- .../CommandLineRunnerProcessor.java | 94 +++++++++++++------ .../runtime/CommandLineRunnerTemplate.java | 25 ----- 12 files changed, 117 insertions(+), 156 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBuildItem.java delete mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java delete mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java delete mode 100644 core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java delete mode 100644 extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java index 7c2f3c61a623f..41d7f6d2aba9a 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java @@ -51,7 +51,6 @@ import io.quarkus.deployment.builditem.AdditionalApplicationArchiveMarkerBuildItem; import io.quarkus.deployment.builditem.CapabilityBuildItem; import io.quarkus.deployment.builditem.ConfigurationBuildItem; -import io.quarkus.deployment.builditem.MainAfterStartupBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem; import io.quarkus.deployment.recording.BytecodeRecorderImpl; @@ -284,21 +283,10 @@ public static Consumer loadStepsFrom(Class clazz) { assert recordAnnotation != null; final ExecutionTime executionTime = recordAnnotation.value(); final boolean optional = recordAnnotation.optional(); - methodStepConfig = methodStepConfig.andThen(bsb -> { - Class buildItem = executionTime == ExecutionTime.STATIC_INIT - ? StaticBytecodeRecorderBuildItem.class - : MainBytecodeRecorderBuildItem.class; - if (executionTime == ExecutionTime.STATIC_INIT) { - buildItem = StaticBytecodeRecorderBuildItem.class; - } else if (executionTime == ExecutionTime.RUNTIME_INIT) { - buildItem = MainBytecodeRecorderBuildItem.class; - } else if (executionTime == ExecutionTime.AFTER_STARTUP) { - buildItem = MainAfterStartupBytecodeRecorderBuildItem.class; - } - bsb.produces( - buildItem, - optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE); - }); + methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces( + executionTime == ExecutionTime.STATIC_INIT ? StaticBytecodeRecorderBuildItem.class + : MainBytecodeRecorderBuildItem.class, + optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE)); } boolean methodConsumingConfig = consumingConfig; if (methodParameters.length == 0) { @@ -461,10 +449,8 @@ public void execute(final BuildContext bc) { // commit recorded data if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) { bc.produce(new StaticBytecodeRecorderBuildItem(bri)); - } else if (recordAnnotation.value() == ExecutionTime.RUNTIME_INIT) { + } else { bc.produce(new MainBytecodeRecorderBuildItem(bri)); - } else if (recordAnnotation.value() == ExecutionTime.AFTER_STARTUP) { - bc.produce(new MainAfterStartupBytecodeRecorderBuildItem(bri)); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java index b34a67347d6b9..7bc6f6efedd03 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java @@ -38,7 +38,6 @@ import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; -import io.quarkus.deployment.builditem.MainArgsBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.substrate.SubstrateResourceBuildItem; import io.quarkus.runtime.LaunchMode; @@ -82,7 +81,6 @@ public BuildResult run() throws Exception { .addInitial(SubstrateResourceBuildItem.class) .addInitial(ArchiveRootBuildItem.class) .addInitial(ShutdownContextBuildItem.class) - .addInitial(MainArgsBuildItem.class) .addInitial(ClassOutputBuildItem.class) .addInitial(LaunchModeBuildItem.class) .addInitial(AdditionalApplicationArchiveBuildItem.class) @@ -106,7 +104,6 @@ public BuildResult run() throws Exception { .produce(new ArchiveRootBuildItem(root)) .produce(new ClassOutputBuildItem(output)) .produce(new ShutdownContextBuildItem()) - .produce(new MainArgsBuildItem()) .produce(new LaunchModeBuildItem(launchMode)) .produce(new ExtensionClassLoaderBuildItem(classLoader)); for (Path i : additionalApplicationArchives) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java b/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java index c37a3544fe669..dcaf9981159a7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/annotations/ExecutionTime.java @@ -27,10 +27,5 @@ public enum ExecutionTime { /** * The bytecode is run from a main method */ - RUNTIME_INIT, - - /** - * The bytecode is run from a main method after all RUNTIME_INIT - */ - AFTER_STARTUP + RUNTIME_INIT } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBuildItem.java new file mode 100644 index 0000000000000..712d4d0d37111 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBuildItem.java @@ -0,0 +1,39 @@ +package io.quarkus.deployment.builditem; + +import java.util.function.Consumer; + +import org.jboss.builder.item.SimpleBuildItem; + +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.ResultHandle; + +public final class MainAfterStartupBuildItem extends SimpleBuildItem { + + private final Consumer bytecodeCreator; + + public MainAfterStartupBuildItem(Consumer bytecodeCreator) { + this.bytecodeCreator = bytecodeCreator; + } + + public Consumer getBytecodeCreator() { + return bytecodeCreator; + } + + public static class Input { + private final MethodCreator doStartMethod; + private final ResultHandle mainArgs; + + public Input(MethodCreator doStartMethod, ResultHandle mainArgs) { + this.doStartMethod = doStartMethod; + this.mainArgs = mainArgs; + } + + public MethodCreator getDoStartMethod() { + return doStartMethod; + } + + public ResultHandle getMainArgs() { + return mainArgs; + } + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java deleted file mode 100644 index 741fddb458459..0000000000000 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainAfterStartupBytecodeRecorderBuildItem.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.quarkus.deployment.builditem; - -import org.jboss.builder.item.MultiBuildItem; - -import io.quarkus.deployment.recording.BytecodeRecorderImpl; - -public final class MainAfterStartupBytecodeRecorderBuildItem extends MultiBuildItem { - - private final BytecodeRecorderImpl bytecodeRecorder; - - public MainAfterStartupBytecodeRecorderBuildItem(BytecodeRecorderImpl bytecodeRecorder) { - this.bytecodeRecorder = bytecodeRecorder; - } - - public BytecodeRecorderImpl getBytecodeRecorder() { - return bytecodeRecorder; - } -} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java deleted file mode 100644 index d111954bfbe73..0000000000000 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/MainArgsBuildItem.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.quarkus.deployment.builditem; - -import org.jboss.builder.item.SimpleBuildItem; - -import io.quarkus.deployment.recording.BytecodeRecorderImpl; -import io.quarkus.runtime.MainArgsSupplier; - -public final class MainArgsBuildItem extends SimpleBuildItem implements BytecodeRecorderImpl.ReturnedProxy, MainArgsSupplier { - @Override - public String __returned$proxy$key() { - return MainArgsSupplier.class.getName(); - } - - @Override - public boolean __static$$init() { - return true; - } - - @Override - public String[] getArgs() { - throw new IllegalStateException(); - } -} 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 c9785dcc023ca..1628d8fc375b2 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 @@ -36,7 +36,7 @@ import io.quarkus.deployment.builditem.ClassOutputBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.JavaLibraryPathAdditionalPathBuildItem; -import io.quarkus.deployment.builditem.MainAfterStartupBytecodeRecorderBuildItem; +import io.quarkus.deployment.builditem.MainAfterStartupBuildItem; import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem; import io.quarkus.deployment.builditem.MainClassBuildItem; import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem; @@ -72,13 +72,13 @@ class MainClassBuildStep { MainClassBuildItem build(List staticInitTasks, List substitutions, List mainMethod, - List mainMethodAfterStartup, List properties, List javaLibraryPathAdditionalPaths, Optional sslTrustStoreSystemProperty, List features, BuildProducer appClassNameProducer, List loaders, + Optional mainAfterStartupBuildItem, Optional shutdownBuildItem, ClassOutputBuildItem classOutput) { @@ -192,9 +192,6 @@ MainClassBuildItem build(List staticInitTasks, mv.invokeStaticMethod(ofMethod(Timing.class, "mainStarted", void.class)); startupContext = mv.readStaticField(scField.getFieldDescriptor()); - mv.invokeVirtualMethod(ofMethod(StartupContext.class, "setMainArgs", void.class, String[].class), - startupContext, mainMethodArgs); - tryBlock = mv.tryBlock(); for (MainBytecodeRecorderBuildItem holder : mainMethod) { final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder(); @@ -224,17 +221,9 @@ MainClassBuildItem build(List staticInitTasks, cb.throwException(RuntimeException.class, "Failed to start quarkus", cb.getCaughtException()); // Application.class: start method after startup - for (MainAfterStartupBytecodeRecorderBuildItem holder : mainMethodAfterStartup) { - final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder(); - if (!recorder.isEmpty()) { - for (BytecodeRecorderObjectLoaderBuildItem item : loaders) { - recorder.registerObjectLoader(item.getObjectLoader()); - } - recorder.writeBytecode(classOutput.getClassOutput()); - ResultHandle dup = mv.newInstance(ofConstructor(recorder.getClassName())); - mv.invokeInterfaceMethod(ofMethod(StartupTask.class, "deploy", void.class, StartupContext.class), dup, - startupContext); - } + if (mainAfterStartupBuildItem.isPresent()) { + mainAfterStartupBuildItem.get().getBytecodeCreator().accept( + new MainAfterStartupBuildItem.Input(mv, mainMethodArgs)); } mv.returnValue(null); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java b/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java deleted file mode 100644 index 9cd724c713222..0000000000000 --- a/core/runtime/src/main/java/io/quarkus/runtime/MainArgsSupplier.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.quarkus.runtime; - -public interface MainArgsSupplier { - - String[] getArgs(); -} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java b/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java index da4814d4c5260..c5a152746514f 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java @@ -42,15 +42,6 @@ public StartupContext() { values.put(ShutdownContext.class.getName(), shutdownContext); } - public void setMainArgs(final String[] args) { - values.put(MainArgsSupplier.class.getName(), new MainArgsSupplier() { - @Override - public String[] getArgs() { - return args; - } - }); - } - public void putValue(String name, Object value) { values.put(name, value); } diff --git a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java index aa9355814995e..43879f0e10c6e 100644 --- a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java +++ b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerBuildItem.java @@ -1,8 +1,8 @@ package io.quarkus.clrunner.deployment; -import org.jboss.builder.item.MultiBuildItem; +import org.jboss.builder.item.SimpleBuildItem; -public final class CommandLineRunnerBuildItem extends MultiBuildItem { +public final class CommandLineRunnerBuildItem extends SimpleBuildItem { private final String className; diff --git a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java index ee3f2f53f7803..06a78955188ac 100644 --- a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java +++ b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java @@ -1,68 +1,104 @@ package io.quarkus.clrunner.deployment; +import static io.quarkus.gizmo.MethodDescriptor.ofMethod; + +import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.Optional; +import java.util.function.Consumer; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.clrunner.CommandLineRunner; -import io.quarkus.clrunner.runtime.CommandLineRunnerTemplate; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.builditem.MainArgsBuildItem; +import io.quarkus.deployment.builditem.MainAfterStartupBuildItem; import io.quarkus.deployment.builditem.ShutdownBuildItem; -import io.quarkus.deployment.recording.RecorderContext; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; public class CommandLineRunnerProcessor { private static final DotName COMMAND_LINER_RUNNER = DotName.createSimple(CommandLineRunner.class.getName()); @BuildStep - List discover(CombinedIndexBuildItem combinedIndexBuildItem) { - final List result = new ArrayList<>(); + void discover(CombinedIndexBuildItem combinedIndexBuildItem, + BuildProducer producer) { + final List names = new ArrayList<>(); for (ClassInfo info : combinedIndexBuildItem.getIndex().getAllKnownImplementors(COMMAND_LINER_RUNNER)) { final DotName name = info.name(); + names.add(name.toString()); + } - result.add(new CommandLineRunnerBuildItem(name.toString())); + if (names.size() == 0) { + return; } - return result; + if (names.size() > 1) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String name : names) { + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append(name); + } + throw new RuntimeException("Multiple classes ( " + sb.toString() + + ") have been annotated with @ApplicationPath which is currently not supported"); + } + + producer.produce(new CommandLineRunnerBuildItem(names.get(0))); } @BuildStep - List beans(List runners) { - final List result = new ArrayList<>(); - for (CommandLineRunnerBuildItem runner : runners) { - result.add(new AdditionalBeanBuildItem(false, runner.getClassName())); + void beans(Optional runner, BuildProducer producer) { + if (runner.isPresent()) { + producer.produce(new AdditionalBeanBuildItem(false, runner.get().getClassName())); } - return result; } @BuildStep - ShutdownBuildItem shutdown() { + void shutdown(BuildProducer producer) { // TODO: this has to be conditional only set when various things are not present - return new ShutdownBuildItem(); + producer.produce(new ShutdownBuildItem()); } @BuildStep - @Record(ExecutionTime.AFTER_STARTUP) - public void runners(List runners, - BeanContainerBuildItem beanContainerBuildItem, - MainArgsBuildItem mainArgsBuildItem, - CommandLineRunnerTemplate template, - RecorderContext context) { + void bytecodeCreation(Optional bi, BuildProducer producer) { + if (bi.isPresent()) { + final Consumer bytecodeCreator = new Consumer() { + @Override + public void accept(MainAfterStartupBuildItem.Input input) { + MethodCreator mv = input.getDoStartMethod(); + ResultHandle clazz = mv.invokeStaticMethod( + ofMethod(Class.class, "forName", Class.class, String.class), + mv.load(bi.get().getClassName())); + ResultHandle arcContainer = mv.invokeStaticMethod( + MethodDescriptor.ofMethod(Arc.class, "container", ArcContainer.class)); + ResultHandle instanceHandle = mv.invokeInterfaceMethod( + MethodDescriptor.ofMethod(ArcContainer.class, "instance", InstanceHandle.class, Class.class, + Annotation[].class), + arcContainer, clazz, mv.newArray(Annotation.class, mv.load(0))); + ResultHandle runner = mv.invokeInterfaceMethod( + ofMethod(InstanceHandle.class, "get", Object.class), + instanceHandle); + mv.invokeInterfaceMethod( + MethodDescriptor.ofMethod(CommandLineRunner.class, "run", void.class, String[].class), + runner, input.getMainArgs()); + } + }; - Set> runnerClasses = new HashSet<>(); - for (CommandLineRunnerBuildItem runner : runners) { - runnerClasses.add((Class) context.classProxy(runner.getClassName())); + producer.produce(new MainAfterStartupBuildItem(bytecodeCreator)); } - template.run(runnerClasses, beanContainerBuildItem.getValue(), mainArgsBuildItem); } } diff --git a/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java b/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java deleted file mode 100644 index 49d5fbd894268..0000000000000 --- a/extensions/command-line-runner/runtime/src/main/java/io/quarkus/clrunner/runtime/CommandLineRunnerTemplate.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.quarkus.clrunner.runtime; - -import java.util.Set; - -import io.quarkus.arc.runtime.BeanContainer; -import io.quarkus.clrunner.CommandLineRunner; -import io.quarkus.runtime.MainArgsSupplier; -import io.quarkus.runtime.annotations.Template; - -@Template -public class CommandLineRunnerTemplate { - - public void run(Set> handlerClasses, BeanContainer beanContainer, - MainArgsSupplier mainArgsSupplier) { - for (Class handlerClass : handlerClasses) { - final BeanContainer.Factory factory = beanContainer.instanceFactory(handlerClass); - final BeanContainer.Instance instance = factory.create(); - final CommandLineRunner commandLineRunner = instance.get(); - - commandLineRunner.run(mainArgsSupplier.getArgs()); - - instance.close(); - } - } -} From 3f4e09933c41015e6ce249b3109f0848b23b7253 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 12 Mar 2019 17:29:22 +0200 Subject: [PATCH 4/8] Apply minor fix --- .../clrunner/deployment/CommandLineRunnerProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java index 06a78955188ac..a30a6d6ab9fc9 100644 --- a/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java +++ b/extensions/command-line-runner/deployment/src/main/java/io/quarkus/clrunner/deployment/CommandLineRunnerProcessor.java @@ -68,9 +68,11 @@ void beans(Optional runner, BuildProducer producer) { + void shutdown(Optional runner, BuildProducer producer) { // TODO: this has to be conditional only set when various things are not present - producer.produce(new ShutdownBuildItem()); + if (runner.isPresent()) { + producer.produce(new ShutdownBuildItem()); + } } @BuildStep From 12ff0a17d9d899fab14ffa751c4ead44c8380471 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 12 Mar 2019 18:15:13 +0200 Subject: [PATCH 5/8] Add an example of how Aesh application could be used --- bom/pom.xml | 6 + build-parent/pom.xml | 10 ++ .../command-line-aesh/deployment/pom.xml | 61 +++++++++ .../AeshCommandLineRunnerBuildItem.java | 16 +++ .../AeshCommandLineRunnerProcessor.java | 125 ++++++++++++++++++ extensions/command-line-aesh/pom.xml | 37 ++++++ extensions/command-line-aesh/runtime/pom.xml | 66 +++++++++ .../io/quarkus/claesh/AeshCommandRunner.java | 58 ++++++++ .../io/quarkus/claesh/QuarkusCommand.java | 6 + .../claesh/QuarkusCommandInvocation.java | 113 ++++++++++++++++ .../QuarkusCommandInvocationProvider.java | 18 +++ .../io/quarkus/claesh/QuarkusContext.java | 20 +++ extensions/pom.xml | 1 + .../pom.xml | 9 +- .../clrunner/tests/CommandLineRunnerIT.java | 55 ++++++++ .../support/MavenProcessInvocationResult.java | 0 .../tests/support/MavenProcessInvoker.java | 0 .../clrunner/tests/support/MojoTestBase.java | 0 .../tests/support/RunningInvoker.java | 0 .../src/test/resources/projects/aesh/pom.xml | 62 +++++++++ .../main/java/org/acme/CapitalizeCommand.java | 41 ++++++ .../src/main/java/org/acme/Capitalizer.java | 0 .../main/java/org/acme/LowercaseCommand.java | 41 ++++++ .../src/main/java/org/acme/Lowercaser.java | 13 ++ .../META-INF/microprofile-config.properties | 0 .../test/resources/projects/classic/pom.xml | 0 .../src/main/java/org/acme/Capitalizer.java | 13 ++ .../src/main/java/org/acme/ExampleRunner.java | 0 .../META-INF/microprofile-config.properties | 3 + integration-tests/pom.xml | 2 +- 30 files changed, 768 insertions(+), 8 deletions(-) create mode 100644 extensions/command-line-aesh/deployment/pom.xml create mode 100644 extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerBuildItem.java create mode 100644 extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerProcessor.java create mode 100644 extensions/command-line-aesh/pom.xml create mode 100644 extensions/command-line-aesh/runtime/pom.xml create mode 100644 extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/AeshCommandRunner.java create mode 100644 extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommand.java create mode 100644 extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocation.java create mode 100644 extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocationProvider.java create mode 100644 extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusContext.java rename integration-tests/{command-line-runner => command-line-apps}/pom.xml (91%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java (52%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java (100%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java (100%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java (100%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java (100%) create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/aesh/pom.xml create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/CapitalizeCommand.java rename integration-tests/{command-line-runner/src/test/resources/projects/classic => command-line-apps/src/test/resources/projects/aesh}/src/main/java/org/acme/Capitalizer.java (100%) create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/LowercaseCommand.java create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Lowercaser.java rename integration-tests/{command-line-runner/src/test/resources/projects/classic => command-line-apps/src/test/resources/projects/aesh}/src/main/resources/META-INF/microprofile-config.properties (100%) rename integration-tests/{command-line-runner => command-line-apps}/src/test/resources/projects/classic/pom.xml (100%) create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java rename integration-tests/{command-line-runner => command-line-apps}/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java (100%) create mode 100644 integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties diff --git a/bom/pom.xml b/bom/pom.xml index 864ed13d74bb0..035bd00cefcb3 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -425,6 +425,12 @@ ${project.version} provided + + io.quarkus + quarkus-command-line-aesh + ${project.version} + provided + diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 67a858b648432..09750aa4a0b2f 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -551,6 +551,16 @@ quarkus-command-line-runner-runtime ${project.version} + + io.quarkus + quarkus-command-line-aesh + ${project.version} + + + io.quarkus + quarkus-command-line-aesh-runtime + ${project.version} + diff --git a/extensions/command-line-aesh/deployment/pom.xml b/extensions/command-line-aesh/deployment/pom.xml new file mode 100644 index 0000000000000..b3f5db6d470c0 --- /dev/null +++ b/extensions/command-line-aesh/deployment/pom.xml @@ -0,0 +1,61 @@ + + + + + quarkus-command-line-aesh-parent + io.quarkus + 999-SNAPSHOT + ../ + + + 4.0.0 + + quarkus-command-line-aesh + Quarkus - Command Line Aesh - Deployment + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-command-line-aesh-runtime + + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerBuildItem.java b/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerBuildItem.java new file mode 100644 index 0000000000000..86448b27d7be1 --- /dev/null +++ b/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerBuildItem.java @@ -0,0 +1,16 @@ +package io.quarkus.claesh.deployment; + +import org.jboss.builder.item.MultiBuildItem; + +public final class AeshCommandLineRunnerBuildItem extends MultiBuildItem { + + private final String className; + + public AeshCommandLineRunnerBuildItem(String className) { + this.className = className; + } + + public String getClassName() { + return className; + } +} diff --git a/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerProcessor.java b/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerProcessor.java new file mode 100644 index 0000000000000..7ba6a228edaff --- /dev/null +++ b/extensions/command-line-aesh/deployment/src/main/java/io/quarkus/claesh/deployment/AeshCommandLineRunnerProcessor.java @@ -0,0 +1,125 @@ +package io.quarkus.claesh.deployment; + +import static io.quarkus.gizmo.MethodDescriptor.ofMethod; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import javax.inject.Singleton; + +import org.aesh.command.CommandDefinition; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Type; + +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.claesh.AeshCommandRunner; +import io.quarkus.claesh.QuarkusCommand; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.ApplicationIndexBuildItem; +import io.quarkus.deployment.builditem.MainAfterStartupBuildItem; +import io.quarkus.deployment.builditem.ShutdownBuildItem; +import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; + +public class AeshCommandLineRunnerProcessor { + + private static final DotName QUARKUS_COMMAND = DotName.createSimple(QuarkusCommand.class.getName()); + private static final DotName SINGLETON = DotName.createSimple(Singleton.class.getName()); + private static final DotName COMMAND_DEFINITION = DotName.createSimple(CommandDefinition.class.getName()); + + @BuildStep + void discover(ApplicationIndexBuildItem indexBuildItem, + BuildProducer commandsProducer, + BuildProducer unremovableProducer) { + + for (ClassInfo info : indexBuildItem.getIndex().getAllKnownImplementors(QUARKUS_COMMAND)) { + if (info.classAnnotation(COMMAND_DEFINITION) == null) { + throw new RuntimeException("All implementations of " + QuarkusCommand.class.getName() + + " must be annotated with " + COMMAND_DEFINITION.toString()); + } + commandsProducer.produce(new AeshCommandLineRunnerBuildItem(info.name().toString())); + } + + // we need to make sure that all application Singleton beans are not removed + Set typesInjectedInCommands = new HashSet<>(); + // TODO: figure out if @Singleton is the only thing that makes sense in a Command Line application? + for (AnnotationInstance annotationInstance : indexBuildItem.getIndex().getAnnotations(SINGLETON)) { + typesInjectedInCommands.add(annotationInstance.target().asClass().name()); + } + if (!typesInjectedInCommands.isEmpty()) { + unremovableProducer.produce(new UnremovableBeanBuildItem( + new Predicate() { + @Override + public boolean test(BeanInfo beanInfo) { + Set types = beanInfo.getTypes(); + for (Type t : types) { + if (typesInjectedInCommands.contains(t.name())) { + return true; + } + } + + return false; + } + })); + } + } + + @BuildStep + List reflection(List commands) { + final List result = new ArrayList<>(); + for (AeshCommandLineRunnerBuildItem command : commands) { + result.add(new ReflectiveClassBuildItem(true, true, command.getClassName())); + } + return result; + } + + @BuildStep + void shutdown(List commands, BuildProducer producer) { + // TODO: this has to be conditional only set when various things are not present + if (!commands.isEmpty()) { + producer.produce(new ShutdownBuildItem()); + } + } + + @BuildStep + void bytecodeCreation(List commands, BuildProducer producer) { + if (commands.isEmpty()) { + return; + } + + // generate the bytecode that will call AeshCommandRunner with the proper parameters + final Consumer bytecodeCreator = new Consumer() { + @Override + public void accept(MainAfterStartupBuildItem.Input input) { + MethodCreator mv = input.getDoStartMethod(); + + ResultHandle commandsArray = mv.newArray(Class.class, mv.load(commands.size())); + + for (int i = 0; i < commands.size(); i++) { + ResultHandle clazz = mv.invokeStaticMethod( + ofMethod(Class.class, "forName", Class.class, String.class), + mv.load(commands.get(i).getClassName())); + mv.writeArrayValue(commandsArray, i, clazz); + } + + final ResultHandle runner = mv.newInstance(MethodDescriptor.ofConstructor(AeshCommandRunner.class)); + + mv.invokeVirtualMethod( + MethodDescriptor.ofMethod(AeshCommandRunner.class, "run", void.class, Class[].class, String[].class), + runner, commandsArray, input.getMainArgs()); + } + }; + + producer.produce(new MainAfterStartupBuildItem(bytecodeCreator)); + } +} diff --git a/extensions/command-line-aesh/pom.xml b/extensions/command-line-aesh/pom.xml new file mode 100644 index 0000000000000..4a4050a297701 --- /dev/null +++ b/extensions/command-line-aesh/pom.xml @@ -0,0 +1,37 @@ + + + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-command-line-aesh-parent + Quarkus - Command Line Aesh + pom + + deployment + runtime + + + diff --git a/extensions/command-line-aesh/runtime/pom.xml b/extensions/command-line-aesh/runtime/pom.xml new file mode 100644 index 0000000000000..442467d683afe --- /dev/null +++ b/extensions/command-line-aesh/runtime/pom.xml @@ -0,0 +1,66 @@ + + + + + + quarkus-command-line-aesh-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-command-line-aesh-runtime + Quarkus - Command Line Aesh - Runtime + + + + + io.quarkus + quarkus-arc-runtime + + + + org.aesh + aesh + 2.0-SNAPSHOT + + + + + + + + maven-dependency-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/AeshCommandRunner.java b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/AeshCommandRunner.java new file mode 100644 index 0000000000000..1a9d4aa9d5668 --- /dev/null +++ b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/AeshCommandRunner.java @@ -0,0 +1,58 @@ +package io.quarkus.claesh; + +import java.io.IOException; + +import org.aesh.command.AeshCommandRuntimeBuilder; +import org.aesh.command.CommandException; +import org.aesh.command.CommandNotFoundException; +import org.aesh.command.CommandRuntime; +import org.aesh.command.impl.registry.AeshCommandRegistryBuilder; +import org.aesh.command.parser.CommandLineParserException; +import org.aesh.command.registry.CommandRegistry; +import org.aesh.command.registry.CommandRegistryException; +import org.aesh.command.validator.CommandValidatorException; +import org.aesh.command.validator.OptionValidatorException; + +import io.quarkus.arc.Arc; + +public class AeshCommandRunner { + + public void run(Class[] commands, String[] args) { + try { + final QuarkusContext quarkusContext = new QuarkusContext(Arc.container()); + + final CommandRegistry registry = AeshCommandRegistryBuilder + . builder() + .commands(commands) + .create(); + + final CommandRuntime runtime = AeshCommandRuntimeBuilder + . builder() + .commandRegistry(registry) + .commandInvocationProvider(new QuarkusCommandInvocationProvider(quarkusContext)) + .build(); + + final StringBuilder sb = new StringBuilder(); + if (args.length > 0) { + sb.append(" "); + if (args.length == 1) { + sb.append(args[0]); + } else { + for (String arg : args) { + if (arg.indexOf(' ') >= 0) { + sb.append('"').append(arg).append("\" "); + } else { + sb.append(arg).append(' '); + } + } + } + } + runtime.executeCommand(sb.toString()); + } + //simplified exceptions for now + catch (CommandNotFoundException | CommandException | CommandLineParserException | CommandValidatorException + | OptionValidatorException | InterruptedException | IOException | CommandRegistryException e) { + throw new RuntimeException(e); + } + } +} diff --git a/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommand.java b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommand.java new file mode 100644 index 0000000000000..f7942975f7e6d --- /dev/null +++ b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommand.java @@ -0,0 +1,6 @@ +package io.quarkus.claesh; + +import org.aesh.command.Command; + +public interface QuarkusCommand extends Command { +} diff --git a/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocation.java b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocation.java new file mode 100644 index 0000000000000..79f8c6a480b36 --- /dev/null +++ b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocation.java @@ -0,0 +1,113 @@ +package io.quarkus.claesh; + +import java.io.IOException; + +import org.aesh.command.CommandException; +import org.aesh.command.CommandNotFoundException; +import org.aesh.command.Executor; +import org.aesh.command.invocation.CommandInvocation; +import org.aesh.command.invocation.CommandInvocationConfiguration; +import org.aesh.command.parser.CommandLineParserException; +import org.aesh.command.shell.Shell; +import org.aesh.command.validator.CommandValidatorException; +import org.aesh.command.validator.OptionValidatorException; +import org.aesh.readline.Prompt; +import org.aesh.readline.action.KeyAction; + +public class QuarkusCommandInvocation implements CommandInvocation { + + private final CommandInvocation delegate; + private final QuarkusContext context; + + QuarkusCommandInvocation(CommandInvocation delegate, QuarkusContext context) { + this.delegate = delegate; + this.context = context; + } + + @Override + public Shell getShell() { + return delegate.getShell(); + } + + @Override + public void setPrompt(Prompt prompt) { + delegate.setPrompt(prompt); + } + + @Override + public Prompt getPrompt() { + return delegate.getPrompt(); + } + + @Override + public String getHelpInfo(String commandName) { + return delegate.getHelpInfo(commandName); + } + + @Override + public String getHelpInfo() { + return delegate.getHelpInfo(); + } + + @Override + public void stop() { + delegate.stop(); + } + + @Override + public KeyAction input() throws InterruptedException { + return delegate.input(); + } + + @Override + public String inputLine() throws InterruptedException { + return delegate.inputLine(); + } + + @Override + public String inputLine(Prompt prompt) throws InterruptedException { + return delegate.inputLine(prompt); + } + + @Override + public void executeCommand(String input) throws CommandNotFoundException, + CommandLineParserException, OptionValidatorException, + CommandValidatorException, CommandException, InterruptedException, IOException { + delegate.executeCommand(input); + } + + @Override + public void print(String msg, boolean paging) { + delegate.print(msg, paging); + } + + @Override + public void println(String msg, boolean paging) { + delegate.println(msg, paging); + } + + @Override + public Executor buildExecutor(String line) throws CommandNotFoundException, + CommandLineParserException, OptionValidatorException, CommandValidatorException, IOException { + return delegate.buildExecutor(line); + } + + @Override + public void print(String msg) { + delegate.print(msg); + } + + @Override + public void println(String msg) { + delegate.println(msg); + } + + @Override + public CommandInvocationConfiguration getConfiguration() { + return null; + } + + public QuarkusContext quarkusContext() { + return context; + } +} diff --git a/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocationProvider.java b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocationProvider.java new file mode 100644 index 0000000000000..8053e588ded9b --- /dev/null +++ b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusCommandInvocationProvider.java @@ -0,0 +1,18 @@ +package io.quarkus.claesh; + +import org.aesh.command.invocation.CommandInvocation; +import org.aesh.command.invocation.CommandInvocationProvider; + +public class QuarkusCommandInvocationProvider implements CommandInvocationProvider { + + private final QuarkusContext context; + + public QuarkusCommandInvocationProvider(QuarkusContext context) { + this.context = context; + } + + @Override + public QuarkusCommandInvocation enhanceCommandInvocation(CommandInvocation commandInvocation) { + return new QuarkusCommandInvocation(commandInvocation, context); + } +} diff --git a/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusContext.java b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusContext.java new file mode 100644 index 0000000000000..47f0c35095a00 --- /dev/null +++ b/extensions/command-line-aesh/runtime/src/main/java/io/quarkus/claesh/QuarkusContext.java @@ -0,0 +1,20 @@ +package io.quarkus.claesh; + +import io.quarkus.arc.ArcContainer; + +public class QuarkusContext { + + private final ArcContainer arcContainer; + + public QuarkusContext(ArcContainer arcContainer) { + this.arcContainer = arcContainer; + } + + public String name() { + return "Quarkus"; + } + + public ArcContainer getArcContainer() { + return arcContainer; + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index a1ac1bc20b35c..954caa72e697d 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -89,6 +89,7 @@ kotlin command-line-runner + command-line-aesh diff --git a/integration-tests/command-line-runner/pom.xml b/integration-tests/command-line-apps/pom.xml similarity index 91% rename from integration-tests/command-line-runner/pom.xml rename to integration-tests/command-line-apps/pom.xml index 8296ebe31e921..cd1f13baed597 100644 --- a/integration-tests/command-line-runner/pom.xml +++ b/integration-tests/command-line-apps/pom.xml @@ -26,15 +26,10 @@ ../ - quarkus-integration-test-command-line-runner - Quarkus - Integration Tests - CLIR + quarkus-integration-test-command-line-apps + Quarkus - Integration Tests - Command Line Apps - - io.quarkus - quarkus-command-line-runner - provided - diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java similarity index 52% rename from integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java rename to integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java index 2f5440e7d6cd6..26add967d2ae6 100644 --- a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java +++ b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/CommandLineRunnerIT.java @@ -59,6 +59,61 @@ public void testCommandLineRunnerIsBuiltProperlyAndExecutesAsExpected() .hasContent("A" + lineSep + "B" + lineSep + "C" + lineSep); } + @Test + public void testAeshCommandIsBuiltProperlyAndExecutesAsExpected() + throws MavenInvocationException, IOException, InterruptedException { + // copy the template + testDir = initProject("projects/aesh", "projects/project-classic-aesh"); + + // invoke the build + running = new RunningInvoker(testDir, false); + final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"), + Collections.emptyMap()); + + // ensure the build completed successfully + assertThat(result.getProcess().waitFor()).isEqualTo(0); + + // ensure the runner jar was created + final File targetDir = getTargetDir(); + final List runnerJarFiles = getFilesEndingWith(targetDir, "-runner.jar"); + assertThat(runnerJarFiles).hasSize(1); + + final File runnerJarFile = runnerJarFiles.get(0); + final String expectedOutputFile = runnerJarFile.getParentFile().getAbsolutePath() + "/p1.txt"; + + // launch a second run to use the capitalize command + final Process process = new ProcessBuilder() + .directory(runnerJarFile.getParentFile()) + .command("java", "-jar", runnerJarFile.getAbsolutePath(), "capitalize", "--file", expectedOutputFile, "a", "b", + "c") + .start(); + + // ensure the application completed successfully + assertThat(process.waitFor()).isEqualTo(0); + + // ensure it created the expected output + final String lineSep = System.lineSeparator(); + assertThat(new File(expectedOutputFile)) + .exists() + .hasContent("A" + lineSep + "B" + lineSep + "C" + lineSep); + + // launch a second run to use the lowecase command + final String secondRunOutputFile = runnerJarFile.getParentFile().getAbsolutePath() + "/p2.txt"; + final Process secondProcess = new ProcessBuilder() + .directory(runnerJarFile.getParentFile()) + .command("java", "-jar", runnerJarFile.getAbsolutePath(), "lowercase", "--file", secondRunOutputFile, "A", "B", + "C") + .start(); + + // ensure the application completed successfully + assertThat(secondProcess.waitFor()).isEqualTo(0); + + // ensure it created the expected output + assertThat(new File(secondRunOutputFile)) + .exists() + .hasContent("a" + lineSep + "b" + lineSep + "c" + lineSep); + } + private File getTargetDir() { return new File(testDir.getAbsoluteFile() + "/target"); } diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java similarity index 100% rename from integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java rename to integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvocationResult.java diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java similarity index 100% rename from integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java rename to integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MavenProcessInvoker.java diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java similarity index 100% rename from integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java rename to integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/MojoTestBase.java diff --git a/integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java b/integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java similarity index 100% rename from integration-tests/command-line-runner/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java rename to integration-tests/command-line-apps/src/test/java/io/quarkus/clrunner/tests/support/RunningInvoker.java diff --git a/integration-tests/command-line-apps/src/test/resources/projects/aesh/pom.xml b/integration-tests/command-line-apps/src/test/resources/projects/aesh/pom.xml new file mode 100644 index 0000000000000..85fa434ab9a35 --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/aesh/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + org.acme + acme + 1.0-SNAPSHOT + + @project.version@ + 1.8 + UTF-8 + 1.8 + + + + @project.groupId@ + quarkus-command-line-aesh + ${quarkus.version} + provided + + + + + + @project.groupId@ + @project.artifactId@ + ${quarkus.version} + + + + build + + + + + + + + + native + + + + @project.groupId@ + @project.artifactId@ + ${quarkus.version} + + + + native-image + + + true + + + + + + + + + diff --git a/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/CapitalizeCommand.java b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/CapitalizeCommand.java new file mode 100644 index 0000000000000..990b35462c0ac --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/CapitalizeCommand.java @@ -0,0 +1,41 @@ +package org.acme; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +import io.quarkus.claesh.QuarkusCommand; +import io.quarkus.claesh.QuarkusCommandInvocation; +import org.aesh.command.CommandDefinition; +import org.aesh.command.CommandException; +import org.aesh.command.CommandResult; +import org.aesh.command.option.Arguments; +import org.aesh.command.option.Option; + +@CommandDefinition(name = "capitalize", description = "capitalizer") +public class CapitalizeCommand implements QuarkusCommand { + + @Option + private String file; + + @Arguments + private List arguments; + + @Override + public CommandResult execute(QuarkusCommandInvocation commandInvocation) throws CommandException, InterruptedException { + final Path out = Paths.get(file); + + try { + final Capitalizer capitalizer = commandInvocation.quarkusContext().getArcContainer().instance(Capitalizer.class).get(); + Files.write(out, arguments.stream().map(capitalizer::perform).collect(Collectors.toList()), Charset.defaultCharset()); + } catch (IOException e) { + throw new CommandException(e); + } + + return CommandResult.SUCCESS; + } +} diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Capitalizer.java similarity index 100% rename from integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java rename to integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Capitalizer.java diff --git a/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/LowercaseCommand.java b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/LowercaseCommand.java new file mode 100644 index 0000000000000..28e8605a0a789 --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/LowercaseCommand.java @@ -0,0 +1,41 @@ +package org.acme; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +import io.quarkus.claesh.QuarkusCommand; +import io.quarkus.claesh.QuarkusCommandInvocation; +import org.aesh.command.CommandDefinition; +import org.aesh.command.CommandException; +import org.aesh.command.CommandResult; +import org.aesh.command.option.Arguments; +import org.aesh.command.option.Option; + +@CommandDefinition(name = "lowercase", description = "lowercaser") +public class LowercaseCommand implements QuarkusCommand { + + @Option + private String file; + + @Arguments + private List arguments; + + @Override + public CommandResult execute(QuarkusCommandInvocation commandInvocation) throws CommandException, InterruptedException { + final Path out = Paths.get(file); + + try { + final Lowercaser capitalizer = commandInvocation.quarkusContext().getArcContainer().instance(Lowercaser.class).get(); + Files.write(out, arguments.stream().map(capitalizer::perform).collect(Collectors.toList()), Charset.defaultCharset()); + } catch (IOException e) { + throw new CommandException(e); + } + + return CommandResult.SUCCESS; + } +} diff --git a/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Lowercaser.java b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Lowercaser.java new file mode 100644 index 0000000000000..f60309c472e83 --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/java/org/acme/Lowercaser.java @@ -0,0 +1,13 @@ +package org.acme; + +import javax.inject.Singleton; + + +@Singleton +public class Lowercaser { + + public String perform(String input) { + return input.toLowerCase(); + } + +} diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties b/integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/resources/META-INF/microprofile-config.properties similarity index 100% rename from integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties rename to integration-tests/command-line-apps/src/test/resources/projects/aesh/src/main/resources/META-INF/microprofile-config.properties diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml b/integration-tests/command-line-apps/src/test/resources/projects/classic/pom.xml similarity index 100% rename from integration-tests/command-line-runner/src/test/resources/projects/classic/pom.xml rename to integration-tests/command-line-apps/src/test/resources/projects/classic/pom.xml diff --git a/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java b/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java new file mode 100644 index 0000000000000..81da4a5537ee2 --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/Capitalizer.java @@ -0,0 +1,13 @@ +package org.acme; + +import javax.inject.Singleton; + + +@Singleton +public class Capitalizer { + + public String perform(String input) { + return input.toUpperCase(); + } + +} diff --git a/integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java b/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java similarity index 100% rename from integration-tests/command-line-runner/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java rename to integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/java/org/acme/ExampleRunner.java diff --git a/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties b/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000000000..a4bf738bbafb3 --- /dev/null +++ b/integration-tests/command-line-apps/src/test/resources/projects/classic/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,3 @@ +# Configuration file +key = value +greeting=bonjour diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 55afc9f2cc4b7..d52f0ac727a8c 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -56,6 +56,6 @@ camel-core camel-salesforce --> - command-line-runner + command-line-apps From b488e520b7c326c1722928cc4c0dff17eafaccc9 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 13 Mar 2019 00:21:32 +0200 Subject: [PATCH 6/8] Fix broken rebase --- integration-tests/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index d52f0ac727a8c..2c59bd98907d7 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -55,7 +55,6 @@ elytron-security camel-core camel-salesforce - --> command-line-apps From f7756097cdd5d267d0129f3bbc740b11d45a92b0 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 13 Mar 2019 01:14:54 +0200 Subject: [PATCH 7/8] Fix broken tests --- integration-tests/command-line-apps/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/integration-tests/command-line-apps/pom.xml b/integration-tests/command-line-apps/pom.xml index cd1f13baed597..716ef8bc8cbeb 100644 --- a/integration-tests/command-line-apps/pom.xml +++ b/integration-tests/command-line-apps/pom.xml @@ -30,6 +30,20 @@ Quarkus - Integration Tests - Command Line Apps + + + io.quarkus + quarkus-command-line-runner + provided + + + io.quarkus + quarkus-command-line-aesh + provided + From e88030665b9c000afa09e18e4e101c3bdba34eb9 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 13 Mar 2019 07:35:57 +0200 Subject: [PATCH 8/8] Use latest released version aesh --- extensions/command-line-aesh/runtime/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/command-line-aesh/runtime/pom.xml b/extensions/command-line-aesh/runtime/pom.xml index 442467d683afe..d284cdcf475b2 100644 --- a/extensions/command-line-aesh/runtime/pom.xml +++ b/extensions/command-line-aesh/runtime/pom.xml @@ -39,7 +39,7 @@ org.aesh aesh - 2.0-SNAPSHOT + 2.0