The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

コマンドモードアプリケーション

このリファレンスでは、実行して終了するアプリケーションの書き方について説明しています。

ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitリポジトリをクローンする: git clone https://github.com/quarkusio/quarkus-quickstarts.git 、またはhttps://github.com/quarkusio/quarkus-quickstarts/archive/main.zip[archive]をダウンロードする。

このソリューションは getting-started-command-mode ディレクトリにあります。

コマンドモードアプリケーションの作成

終了するアプリケーションを実装するためには、2つの異なるアプローチがあります。

  1. QuarkusApplication を実装し、Quarkusがこのメソッドを自動的に実行するようにします。

  2. QuarkusApplication とJava mainメソッドを実装し、Java mainメソッドを使用してQuarkusを起動します。

このドキュメントでは、 QuarkusApplication インスタンスをアプリケーション mainと呼び、Java mainメソッドを持つクラスを Java mainと呼びます。

The simplest possible command mode application with access to Quarkus APIs might appear as follows:

import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain    (1)
public class HelloWorldMain implements QuarkusApplication {
  @Override
  public int run(String... args) throws Exception {   (2)
    System.out.println("Hello " + args[1]);
    return 0;
 }
}
1 @QuarkusMain アノテーションは、これがメインのエントリーポイントであることをQuarkusに伝えます。 <.> run メソッドは、Quarkusが起動すると呼び出され、終了するとアプリケーションが停止します。

コンテキスト

`ContextNotActiveException`が発生しましたか?

A command mode application (like a CLI) is a bit different from say an HTTP service, there is a single call from the command line. So the notion of request let alone multiple requests does not exist per se. Therefore, request scope is not the default.

アプリケーションBeanやサービスにアクセスするために、 @QuarkusMain インスタンスはデフォルトでapplicationスコープのBeanであることに注意してください。singleton、application、dependentスコープのBeanへのアクセスを持っています。requestスコープを必要とするBeanと対話したい場合は、 run() メソッドに @ActivateRequestContext を記述してください。

If you want to interact with beans that requires a request scope, simply add the @ActivateRequestContext annotation on your run() method. This let run() have access to methods like listAll() and query* methods on a Panache Entity. Without it, you will eventually get a ContextNotActiveException when accessing such classes/beans.

Main method

Java mainでアプリケーション mainを実行したい場合は以下のようになります:

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class JavaMain {

    public static void main(String... args) {
        Quarkus.run(HelloWorldMain.class, args);
    }
}

これは HelloWorldMain アプリケーション mainを直接実行するのと実質的には同じですが、IDE から実行できるという利点があります。

QuarkusApplication を実装したクラスで Java main がある場合は Java main が実行されます。
Java mainはほとんどロジックを実行せず、アプリケーション mainを起動するだけにすることをお勧めします。開発モードでは、Java mainはアプリケーション mainとは異なるClassLoaderで実行されるので、期待通りの動作をしないかもしれません。

複数のmainメソッド

アプリケーション内に複数のmainメソッドを持ち、ビルド時にそれらの間で選択することが可能です。 @QuarkusMain アノテーションはオプションの 'name' パラメーターを取り、 quarkus.package.main-class ビルド時設定オプションを使用して実行するmainを選択するために使用できます。アノテーションを使用したくない場合は、メインクラスの完全修飾名を指定するために使用することもできます。

By default, the @QuarkusMain with no name (i.e. the empty string) will be used, and if it is not present and quarkus.package.main-class is not specified then Quarkus will automatically generate a main class that just runs the application.

@QuarkusMainname は一意である必要があります(デフォルトの空文字列を含む)。アプリケーション内に複数の @QuarkusMain アノテーションがある場合、名前が一意でないとビルドに失敗します。

コマンドモードのライフサイクル

コマンドモードのアプリケーションを実行する場合、基本的なライフサイクルは以下の通りです。

  1. Quarkusの起動

  2. QuarkusApplication mainメソッドの実行

  3. mainメソッドがreturnされた後にQuarkusをシャットダウンし、JVMを終了する

シャットダウンは常にアプリケーションのメインスレッドがreturnされることで開始されます。起動時に何らかのロジックを実行して、通常のアプリケーションのように実行したい場合 (つまり終了しない) は、メインスレッドから Quarkus.waitForExit を呼び出す必要があります (非コマンドモードのアプリケーションは、基本的に waitForExit を呼び出すだけのアプリケーションを実行しているだけです)。

