このページを編集

Building my first extension

Quarkus のエクステンションは、プロジェクトの依存関係がそうであるように、アプリケーションを強化します。エクステンションの役割は、Quarkus のパラダイムを活用して、ライブラリを Quarkus アーキテクチャにシームレスに統合することです。たとえば、ビルド時に、より多くのことを行います。これが、実績のあるエコシステムを利用して、Quarkus のパフォーマンスとネイティブコンパイルを活用することができる仕組みです。 code.quarkus.io にアクセスして、サポートされているエクステンションのリストを入手してください。

このガイドでは、サンプル Greeting エクステンション を開発します。このエクステンションは、訪問者に挨拶するだけのカスタマイズ可能な HTTP エンドポイントを公開します。

免責事項

念のため、アプリケーションにサーブレットを追加するのにエクステンションは必要ないことを明確にしておきます。このガイドは、エクステンション開発の概念を説明するための簡略化された例であり、より詳しい情報が必要な場合は 完全なドキュメント を参照してください。ビルド時に処理を移動したり、ネイティブイメージのビルドを簡素化したりする機能の強力さを代表するものではないことに留意してください。

前提条件

このガイドを完成させるには、以下が必要です:

  • ざっと 30 minutes

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.16

  • 使用したい場合は、 Quarkus CLI

Java および Maven 以外でエクステンションを作成することは Quarkus チームによってテストされて いません。そのため、このパスから外れると期待通りに動作しない可能性があります。

基本概念

まず最初に、いくつかの基本的な概念から始めましょう。

  • JVM モードとネイティブモード

    • Quarkus は何よりもまず Java フレームワークです。つまり、従来の JAR アプリケーションを開発、パッケージ化、実行できます。これを JVM モード と呼びます。

    • GraalVM により、Java アプリケーションをマシン固有のコード (Go や C++ のように) にコンパイルすることができます。これは、ネイティブモード と呼ばれます。

Java バイトコードをネイティブシステム固有のマシンコードにコンパイルする操作は、Ahead of Time Compilation (AoT) と呼ばれています。

  • 従来型の Java フレームワークにおけるビルド時と実行時の比較

    • ビルド時は、Javaソース・ファイルを実行可能なもの(クラス・ファイル、jar/war、ネイティブ・イメージ)に変換するために適用するすべてのアクションに対応します。 通常、この段階はコンパイル、アノテーション処理、バイトコード生成などで構成されます。この時点では、すべてが開発者の範囲と制御下にあります。

    • 実行時はアプリケーションを実行するときに起こるすべてのアクションです。 実行時は明らかにビジネス指向のアクションを開始することに重点を置いていますが、ライブラリや設定ファイルのロード、アプリケーションのクラスパスのスキャン、依存性注入の設定、オブジェクトリレーショナルマッピングの設定、RESTコントローラのインスタンス化など、多くの技術的なアクションに依存しています。

通常、Javaフレームワークは、アプリケーションの "ビジネス志向のレイヤー" を実際に起動する前に、実行時中にブートストラップを行います。ブートストラップの際、フレームワークは、リフレクションによって適切なオブジェクトをインスタンス化するために、設定、エンティティー定義、依存関係の注入バインディングなどを見つけるためにクラスパスをスキャンします。これにより、動的にメタデータを収集します。主な結果は以下の通りです。

  • アプリケーションの準備が遅れる: 実際にビジネスリクエストに対応する前に数秒待つ必要があります。

  • ブートストラップでリソース消費のピークを持つ: 制約のある環境では、実際のビジネスニーズではなく、技術的なブートストラップのニーズに基づいて、必要なリソースのサイズを決定する必要があります。

Quarkus の概念は、これらのアクションの左シフトを行い、最終的にはビルド時に実行することで、可能な限り低速でメモリー集約的な動的コードの実行を防ぐことです。Quarkus のエクステンションは、お気に入りのライブラリやテクノロジーのアダプターレイヤーとして機能する Java のコードです。

Quarkus のエクステンションの説明

