Red Hat build of Apache Camel for Quarkus を使用したアプリケーションの開発
Red Hat build of Apache Camel for Quarkus を使用したアプリケーションの開発
概要
はじめに
多様性を受け入れるオープンソースの強化
Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。まずは、マスター (master)、スレーブ (slave)、ブラックリスト (blacklist)、ホワイトリスト (whitelist) の 4 つの用語の置き換えから始めます。この取り組みは膨大な作業を要するため、今後の複数のリリースで段階的に用語の置き換えを実施して参ります。詳細は、Red Hat CTO である Chris Wright のメッセージ をご覧ください。
第1章 Red Hat build of Apache Camel for Quarkus を使用したアプリケーション開発の概要
このガイドは、Red Hat build of Apache Camel for Quarkus 上で Camel アプリケーションを作成する開発者を対象としています。
Red Hat build of Apache Camel for Quarkus でサポートされている Camel コンポーネントには、関連する Red Hat build of Apache Camel for Quarkus エクステンションがあります。このディストリビューションでサポートされている Red Hat build of Apache Camel for Quarkus エクステンションの詳細は、Red Hat build of Apache Camel for Quarkus リファレンス ガイドを参照してください。
第2章 依存関係の管理
特定の Red Hat build of Apache Camel for Quarkus リリースは、特定の Quarkus リリースでのみ動作するように設計されています。
2.1. 新規プロジェクトを起動する Quarkus ツール
新規プロジェクトで適切な依存関係バージョンを取得する最も簡単でわかりやすい方法は、Quarkus ツールのいずれかを使用することです。
- code.quarkus.redhat.com - オンラインプロジェクトジェネレーター
- Quarkus Maven プラグイン
これらのツールを使用すると、エクステンションを選択し、新しい Maven プロジェクトのスキャフォールディングを行うことができます。
利用可能なエクステンションの領域は、Quarkus Core、Camel Quarkus、およびその他のサードパーティー参加プロジェクト (Hazelcast、Cassandra、Kogito、OptaPlanner など) にまたがるものです。
生成される pom.xml
は以下のようになります。
<project> ... <properties> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>com.redhat.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version> <!-- The latest 3.2.x version from https://maven.repository.redhat.com/ga/com/redhat/quarkus/platform/quarkus-bom --> </quarkus.platform.version> ... </properties> <dependencyManagement> <dependencies> <!-- The BOMs managing the dependency versions --> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>quarkus-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>quarkus-camel-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- The extensions you chose in the project generator tool --> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-sql</artifactId> <!-- No explicit version required here and below --> </dependency> ... </dependencies> ... </project>
BOM は "Bill of Materials" を指します。この pom.xml
の主目的は、アーティファクトのバージョンを管理することです。これにより、BOM をプロジェクトにインポートするエンドユーザーが、互いに機能するアーティファクトのバージョンに注意を払う必要がなくなります。つまり、pom.xml
の <depependencyManagement>
セクションに BOM をインポートすると、その BOM によって管理される依存関係のバージョンを指定する必要がなくなります。
どの BOM が pom.xml
ファイルに保存されるかは、ジェネレーターツールで選択したエクステンションによって異なります。ジェネレーターツールは、一貫した最小限のセットを選択します。
pom.xml
ファイルの BOM で管理されていないエクステンションを後で追加する場合も、適切な BOM を手動で検索する必要はありません。
quarkus-maven-plugin
を使用してエクステンションを選択すると、ツールが必要に応じて適切な BOM を追加します。また、quarkus-maven-plugin
を使用して、BOM バージョンをアップグレードすることもできます。
com.redhat.quarkus.platform
の BOM は相互に調整されます。つまり、アーティファクトが複数の BOM で管理されている場合は、常に同じバージョンで管理されます。これには、アプリケーション開発者が、独立した各種プロジェクトからの個々のアーティファクトの互換性に注意する必要がないという利点があります。
2.2. 他の BOM との統合
camel-quarkus-bom
を他の BOM と組み合わせる場合は、インポートの順序が優先順位を定義するため、これらをインポートする順番を慎重に検討してください。
つまり、my-foo-bom
を camel-quarkus-bom
より前にインポートした場合、my-foo-bom
で定義されたバージョンが優先されます。この優先順位が希望するものかどうかは、my-foo-bom
と camel-quarkus-bom
の間に重複があるかどうか、および優先順位の高いバージョンが camel-quarkus-bom
で管理される残りのアーティファクトと連携するかどうかによって決まります。
第3章 Camel ルートの定義
Red Hat build of Apache Camel for Quarkus は、Camel ルートを定義する Java DSL 言語をサポートしています。
3.1. Java DSL
org.apache.camel.builder.RouteBuilder
を拡張し、そこで利用可能な Fluent Builder メソッドを使用することが、Camel ルートを定義する最も一般的な方法です。以下は、タイマーコンポーネントを使用したルートの簡単な例です。
import org.apache.camel.builder.RouteBuilder; public class TimerRoute extends RouteBuilder { @Override public void configure() throws Exception { from("timer:foo?period=1000") .log("Hello World"); } }
3.1.1. エンドポイント DSL
Camel 3.0 以降、Fluent Builder を使用して Camel エンドポイントを定義することもできます。以下は前述の例と同等です。
import org.apache.camel.builder.RouteBuilder; import static org.apache.camel.builder.endpoint.StaticEndpointBuilders.timer; public class TimerRoute extends RouteBuilder { @Override public void configure() throws Exception { from(timer("foo").period(1000)) .log("Hello World"); } }
すべての Camel コンポーネントの Builder メソッドは camel-quarkus-core
で利用できますが、ルートが適切に機能するように、指定のコンポーネントのエクステンションを依存関係として追加する必要があります。上記の例では、camel-quarkus-timer
になります。
第4章 設定
Camel Quarkus は、デフォルトでは Quarkus アプリケーションライフサイクルに応じて起動/停止する Camel Context Bean を自動的に設定およびデプロイします。設定ステップは、Quarkus の拡張フェーズ中のビルド時に実行され、Camel Quarkus 固有の quarkus.camel.*
プロパティーを使用して調整できる Camel Quarkus エクステンションによって実行されます。
quarkus.camel.*
設定プロパティーは、個々のエクステンションページに記載されています。たとえば、Camel Quarkus Core を参照してください。
設定が完了すると、最小の Camel Runtime がアセンブルされ、RUNTIME_INIT フェーズで起動します。
4.1. Camel コンポーネントの設定
4.1.1. application.properties
プロパティーを使用して Apache Camel のコンポーネントおよびその他の要素を設定するには、アプリケーションが camel-quarkus-core
に直接、または推移的に依存するようにしてください。ほとんどの Camel Quarkus エクステンションは camel-quarkus-core
に依存するため、通常は明示的に追加する必要はありません。
camel-quarkus-core
は、Camel Main から Camel Quarkus に機能を提供します。
以下の例では、application.properties
経由で LogComponent
に特定の ExchangeFormatter
を設定します。
camel.component.log.exchange-formatter = #class:org.apache.camel.support.processor.DefaultExchangeFormatter camel.component.log.exchange-formatter.show-exchange-pattern = false camel.component.log.exchange-formatter.show-body-type = false
4.1.2. CDI
CDI を使用して、コンポーネントをプログラムで設定することもできます。
推奨される方法は、ComponentAddEvent
を監視し、ルートおよび CamelContext
を起動する前にコンポーネントを設定することです。
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import org.apache.camel.quarkus.core.events.ComponentAddEvent; import org.apache.camel.component.log.LogComponent; import org.apache.camel.support.processor.DefaultExchangeFormatter; @ApplicationScoped public static class EventHandler { public void onComponentAdd(@Observes ComponentAddEvent event) { if (event.getComponent() instanceof LogComponent) { /* Perform some custom configuration of the component */ LogComponent logComponent = ((LogComponent) event.getComponent()); DefaultExchangeFormatter formatter = new DefaultExchangeFormatter(); formatter.setShowExchangePattern(false); formatter.setShowBodyType(false); logComponent.setExchangeFormatter(formatter); } } }
4.1.2.1. @Named
コンポーネントインスタンスの生成
別の方法として、@Named
プロデューサーメソッドでコンポーネントを作成および設定できます。これは、Camel がコンポーネント URI スキームを使用してレジストリーからコンポーネントを検索する際に機能します。たとえば、LogComponent
Camel の場合は bean という名前の log
を検索します。
@Named
コンポーネント Bean の生成は通常は機能しますが、一部のコンポーネントで軽微な問題が発生する可能性があります。
Camel Quarkus エクステンションは、以下のいずれかを行う場合があります。
- デフォルトの Camel コンポーネントタイプのカスタムサブタイプを渡す。Vert.x WebSocket エクステンション の例を参照してください。
- コンポーネントの Quarkus 固有のカスタマイズを実行する。JPA エクステンション の例を参照してください。
これらのアクションは、独自のコンポーネントインスタンスを作成する場合には実行されないため、オブザーバーメソッドでコンポーネントを設定する方法を推奨します。
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
import org.apache.camel.component.log.LogComponent;
import org.apache.camel.support.processor.DefaultExchangeFormatter;
@ApplicationScoped
public class Configurations {
/**
* Produces a {@link LogComponent} instance with a custom exchange formatter set-up.
*/
@Named("log") 1
LogComponent log() {
DefaultExchangeFormatter formatter = new DefaultExchangeFormatter();
formatter.setShowExchangePattern(false);
formatter.setShowBodyType(false);
LogComponent component = new LogComponent();
component.setExchangeFormatter(formatter);
return component;
}
}
- 1
- メソッドの名前が同じであれば、
@Named
アノテーションの"log"
引数は省略できます。
4.2. 規則による設定
プロパティーによる Camel の設定に加え、camel-quarkus-core
では、規則を使用して Camel の動作を設定できます。たとえば、CDI コンテナーに単一の ExchangeFormatter
インスタンスがある場合は、その Bean を LogComponent
に自動的に接続します。
第5章 Camel Quarkus のコンテキストと依存性注入 (CDI)
CDI は Quarkus で中心的な役割を果たしています。また、Camel Quarkus も CDI に対して優れたサポートを提供します。
たとえば、@Inject
、@ConfigProperty
、および同様のアノテーションを使用して、Bean と設定値を Camel RouteBuilder
に注入することができます。以下に例を示します。
import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.camel.builder.RouteBuilder; import org.eclipse.microprofile.config.inject.ConfigProperty; @ApplicationScoped 1 public class TimerRoute extends RouteBuilder { @ConfigProperty(name = "timer.period", defaultValue = "1000") 2 String period; @Inject Counter counter; @Override public void configure() throws Exception { fromF("timer:foo?period=%s", period) .setBody(exchange -> "Incremented the counter: " + counter.increment()) .to("log:cdi-example?showExchangePattern=false&showBodyType=false"); } }
- 1
@ApplicationScoped
アノテーションは@Inject
および@ConfigProperty
がRouteBuilder
で機能するために必要です。@ApplicationScoped
Bean は、CDI コンテナーによって管理され、そのライフサイクルは単純なRouteBuilder
のライフサイクルよりも複雑です。つまり、RouteBuilder
で@ApplicationScoped
を使用すると、ブート時にデメリットが生じることがあるため、本当に必要なときにのみRouteBuilder
に@ApplicationScoped
のアノテーションを付ける必要があります。- 2
timer.period
プロパティーの値は、サンプルプロジェクトのsrc/main/resources/application.properties
で定義されています。
詳細は、Quarkus Dependency Injection guide を参照してください。
5.1. CamelContext
へのアクセス
CamelContext
にアクセスするには、CamelContext を Bean に注入します。
import jakarta.inject.Inject; import jakarta.enterprise.context.ApplicationScoped; import java.util.stream.Collectors; import java.util.List; import org.apache.camel.CamelContext; @ApplicationScoped public class MyBean { @Inject CamelContext context; public List<String> listRouteIds() { return context.getRoutes().stream().map(Route::getId).sorted().collect(Collectors.toList()); } }
5.2. @EndpointInject
と @Produce
プレーン Camel または SpringBoot 上の Camel から @org.apache.camel.EndpointInject
と @org.apache.camel.Produce
を使用することに慣れている場合は、これらを Quarkus でも引き続き使用できます。
次のユースケースは、org.apache.camel.quarkus:camel-quarkus-core
によってサポートされています。
import jakarta.enterprise.context.ApplicationScoped; import org.apache.camel.EndpointInject; import org.apache.camel.FluentProducerTemplate; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; @ApplicationScoped class MyBean { @EndpointInject("direct:myDirect1") ProducerTemplate producerTemplate; @EndpointInject("direct:myDirect2") FluentProducerTemplate fluentProducerTemplate; @EndpointInject("direct:myDirect3") DirectEndpoint directEndpoint; @Produce("direct:myDirect4") ProducerTemplate produceProducer; @Produce("direct:myDirect5") FluentProducerTemplate produceProducerFluent; }
direct:myDirect*
の代わりに、他の Camel プロデューサーエンドポイント URI を使用できます。
@EndpointInject
と @Produce
は、セッターメソッドではサポートされていません。#2579 を参照してください。
次のユースケースは org.apache.camel.quarkus:camel-quarkus-bean
でサポートされています。
import jakarta.enterprise.context.ApplicationScoped; import org.apache.camel.Produce; @ApplicationScoped class MyProduceBean { public interface ProduceInterface { String sayHello(String name); } @Produce("direct:myDirect6") ProduceInterface produceInterface; void doSomething() { produceInterface.sayHello("Kermit") } }
5.3. CDI および Camel Bean コンポーネント
5.3.1. 名前による Bean の参照
ルート定義内の Bean を名前で参照するには、Bean に @Named ("myNamedBean")
および @ApplicationScoped
(またはその他の サポートされている スコープ) のアノテーションを付けます。@RegisterForReflection
アノテーションは、ネイティブモードにとって重要です。
import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Named; import io.quarkus.runtime.annotations.RegisterForReflection; @ApplicationScoped @Named("myNamedBean") @RegisterForReflection public class NamedBean { public String hello(String name) { return "Hello " + name + " from the NamedBean"; } }
その後、ルート定義で myNamedBean
名を使用できます。
import org.apache.camel.builder.RouteBuilder; public class CamelRoute extends RouteBuilder { @Override public void configure() { from("direct:named") .bean("myNamedBean", "hello"); /* ... which is an equivalent of the following: */ from("direct:named") .to("bean:myNamedBean?method=hello"); } }
@Named
の代わりに、io.smallrye.common.annotation.Identifier
を使用して Bean に名前を付けて識別することもできます。
import jakarta.enterprise.context.ApplicationScoped; import io.quarkus.runtime.annotations.RegisterForReflection; import io.smallrye.common.annotation.Identifier; @ApplicationScoped @Identifier("myBeanIdentifier") @RegisterForReflection public class MyBean { public String hello(String name) { return "Hello " + name + " from MyBean"; } }
次に、Camel ルート内の識別子の値を参照します。
import org.apache.camel.builder.RouteBuilder; public class CamelRoute extends RouteBuilder { @Override public void configure() { from("direct:start") .bean("myBeanIdentifier", "Camel"); } }
Red Hat は、Camel ドキュメントの Bean Binding セクションにリストされているすべてのユースケースをサポートすることを目指しています。機能しない Bean バインディングシナリオがある場合は、問題の報告 をぜひお願いいたします。
5.3.2. @Consume
Camel Quarkus 2.0.0 以降、camel-quarkus-bean
アーティファクトにより @org.apache.camel.Consume
がサポートされるようになりました。Camel ドキュメントの Pojo Consuming セクションを参照してください。
次のようなクラスを宣言します。
import org.apache.camel.Consume; public class Foo { @Consume("activemq:cheese") public void onCheese(String name) { ... } }
すると、次の Camel ルートが
from("activemq:cheese").bean("foo1234", "onCheese")
自動的に作成されます。Camel Quarkus は @jakarta.inject.Singleton
と jakarta.inject.Named("foo1234")
を Bean クラスに暗黙的に追加します。1234
は完全修飾クラス名から取得されたハッシュコードです。Bean に何らかの CDI スコープ (@ApplicationScoped
など) または @Named("someName")
がすでに設定されている場合、それらは自動作成されたルートで使用されます。
第6章 可観測性
6.1. ヘルス/liveness チェック
ヘルス/liveness チェックは MicroProfile Health エクステンションでサポートされます。チェックは Camel Health API または Quarkus MicroProfile Health を使用して設定できます。
設定したチェックは、すべて標準の MicroProfile Health エンドポイント URL で利用できます。
6.2. メトリクス
Red Hat は、メトリクスを公開するための MicroProfile メトリクス を提供しています。
一部の基本的な Camel メトリクスは最初から提供されており、ルートに追加メトリクスを設定することで、このメトリクスを補完できます。
メトリックは、標準の Quarkus メトリックエンドポイントで利用できます。
第7章 ネイティブモード
ネイティブモードでのアプリケーションのコンパイルおよびテストについての詳細は、Quarkus アプリケーションのネイティブ実行可能ファイルへのコンパイル ガイドの ネイティブ実行可能ファイルの作成 を参照してください。
7.1. 文字エンコーディング
デフォルトでは、すべての Charset
がネイティブモードで利用できる訳ではありません。
Charset.defaultCharset(), US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16
アプリケーションでこのセットに含まれていないエンコーディングが必要な場合や、ネイティブモードで UnsupportedCharsetException
が出力された場合は、以下のエントリーを application.properties
に追加してください。
quarkus.native.add-all-charsets = true
Quarkus ドキュメントの quarkus.native.add-all-charsets も参照してください。
7.2. ロケール
デフォルトでは、JVM のデフォルトロケールのビルドのみがネイティブイメージに含まれます。Quarkus では、application.properties
を介してロケールを設定する方法を提供しているため、LANG
および LC_*
環境変数に依存する必要はありません。
quarkus.native.user-country=US quarkus.native.user-language=en
また、複数のロケールをネイティブイメージに組み込むことや、Mandel コマンドラインオプション -H:IncludeLocales=fr,en
、H:+IncludeAllLocales
、および -H:DefaultLocale=de
を使用してデフォルトのロケールを選択することもサポートされます。これらは、Quarkus quarkus.native.additional-build-args
プロパティーで設定できます。
7.3. ネイティブ実行可能ファイルへのリソースの埋め込み
ランタイム時に Class.getResource()
、Class.getResourceAsStream()
、ClassLoader.getResource()
、ClassLoader.getResourceAsStream()
などを介してアクセスされるリソースをネイティブ実行可能ファイルに組み込むには、明示的にリストする必要があります。
これは、以下のように application.properties
ファイルの Quarkus quarkus.native.resources.includes
および quarkus.native.resources.excludes
プロパティーを使用して実行できます。
quarkus.native.resources.includes = docs/*,images/* quarkus.native.resources.excludes = docs/ignored.adoc,images/ignored.png
上記の例では、docs/included.adoc
および images/included.png
という名前のリソースはネイティブ実行可能ファイルに組み込まれ、docs/ignored.adoc
および images/ignored.png
のリソースはネイティブ実行可能ファイルに組み込まれません。
resources.includes
および resources.excludes
は、どちらも Ant パススタイルの glob パターンのコンマ区切りリストです。
詳細は、Red Hat build of Apache Camel for Quarkus リファレンス を参照してください。
7.4. ネイティブモードでの onException 句の使用
Camel onException
処理 をネイティブモードで使用する場合、ユーザーはリフレクションの例外クラスを登録する必要があります。
たとえば、onException
処理のある camel コンテキストは以下のようになります。
onException(MyException.class).handled(true); from("direct:route-that-could-produce-my-exception").throw(MyException.class);
クラス mypackage.MyException
をリフレクション用に登録する必要があります。詳細は、リフレクション用のクラスの登録 を参照してください。
7.5. リフレクション用のクラスの登録
デフォルトでは、動的リフレクションはネイティブモードでは使用できません。リフレクションアクセスが必要なクラスは、コンパイル時にリフレクション用に登録する必要があります。
多くの場合、Quarkus エクステンションはリフレクションを必要とするクラスを検出して自動的に登録できるため、アプリケーション開発者は注意する必要はありません。
ただし、状況によっては、Quarkus のエクステンションはクラスの一部を見逃す場合があるため、アプリケーション開発者が登録を行う必要があります。これには 2 つの方法があります。
@io.quarkus.runtime.annotations.RegisterForReflection
アノテーションを使用して、使用するクラスを登録できます。targets
属性を使用してサードパーティークラスを登録することもできます。import io.quarkus.runtime.annotations.RegisterForReflection; @RegisterForReflection class MyClassAccessedReflectively { } @RegisterForReflection( targets = { org.third-party.Class1.class, org.third-party.Class2.class } ) class ReflectionRegistrations { }
application.properties
のquarkus.camel.native.reflection
オプションを使用します。quarkus.camel.native.reflection.include-patterns = org.apache.commons.lang3.tuple.* quarkus.camel.native.reflection.exclude-patterns = org.apache.commons.lang3.tuple.*Triple
このオプションが適切に機能するには、選択したクラスが含まれるアーティファクトに Jandex インデックス ('META-INF/jandex.idx') が含まれているか、選択したクラスが含まれるアーティファクトを、'application.properties' の 'quarkus.index-dependency.*' オプションを使用して、インデックス化のために登録する必要があります。以下に例を示します。
quarkus.index-dependency.commons-lang3.group-id = org.apache.commons quarkus.index-dependency.commons-lang3.artifact-id = commons-lang3
7.6. シリアル化のためのクラスの登録
quarkus.camel.native.reflection.serialization-enabled
を介してシリアル化サポートが要求されると、CamelSerializationProcessor.BASE_SERIALIZATION_CLASSES に一覧表示されているクラスは、シリアル化のために自動的に登録されます。
@RegisterForReflection(serialization = true)
を使用してさらに多くのクラスを登録できます。