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

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

プレビュー

Quarkusは jbang との統合を提供しており、MavenもGradleも必要としないJavaスクリプト/アプリケーションを書くことができます。

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

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

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

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

前提条件

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

  • ざっと 5 minutes

  • IDE

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

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

  • JBang

ソリューション

通常はGitリポジトリにリンクしてクローンするのですが、今回は以下のみで追加ファイルはありません。

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

import io.quarkus.runtime.Quarkus;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.logging.Logger;

@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

jbang quarkusapp.java このコマンドは.javaファイルを生成し、LinuxやmacOS、つまり ./quarkusapp.java で直接実行できるようにします。

この初期バージョンは、実行すると Hello World が表示されます。

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

一番上にこんな感じの行があります。

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

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

次の行

// //DEPS <dependency1> <dependency2>

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

この行を quarkus-resteasy の依存関係を含むように更新します。

//DEPS io.quarkus.platform:quarkus-bom:2.16.12.Final@pom
//DEPS io.quarkus:quarkus-resteasy-reactive

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

$ jbang quarkusapp.java

[jbang] Resolving dependencies...
[jbang]     Resolving io.quarkus:quarkus-resteasy:2.16.12.Final...Done
[jbang] Dependencies resolved
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Aug 30, 2020 5:40:55 AM org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.1.1.Final
Aug 30, 2020 5:40:56 AM io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 722ms
Hello World

今のところ、アプリケーションは何も新しいことをしていません。

このファイルを編集してコンテンツアシストを受けるにはどうすればいいですか?

.java ファイルしかないので、ほとんどの IDE はコンテンツアシストをうまく処理できません。これを回避するには、 jbang edit quarkusapp.java を実行すると、IDE で使用できる一時的なプロジェクトの設定があるディレクトリーが出力されます。

Linux/macOSでは、 <idecommand> `jbang edit quarkusapp.java` で実行できます。

編集中に依存関係を追加した場合は、 jbang edit --live=<idecommand> quarkusapp.java を使用することで、IDE プロジェクトを自動的に更新するように jbang を設定することができます。

JAX-RSリソース

ここで、Quarkusの機能を使用するクラスに置き換えてみましょう。

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

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

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

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

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

なぜそこに main メソッドがあるのか?

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

アプリケーションの実行

これでアプリケーションを実行すると、Quarkusが起動するのがわかります。

使用方法: jbang quarkusapp.java

$ jbang quarkusapp.java

[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Aug 30, 2020 5:49:01 AM org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.1.1.Final
Aug 30, 2020 5:49:02 AM io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 681ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-08-30 05:49:03,255 INFO  [io.quarkus] (main) Quarkus 2.16.12.Final on JVM started in 0.638s. Listening on: http://0.0.0.0:8080
2020-08-30 05:49:03,272 INFO  [io.quarkus] (main) Profile prod activated.
2020-08-30 05:49:03,272 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive]

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

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

その後、 CTRL+C を叩いてアプリケーションを停止させます。

curl -w "\n" で自動的に改行を追加します。

例では、 curl -w "\n" を使用して、結果と次のコマンドプロンプトが同じ行に表示されるのを防止しています。

quarkus-resteasy-reactive が解決されない理由

1回めの実行で jbang が依存関係の解決をキャッシュしているため、2回目の実行では quarkus-resteasy の依存関係を解決する行は表示されません。キャッシュをクリアして強制的に解決させたい場合は jbang cache clear を利用します。

インジェクションの使用

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

ArCは quarkus-resteasy の依存関係として提供されるため、新たに追加する必要はありません。

アプリケーションを修正してコンパニオンBeanを追加してみましょう。

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

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

@ApplicationScoped
static public class GreetingService {

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

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

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

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

  2. イントロスペクションに依存するすべてのJavaフレームワークは、トップレベルのクラスを使用することに課題があります。

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

//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus:quarkus-resteasy-reactive:2.16.12.Final

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

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

    @GET
    public String sayHello() {
        return "hello from Quarkus with jbang.dev";
    }

    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 を返しているのでしょうか?

これは、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:2.16.12.Final
//DEPS io.quarkus:quarkus-swagger-ui:2.16.12.Final
//Q:CONFIG quarkus.swagger-ui.always-include=true

これでビルド中に quarkus.swagger-ui.always-include が生成され、結果の jar と実行時に http://0.0.0.0:8080/q/swagger-ui が利用可能になります。

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

native-image バイナリーがインストールされていて GRAALVM_HOME が設定されている場合は、 jbang --native quarkusapp.java を使用してネイティブ実行可能ファイルをビルドして実行することができます。

$ jbang --native quarkusapp.java

[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Aug 30, 2020 6:21:15 AM org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.1.1.Final
Aug 30, 2020 6:21:16 AM io.quarkus.deployment.pkg.steps.JarResultBuildStep buildNativeImageThinJar
INFO: Building native image source jar: /var/folders/yb/sytszfld4sg8vwr1h0w20jlw0000gn/T/quarkus-jbang3291688251685023074/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Aug 30, 2020 6:21:16 AM io.quarkus.deployment.pkg.steps.NativeImageBuildStep build
INFO: Building native image from /var/folders/yb/sytszfld4sg8vwr1h0w20jlw0000gn/T/quarkus-jbang3291688251685023074/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Aug 30, 2020 6:21:16 AM io.quarkus.deployment.pkg.steps.NativeImageBuildStep checkGraalVMVersion
INFO: Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
Aug 30, 2020 6:21:16 AM io.quarkus.deployment.pkg.steps.NativeImageBuildStep build
INFO: /Users/max/.sdkman/candidates/java/20.1.0.r11-grl/bin/native-image -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -H:+JNI -jar quarkus-application-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http --no-server -H:-UseServiceLoaderFeature -H:+StackTrace quarkus-application-runner

Aug 30, 2020 6:22:31 AM io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 76010ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-08-30 06:22:32,012 INFO  [io.quarkus] (main) Quarkus 2.16.12.Final native started in 0.017s. Listening on: http://0.0.0.0:8080
2020-08-30 06:22:32,013 INFO  [io.quarkus] (main) Profile prod activated.
2020-08-30 06:22:32,013 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive]

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

$ jbang --native quarkusapp.java
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-08-30 06:23:36,846 INFO  [io.quarkus] (main) Quarkus 2.16.12.Final native started in 0.015s. Listening on: http://0.0.0.0:8080
2020-08-30 06:23:36,846 INFO  [io.quarkus] (main) Profile prod activated.
2020-08-30 06:23:36,846 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive]

まとめ

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