Quarkus のエクステンションは、2 つの部分から構成されています。

  • エクステンション開発者がアプリケーション開発者に公開する機能を表す ランタイムモジュール (認証フィルター、強化されたデータ層 API など)。ランタイム依存関係は、ユーザーがアプリケーションの依存関係として追加するものです (Maven POM または Gradle ビルドスクリプト)。

  • ビルドのオーグメンテーションフェーズで使用される デプロイメント モジュール は、Quarkus の哲学に従ってライブラリーを「デプロイ」する方法を記述します。言い換えれば、ビルド中にアプリケーションにすべての Quarkus 最適化を適用します。デプロイメント モジュールは、GraalVM のネイティブコンパイルの準備を行う場所でもあります。

Users should not be adding the deployment modules of extension as application dependencies. The deployment dependencies are resolved by Quarkus during the augmentation phase from the runtime dependencies of the application.

この時点で、デプロイメント モジュールのおかげで、ほとんどのマジックがオーグメンテーション ビルド時に発生することを理解できたはずです。

Quarkus アプリケーションブートストラップ

Quarkus アプリケーションには、3 つの異なるブートストラップフェーズがあります。

  • オーグメンテーション。ビルド時に、Quarkus エクステンションはアプリケーションのバイトコード (依存関係を含む) と設定をロードしてスキャンします。この段階で、エクステンションは設定ファイルを読み込んだり、特定の アノテーション のためにクラスをスキャンしたりできます。すべてのメタデータが収集されると、エクステンションは ORM、DI、または REST コントローラー設定のようなライブラリーのブートストラップアクションを前処理できます。ブートストラップの結果はバイトコードに直接記録され、最終的なアプリケーションパッケージの一部となります。

  • 静的 Init 実行時に、Quarkus は最初に、いくつかの拡張アクション/設定を含む最初の静的 init メソッドを実行します。 ネイティブパッケージングを行う場合、このスタティックメソッドはビルド時に前処理され、生成されたオブジェクトは最終的なネイティブ実行可能ファイルにシリアライズされるため、初期化コードはネイティブモードでは実行されません (このフェーズではフィボナッチ関数を実行すると想像してください。計算の結果は、ネイティブ実行可能ファイルに直接記録されます)。 JVM モードでアプリケーションを実行している場合、この静的 init フェーズはアプリケーションの開始時に実行されます。

  • ランタイム Init このフェーズでは特に目立つことはありません。従来的なランタイムコード実行を行います。 つまり、上記の 2 つのフェーズでより多くのコードを実行すればするほど、アプリケーションの起動が速くなります。

これで全てが説明されたので、さっそくコーディングに取り掛かりましょう

プロジェクトのセットアップ

エクステンションは、MavenまたはGradleのいずれかでビルドできます。お使いのビルドツールに応じて、以下のようにセットアップを行います。

The Gradle extension plugin is still experimental and may be missing features available in the Maven plugin.

Maven のセットアップ

Quarkus は、 create-extension Maven Mojo を提供し、エクステンションプロジェクトを初期化します。

オプションの自動検出が試行されます。

  • quarkus (Quarkus Core) または quarkus/extensions ディレクトリからアクセスすると、'Quarkus Core' エクステンションのレイアウトとデフォルトが使用されます。

  • -DgroupId=io.quarkiverse.[extensionId] を使用すると、'Quarkiverse' エクステンションのレイアウトとデフォルトを使用します。

  • それ以外の場合は 'Standalone' エクステンションのレイアウトとデフォルトを使用します。

  • 将来的には他のレイアウトタイプを導入する可能性があります。

You may call it without any parameter to use the interactive mode: mvn io.quarkus.platform:quarkus-maven-plugin:3.38.3:create-extension -N
$ mvn io.quarkus.platform:quarkus-maven-plugin:3.38.3:create-extension -N \
    -DgroupId=org.acme \ (1)
    -DextensionId=greeting-extension \  (2)
    -DwithoutTests (3)

[INFO] --- quarkus-maven-plugin:3.38.3:create-extension (default-cli) @ standalone-pom ---

Detected layout type is 'standalone' (4)
Generated runtime artifactId is 'greeting-extension' (5)


applying codestarts...
🔠 java
🧰 maven
🗃 quarkus-extension
🐒 extension-base

