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

Quarkus によるスクリプティング

Quarkus は Maven も Gradleも必要としない Java のスクリプトおよびアプリケーションを記述することができる jbang との統合を提供しています。

このガイドでは、1つの Java ファイルだけを使って REST アプリケーションを書く方法を見ていきます。

この技術は、previewと考えられています。

preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリストGitHub の課題管理 で受け付けています。

とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。

前提条件

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

  • ざっと 5 minutes

  • IDE

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

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

  • JBang

ソリューション

通常は Git リポジトリに関連付けてクローンしますが、今回利用するのは以下のファイルだけです。

//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus.platform:quarkus-bom:3.9.3@pom
//DEPS io.quarkus:quarkus-rest
//JAVAC_OPTIONS -parameters
//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(String name) {
        return service.greeting(name);
    }

    @ApplicationScoped
    static public class GreetingService {

        public String greeting(String name) {
            return "hello " + name;
        }
    }
}

アーキテクチャ

このガイドでは、 hello エンドポイントを提供する簡単なアプリケーションを、 pom.xmlbuild.gradle のようなビルドファイルを追加することなく、単一のソースファイルで作成します。依存関係の注入の動作を確認するために、このエンドポイントは greeting Bean を使用します。

Architecture

初期ファイルの作成

最初に、Java ファイルが必要です。JBang を使うと、次のように初期バージョンの Java ファイルを作成することができます。

jbang init scripting/quarkusapp.java
cd scripting

このコマンドは、Linux と macOS で直接実行できる .java ファイルとして ./quarkusapp.java を生成します。Windows では、jbang quarkusapp.java を使用する必要があります。

この初期バージョンを実行すると Hello World を表示します。

生成された quarkusapp.java ファイルを確認します。

一番上にこのような行があります。

//usr/bin/env jbang "$0" "$@" ; exit $?

この行は、Linux や macOS でスクリプトとして実行できるようにするためのものです。Windowsでは、この行は無視されます。

次の行

// //DEPS <dependency1> <dependency2>

このスクリプトに依存関係を追加する方法を示しています。これは jbang の機能です。

この行を更新して、 quarkus-bomquarkus-rest の依存関係を追加します:

//DEPS io.quarkus.platform:quarkus-bom:3.9.3@pom
//DEPS io.quarkus:quarkus-rest

ここで jbang quarkusapp.java を実行すると、 jbang がこの依存関係を解決し、Quarkus の jbang 統合の機能を利用して jar をビルドしていることがわかります。

$ jbang quarkusapp.java

[jbang] Resolving dependencies...
[jbang]     Resolving io.quarkus:quarkus-resteasy:3.9.3...Done
[jbang] Dependencies resolved
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:47:51 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:47:51 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 722ms
Hello World

ここまでは、このアプリケーションは特に新しい動作をしていません。

このファイルの編集時にコンテンツアシスト機能を利用する方法

JBangスクリプトをコンテンツアシストのあるIDE/エディタで編集するには、 jbang edit quarkusapp.java または jbang edit quarkusapp.java を実行します。

詳細は JBangのドキュメント を参照してください。

Jakarta RESTリソース

これを Quarkus の機能を使用するクラスに置き換えてみます。

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }
}

Quarkus で REST エンドポイントを起動し、「/hello」へのリクエスト対して「hello」を返すメインメソッドを持つ、非常にシンプルなクラスです。

main メソッドが存在する理由

現時点で jbang の統合が動作するためには main メソッドが必要です。

アプリケーションの実行

アプリケーションを実行すると、 Quarkus が起動するのを確認することができます。

jbang quarkusapp.java の利用

$ jbang quarkusapp.java

[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:48:39 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:48:39 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 521ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:48:39,891 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT on JVM (powered by Quarkus 3.9.3) started in 0.283s. Listening on: http://0.0.0.0:8080
2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

起動後、提供されたエンドポイントにリクエストを送信することができます。

$ curl -w "\n" http://localhost:8080/hello
hello

その後 CTRL+C を入力し、アプリケーションを停止させます。

curl -w "\n" で自動的に改行が追加されるようにします。

この例では curl -w "\n" を使用して、結果とプロンプトが1行で表示されてしまうことを防止しています。

なぜ quarkus-rest は解決されないのでしょうか?

この2回目の実行では、JBangが実行の間に依存関係の解決をキャッシュするので、 quarkus-rest を解決しているという行は表示されないはずです。 キャッシュをクリアして強制的に解決したい場合は、 jbang cache clear を使ってください。

依存性注入の使用

Quarkusの依存性インジェクションは、Quarkusのアーキテクチャに合わせて調整されたCDIベースの依存性インジェクションソリューションであるArCをベースにしています。ArCの詳細については、 コンテキストと依存性注入ガイド を参照してください。

ArC は quarkus-rest の依存関係として提供されているので、既に手元にあります。

アプリケーションを修正してコンパニオンクラスのBeanを追加してみます。

通常は別のクラスを追加しますが、一つのファイルにまとめようとしているので、入れ子クラスを追加します。

quarkusapp クラスの 内側に 以下を追加します。

@ApplicationScoped
static public class GreetingService {

    public String greeting(String name) {
        return "hello " + name;
    }

}
ネストされた静的パブリッククラスの使用

トップレベルのクラスではなく、ネストされた静的パブリッククラスを使用しているのには、2つの理由があります。

  1. 現在、 jbang は複数のソースファイルをサポートしていません。

  2. イントロスペクションに依存する全てのJavaフレームワークは、トップレベルクラスがパブリッククラスのようなアクセス修飾子を持たないため、トップレベルクラスを使用することに課題があります。そして Java では、1つのファイルにトップレベルのパブリッククラスは1つしか記述できません。

quarksapp クラスを編集して GreetingService を注入し、それを使って新しいエンドポイントを作成します。

//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus.platform:quarkus-bom:3.9.3@pom
//DEPS io.quarkus:quarkus-rest

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(String name) {
        return service.greeting(name);
    }

    @ApplicationScoped
    static public class GreetingService {

        public String greeting(String name) {
            return "hello " + name;
        }
    }
}

