Picocli を使ったコマンドモード
Picocli は、リッチなコマンドラインアプリケーションを作成するためのオープンソースツールです。
Quarkus では、Picocli を使用するためのサポートを提供しています。このガイドには、 picocli
エクステンションの使用例が記載されています。
Quarkus のコマンドモードに詳しくない場合は、まず コマンドモードのリファレンスガイド を読むことを検討してください。 |
エクステンション
Quarkus プロジェクトを設定すると、プロジェクトのベースディレクトリーで次のコマンドを実行することで、 picocli
エクステンションをプロジェクトに追加できます。
quarkus extension add picocli
./mvnw quarkus:add-extension -Dextensions='picocli'
./gradlew addExtension --extensions='picocli'
これにより、pom.xml
に以下が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli</artifactId>
</dependency>
implementation("io.quarkus:quarkus-picocli")
コマンドラインアプリケーションの構築
シンプルなアプリケーション
1 つの Command
のみを含むシンプルな Picocli アプリケーションは、次のように作成できます。
package com.acme.picocli;
import picocli.CommandLine;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
@CommandLine.Command (1)
public class HelloCommand implements Runnable {
@CommandLine.Option(names = {"-n", "--name"}, description = "Who will we greet?", defaultValue = "World")
String name;
private final GreetingService greetingService;
public HelloCommand(GreetingService greetingService) { (2)
this.greetingService = greetingService;
}
@Override
public void run() {
greetingService.sayHello(name);
}
}
@Dependent
class GreetingService {
void sayHello(String name) {
System.out.println("Hello " + name + "!");
}
}
1 | picocli.CommandLine.Command でアノテーションが付けられたクラスが 1 つだけの場合、それがコマンドラインアプリケーションのエントリーポイントとして自動的に使用されます。 |
2 | picocli.CommandLine.Command でアノテーションされたクラスはすべて CDI Bean として登録されています。 |
@CommandLine.Command でアノテーションが付けられた Bean では、プロキシーされるスコープ (例: @ApplicationScoped ) を使用しないでください 。
Picocli はそのような Bean にフィールド値を設定できないためです。
デフォルトでは、この Picocli エクステンションは @CommandLine.Command でアノテーションが付けられたクラスを @Dependent スコープを使用して登録します。プロキシーされるスコープを使用する必要がある場合は、次の例のように、フィールドではなくセッターにアノテーションを付けてください。
|
@CommandLine.Command
@ApplicationScoped
public class EntryCommand {
private String name;
@CommandLine.Option(names = "-n")
public void setName(String name) {
this.name = name;
}
}
複数のコマンドを使用したコマンドラインアプリケーション
複数のクラスが picocli.CommandLine.Command
アノテーションを持つ場合、そのうちの 1 つに io.quarkus.picocli.runtime.annotations.TopCommand
アノテーションを付ける必要があります。これは quarkus.picocli.top-command
プロパティーで上書きすることができます。
package com.acme.picocli;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
@TopCommand
@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {HelloCommand.class, GoodByeCommand.class})
public class EntryCommand {
}
@CommandLine.Command(name = "hello", description = "Greet World!")
class HelloCommand implements Runnable {
@Override
public void run() {
System.out.println("Hello World!");
}
}
@CommandLine.Command(name = "goodbye", description = "Say goodbye to World!")
class GoodByeCommand implements Runnable {
@Override
public void run() {
System.out.println("Goodbye World!");
}
}
Picocli CommandLine インスタンスのカスタマイズ
独自の Bean インスタンスを生成することで、 picocli
エクステンションで使用される CommandLine クラスをカスタマイズすることができます。
package com.acme.picocli;
import io.quarkus.picocli.runtime.PicocliCommandLineFactory;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
@TopCommand
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;
@Override
public void run() {
System.out.println("My name is: " + spec.name());
}
}
@ApplicationScoped
class CustomConfiguration {
@Produces
CommandLine customCommandLine(PicocliCommandLineFactory factory) { (1)
return factory.create().setCommandName("CustomizedName");
}
}
1 | PicocliCommandLineFactory は、 TopCommand と CommandLine.IFactory を注入した CommandLine のインスタンスを作成します。 |
プロファイルごとに異なるエントリーコマンド
@IfBuildProfile
を使用して、プロファイルごとに異なるエントリーコマンドを作成することができます。
@ApplicationScoped
public class Config {
@Produces
@TopCommand
@IfBuildProfile("dev")
public Object devCommand() {
return DevCommand.class; (1)
}
@Produces
@TopCommand
@IfBuildProfile("prod")
public Object prodCommand() {
return new ProdCommand("Configured by me!");
}
}
1 | ここでは java.lang.Class のインスタンスを返すことができます。この場合、 CommandLine は CommandLine.IFactory を使ってこのクラスのインスタンスを作成しようとします。 |
解析された引数での CDI Beans の設定
Picocli によって解析された引数に基づいて CDI Bean を設定するために、 Event<CommandLine.ParseResult>
、または単に CommandLine.ParseResult
を使用することができます。このイベントは、このエクステンションによって作成された QuarkusApplication
クラスで生成されます。独自の @QuarkusMain
を提供している場合、このイベントは発生しません。 CommandLine.ParseResult
はデフォルトの CommandLine
Bean から作成されます。
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Option(names = "-c", description = "JDBC connection string")
String connectionString;
@Inject
DataSource dataSource;
@Override
public void run() {
try (Connection c = dataSource.getConnection()) {
// Do something
} catch (SQLException throwables) {
// Handle error
}
}
}
@ApplicationScoped
class DatasourceConfiguration {
@Produces
@ApplicationScoped (1)
DataSource dataSource(CommandLine.ParseResult parseResult) {
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setURL(parseResult.matchedOption("c").getValue().toString());
return ds;
}
}
1 | @ApplicationScoped 遅延初期化に使用 |
独自の QuarkusMain の提供
また、 QuarkusMain
でアノテーションされた独自のアプリケーションのエントリーポイントを提供することもできます (コマンドモードのリファレンスガイド に記載されています)。
package com.acme.picocli;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import picocli.CommandLine;
import jakarta.inject.Inject;
@QuarkusMain
@CommandLine.Command(name = "demo", mixinStandardHelpOptions = true)
public class ExampleApp implements Runnable, QuarkusApplication {
@Inject
CommandLine.IFactory factory; (1)
@Override
public void run() {
// business logic
}
@Override
public int run(String... args) throws Exception {
return new CommandLine(this, factory).execute(args);
}
}
1 | picocli のエクステンションで作成された Quarkus 互換の CommandLine.IFactory Bean。 |
開発モード
開発モード、つまり mvn quarkus:dev
を実行している場合、アプリケーションは Space bar
キーが押されるたびに実行され、再起動されます。また、コマンドラインアプリに引数を渡すには、 quarkus.args
システムプロパティを使用します (例: mvn quarkus:dev -Dquarkus.args='--help'
と mvn quarkus:dev -Dquarkus.args='-c -w --val 1'
)。
Gradle プロジェクトの場合、引数は --quarkus-args
を使用して渡すことができます。
コマンドライン機能を含む一般的なQuarkusアプリケーション(HTTPベースのサービスなど)を作成する場合は、アプリケーションのライフサイクルを異なる方法で処理する必要があります。
コマンドの |
アプリケーションのパッケージ化
Picocli コマンドラインアプリケーションは、複数のフォーマット (JAR、ネイティブ実行可能ファイルなど) でパッケージ化でき、さまざまなリポジトリー (Homebrew、Chocolatey、SDKMAN など) に公開できます。
jar として
Picocli コマンドラインアプリケーションは標準の Quarkus アプリケーションであるため、さまざまなパッケージフォーマット (fast-jar、uber-jar など) で JAR として公開できます。
コマンドラインアプリケーションのコンテキストでは、JAR をそのまま公開する予定の場合は、uber-jar をビルドする方が実用的です。
uber-jar のビルド方法の詳細は、次のドキュメントを参照してください。
その後、標準の java -jar your-application.jar
コマンドを使用してアプリケーションを実行できます。
really-executable-jar-maven-plugin などのプラグインを使用すると、コマンドラインアプリケーションの実行を簡素化できます。
ネイティブ実行可能ファイルとして
ネイティブ実行可能ファイル をビルドすることもできますが、ネイティブ実行可能ファイルは移植可能ではなく、サポートされているプラットフォームごとに 1 つのバイナリーが必要であることに注意してください。
アプリケーションの公開
コマンドラインアプリケーションをリポジトリーに公開すると、使用がはるかに簡単になります。 要件に応じて、https://sdkman.io/[SDKMAN!]、macOS の場合は Homebrew、Windows の場合は Chocolatey など、さまざまなアプリケーションリポジトリーを利用できます。
これらのリポジトリーに公開する際には、https://jreleaser.org/[JReleaser] の使用を推奨します。
JReleaser は、アプリケーションを簡単に実行できるように、JAR に実行可能なラッパーを追加します。
Kubernetesサポート
コマンドラインアプリケーションを作成したら、 kubernetes
エクステンションを追加することで、このアプリケーションを Kubernetes にインストールして使用するために必要なリソースを生成することもできます。 kubernetes
エクステンションをインストールするには、プロジェクトのベースディレクトリで以下のコマンドを実行します。
quarkus extension add kubernetes
./mvnw quarkus:add-extension -Dextensions='kubernetes'
./gradlew addExtension --extensions='kubernetes'
これにより、 pom.xml
に以下が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes</artifactId>
</dependency>
そして次に、アプリケーションをビルドします:
quarkus build
./mvnw install
./gradlew build
Kubernetes エクステンションは Picocli エクステンションの存在を検出し、 target/kubernetes/
ディレクトリに Deployment リソースの代わりに Job リソースを生成します。
Jobリソースを生成しない場合は、プロパティ quarkus.kubernetes.deployment-kind を使用して生成したいリソースを指定します。たとえば、Deployment リソースを生成したい場合は、 quarkus.kubernetes.deployment-kind=Deployment を使用します。
|
さらに、Kubernetes Jobが使用する引数を、プロパティ quarkus.kubernetes.arguments
を介して提供することができます。例えば、 quarkus.kubernetes.arguments=A,B
というプロパティを追加し、プロジェクトをビルドすると、以下のようなJobリソースが生成されます。
apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/name: app
app.kubernetes.io/version: 0.1-SNAPSHOT
name: app
spec:
completionMode: NonIndexed
suspend: false
template:
metadata:
labels:
app.kubernetes.io/name: app
app.kubernetes.io/version: 0.1-SNAPSHOT
spec:
containers:
- args:
- A
- B
env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: docker.io/user/app:0.1-SNAPSHOT
imagePullPolicy: Always
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
restartPolicy: OnFailure
terminationGracePeriodSeconds: 10
最後に、KubernetesにインストールされるたびにKubernetesジョブが起動されます。Kubernetesのジョブの実行方法については、こちらの ドキュメント で詳しく解説しています。
設定リファレンス
ビルド時に固定される設定プロパティ - 他のすべての設定プロパティは実行時にオーバーライド可能
Configuration property |
型 |
デフォルト |
---|---|---|
Name of bean annotated with Environment variable: Show more |
string |