-----------
👍 extension has been successfully generated in:
--> /Users/ia3andy/workspace/redhat/quarkus/demo/greeting-extension
-----------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.659 s
[INFO] Finished at: 2021-01-25T16:17:16+01:00
[INFO] ------------------------------------------------------------------------
1 エクステンション groupId
2 エクステンション ID (名前空間ではない)。
3 テスト不要を示す
4 それ以上のオプションなしで、pom.xml のないディレクトリから、ジェネレーターは自動的に 'standalone' 拡張レイアウトを選択します。
5 'standalone' レイアウトでは、 namespaceId はデフォルトでは空なので、計算される実行時モジュールの artifactId は extensionId です。

Maven は、親 pom.xmlruntime モジュール、 deployment モジュールで構成されるエクステンションプロジェクトを含む greeting-extension ディレクトリーを生成しました。

親 pom.xml

お使いのエクステンションはマルチモジュールプロジェクトです。まずは、 ./quarkus-greeting/pom.xml の親 POM から見ていきましょう。

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.acme</groupId>
  <artifactId>greeting-extension-parent</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>Greeting Extension - Parent</name>
  <modules>(1)
    <module>deployment</module>
    <module>runtime</module>
  </modules>
  <properties>
    <compiler-plugin.version>3.15.0</compiler-plugin.version>(2)
    <failsafe-plugin.version>${surefire-plugin.version}</failsafe-plugin.version>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.version>3.38.3</quarkus.version>
    <surefire-plugin.version>3.5.4</surefire-plugin.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>(3)
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-bom</artifactId>
        <version>${quarkus.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <build>
    <pluginManagement>
      <plugins>
        <plugin>(4)
          <artifactId>maven-surefire-plugin</artifactId>
          <version>${surefire-plugin.version}</version>
          <configuration>
            <systemPropertyVariables>
              <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
              <maven.home>${maven.home}</maven.home>
              <maven.repo>${settings.localRepository}</maven.repo>
            </systemPropertyVariables>
          </configuration>
        </plugin>
        <plugin>(4)
          <artifactId>maven-failsafe-plugin</artifactId>
          <version>${failsafe-plugin.version}</version>
          <configuration>
            <systemPropertyVariables>
              <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
              <maven.home>${maven.home}</maven.home>
              <maven.repo>${settings.localRepository}</maven.repo>
            </systemPropertyVariables>
          </configuration>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>${compiler-plugin.version}</version>
          <configuration>
            <parameters>true</parameters>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
1 エクステンションは、2 つのサブモジュール deploymentruntime を宣言しています。
2 Quarkus では、annotationProcessorPaths 設定に対応する、最新バージョンの Maven コンパイラープラグインのが必要です。
3 quarkus-bom は、オーグメンテーションの段階で Quarkus が使用している依存関係を調整します。
4 Quarkusは、テストを適切に実行するためにこれらの設定を必要とします。

Deployment モジュール

デプロイメントの ./quarkus-greeting/deployment/pom.xml を見てみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.acme</groupId>
        <artifactId>greeting-extension-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>greeting-extension-deployment</artifactId> (1)
    <name>Greeting Extension - Deployment</name>

    <dependencies>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-arc-deployment</artifactId> (2)
        </dependency>
        <dependency>
            <groupId>org.acme</groupId>
            <artifactId>greeting-extension</artifactId> (3)
            <version>${project.version}</version>
        </dependency>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-junit-internal</artifactId>
          <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

主なポイント:

1 決まりとして、配置モジュールのサフィックスは -deployment (greeting-deployment) です。
2 デプロイメントモジュールは quarkus-core-deployment アーティファクトに依存しています。どの依存関係を追加するのが便利かは後ほど見ていきます。
3 デプロイメントモジュールも実行時モジュールに依存している 必要があります
4 コンパイラーのアノテーションプロセッサーに quarkus-extension-processor を追加します。

pom.xml create-extension に加えて org.acme.quarkus.greeting.deployment.GreetingProcessor クラスも生成されました。

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE);
    }

}
FeatureBuildItem represents a functionality provided by an extension. The name of the feature gets displayed in the log during application bootstrap. An extension should provide at most one feature.