If you want to shut down a running application and you are not in the main thread, then you should call Quarkus.asyncExit in order to unblock the main thread and initiate the shutdown process.

開発モード

Also, for command mode applications, the dev mode is supported. When you start your application in dev mode, the command mode application is executed:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

As command mode applications will often require arguments to be passed on the command line, this is also possible in dev mode:

CLI
quarkus dev '--help'
Maven
./mvnw quarkus:dev -Dquarkus.args='--help'
Gradle
./gradlew quarkusDev --quarkus-args='--help'

アプリケーションが停止した後、画面の下に以下のように表示されます。

--
Press [space] to restart, [e] to edit command line args (currently '-w --tags 1.0.1.Final'), [r] to resume testing, [o] Toggle test output, [h] for more options>

スペースバー キーを押せば、アプリケーションが再び起動します。また、 e ホットキーを使って、コマンドライン引数を編集し、アプリケーションを再起動することもできます。

コマンドモードアプリケーションのテスト

コマンドモード・アプリケーションは、 @QuarkusMainTest および @QuarkusMainIntegrationTest のアノテーションを使用してテストすることができます。これらは、 @QuarkusTest および @QuarkusIntegrationTest と同様の方法で動作します。 @QuarkusMainTest は現在のJVM内でCLIテストを実行し、 QuarkusIntegrationTest は生成された実行ファイル(ジャーおよびネイティブの両方)を実行するために使用されます。

上記のCLIアプリケーションの簡単なテストを以下のように書くことができます。

import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import io.quarkus.test.junit.main.QuarkusMainLauncher;
import io.quarkus.test.junit.main.QuarkusMainTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

@QuarkusMainTest
public class HelloTest {

    @Test
    @Launch("World")
    public void testLaunchCommand(LaunchResult result) {
        Assertions.assertEquals("Hello World", result.getOutput());
    }

    @Test
    @Launch(value = {}, exitCode = 1)
    public void testLaunchCommandFailed() {
    }

    @Test
    public void testManualLaunch(QuarkusMainLauncher launcher) {
        LaunchResult result = launcher.launch("Everyone");
        Assertions.assertEquals(0, result.exitCode());
        Assertions.assertEquals("Hello Everyone", result.getOutput());
    }
}

そして、これを統合テストで拡張し、ネイティブ実行可能ファイルや実行可能jarをテストするために使用することができます。

import io.quarkus.test.junit.main.QuarkusMainIntegrationTest;

@QuarkusMainIntegrationTest
public class HelloIT extends HelloTest {
}

モック

CDI injection is not supported in the @QuarkusMainTest tests. Consequently, mocking CDI beans with QuarkusMock or @InjectMock is not supported either.

It is possible to mock CDI beans by leveraging test profiles though.

For instance, in the following test, the singleton CdiBean1 will be mocked by MockedCdiBean1:

package org.acme.commandmode.test;

import java.util.Set;

import javax.enterprise.inject.Alternative;
import javax.inject.Singleton;

import org.junit.jupiter.api.Test;
import org.acme.commandmode.test.MyCommandModeTest.MyTestProfile;

import io.quarkus.test.junit.QuarkusTestProfile;
import io.quarkus.test.junit.TestProfile;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import io.quarkus.test.junit.main.QuarkusMainTest;

@QuarkusMainTest
@TestProfile(MyTestProfile.class)
public class MyCommandModeTest {

    @Test
    @Launch(value = {})
    public void testLaunchCommand(LaunchResult result) {
        // ... assertions ...
    }

    public static class MyTestProfile implements QuarkusTestProfile {

        @Override
        public Set<Class<?>> getEnabledAlternatives() {
            return Set.of(MockedCdiBean1.class); (1)
        }
    }

    @Alternative (2)
    @Singleton (3)
    public static class MockedCdiBean1 implements CdiBean1 {

        @Override
        public String myMethod() {
            return "mocked value";
        }
    }
}
1 List all the CDI beans for which you want to enable an alternative mocked bean.
2 Use @Alternative without a @Priority. Make sure you don’t use @Mock.
3 The scope of the mocked bean should be consistent with the original one.

Using this pattern, you can enable specific alternatives for any given test.