Skip to content

Commit

Permalink
Add very basic support for command line only applications
Browse files Browse the repository at this point in the history
Relates to: quarkusio#284
  • Loading branch information
geoand committed Mar 7, 2019
1 parent 38d5ec9 commit 79814e9
Show file tree
Hide file tree
Showing 31 changed files with 1,033 additions and 6 deletions.
6 changes: 6 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-command-line-runner</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>

<!-- Quarkus test dependencies -->

Expand Down
10 changes: 10 additions & 0 deletions build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,16 @@
<artifactId>quarkus-legacy-launcher</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-command-line-runner</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-command-line-runner-runtime</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Quarkus test dependencies -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -283,10 +284,21 @@ public static Consumer<BuildChainBuilder> 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<? extends BuildItem> 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) {
Expand Down Expand Up @@ -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));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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;
Expand Down Expand Up @@ -76,6 +77,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(ExtensionClassLoaderBuildItem.class);
Expand All @@ -97,6 +99,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))
.execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.quarkus.deployment.builditem;

import org.jboss.builder.item.SimpleBuildItem;

public final class ShutdownBuildItem extends SimpleBuildItem {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,12 +72,14 @@ class MainClassBuildStep {
MainClassBuildItem build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
List<ObjectSubstitutionBuildItem> substitutions,
List<MainBytecodeRecorderBuildItem> mainMethod,
List<MainAfterStartupBytecodeRecorderBuildItem> mainMethodAfterStartup,
List<SystemPropertyBuildItem> properties,
List<JavaLibraryPathAdditionalPathBuildItem> javaLibraryPathAdditionalPaths,
Optional<SslTrustStoreSystemPropertyBuildItem> sslTrustStoreSystemProperty,
List<FeatureBuildItem> features,
BuildProducer<ApplicationClassNameBuildItem> appClassNameProducer,
List<BytecodeRecorderObjectLoaderBuildItem> loaders,
Optional<ShutdownBuildItem> shutdownBuildItem,
ClassOutputBuildItem classOutput) {

String appClassName = APP_CLASS + COUNT.incrementAndGet();
Expand Down Expand Up @@ -131,6 +135,7 @@ MainClassBuildItem build(List<StaticBytecodeRecorderBuildItem> 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)
Expand Down Expand Up @@ -187,6 +192,9 @@ MainClassBuildItem build(List<StaticBytecodeRecorderBuildItem> 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();
Expand Down Expand Up @@ -214,8 +222,31 @@ MainClassBuildItem build(List<StaticBytecodeRecorderBuildItem> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.quarkus.runtime;

public interface MainArgsSupplier {

String[] getArgs();
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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);
}
Expand Down
61 changes: 61 additions & 0 deletions extensions/command-line-runner/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2018 Red Hat, Inc.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-command-line-runner-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-command-line-runner</artifactId>
<name>Quarkus - Command Line Runner - Deployment</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-command-line-runner-runtime</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 79814e9

Please sign in to comment.