ご安心ください。Build Step Processor の概念とすべてのエクステンションのデプロイメント API については後で説明します。この時点では、このクラスが Quarkus に対して、エクステンションである greeting という名前の機能をデプロイする方法を説明していることを理解する必要があります。言い換えれば、Quarkus のすべての利点 (ビルド時最適化、ネイティブサポートなど) とともに greeting エクステンションを使用するようにアプリケーションをオーグメンテーションしています。

実行時モジュール

最後に ./greeting-extension/runtime/pom.xml を見ていきます。

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemalocation="http://maven.apache.org/pom/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/pom/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.acme</groupId>
        <artifactId>greeting-extension-parent</artifactId>
        <version>0.0.1-snapshot</version>
    </parent>

    <artifactId>greeting-extension</artifactId>  (1)
    <name>Greeting Extension - Runtime</name>

    <dependencies>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-arc</artifactId> (2)
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-extension-maven-plugin</artifactId>  (3)
                <version>${quarkus.version}</version>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>extension-descriptor</goal>
                        </goals>
                        <configuration>
                            <deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}
                            </deployment>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>io.quarkus</groupId>
                            <artifactId>quarkus-extension-processor</artifactId> (4)
                            <version>${quarkus.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

主なポイント:

1 決まりとして、実行時モジュールはエンドユーザーに公開されるアーティファクトであるため、接尾辞 (greeting) を持たない。
2 実行時モジュールは quarkus-arc アーティファクトに依存しています。
3 quarkus-extension-maven-plugin を追加して、ランタイムアーティファクトに含まれる Quarkus エクステンションディスクリプターを生成します。これは、対応するデプロイメントアーティファクトとリンクします。
4 コンパイラーのアノテーションプロセッサーに quarkus-extension-processor を追加します。

Gradle のセットアップ

Quarkus は、エクステンションのGradleプロジェクトを初期化する方法を提供していません。

前述したように、エクステンションは2つのモジュールで構成されています。

  • runtime

  • deployment

この2つのモジュールから構成される、Gradleのマルチモジュールプロジェクトを作ってみます。これが、簡単な settings.gradle のサンプルファイルです。

pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    plugins {
        id 'io.quarkus.extension' version "${quarkus.version}" (1)
    }
}

include 'runtime', 'deployment' (2)

rootProject.name = 'greeting-extension'
1 quarkusエクステンションプラグインのバージョン設定
2 runtimedeployment の両方のモジュールをインクルード

以下は、ルート build.gradle ファイルのサンプルです。

subprojects {
    apply plugin: 'java-library' (1)
    apply plugin: 'maven-publish' (2)

    group 'org.acme' (3)
    version '1.0-SNAPSHOT'
}
1 すべてのサブモジュールに java-library プラグインを適用
2 アーティファクトの公開に使用する maven-publish プラグインの適用
3 公開に使用するグループIDをグローバルに設定

io.quarkus.extension プラグインは、エクステンションをビルドするために使用されます。このプラグインは、 runtime モジュールに のみ 適用されます。

deployment モジュール

deployment モジュールは、特定のプラグインを必要としません。以下は、 deployment モジュールの最小の build.gradle ファイルの例です。

name = 'greeting-extension-deployment' (1)

dependencies {
    implementation project(':runtime') (2)

    implementation platform("io.quarkus:quarkus-bom:${quarkus.version}")

    testImplementation 'io.quarkus:quarkus-junit-internal'
}
1 決まりとして、配置モジュールのサフィックスは -deployment (greeting-deployment) です。
2 デプロイメントモジュールは実行時モジュールに依存してい なければなりません

実行時モジュール

実行時モジュールは quarkus-arc アーティファクトに依存しています。

  • 両方のモジュールに quarkus-extension-process をアノテーション プロセッサーとして追加します。

  • エクステンションの説明ファイルを生成します。

以下は、 runtime モジュール用の build.gradle ファイルの例です:

plugins {
    id 'io.quarkus.extension' (1)
}

name = 'greeting-extension' (2)
description = 'Greeting extension'

dependencies {
    implementation platform("io.quarkus:quarkus-bom:${quarkus.version}")
}
1 io.quarkus.extension プラグインを適用します。
2 決まりとして、実行時モジュールはエンドユーザーに公開されるアーティファクトであるため、接尾辞 (greeting) を持たない。