jbang quarkusapp.java` を実行すると、新しいエンドポイントが返す内容を確認することができます。

$ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
hello null

予想に反し、 hello quarkus ではなく hello null が返却されます。この理由は何でしょうか?

Quarkus REST(旧RESTEasy Reactive)では、 {name}name パラメータにマッピングするために、 -parameters コンパイラフラグが設定されている必要があるためです。

ファイルに以下のコメント命令文を追加することで修正します。

//JAVAC_OPTIONS -parameters

jbang quarkusapp.java を実行すると、エンドポイントは期待通りの値を返すようになったことが確認できます。

$ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
hello quarkus

デバッグ

jbang --debug=5005 quarkusapp.java アプリケーションのデバッグには jbang --debug quarkusapp.java を使用し、IDEを使用してポート4004で接続することができます。

注意: jbang デバッグは常に一時停止しているため、アプリケーションを実行させためるにはデバッガーを接続する必要があります。

ロギング

jbang による Quarkus スクリプトでロギングを使用するには、通常通りロガーを設定します。

public static final Logger LOG = Logger.getLogger(quarkusapp.class);

これを動作させるためには、ロギングが適切に初期化されるように Java オプションを追加する必要があります。

//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

これにより、 jbang quarkusapp.java を実行すると、期待通りにロギングが行われます。

アプリケーションの設定

アプリケーションの静的な設定を行うために、 //Q:CONFIG <property>=<value> を使用することができます。

例えば smallrye-openapiswagger-ui のエクステンションを追加して Swagger UI を常に表示させたい場合は、以下のように追加します。

//DEPS io.quarkus:quarkus-smallrye-openapi:3.9.3
//DEPS io.quarkus:quarkus-swagger-ui:3.9.3
//Q:CONFIG quarkus.swagger-ui.always-include=true

これでビルドによって出来上がるjar内に quarkus.swagger-ui.always-include が生成されるようになり、実行時に http://0.0.0.0:8080/q/swagger-ui が利用可能になります。

ネイティブアプリケーションとしての実行

native-image のバイナリがインストールされ、 GRAALVM_HOME が設定されているか、Linux にコンテナランタイム(podman や docker など)がインストールされていれば、 jbang --native quarkusapp.java を使ってネイティブ実行可能ファイルをビルドし、実行することができます:

$ jbang --native quarkusapp.java
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:58:47 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.JarResultBuildStep buildNativeImageThinJar
INFO: Building native image source jar: /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep build
INFO: Building native image from /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep getNativeImageBuildRunner
WARN: Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image` Attempting to fall back to container build.
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner <init>
INFO: Using docker to run the native image builder
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner setup
INFO: Checking image status quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17
Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep checkGraalVMVersion
INFO: Running Quarkus native-image plugin on native-image 22.3.1.0-Final Mandrel Distribution (Java Version 17.0.6+10)
Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner build
INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar:/project:z --name build-native-XaZUc quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dlogging.initial-configurator.min-level=500 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.noUnsafe=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Duser.language=en -J-Duser.country=IE -J-Dfile.encoding=UTF-8 --features=io.quarkus.runner.Feature,io.quarkus.runtime.graal.DisableLoggingFeature -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -J--add-opens=java.base/java.io=ALL-UNNAMED -J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED -H:+CollectImageBuildStatistics -H:ImageBuildStatisticsFile=quarkus-application-runner-timing-stats.json -H:BuildOutputJSONFile=quarkus-application-runner-build-output-stats.json -H:+AllowFoldMethods -J-Djava.awt.headless=true --no-fallback --link-at-build-time -H:+ReportExceptionStackTraces -H:-AddAllCharsets --enable-url-protocols=http -H:NativeLinkerOption=-no-pie -H:-UseServiceLoaderFeature -H:+StackTrace -J--add-exports=org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED --exclude-config io\.netty\.netty-codec /META-INF/native-image/io\.netty/netty-codec/generated/handlers/reflect-config\.json --exclude-config io\.netty\.netty-handler /META-INF/native-image/io\.netty/netty-handler/generated/handlers/reflect-config\.json quarkus-application-runner -jar quarkus-application-runner.jar
Mar 22, 2023 9:37:56 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner runCommand
INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang9315448339582904220/quarkus-application-native-image-source-jar:/project:z --entrypoint /bin/bash quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -c objcopy --strip-debug quarkus-application-runner
Mar 22, 2023 9:37:57 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 31729ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:37:57,471 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.9.3) started in 0.009s. Listening on: http://0.0.0.0:8080
2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

このネイティブビルドは最初の実行には時間がかかりますが、その後は ( quarkusapp.java を変更せずに) jbang キャッシュのおかげですぐに実行できるようになります。

$ jbang --native quarkusapp.java
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.9.3) started in 0.009s. Listening on: http://0.0.0.0:8080
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

まとめ

Quarkusを使い始めたい、または何かを素早くコーディングしたい場合、 jbang による Quarkus スクリプティングを使用すると便利です。Java ファイルだけでよく、Maven や Gradle は必要ありません。このガイドでは、JBang を使用した Quarkus の基本的な使い方を説明しました。JBangでできることの詳細については、 https://jbang.dev を参照してください。