サンプル Greeting エクステンションの基本バージョン

Greeting 機能の実装

ここでのエクステンションの素晴らしい機能は、ユーザーに挨拶をすることです。これを行うには、このエクステンションはユーザーアプリケーションで、平文 Hello で GET 動詞に応答する HTTP エンドポイント /greeting を公開するサーブレットを展開します。

この 実行時 モジュールは、ユーザーに提案したい機能を開発するところなので、そろそろ Web Servlet を作成しましょう。

アプリケーションでサーブレットを使用するには、 Undertow のようなサーブレット コンテナーが必要です。幸いなことに、親 pom.xml によってインポートされた quarkus-bom には、すでに Undertow Quarkus エクステンションが含まれています。

必要なことは、quarkus-undertow./greeting-extension/runtime/pom.xml への依存関係として追加することだけです。

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-undertow</artifactId>
    </dependency>

Gradleの場合は、 ./greeting-extension/runtime/build.gradle ファイルに依存関係を追加します:

    implementation 'io.quarkus:quarkus-undertow'
The dependency on quarkus-arc generated by the create-extension mojo can now be removed since quarkus-undertow already depends on it.

これで、 runtime モジュールでサーブレット org.acme.quarkus.greeting.GreetingServlet を作成することができるようになりました。

mkdir -p ./greeting-extension/runtime/src/main/java/org/acme/greeting/extension
package org.acme.greeting.extension;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet
public class GreetingExtensionServlet extends HttpServlet { (1)

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { (2)
        resp.getWriter().write("Hello");
    }
}
1 例によって、サーブレットを定義するには、 jakarta.servlet.http.HttpServlet を拡張する必要があります。
2 HTTP GET 動詞に応答したいので、 doGet メソッドをオーバーライドして、サーブレット応答の出力ストリームに Hello を記述します。

Greeting 機能のデプロイ

Quarkus マジックは、実行時のコード評価を待つのではなく、ビルド時のバイトコード生成に依存します。これがエクステンションの deployment モジュールの役割です。落ち着いてください、バイトコードは難しく、手動で行いたくないことは理解しています。Quarkus は、作業を楽にする高レベルの API を提案しています。基本的な概念のおかげで、デプロイ時に生成するバイトコードを生成するために、生成/消費する項目と対応する手順を記述できます。

io.quarkus.builder.item.BuildItem の概念は、 @io.quarkus.deployment.annotations.BuildStep でアノテーションされたメソッドのおかげで、生成または消費 (そしてある時点でバイトコードに変換したり) するオブジェクトインスタンスを表しています。このアノテーションは、エクステンションのデプロイメントタスクを説明しています。

生成された org.acme.quarkus.greeting.deployment.GreetingProcessor クラスに戻ります。

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep (1)
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE); (2)
    }

}
1 feature() メソッドには @BuildStep というアノテーションが付けられています。これは、Quarkus がデプロイ時に実行しなければならないデプロイタスクとして識別されます。 BuildStep メソッドは、アプリケーションを拡張するための拡張時に同時に実行されます。これらは、プロデューサー/コンシューマーモデルを使用しています。このステップは、消費しているすべての項目が生成されるまで、実行されないことが保証されます。
2 io.quarkus.deployment.builditem.FeatureBuildItem は、エクステンションの説明を表す BuildItem の実装です。この BuildItem は、アプリケーションの起動時にユーザーに情報を表示するために Quarkus が使用します。

多くの BuildItem 実装があり、それぞれが展開プロセスの一面を表しています。ここではいくつかの例を紹介します。

  • ServletBuildItem: デプロイ時に生成したいサーブレット (名前、パスなど) を記述します。

  • BeanContainerBuildItem: デプロイ時にオブジェクトインスタンスを保存・取得するために使用するコンテナーについて説明します。

実現したいことに対応する BuildItem が見つからない場合は、独自の実装を作成することができます。 BuildItem は、デプロイメントの特定の部分を表すもので、できるだけ細かいものでなければならないということを覚えておいてください。 BuildItem を作成する方法:

  • io.quarkus.builder.item.SimpleBuildItem デプロイ時にアイテムのインスタンスを1つだけ必要とする場合 (例: BeanContainerBuildItem、コンテナーが 1 つのみ必要な場合)。

  • 複数のインスタンスを使いたい場合は io.quarkus.builder.item.MultiBuildItem (例: ServletBuildItem、デプロイ時に多くのサーブレットを生成することができます)。

次は、HTTP エンドポイントを宣言します。これを行うには、 ServletBuildItem を生成する必要があります。この時点では、 quarkus-undertow 依存関係が runtime モジュールの Servlet サポートを提案している場合は、 deployment モジュールの quarkus-undertow-deployment 依存関係が io.quarkus.undertow.deployment.ServletBuildItem にアクセスできるようにする必要があることをご理解いただけたと思います。

quarkus-undertow-deployment を依存関係として ./greeting-extension/deployment/pom.xml に追加してみましょう。

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-undertow-deployment</artifactId>
    </dependency>
The dependency on quarkus-arc-deployment generated by the create-extension mojo can now be removed since quarkus-undertow-deployment already depends on it.

Gradleの場合は、 ./greeting-extension/deployment/build.gradle ファイルに依存関係を追加します:

    implementation 'io.quarkus:quarkus-undertow-deployment'

これで org.acme.quarkus.greeting.deployment.GreetingProcessor を更新できるようになりました。

package org.acme.greeting.extension.deployment;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import org.acme.greeting.extension.GreetingExtensionServlet;
import io.quarkus.undertow.deployment.ServletBuildItem;

class GreetingExtensionProcessor {

    private static final String FEATURE = "greeting-extension";

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem(FEATURE);
    }

    @BuildStep
    ServletBuildItem createServlet() { (1)
      ServletBuildItem servletBuildItem = ServletBuildItem.builder("greeting-extension", GreetingExtensionServlet.class.getName())
        .addMapping("/greeting")
        .build(); (2)
      return servletBuildItem;
    }

}
1 ServletBuildItem を返す createServlet メソッドを追加し、 @BuildStep でアノテーションを付けます。これで、Quarkus はこの新しいタスクを処理して、ビルド時にサーブレット登録のバイトコードを生成します。
2 ServletBuildItem は、greeting-extension という名前の GreetingExtensionServlet 型のサーブレット (これはエクステンションの runtime モジュールによって提供されるクラスです) をインスタンス化し、/greeting パスにマップするための流れるような API を提案します。

グリーティングエクステンションのテスト

Quarkus エクステンションを開発する際には、主に機能がアプリケーションに適切にデプロイされ、期待通りに動作するかどうかをテストしたいと思うでしょう。そのため、テストは deployment モジュールでホストされます。

Quarkus proposes facilities to test extensions via the quarkus-junit-internal artifact (which should already be in the deployment pom.xml), in particular the io.quarkus.test.QuarkusExtensionTest runner, which starts an application with your extension.

RestAssured (Quarkus で非常によく使われる) を使用して HTTP エンドポイントをテストします。 rest-assured の依存関係を ./greeting-extension/deployment/pom.xml に追加しましょう。

    ...
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>

Gradleの場合は、 ./greeting-extension/deployment/build.gradle ファイルに依存関係を追加します:

    ...
    testImplementation 'io.rest-assured:rest-assured'

現在、 create-extension Maven Mojo はテストと統合テスト構造 (-DwithoutTests をドロップ) を作成できます。ここでは、自分たちで作成します。

mkdir -p ./greeting-extension/deployment/src/test/java/org/acme/greeting/extension/deployment

エクステンションのテストを開始するには、次の org.acme.quarkus.greeting.deployment.GreetingTest テストクラスを作成します。

package org.acme.greeting.extension.deployment;

import io.quarkus.test.QuarkusExtensionTest;
import io.restassured.RestAssured;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import static org.hamcrest.Matchers.containsString;

public class GreetingExtensionTest {

  @RegisterExtension
  static final QuarkusExtensionTest config = new QuarkusExtensionTest()
    .withEmptyApplication(); (1)

  @Test
  public void testGreeting() {
    RestAssured.when().get("/greeting").then().statusCode(200).body(containsString("Hello")); (2)
  }

}
1 Greeting エクステンションで Quarkus アプリを起動する Junit エクステンションを登録します。
2 アプリケーションが greeting エンドポイントから OK ステータス (200) で HTTP GET 要求に応答していることと、 Hello を含むプレーンテキスト本文があることを確認します。

テストしてローカルの maven リポジトリーにインストールしてみましょう。

$ mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Greeting Extension - Parent                                        [pom]
[INFO] Greeting Extension - Runtime                                       [jar]
[INFO] Greeting Extension - Deployment                                    [jar]
[INFO]
[INFO] -----------------< org.acme:greeting-extension-parent >-----------------
[INFO] Building Greeting Extension - Parent 1.0.0-SNAPSHOT                [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.acme.greeting.extension.deployment.GreetingExtensionTest
2021-01-27 10:24:42,506 INFO  [io.quarkus] (main) Quarkus 3.38.3 on JVM started in 0.470s. Listening on: http://localhost:8081
2021-01-27 10:24:42,508 INFO  [io.quarkus] (main) Profile test activated.
2021-01-27 10:24:42,508 INFO  [io.quarkus] (main) Installed features: [cdi, greeting-extension, servlet]
2021-01-27 10:24:43,764 INFO  [io.quarkus] (main) Quarkus stopped in 0.018s
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.799 s - in org.acme.greeting.extension.deployment.GreetingExtensionTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ greeting-extension-deployment ---
[INFO] Building jar: /Users/ia3andy/workspace/redhat/quarkus/demo/greeting-extension/deployment/target/greeting-extension-deployment-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ greeting-extension-deployment ---
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for Greeting Extension - Parent 1.0.0-SNAPSHOT:
[INFO]
[INFO] Greeting Extension - Parent ........................ SUCCESS [  0.303 s]
[INFO] Greeting Extension - Runtime ....................... SUCCESS [  3.345 s]
[INFO] Greeting Extension - Deployment .................... SUCCESS [  7.365 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.246 s
[INFO] Finished at: 2021-01-27T10:24:44+01:00
[INFO] ------------------------------------------------------------------------

問題なさそうですね。初めてのエクステンションの作成、おつかれさまでした。

エクステンションのデバッグ

デバッグ作業がバグを取り除く作業であるならば、プログラミングとは、バグを入れ込む作業ということになる。エドガー・W・ダイクストラ

アプリケーションビルドのデバッグ

エクステンションのデプロイはアプリケーションのビルド中に行われるため、このプロセスはビルドツールによって起動されます。 つまり、このフェーズをデバッグしたい場合は、リモートデバッグモードをオンにしてビルドツールを起動する必要があります。

Maven

mvnDebug を使用して、Maven リモートデバッグを有効にすることができます。以下のコマンドラインでアプリケーションを起動できます。

mvnDebug quarkus:dev

デフォルトでは、Maven は、 localhost:8000 上の接続を待ちます。これで、 localhost:8000 にアタッチするために、IDE Remote の設定を実行することができます。

Gradle

Gradle のリモートデバッグは、デーモンモードでフラグ org.gradle.debug=true または org.gradle.daemon.debug=true を使用して有効にすることができます。以下のコマンドラインでアプリケーションを起動することができます。

./gradlew quarkusDev -Dorg.gradle.daemon.debug=true

デフォルトでは、Gradle は、 localhost:5005 上の接続を待ちます。これで、 localhost:5005 にアタッチするために、IDE Remote の設定を実行することができます。

エクステンションテストのデバッグ

エクステンションをテストする方法を一緒に見てきましたが、時にはうまくいかず、テストをデバッグしたい場合もあります。ここでも同じ原理で、IDE Remote の設定をアタッチするために Maven Surefire リモートデバッグを有効にします。

cd ./greeting-extension
mvn clean test -Dmaven.surefire.debug

デフォルトでは、Maven は localhost:5005 で接続を待ちます。

新しいエクステンションの使用

初めてのエクステンションのビルドを終えたばかりなので、Quarkus アプリケーションでそれを使うことに熱心になるでしょう。

クラッシックMaven publication

前のステップでまだ行っていない場合は、ローカルリポジトリに greeting-extension をインストールする必要があります。

cd ./greeting-extension
mvn clean install

次に、別のディレクトリから、我々のツールを使用して、新しいエクステンションを持つ新しい greeting-app Quarkus アプリケーションを作成します。

mvn io.quarkus.platform:quarkus-maven-plugin:3.38.3:create \
     -DprojectGroupId=org.acme \
     -DprojectArtifactId=greeting-app \
     -Dextensions="org.acme:greeting-extension:1.0.0-SNAPSHOT" \
     -DnoCode

cd into greeting-app.

The greeting-extension extension has to be installed in the local Maven repository to be usable in the application.

アプリケーションを実行して、 Installed Features のリストに quarkus-greeting のエクステンションが含まれていることに注目してください。

$ mvn quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< org.acme:greeting-app >------------------------
[INFO] Building greeting-app 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-compiler-plugin:3.15.0:compile (default-compile) @ greeting-app ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:3.38.3:dev (default-cli) @ greeting-app ---
Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2022-11-20 04:25:36,885 INFO  [io.quarkus] (Quarkus Main Thread) greeting-app 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.38.3) started in 4.591s. Listening on: http://localhost:8080
2022-11-20 04:25:36,911 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2022-11-20 04:25:36,913 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, greeting-extension, resteasy-reactive, smallrye-context-propagation, vertx]

エクステンション開発者の視点から見ると、Maven の公開戦略は非常に便利で高速ですが、Quarkus はさらに一歩進んで、エクステンションを使用する人々にとってエコシステムの信頼性を確保したいと考えています。考えてみてください、私たちは皆、メンテナンスされていないライブラリーや依存関係間の非互換性 (法的な問題については触れません) によって、開発者体験が乏しかった経験があるはずです。Quarkus Platform はそのために存在します。

Quarkus Platform

Quarkusプラットフォームは、開発フレームワークとしてのQuarkusの主なユースケースをターゲットとしたエクステンションのセットであり、依存関係の競合を発生させることなく、同じアプリケーション内でどのような組み合わせでも安全に使用できます。アプリケーション開発者の視点から見ると、Quarkusプラットフォームは、1つまたは複数のMaven BOMとして表現されます。例えば、 io.quarkus.platform:quarkus-bom:3.38.3io.quarkus.platform:quarkus-camel-bom:3.38.3 などです。これらのBOMは、依存関係の競合を発生させることなく、同じアプリケーションで任意の順序でインポートできるように、依存関係のバージョン制約がグローバルに調整されています。

Quarkiverse Hub

Quarkiverse Hub は、コミュニティから投稿された Quarkus エクステンションプロジェクトのリポジトリーホスティング (ビルド、CI、リリースパブリッシングのセットアップを含む) を提供する GitHub 組織です。

新しいQuarkusエクステンションを作成してQuarkusエコシステムに追加し、Quarkus開発ツール( Quarkus CLIcode.quarkus.io を含む)を使用してQuarkusコミュニティがそのエクステンションを発見できるようにしたいと考えている場合、 Quarkiverse Hub GitHub organizationはそのための良いホームとなるでしょう。

エクステンションリクエスト 投稿を作成 (こちら) で、まだ投稿されていないかどうかを最初に確認してください) し、それをリードするように依頼することから始めることができます。

我々は、新しいリポジトリのプロビジョニングを行い、以下のように設定します。

  • 我々のツールでサポート。

  • エクステンションのために作成したドキュメントを Quarkiverse の Web サイトに公開します。

  • Quarkus エコシステム CI を使用して、最新の Quarkus Core の変更に対応してビルドするようにエクステンションを設定します。

  • プロジェクトを管理し、好きなように Maven Central にリリースする自由を与えます。

Extensions hosted in the Quarkiverse Hub may or may not end up in the Quarkus platform.

詳しくは、Quarkiverse Wikiこのブログ記事 をチェックしてみてください。

まとめ

新しいエクステンションの作成は、最初は複雑な作業に見えるかもしれませんが、Quarkus の画期的なパラダイム (ビルド時とランタイム) を理解すれば、エクステンションの構造は完全に理にかなったものになります。

いつものように、Quarkus は、新しい機能を楽しく開発できるように、舞台裏で物事を簡素化しています (Maven Mojo、バイトコード生成、テストなど)。

さらに詳しく

関連コンテンツ